]> git.saurik.com Git - wxWidgets.git/blob - src/common/datetime.cpp
Declare strptime when using MSL headers on OS X.
[wxWidgets.git] / src / common / datetime.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: wx/datetime.h
3 // Purpose: implementation of time/date related classes
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 11.05.99
7 // RCS-ID: $Id$
8 // Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // parts of code taken from sndcal library by Scott E. Lee:
10 //
11 // Copyright 1993-1995, Scott E. Lee, all rights reserved.
12 // Permission granted to use, copy, modify, distribute and sell
13 // so long as the above copyright and this permission statement
14 // are retained in all copies.
15 //
16 // Licence: wxWindows licence
17 ///////////////////////////////////////////////////////////////////////////////
18
19 // TODO: for $DEITY sake, someone please fix the #ifdef __WXWINCE__ everywhere,
20 // the proper way to do it is to implement (subset of) wxStrftime() for
21 // CE instead of this horror!!
22
23 /*
24 * Implementation notes:
25 *
26 * 1. the time is stored as a 64bit integer containing the signed number of
27 * milliseconds since Jan 1. 1970 (the Unix Epoch) - so it is always
28 * expressed in GMT.
29 *
30 * 2. the range is thus something about 580 million years, but due to current
31 * algorithms limitations, only dates from Nov 24, 4714BC are handled
32 *
33 * 3. standard ANSI C functions are used to do time calculations whenever
34 * possible, i.e. when the date is in the range Jan 1, 1970 to 2038
35 *
36 * 4. otherwise, the calculations are done by converting the date to/from JDN
37 * first (the range limitation mentioned above comes from here: the
38 * algorithm used by Scott E. Lee's code only works for positive JDNs, more
39 * or less)
40 *
41 * 5. the object constructed for the given DD-MM-YYYY HH:MM:SS corresponds to
42 * this moment in local time and may be converted to the object
43 * corresponding to the same date/time in another time zone by using
44 * ToTimezone()
45 *
46 * 6. the conversions to the current (or any other) timezone are done when the
47 * internal time representation is converted to the broken-down one in
48 * wxDateTime::Tm.
49 */
50
51 // ============================================================================
52 // declarations
53 // ============================================================================
54
55 // ----------------------------------------------------------------------------
56 // headers
57 // ----------------------------------------------------------------------------
58
59 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
60 #pragma implementation "datetime.h"
61 #endif
62
63 // For compilers that support precompilation, includes "wx.h".
64 #include "wx/wxprec.h"
65
66 #ifdef __BORLANDC__
67 #pragma hdrstop
68 #endif
69
70 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
71
72 #ifndef WX_PRECOMP
73 #include "wx/string.h"
74 #include "wx/log.h"
75 #endif // WX_PRECOMP
76
77 #include "wx/intl.h"
78 #include "wx/thread.h"
79 #include "wx/tokenzr.h"
80 #include "wx/module.h"
81
82 #include <ctype.h>
83
84 #ifdef __WINDOWS__
85 #include "wx/msw/wrapwin.h"
86 #include <winnls.h>
87 #ifndef __WXWINCE__
88 #include <locale.h>
89 #endif
90 #endif
91
92 #include "wx/datetime.h"
93 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
94
95 const long wxDateTime::TIME_T_FACTOR = 1000l;
96
97 #if wxUSE_EXTENDED_RTTI
98
99 template<> void wxStringReadValue(const wxString &s , wxDateTime &data )
100 {
101 data.ParseFormat(s,wxT("%Y-%m-%d %H:%M:%S")) ;
102 }
103
104 template<> void wxStringWriteValue(wxString &s , const wxDateTime &data )
105 {
106 s = data.Format(wxT("%Y-%m-%d %H:%M:%S")) ;
107 }
108
109 wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter<wxDateTime> , wxFromStringConverter<wxDateTime>)
110
111 #endif
112
113 //
114 // ----------------------------------------------------------------------------
115 // conditional compilation
116 // ----------------------------------------------------------------------------
117
118 #if defined(HAVE_STRPTIME) && defined(__GLIBC__) && \
119 ((__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0))
120 // glibc 2.0.7 strptime() is broken - the following snippet causes it to
121 // crash (instead of just failing):
122 //
123 // strncpy(buf, "Tue Dec 21 20:25:40 1999", 128);
124 // strptime(buf, "%x", &tm);
125 //
126 // so don't use it
127 #undef HAVE_STRPTIME
128 #endif // broken strptime()
129
130 #if defined(HAVE_STRPTIME) && defined(__DARWIN__) && defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
131 // configure detects strptime as linkable because it's in the OS X
132 // System library but MSL headers don't declare it.
133 char *strptime(const char *, const char *, struct tm *);
134 #endif
135
136 #if defined(__MWERKS__) && wxUSE_UNICODE
137 #include <wtime.h>
138 #endif
139
140 #if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM)
141 #if defined(__WXPALMOS__)
142 #define WX_GMTOFF_IN_TM
143 #elif defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
144 #define WX_TIMEZONE _timezone
145 #elif defined(__MWERKS__)
146 long wxmw_timezone = 28800;
147 #define WX_TIMEZONE wxmw_timezone
148 #elif defined(__DJGPP__) || defined(__WINE__)
149 #include <sys/timeb.h>
150 #include <values.h>
151 static long wxGetTimeZone()
152 {
153 static long timezone = MAXLONG; // invalid timezone
154 if (timezone == MAXLONG)
155 {
156 struct timeb tb;
157 ftime(&tb);
158 timezone = tb.timezone;
159 }
160 return timezone;
161 }
162 #define WX_TIMEZONE wxGetTimeZone()
163 #elif defined(__DARWIN__)
164 #define WX_GMTOFF_IN_TM
165 #else // unknown platform - try timezone
166 #define WX_TIMEZONE timezone
167 #endif
168 #endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
169
170 // ----------------------------------------------------------------------------
171 // macros
172 // ----------------------------------------------------------------------------
173
174 // debugging helper: just a convenient replacement of wxCHECK()
175 #define wxDATETIME_CHECK(expr, msg) \
176 if ( !(expr) ) \
177 { \
178 wxFAIL_MSG(msg); \
179 *this = wxInvalidDateTime; \
180 return *this; \
181 }
182
183 // ----------------------------------------------------------------------------
184 // private classes
185 // ----------------------------------------------------------------------------
186
187 class wxDateTimeHolidaysModule : public wxModule
188 {
189 public:
190 virtual bool OnInit()
191 {
192 wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays);
193
194 return true;
195 }
196
197 virtual void OnExit()
198 {
199 wxDateTimeHolidayAuthority::ClearAllAuthorities();
200 wxDateTimeHolidayAuthority::ms_authorities.clear();
201 }
202
203 private:
204 DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule)
205 };
206
207 IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule, wxModule)
208
209 // ----------------------------------------------------------------------------
210 // constants
211 // ----------------------------------------------------------------------------
212
213 // some trivial ones
214 static const int MONTHS_IN_YEAR = 12;
215
216 static const int SEC_PER_MIN = 60;
217
218 static const int MIN_PER_HOUR = 60;
219
220 static const int HOURS_PER_DAY = 24;
221
222 static const long SECONDS_PER_DAY = 86400l;
223
224 static const int DAYS_PER_WEEK = 7;
225
226 static const long MILLISECONDS_PER_DAY = 86400000l;
227
228 // this is the integral part of JDN of the midnight of Jan 1, 1970
229 // (i.e. JDN(Jan 1, 1970) = 2440587.5)
230 static const long EPOCH_JDN = 2440587l;
231
232 // the date of JDN -0.5 (as we don't work with fractional parts, this is the
233 // reference date for us) is Nov 24, 4714BC
234 static const int JDN_0_YEAR = -4713;
235 static const int JDN_0_MONTH = wxDateTime::Nov;
236 static const int JDN_0_DAY = 24;
237
238 // the constants used for JDN calculations
239 static const long JDN_OFFSET = 32046l;
240 static const long DAYS_PER_5_MONTHS = 153l;
241 static const long DAYS_PER_4_YEARS = 1461l;
242 static const long DAYS_PER_400_YEARS = 146097l;
243
244 // this array contains the cumulated number of days in all previous months for
245 // normal and leap years
246 static const wxDateTime::wxDateTime_t gs_cumulatedDays[2][MONTHS_IN_YEAR] =
247 {
248 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
249 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
250 };
251
252 // ----------------------------------------------------------------------------
253 // global data
254 // ----------------------------------------------------------------------------
255
256 const wxChar * wxDefaultDateTimeFormat = wxT("%c");
257 const wxChar * wxDefaultTimeSpanFormat = wxT("%H:%M:%S");
258
259 // in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
260 // indicate an invalid wxDateTime object
261 const wxDateTime wxDefaultDateTime;
262
263 wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown;
264
265 // ----------------------------------------------------------------------------
266 // private functions
267 // ----------------------------------------------------------------------------
268
269 // debugger helper: shows what the date really is
270 #ifdef __WXDEBUG__
271 extern const wxChar *wxDumpDate(const wxDateTime* dt)
272 {
273 static wxChar buf[128];
274
275 wxStrcpy(buf, dt->Format(_T("%Y-%m-%d (%a) %H:%M:%S")));
276
277 return buf;
278 }
279 #endif // Debug
280
281 // get the number of days in the given month of the given year
282 static inline
283 wxDateTime::wxDateTime_t GetNumOfDaysInMonth(int year, wxDateTime::Month month)
284 {
285 // the number of days in month in Julian/Gregorian calendar: the first line
286 // is for normal years, the second one is for the leap ones
287 static wxDateTime::wxDateTime_t daysInMonth[2][MONTHS_IN_YEAR] =
288 {
289 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
290 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
291 };
292
293 return daysInMonth[wxDateTime::IsLeapYear(year)][month];
294 }
295
296 // returns the time zone in the C sense, i.e. the difference UTC - local
297 // (in seconds)
298 static int GetTimeZone()
299 {
300 #ifdef WX_GMTOFF_IN_TM
301 // set to true when the timezone is set
302 static bool s_timezoneSet = false;
303 static long gmtoffset = LONG_MAX; // invalid timezone
304
305 // ensure that the timezone variable is set by calling localtime
306 if ( !s_timezoneSet )
307 {
308 // just call localtime() instead of figuring out whether this system
309 // supports tzset(), _tzset() or something else
310 time_t t = 0;
311 struct tm *tm;
312
313 tm = localtime(&t);
314 s_timezoneSet = true;
315
316 // note that GMT offset is the opposite of time zone and so to return
317 // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM
318 // cases we have to negate it
319 gmtoffset = -tm->tm_gmtoff;
320 }
321
322 return (int)gmtoffset;
323 #else // !WX_GMTOFF_IN_TM
324 return (int)WX_TIMEZONE;
325 #endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
326 }
327
328 // return the integral part of the JDN for the midnight of the given date (to
329 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
330 // noon of the previous day)
331 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day,
332 wxDateTime::Month mon,
333 int year)
334 {
335 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
336
337 // check the date validity
338 wxASSERT_MSG(
339 (year > JDN_0_YEAR) ||
340 ((year == JDN_0_YEAR) && (mon > JDN_0_MONTH)) ||
341 ((year == JDN_0_YEAR) && (mon == JDN_0_MONTH) && (day >= JDN_0_DAY)),
342 _T("date out of range - can't convert to JDN")
343 );
344
345 // make the year positive to avoid problems with negative numbers division
346 year += 4800;
347
348 // months are counted from March here
349 int month;
350 if ( mon >= wxDateTime::Mar )
351 {
352 month = mon - 2;
353 }
354 else
355 {
356 month = mon + 10;
357 year--;
358 }
359
360 // now we can simply add all the contributions together
361 return ((year / 100) * DAYS_PER_400_YEARS) / 4
362 + ((year % 100) * DAYS_PER_4_YEARS) / 4
363 + (month * DAYS_PER_5_MONTHS + 2) / 5
364 + day
365 - JDN_OFFSET;
366 }
367
368 #ifndef __WXWINCE__
369 // this function is a wrapper around strftime(3) adding error checking
370 static wxString CallStrftime(const wxChar *format, const tm* tm)
371 {
372 wxChar buf[4096];
373 if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) )
374 {
375 // buffer is too small?
376 wxFAIL_MSG(_T("strftime() failed"));
377 }
378
379 return wxString(buf);
380 }
381 #endif
382
383 #ifdef HAVE_STRPTIME
384
385 // glibc2 doesn't define this in the headers unless _XOPEN_SOURCE is defined
386 // which, unfortunately, wreaks havoc elsewhere
387 #if defined(__GLIBC__) && (__GLIBC__ == 2)
388 extern "C" char *strptime(const char *, const char *, struct tm *);
389 #endif
390
391 // Unicode-friendly strptime() wrapper
392 static const wxChar *
393 CallStrptime(const wxChar *input, const char *fmt, tm *tm)
394 {
395 // the problem here is that strptime() returns pointer into the string we
396 // passed to it while we're really interested in the pointer into the
397 // original, Unicode, string so we try to transform the pointer back
398 #if wxUSE_UNICODE
399 wxCharBuffer inputMB(wxConvertWX2MB(input));
400 #else // ASCII
401 const char * const inputMB = input;
402 #endif // Unicode/Ascii
403
404 const char *result = strptime(inputMB, fmt, tm);
405 if ( !result )
406 return NULL;
407
408 #if wxUSE_UNICODE
409 // FIXME: this is wrong in presence of surrogates &c
410 return input + (result - inputMB.data());
411 #else // ASCII
412 return result;
413 #endif // Unicode/Ascii
414 }
415
416 #endif // HAVE_STRPTIME
417
418 // if year and/or month have invalid values, replace them with the current ones
419 static void ReplaceDefaultYearMonthWithCurrent(int *year,
420 wxDateTime::Month *month)
421 {
422 struct tm *tmNow = NULL;
423
424 if ( *year == wxDateTime::Inv_Year )
425 {
426 tmNow = wxDateTime::GetTmNow();
427
428 *year = 1900 + tmNow->tm_year;
429 }
430
431 if ( *month == wxDateTime::Inv_Month )
432 {
433 if ( !tmNow )
434 tmNow = wxDateTime::GetTmNow();
435
436 *month = (wxDateTime::Month)tmNow->tm_mon;
437 }
438 }
439
440 // fll the struct tm with default values
441 static void InitTm(struct tm& tm)
442 {
443 // struct tm may have etxra fields (undocumented and with unportable
444 // names) which, nevertheless, must be set to 0
445 memset(&tm, 0, sizeof(struct tm));
446
447 tm.tm_mday = 1; // mday 0 is invalid
448 tm.tm_year = 76; // any valid year
449 tm.tm_isdst = -1; // auto determine
450 }
451
452 // parsing helpers
453 // ---------------
454
455 // return the month if the string is a month name or Inv_Month otherwise
456 static wxDateTime::Month GetMonthFromName(const wxString& name, int flags)
457 {
458 wxDateTime::Month mon;
459 for ( mon = wxDateTime::Jan; mon < wxDateTime::Inv_Month; wxNextMonth(mon) )
460 {
461 // case-insensitive comparison either one of or with both abbreviated
462 // and not versions
463 if ( flags & wxDateTime::Name_Full )
464 {
465 if ( name.CmpNoCase(wxDateTime::
466 GetMonthName(mon, wxDateTime::Name_Full)) == 0 )
467 {
468 break;
469 }
470 }
471
472 if ( flags & wxDateTime::Name_Abbr )
473 {
474 if ( name.CmpNoCase(wxDateTime::
475 GetMonthName(mon, wxDateTime::Name_Abbr)) == 0 )
476 {
477 break;
478 }
479 }
480 }
481
482 return mon;
483 }
484
485 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
486 static wxDateTime::WeekDay GetWeekDayFromName(const wxString& name, int flags)
487 {
488 wxDateTime::WeekDay wd;
489 for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) )
490 {
491 // case-insensitive comparison either one of or with both abbreviated
492 // and not versions
493 if ( flags & wxDateTime::Name_Full )
494 {
495 if ( name.CmpNoCase(wxDateTime::
496 GetWeekDayName(wd, wxDateTime::Name_Full)) == 0 )
497 {
498 break;
499 }
500 }
501
502 if ( flags & wxDateTime::Name_Abbr )
503 {
504 if ( name.CmpNoCase(wxDateTime::
505 GetWeekDayName(wd, wxDateTime::Name_Abbr)) == 0 )
506 {
507 break;
508 }
509 }
510 }
511
512 return wd;
513 }
514
515 // scans all digits (but no more than len) and returns the resulting number
516 static bool GetNumericToken(size_t len, const wxChar*& p, unsigned long *number)
517 {
518 size_t n = 1;
519 wxString s;
520 while ( wxIsdigit(*p) )
521 {
522 s += *p++;
523
524 if ( len && ++n > len )
525 break;
526 }
527
528 return !s.empty() && s.ToULong(number);
529 }
530
531 // scans all alphabetic characters and returns the resulting string
532 static wxString GetAlphaToken(const wxChar*& p)
533 {
534 wxString s;
535 while ( wxIsalpha(*p) )
536 {
537 s += *p++;
538 }
539
540 return s;
541 }
542
543 // ============================================================================
544 // implementation of wxDateTime
545 // ============================================================================
546
547 // ----------------------------------------------------------------------------
548 // struct Tm
549 // ----------------------------------------------------------------------------
550
551 wxDateTime::Tm::Tm()
552 {
553 year = (wxDateTime_t)wxDateTime::Inv_Year;
554 mon = wxDateTime::Inv_Month;
555 mday = 0;
556 hour = min = sec = msec = 0;
557 wday = wxDateTime::Inv_WeekDay;
558 }
559
560 wxDateTime::Tm::Tm(const struct tm& tm, const TimeZone& tz)
561 : m_tz(tz)
562 {
563 msec = 0;
564 sec = (wxDateTime::wxDateTime_t)tm.tm_sec;
565 min = (wxDateTime::wxDateTime_t)tm.tm_min;
566 hour = (wxDateTime::wxDateTime_t)tm.tm_hour;
567 mday = (wxDateTime::wxDateTime_t)tm.tm_mday;
568 mon = (wxDateTime::Month)tm.tm_mon;
569 year = 1900 + tm.tm_year;
570 wday = (wxDateTime::wxDateTime_t)tm.tm_wday;
571 yday = (wxDateTime::wxDateTime_t)tm.tm_yday;
572 }
573
574 bool wxDateTime::Tm::IsValid() const
575 {
576 // we allow for the leap seconds, although we don't use them (yet)
577 return (year != wxDateTime::Inv_Year) && (mon != wxDateTime::Inv_Month) &&
578 (mday <= GetNumOfDaysInMonth(year, mon)) &&
579 (hour < 24) && (min < 60) && (sec < 62) && (msec < 1000);
580 }
581
582 void wxDateTime::Tm::ComputeWeekDay()
583 {
584 // compute the week day from day/month/year: we use the dumbest algorithm
585 // possible: just compute our JDN and then use the (simple to derive)
586 // formula: weekday = (JDN + 1.5) % 7
587 wday = (wxDateTime::wxDateTime_t)((wxDateTime::WeekDay)(GetTruncatedJDN(mday, mon, year) + 2) % 7);
588 }
589
590 void wxDateTime::Tm::AddMonths(int monDiff)
591 {
592 // normalize the months field
593 while ( monDiff < -mon )
594 {
595 year--;
596
597 monDiff += MONTHS_IN_YEAR;
598 }
599
600 while ( monDiff + mon >= MONTHS_IN_YEAR )
601 {
602 year++;
603
604 monDiff -= MONTHS_IN_YEAR;
605 }
606
607 mon = (wxDateTime::Month)(mon + monDiff);
608
609 wxASSERT_MSG( mon >= 0 && mon < MONTHS_IN_YEAR, _T("logic error") );
610
611 // NB: we don't check here that the resulting date is valid, this function
612 // is private and the caller must check it if needed
613 }
614
615 void wxDateTime::Tm::AddDays(int dayDiff)
616 {
617 // normalize the days field
618 while ( dayDiff + mday < 1 )
619 {
620 AddMonths(-1);
621
622 dayDiff += GetNumOfDaysInMonth(year, mon);
623 }
624
625 mday = (wxDateTime::wxDateTime_t)( mday + dayDiff );
626 while ( mday > GetNumOfDaysInMonth(year, mon) )
627 {
628 mday -= GetNumOfDaysInMonth(year, mon);
629
630 AddMonths(1);
631 }
632
633 wxASSERT_MSG( mday > 0 && mday <= GetNumOfDaysInMonth(year, mon),
634 _T("logic error") );
635 }
636
637 // ----------------------------------------------------------------------------
638 // class TimeZone
639 // ----------------------------------------------------------------------------
640
641 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz)
642 {
643 switch ( tz )
644 {
645 case wxDateTime::Local:
646 // get the offset from C RTL: it returns the difference GMT-local
647 // while we want to have the offset _from_ GMT, hence the '-'
648 m_offset = -GetTimeZone();
649 break;
650
651 case wxDateTime::GMT_12:
652 case wxDateTime::GMT_11:
653 case wxDateTime::GMT_10:
654 case wxDateTime::GMT_9:
655 case wxDateTime::GMT_8:
656 case wxDateTime::GMT_7:
657 case wxDateTime::GMT_6:
658 case wxDateTime::GMT_5:
659 case wxDateTime::GMT_4:
660 case wxDateTime::GMT_3:
661 case wxDateTime::GMT_2:
662 case wxDateTime::GMT_1:
663 m_offset = -3600*(wxDateTime::GMT0 - tz);
664 break;
665
666 case wxDateTime::GMT0:
667 case wxDateTime::GMT1:
668 case wxDateTime::GMT2:
669 case wxDateTime::GMT3:
670 case wxDateTime::GMT4:
671 case wxDateTime::GMT5:
672 case wxDateTime::GMT6:
673 case wxDateTime::GMT7:
674 case wxDateTime::GMT8:
675 case wxDateTime::GMT9:
676 case wxDateTime::GMT10:
677 case wxDateTime::GMT11:
678 case wxDateTime::GMT12:
679 m_offset = 3600*(tz - wxDateTime::GMT0);
680 break;
681
682 case wxDateTime::A_CST:
683 // Central Standard Time in use in Australia = UTC + 9.5
684 m_offset = 60l*(9*60 + 30);
685 break;
686
687 default:
688 wxFAIL_MSG( _T("unknown time zone") );
689 }
690 }
691
692 // ----------------------------------------------------------------------------
693 // static functions
694 // ----------------------------------------------------------------------------
695
696 /* static */
697 bool wxDateTime::IsLeapYear(int year, wxDateTime::Calendar cal)
698 {
699 if ( year == Inv_Year )
700 year = GetCurrentYear();
701
702 if ( cal == Gregorian )
703 {
704 // in Gregorian calendar leap years are those divisible by 4 except
705 // those divisible by 100 unless they're also divisible by 400
706 // (in some countries, like Russia and Greece, additional corrections
707 // exist, but they won't manifest themselves until 2700)
708 return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
709 }
710 else if ( cal == Julian )
711 {
712 // in Julian calendar the rule is simpler
713 return year % 4 == 0;
714 }
715 else
716 {
717 wxFAIL_MSG(_T("unknown calendar"));
718
719 return false;
720 }
721 }
722
723 /* static */
724 int wxDateTime::GetCentury(int year)
725 {
726 return year > 0 ? year / 100 : year / 100 - 1;
727 }
728
729 /* static */
730 int wxDateTime::ConvertYearToBC(int year)
731 {
732 // year 0 is BC 1
733 return year > 0 ? year : year - 1;
734 }
735
736 /* static */
737 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal)
738 {
739 switch ( cal )
740 {
741 case Gregorian:
742 return Now().GetYear();
743
744 case Julian:
745 wxFAIL_MSG(_T("TODO"));
746 break;
747
748 default:
749 wxFAIL_MSG(_T("unsupported calendar"));
750 break;
751 }
752
753 return Inv_Year;
754 }
755
756 /* static */
757 wxDateTime::Month wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal)
758 {
759 switch ( cal )
760 {
761 case Gregorian:
762 return Now().GetMonth();
763
764 case Julian:
765 wxFAIL_MSG(_T("TODO"));
766 break;
767
768 default:
769 wxFAIL_MSG(_T("unsupported calendar"));
770 break;
771 }
772
773 return Inv_Month;
774 }
775
776 /* static */
777 wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(int year, Calendar cal)
778 {
779 if ( year == Inv_Year )
780 {
781 // take the current year if none given
782 year = GetCurrentYear();
783 }
784
785 switch ( cal )
786 {
787 case Gregorian:
788 case Julian:
789 return IsLeapYear(year) ? 366 : 365;
790
791 default:
792 wxFAIL_MSG(_T("unsupported calendar"));
793 break;
794 }
795
796 return 0;
797 }
798
799 /* static */
800 wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(wxDateTime::Month month,
801 int year,
802 wxDateTime::Calendar cal)
803 {
804 wxCHECK_MSG( month < MONTHS_IN_YEAR, 0, _T("invalid month") );
805
806 if ( cal == Gregorian || cal == Julian )
807 {
808 if ( year == Inv_Year )
809 {
810 // take the current year if none given
811 year = GetCurrentYear();
812 }
813
814 return GetNumOfDaysInMonth(year, month);
815 }
816 else
817 {
818 wxFAIL_MSG(_T("unsupported calendar"));
819
820 return 0;
821 }
822 }
823
824 /* static */
825 wxString wxDateTime::GetMonthName(wxDateTime::Month month,
826 wxDateTime::NameFlags flags)
827 {
828 wxCHECK_MSG( month != Inv_Month, wxEmptyString, _T("invalid month") );
829 #ifndef __WXWINCE__
830 // notice that we must set all the fields to avoid confusing libc (GNU one
831 // gets confused to a crash if we don't do this)
832 tm tm;
833 InitTm(tm);
834 tm.tm_mon = month;
835
836 return CallStrftime(flags == Name_Abbr ? _T("%b") : _T("%B"), &tm);
837 #else
838 wxString ret;
839 switch(month)
840 {
841 case Jan:
842 ret = (flags == Name_Abbr ? wxT("Jan"): wxT("January"));
843 break;
844 case Feb:
845 ret = (flags == Name_Abbr ? wxT("Feb"): wxT("Febuary"));
846 break;
847 case Mar:
848 ret = (flags == Name_Abbr ? wxT("Mar"): wxT("March"));
849 break;
850 case Apr:
851 ret = (flags == Name_Abbr ? wxT("Apr"): wxT("April"));
852 break;
853 case May:
854 ret = (flags == Name_Abbr ? wxT("May"): wxT("May"));
855 break;
856 case Jun:
857 ret = (flags == Name_Abbr ? wxT("Jun"): wxT("June"));
858 break;
859 case Jul:
860 ret = (flags == Name_Abbr ? wxT("Jul"): wxT("July"));
861 break;
862 case Aug:
863 ret = (flags == Name_Abbr ? wxT("Aug"): wxT("August"));
864 break;
865 case Sep:
866 ret = (flags == Name_Abbr ? wxT("Sep"): wxT("September"));
867 break;
868 case Oct:
869 ret = (flags == Name_Abbr ? wxT("Oct"): wxT("October"));
870 break;
871 case Nov:
872 ret = (flags == Name_Abbr ? wxT("Nov"): wxT("November"));
873 break;
874 case Dec:
875 ret = (flags == Name_Abbr ? wxT("Dec"): wxT("December"));
876 break;
877 }
878 return ret;
879 #endif
880 }
881
882 /* static */
883 wxString wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday,
884 wxDateTime::NameFlags flags)
885 {
886 wxCHECK_MSG( wday != Inv_WeekDay, wxEmptyString, _T("invalid weekday") );
887 #ifndef __WXWINCE__
888 // take some arbitrary Sunday (but notice that the day should be such that
889 // after adding wday to it below we still have a valid date, e.g. don't
890 // take 28 here!)
891 tm tm;
892 InitTm(tm);
893 tm.tm_mday = 21;
894 tm.tm_mon = Nov;
895 tm.tm_year = 99;
896
897 // and offset it by the number of days needed to get the correct wday
898 tm.tm_mday += wday;
899
900 // call mktime() to normalize it...
901 (void)mktime(&tm);
902
903 // ... and call strftime()
904 return CallStrftime(flags == Name_Abbr ? _T("%a") : _T("%A"), &tm);
905 #else
906 wxString ret;
907 switch(wday)
908 {
909 case Sun:
910 ret = (flags == Name_Abbr ? wxT("Sun") : wxT("Sunday"));
911 break;
912 case Mon:
913 ret = (flags == Name_Abbr ? wxT("Mon") : wxT("Monday"));
914 break;
915 case Tue:
916 ret = (flags == Name_Abbr ? wxT("Tue") : wxT("Tuesday"));
917 break;
918 case Wed:
919 ret = (flags == Name_Abbr ? wxT("Wed") : wxT("Wednesday"));
920 break;
921 case Thu:
922 ret = (flags == Name_Abbr ? wxT("Thu") : wxT("Thursday"));
923 break;
924 case Fri:
925 ret = (flags == Name_Abbr ? wxT("Fri") : wxT("Friday"));
926 break;
927 case Sat:
928 ret = (flags == Name_Abbr ? wxT("Sat") : wxT("Saturday"));
929 break;
930 }
931 return ret;
932
933 #endif
934 }
935
936 /* static */
937 void wxDateTime::GetAmPmStrings(wxString *am, wxString *pm)
938 {
939 tm tm;
940 InitTm(tm);
941 wxChar buffer[64];
942 // @Note: Do not call 'CallStrftime' here! CallStrftime checks the return code
943 // and causes an assertion failed if the buffer is to small (which is good) - OR -
944 // if strftime does not return anything because the format string is invalid - OR -
945 // if there are no 'am' / 'pm' tokens defined for the current locale (which is not good).
946 // wxDateTime::ParseTime will try several different formats to parse the time.
947 // As a result, GetAmPmStrings might get called, even if the current locale
948 // does not define any 'am' / 'pm' tokens. In this case, wxStrftime would
949 // assert, even though it is a perfectly legal use.
950 if ( am )
951 {
952 if (wxStrftime(buffer, sizeof(buffer)/sizeof(wxChar), _T("%p"), &tm) > 0)
953 *am = wxString(buffer);
954 else
955 *am = wxString();
956 }
957 if ( pm )
958 {
959 tm.tm_hour = 13;
960 if (wxStrftime(buffer, sizeof(buffer)/sizeof(wxChar), _T("%p"), &tm) > 0)
961 *pm = wxString(buffer);
962 else
963 *pm = wxString();
964 }
965 }
966
967 // ----------------------------------------------------------------------------
968 // Country stuff: date calculations depend on the country (DST, work days,
969 // ...), so we need to know which rules to follow.
970 // ----------------------------------------------------------------------------
971
972 /* static */
973 wxDateTime::Country wxDateTime::GetCountry()
974 {
975 // TODO use LOCALE_ICOUNTRY setting under Win32
976 #ifndef __WXWINCE__
977 if ( ms_country == Country_Unknown )
978 {
979 // try to guess from the time zone name
980 time_t t = time(NULL);
981 struct tm *tm = localtime(&t);
982
983 wxString tz = CallStrftime(_T("%Z"), tm);
984 if ( tz == _T("WET") || tz == _T("WEST") )
985 {
986 ms_country = UK;
987 }
988 else if ( tz == _T("CET") || tz == _T("CEST") )
989 {
990 ms_country = Country_EEC;
991 }
992 else if ( tz == _T("MSK") || tz == _T("MSD") )
993 {
994 ms_country = Russia;
995 }
996 else if ( tz == _T("AST") || tz == _T("ADT") ||
997 tz == _T("EST") || tz == _T("EDT") ||
998 tz == _T("CST") || tz == _T("CDT") ||
999 tz == _T("MST") || tz == _T("MDT") ||
1000 tz == _T("PST") || tz == _T("PDT") )
1001 {
1002 ms_country = USA;
1003 }
1004 else
1005 {
1006 // well, choose a default one
1007 ms_country = USA;
1008 }
1009 }
1010 #else
1011 ms_country = USA;
1012 #endif
1013
1014 return ms_country;
1015 }
1016
1017 /* static */
1018 void wxDateTime::SetCountry(wxDateTime::Country country)
1019 {
1020 ms_country = country;
1021 }
1022
1023 /* static */
1024 bool wxDateTime::IsWestEuropeanCountry(Country country)
1025 {
1026 if ( country == Country_Default )
1027 {
1028 country = GetCountry();
1029 }
1030
1031 return (Country_WesternEurope_Start <= country) &&
1032 (country <= Country_WesternEurope_End);
1033 }
1034
1035 // ----------------------------------------------------------------------------
1036 // DST calculations: we use 3 different rules for the West European countries,
1037 // USA and for the rest of the world. This is undoubtedly false for many
1038 // countries, but I lack the necessary info (and the time to gather it),
1039 // please add the other rules here!
1040 // ----------------------------------------------------------------------------
1041
1042 /* static */
1043 bool wxDateTime::IsDSTApplicable(int year, Country country)
1044 {
1045 if ( year == Inv_Year )
1046 {
1047 // take the current year if none given
1048 year = GetCurrentYear();
1049 }
1050
1051 if ( country == Country_Default )
1052 {
1053 country = GetCountry();
1054 }
1055
1056 switch ( country )
1057 {
1058 case USA:
1059 case UK:
1060 // DST was first observed in the US and UK during WWI, reused
1061 // during WWII and used again since 1966
1062 return year >= 1966 ||
1063 (year >= 1942 && year <= 1945) ||
1064 (year == 1918 || year == 1919);
1065
1066 default:
1067 // assume that it started after WWII
1068 return year > 1950;
1069 }
1070 }
1071
1072 /* static */
1073 wxDateTime wxDateTime::GetBeginDST(int year, Country country)
1074 {
1075 if ( year == Inv_Year )
1076 {
1077 // take the current year if none given
1078 year = GetCurrentYear();
1079 }
1080
1081 if ( country == Country_Default )
1082 {
1083 country = GetCountry();
1084 }
1085
1086 if ( !IsDSTApplicable(year, country) )
1087 {
1088 return wxInvalidDateTime;
1089 }
1090
1091 wxDateTime dt;
1092
1093 if ( IsWestEuropeanCountry(country) || (country == Russia) )
1094 {
1095 // DST begins at 1 a.m. GMT on the last Sunday of March
1096 if ( !dt.SetToLastWeekDay(Sun, Mar, year) )
1097 {
1098 // weird...
1099 wxFAIL_MSG( _T("no last Sunday in March?") );
1100 }
1101
1102 dt += wxTimeSpan::Hours(1);
1103
1104 // disable DST tests because it could result in an infinite recursion!
1105 dt.MakeGMT(true);
1106 }
1107 else switch ( country )
1108 {
1109 case USA:
1110 switch ( year )
1111 {
1112 case 1918:
1113 case 1919:
1114 // don't know for sure - assume it was in effect all year
1115
1116 case 1943:
1117 case 1944:
1118 case 1945:
1119 dt.Set(1, Jan, year);
1120 break;
1121
1122 case 1942:
1123 // DST was installed Feb 2, 1942 by the Congress
1124 dt.Set(2, Feb, year);
1125 break;
1126
1127 // Oil embargo changed the DST period in the US
1128 case 1974:
1129 dt.Set(6, Jan, 1974);
1130 break;
1131
1132 case 1975:
1133 dt.Set(23, Feb, 1975);
1134 break;
1135
1136 default:
1137 // before 1986, DST begun on the last Sunday of April, but
1138 // in 1986 Reagan changed it to begin at 2 a.m. of the
1139 // first Sunday in April
1140 if ( year < 1986 )
1141 {
1142 if ( !dt.SetToLastWeekDay(Sun, Apr, year) )
1143 {
1144 // weird...
1145 wxFAIL_MSG( _T("no first Sunday in April?") );
1146 }
1147 }
1148 else
1149 {
1150 if ( !dt.SetToWeekDay(Sun, 1, Apr, year) )
1151 {
1152 // weird...
1153 wxFAIL_MSG( _T("no first Sunday in April?") );
1154 }
1155 }
1156
1157 dt += wxTimeSpan::Hours(2);
1158
1159 // TODO what about timezone??
1160 }
1161
1162 break;
1163
1164 default:
1165 // assume Mar 30 as the start of the DST for the rest of the world
1166 // - totally bogus, of course
1167 dt.Set(30, Mar, year);
1168 }
1169
1170 return dt;
1171 }
1172
1173 /* static */
1174 wxDateTime wxDateTime::GetEndDST(int year, Country country)
1175 {
1176 if ( year == Inv_Year )
1177 {
1178 // take the current year if none given
1179 year = GetCurrentYear();
1180 }
1181
1182 if ( country == Country_Default )
1183 {
1184 country = GetCountry();
1185 }
1186
1187 if ( !IsDSTApplicable(year, country) )
1188 {
1189 return wxInvalidDateTime;
1190 }
1191
1192 wxDateTime dt;
1193
1194 if ( IsWestEuropeanCountry(country) || (country == Russia) )
1195 {
1196 // DST ends at 1 a.m. GMT on the last Sunday of October
1197 if ( !dt.SetToLastWeekDay(Sun, Oct, year) )
1198 {
1199 // weirder and weirder...
1200 wxFAIL_MSG( _T("no last Sunday in October?") );
1201 }
1202
1203 dt += wxTimeSpan::Hours(1);
1204
1205 // disable DST tests because it could result in an infinite recursion!
1206 dt.MakeGMT(true);
1207 }
1208 else switch ( country )
1209 {
1210 case USA:
1211 switch ( year )
1212 {
1213 case 1918:
1214 case 1919:
1215 // don't know for sure - assume it was in effect all year
1216
1217 case 1943:
1218 case 1944:
1219 dt.Set(31, Dec, year);
1220 break;
1221
1222 case 1945:
1223 // the time was reset after the end of the WWII
1224 dt.Set(30, Sep, year);
1225 break;
1226
1227 default:
1228 // DST ends at 2 a.m. on the last Sunday of October
1229 if ( !dt.SetToLastWeekDay(Sun, Oct, year) )
1230 {
1231 // weirder and weirder...
1232 wxFAIL_MSG( _T("no last Sunday in October?") );
1233 }
1234
1235 dt += wxTimeSpan::Hours(2);
1236
1237 // TODO what about timezone??
1238 }
1239 break;
1240
1241 default:
1242 // assume October 26th as the end of the DST - totally bogus too
1243 dt.Set(26, Oct, year);
1244 }
1245
1246 return dt;
1247 }
1248
1249 // ----------------------------------------------------------------------------
1250 // constructors and assignment operators
1251 // ----------------------------------------------------------------------------
1252
1253 // return the current time with ms precision
1254 /* static */ wxDateTime wxDateTime::UNow()
1255 {
1256 return wxDateTime(wxGetLocalTimeMillis());
1257 }
1258
1259 // the values in the tm structure contain the local time
1260 wxDateTime& wxDateTime::Set(const struct tm& tm)
1261 {
1262 struct tm tm2(tm);
1263 time_t timet = mktime(&tm2);
1264
1265 if ( timet == (time_t)-1 )
1266 {
1267 // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
1268 // less than timezone - try to make it work for this case
1269 if ( tm2.tm_year == 70 && tm2.tm_mon == 0 && tm2.tm_mday == 1 )
1270 {
1271 return Set((time_t)(
1272 GetTimeZone() +
1273 tm2.tm_hour * MIN_PER_HOUR * SEC_PER_MIN +
1274 tm2.tm_min * SEC_PER_MIN +
1275 tm2.tm_sec));
1276 }
1277
1278 wxFAIL_MSG( _T("mktime() failed") );
1279
1280 *this = wxInvalidDateTime;
1281
1282 return *this;
1283 }
1284 else
1285 {
1286 return Set(timet);
1287 }
1288 }
1289
1290 wxDateTime& wxDateTime::Set(wxDateTime_t hour,
1291 wxDateTime_t minute,
1292 wxDateTime_t second,
1293 wxDateTime_t millisec)
1294 {
1295 // we allow seconds to be 61 to account for the leap seconds, even if we
1296 // don't use them really
1297 wxDATETIME_CHECK( hour < 24 &&
1298 second < 62 &&
1299 minute < 60 &&
1300 millisec < 1000,
1301 _T("Invalid time in wxDateTime::Set()") );
1302
1303 // get the current date from system
1304 struct tm *tm = GetTmNow();
1305
1306 wxDATETIME_CHECK( tm, _T("localtime() failed") );
1307
1308 // make a copy so it isn't clobbered by the call to mktime() below
1309 struct tm tm1(*tm);
1310
1311 // adjust the time
1312 tm1.tm_hour = hour;
1313 tm1.tm_min = minute;
1314 tm1.tm_sec = second;
1315
1316 // and the DST in case it changes on this date
1317 struct tm tm2(tm1);
1318 mktime(&tm2);
1319 if ( tm2.tm_isdst != tm1.tm_isdst )
1320 tm1.tm_isdst = tm2.tm_isdst;
1321
1322 (void)Set(tm1);
1323
1324 // and finally adjust milliseconds
1325 return SetMillisecond(millisec);
1326 }
1327
1328 wxDateTime& wxDateTime::Set(wxDateTime_t day,
1329 Month month,
1330 int year,
1331 wxDateTime_t hour,
1332 wxDateTime_t minute,
1333 wxDateTime_t second,
1334 wxDateTime_t millisec)
1335 {
1336 wxDATETIME_CHECK( hour < 24 &&
1337 second < 62 &&
1338 minute < 60 &&
1339 millisec < 1000,
1340 _T("Invalid time in wxDateTime::Set()") );
1341
1342 ReplaceDefaultYearMonthWithCurrent(&year, &month);
1343
1344 wxDATETIME_CHECK( (0 < day) && (day <= GetNumberOfDays(month, year)),
1345 _T("Invalid date in wxDateTime::Set()") );
1346
1347 // the range of time_t type (inclusive)
1348 static const int yearMinInRange = 1970;
1349 static const int yearMaxInRange = 2037;
1350
1351 // test only the year instead of testing for the exact end of the Unix
1352 // time_t range - it doesn't bring anything to do more precise checks
1353 if ( year >= yearMinInRange && year <= yearMaxInRange )
1354 {
1355 // use the standard library version if the date is in range - this is
1356 // probably more efficient than our code
1357 struct tm tm;
1358 tm.tm_year = year - 1900;
1359 tm.tm_mon = month;
1360 tm.tm_mday = day;
1361 tm.tm_hour = hour;
1362 tm.tm_min = minute;
1363 tm.tm_sec = second;
1364 tm.tm_isdst = -1; // mktime() will guess it
1365
1366 (void)Set(tm);
1367
1368 // and finally adjust milliseconds
1369 if (IsValid())
1370 SetMillisecond(millisec);
1371
1372 return *this;
1373 }
1374 else
1375 {
1376 // do time calculations ourselves: we want to calculate the number of
1377 // milliseconds between the given date and the epoch
1378
1379 // get the JDN for the midnight of this day
1380 m_time = GetTruncatedJDN(day, month, year);
1381 m_time -= EPOCH_JDN;
1382 m_time *= SECONDS_PER_DAY * TIME_T_FACTOR;
1383
1384 // JDN corresponds to GMT, we take localtime
1385 Add(wxTimeSpan(hour, minute, second + GetTimeZone(), millisec));
1386 }
1387
1388 return *this;
1389 }
1390
1391 wxDateTime& wxDateTime::Set(double jdn)
1392 {
1393 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1394 // EPOCH_JDN + 0.5
1395 jdn -= EPOCH_JDN + 0.5;
1396
1397 jdn *= MILLISECONDS_PER_DAY;
1398
1399 m_time.Assign(jdn);
1400
1401 // JDNs always suppose an UTC date, so bring it back to local time zone
1402 // (also see GetJulianDayNumber() implementation)
1403 long tzDiff = GetTimeZone();
1404 if ( IsDST() == 1 )
1405 {
1406 // FIXME: again, we suppose that DST is always one hour
1407 tzDiff -= 3600;
1408 }
1409
1410 m_time += tzDiff*1000; // tzDiff is in seconds
1411
1412 return *this;
1413 }
1414
1415 wxDateTime& wxDateTime::ResetTime()
1416 {
1417 Tm tm = GetTm();
1418
1419 if ( tm.hour || tm.min || tm.sec || tm.msec )
1420 {
1421 tm.msec =
1422 tm.sec =
1423 tm.min =
1424 tm.hour = 0;
1425
1426 Set(tm);
1427 }
1428
1429 return *this;
1430 }
1431
1432 // ----------------------------------------------------------------------------
1433 // DOS Date and Time Format functions
1434 // ----------------------------------------------------------------------------
1435 // the dos date and time value is an unsigned 32 bit value in the format:
1436 // YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
1437 //
1438 // Y = year offset from 1980 (0-127)
1439 // M = month (1-12)
1440 // D = day of month (1-31)
1441 // h = hour (0-23)
1442 // m = minute (0-59)
1443 // s = bisecond (0-29) each bisecond indicates two seconds
1444 // ----------------------------------------------------------------------------
1445
1446 wxDateTime& wxDateTime::SetFromDOS(unsigned long ddt)
1447 {
1448 struct tm tm;
1449 InitTm(tm);
1450
1451 long year = ddt & 0xFE000000;
1452 year >>= 25;
1453 year += 80;
1454 tm.tm_year = year;
1455
1456 long month = ddt & 0x1E00000;
1457 month >>= 21;
1458 month -= 1;
1459 tm.tm_mon = month;
1460
1461 long day = ddt & 0x1F0000;
1462 day >>= 16;
1463 tm.tm_mday = day;
1464
1465 long hour = ddt & 0xF800;
1466 hour >>= 11;
1467 tm.tm_hour = hour;
1468
1469 long minute = ddt & 0x7E0;
1470 minute >>= 5;
1471 tm.tm_min = minute;
1472
1473 long second = ddt & 0x1F;
1474 tm.tm_sec = second * 2;
1475
1476 return Set(mktime(&tm));
1477 }
1478
1479 unsigned long wxDateTime::GetAsDOS() const
1480 {
1481 unsigned long ddt;
1482 time_t ticks = GetTicks();
1483 struct tm *tm = localtime(&ticks);
1484
1485 long year = tm->tm_year;
1486 year -= 80;
1487 year <<= 25;
1488
1489 long month = tm->tm_mon;
1490 month += 1;
1491 month <<= 21;
1492
1493 long day = tm->tm_mday;
1494 day <<= 16;
1495
1496 long hour = tm->tm_hour;
1497 hour <<= 11;
1498
1499 long minute = tm->tm_min;
1500 minute <<= 5;
1501
1502 long second = tm->tm_sec;
1503 second /= 2;
1504
1505 ddt = year | month | day | hour | minute | second;
1506 return ddt;
1507 }
1508
1509 // ----------------------------------------------------------------------------
1510 // time_t <-> broken down time conversions
1511 // ----------------------------------------------------------------------------
1512
1513 wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
1514 {
1515 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1516
1517 time_t time = GetTicks();
1518 if ( time != (time_t)-1 )
1519 {
1520 // use C RTL functions
1521 tm *tm;
1522 if ( tz.GetOffset() == -GetTimeZone() )
1523 {
1524 // we are working with local time
1525 tm = localtime(&time);
1526
1527 // should never happen
1528 wxCHECK_MSG( tm, Tm(), _T("localtime() failed") );
1529 }
1530 else
1531 {
1532 time += (time_t)tz.GetOffset();
1533 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
1534 int time2 = (int) time;
1535 if ( time2 >= 0 )
1536 #else
1537 if ( time >= 0 )
1538 #endif
1539 {
1540 tm = gmtime(&time);
1541
1542 // should never happen
1543 wxCHECK_MSG( tm, Tm(), _T("gmtime() failed") );
1544 }
1545 else
1546 {
1547 tm = (struct tm *)NULL;
1548 }
1549 }
1550
1551 if ( tm )
1552 {
1553 // adjust the milliseconds
1554 Tm tm2(*tm, tz);
1555 long timeOnly = (m_time % MILLISECONDS_PER_DAY).ToLong();
1556 tm2.msec = (wxDateTime_t)(timeOnly % 1000);
1557 return tm2;
1558 }
1559 //else: use generic code below
1560 }
1561
1562 // remember the time and do the calculations with the date only - this
1563 // eliminates rounding errors of the floating point arithmetics
1564
1565 wxLongLong timeMidnight = m_time + tz.GetOffset() * 1000;
1566
1567 long timeOnly = (timeMidnight % MILLISECONDS_PER_DAY).ToLong();
1568
1569 // we want to always have positive time and timeMidnight to be really
1570 // the midnight before it
1571 if ( timeOnly < 0 )
1572 {
1573 timeOnly = MILLISECONDS_PER_DAY + timeOnly;
1574 }
1575
1576 timeMidnight -= timeOnly;
1577
1578 // calculate the Gregorian date from JDN for the midnight of our date:
1579 // this will yield day, month (in 1..12 range) and year
1580
1581 // actually, this is the JDN for the noon of the previous day
1582 long jdn = (timeMidnight / MILLISECONDS_PER_DAY).ToLong() + EPOCH_JDN;
1583
1584 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1585
1586 wxASSERT_MSG( jdn > -2, _T("JDN out of range") );
1587
1588 // calculate the century
1589 long temp = (jdn + JDN_OFFSET) * 4 - 1;
1590 long century = temp / DAYS_PER_400_YEARS;
1591
1592 // then the year and day of year (1 <= dayOfYear <= 366)
1593 temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3;
1594 long year = (century * 100) + (temp / DAYS_PER_4_YEARS);
1595 long dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
1596
1597 // and finally the month and day of the month
1598 temp = dayOfYear * 5 - 3;
1599 long month = temp / DAYS_PER_5_MONTHS;
1600 long day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
1601
1602 // month is counted from March - convert to normal
1603 if ( month < 10 )
1604 {
1605 month += 3;
1606 }
1607 else
1608 {
1609 year += 1;
1610 month -= 9;
1611 }
1612
1613 // year is offset by 4800
1614 year -= 4800;
1615
1616 // check that the algorithm gave us something reasonable
1617 wxASSERT_MSG( (0 < month) && (month <= 12), _T("invalid month") );
1618 wxASSERT_MSG( (1 <= day) && (day < 32), _T("invalid day") );
1619
1620 // construct Tm from these values
1621 Tm tm;
1622 tm.year = (int)year;
1623 tm.mon = (Month)(month - 1); // algorithm yields 1 for January, not 0
1624 tm.mday = (wxDateTime_t)day;
1625 tm.msec = (wxDateTime_t)(timeOnly % 1000);
1626 timeOnly -= tm.msec;
1627 timeOnly /= 1000; // now we have time in seconds
1628
1629 tm.sec = (wxDateTime_t)(timeOnly % 60);
1630 timeOnly -= tm.sec;
1631 timeOnly /= 60; // now we have time in minutes
1632
1633 tm.min = (wxDateTime_t)(timeOnly % 60);
1634 timeOnly -= tm.min;
1635
1636 tm.hour = (wxDateTime_t)(timeOnly / 60);
1637
1638 return tm;
1639 }
1640
1641 wxDateTime& wxDateTime::SetYear(int year)
1642 {
1643 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1644
1645 Tm tm(GetTm());
1646 tm.year = year;
1647 Set(tm);
1648
1649 return *this;
1650 }
1651
1652 wxDateTime& wxDateTime::SetMonth(Month month)
1653 {
1654 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1655
1656 Tm tm(GetTm());
1657 tm.mon = month;
1658 Set(tm);
1659
1660 return *this;
1661 }
1662
1663 wxDateTime& wxDateTime::SetDay(wxDateTime_t mday)
1664 {
1665 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1666
1667 Tm tm(GetTm());
1668 tm.mday = mday;
1669 Set(tm);
1670
1671 return *this;
1672 }
1673
1674 wxDateTime& wxDateTime::SetHour(wxDateTime_t hour)
1675 {
1676 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1677
1678 Tm tm(GetTm());
1679 tm.hour = hour;
1680 Set(tm);
1681
1682 return *this;
1683 }
1684
1685 wxDateTime& wxDateTime::SetMinute(wxDateTime_t min)
1686 {
1687 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1688
1689 Tm tm(GetTm());
1690 tm.min = min;
1691 Set(tm);
1692
1693 return *this;
1694 }
1695
1696 wxDateTime& wxDateTime::SetSecond(wxDateTime_t sec)
1697 {
1698 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1699
1700 Tm tm(GetTm());
1701 tm.sec = sec;
1702 Set(tm);
1703
1704 return *this;
1705 }
1706
1707 wxDateTime& wxDateTime::SetMillisecond(wxDateTime_t millisecond)
1708 {
1709 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1710
1711 // we don't need to use GetTm() for this one
1712 m_time -= m_time % 1000l;
1713 m_time += millisecond;
1714
1715 return *this;
1716 }
1717
1718 // ----------------------------------------------------------------------------
1719 // wxDateTime arithmetics
1720 // ----------------------------------------------------------------------------
1721
1722 wxDateTime& wxDateTime::Add(const wxDateSpan& diff)
1723 {
1724 Tm tm(GetTm());
1725
1726 tm.year += diff.GetYears();
1727 tm.AddMonths(diff.GetMonths());
1728
1729 // check that the resulting date is valid
1730 if ( tm.mday > GetNumOfDaysInMonth(tm.year, tm.mon) )
1731 {
1732 // We suppose that when adding one month to Jan 31 we want to get Feb
1733 // 28 (or 29), i.e. adding a month to the last day of the month should
1734 // give the last day of the next month which is quite logical.
1735 //
1736 // Unfortunately, there is no logic way to understand what should
1737 // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1738 // We make it Feb 28 (last day too), but it is highly questionable.
1739 tm.mday = GetNumOfDaysInMonth(tm.year, tm.mon);
1740 }
1741
1742 tm.AddDays(diff.GetTotalDays());
1743
1744 Set(tm);
1745
1746 wxASSERT_MSG( IsSameTime(tm),
1747 _T("Add(wxDateSpan) shouldn't modify time") );
1748
1749 return *this;
1750 }
1751
1752 // ----------------------------------------------------------------------------
1753 // Weekday and monthday stuff
1754 // ----------------------------------------------------------------------------
1755
1756 // convert Sun, Mon, ..., Sat into 6, 0, ..., 5
1757 static inline int ConvertWeekDayToMondayBase(int wd)
1758 {
1759 return wd == wxDateTime::Sun ? 6 : wd - 1;
1760 }
1761
1762 /* static */
1763 wxDateTime
1764 wxDateTime::SetToWeekOfYear(int year, wxDateTime_t numWeek, WeekDay wd)
1765 {
1766 wxASSERT_MSG( numWeek > 0,
1767 _T("invalid week number: weeks are counted from 1") );
1768
1769 // Jan 4 always lies in the 1st week of the year
1770 wxDateTime dt(4, Jan, year);
1771 dt.SetToWeekDayInSameWeek(wd);
1772 dt += wxDateSpan::Weeks(numWeek - 1);
1773
1774 return dt;
1775 }
1776
1777 // use a separate function to avoid warnings about using deprecated
1778 // SetToTheWeek in GetWeek below
1779 static wxDateTime
1780 SetToTheWeek(int year,
1781 wxDateTime::wxDateTime_t numWeek,
1782 wxDateTime::WeekDay weekday,
1783 wxDateTime::WeekFlags flags)
1784 {
1785 // Jan 4 always lies in the 1st week of the year
1786 wxDateTime dt(4, wxDateTime::Jan, year);
1787 dt.SetToWeekDayInSameWeek(weekday, flags);
1788 dt += wxDateSpan::Weeks(numWeek - 1);
1789
1790 return dt;
1791 }
1792
1793 bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek,
1794 WeekDay weekday,
1795 WeekFlags flags)
1796 {
1797 int year = GetYear();
1798 *this = ::SetToTheWeek(year, numWeek, weekday, flags);
1799 if ( GetYear() != year )
1800 {
1801 // oops... numWeek was too big
1802 return false;
1803 }
1804
1805 return true;
1806 }
1807
1808 wxDateTime wxDateTime::GetWeek(wxDateTime_t numWeek,
1809 WeekDay weekday,
1810 WeekFlags flags) const
1811 {
1812 return ::SetToTheWeek(GetYear(), numWeek, weekday, flags);
1813 }
1814
1815 wxDateTime& wxDateTime::SetToLastMonthDay(Month month,
1816 int year)
1817 {
1818 // take the current month/year if none specified
1819 if ( year == Inv_Year )
1820 year = GetYear();
1821 if ( month == Inv_Month )
1822 month = GetMonth();
1823
1824 return Set(GetNumOfDaysInMonth(year, month), month, year);
1825 }
1826
1827 wxDateTime& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday, WeekFlags flags)
1828 {
1829 wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
1830
1831 int wdayDst = weekday,
1832 wdayThis = GetWeekDay();
1833 if ( wdayDst == wdayThis )
1834 {
1835 // nothing to do
1836 return *this;
1837 }
1838
1839 if ( flags == Default_First )
1840 {
1841 flags = GetCountry() == USA ? Sunday_First : Monday_First;
1842 }
1843
1844 // the logic below based on comparing weekday and wdayThis works if Sun (0)
1845 // is the first day in the week, but breaks down for Monday_First case so
1846 // we adjust the week days in this case
1847 if ( flags == Monday_First )
1848 {
1849 if ( wdayThis == Sun )
1850 wdayThis += 7;
1851 if ( wdayDst == Sun )
1852 wdayDst += 7;
1853 }
1854 //else: Sunday_First, nothing to do
1855
1856 // go forward or back in time to the day we want
1857 if ( wdayDst < wdayThis )
1858 {
1859 return Subtract(wxDateSpan::Days(wdayThis - wdayDst));
1860 }
1861 else // weekday > wdayThis
1862 {
1863 return Add(wxDateSpan::Days(wdayDst - wdayThis));
1864 }
1865 }
1866
1867 wxDateTime& wxDateTime::SetToNextWeekDay(WeekDay weekday)
1868 {
1869 wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
1870
1871 int diff;
1872 WeekDay wdayThis = GetWeekDay();
1873 if ( weekday == wdayThis )
1874 {
1875 // nothing to do
1876 return *this;
1877 }
1878 else if ( weekday < wdayThis )
1879 {
1880 // need to advance a week
1881 diff = 7 - (wdayThis - weekday);
1882 }
1883 else // weekday > wdayThis
1884 {
1885 diff = weekday - wdayThis;
1886 }
1887
1888 return Add(wxDateSpan::Days(diff));
1889 }
1890
1891 wxDateTime& wxDateTime::SetToPrevWeekDay(WeekDay weekday)
1892 {
1893 wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
1894
1895 int diff;
1896 WeekDay wdayThis = GetWeekDay();
1897 if ( weekday == wdayThis )
1898 {
1899 // nothing to do
1900 return *this;
1901 }
1902 else if ( weekday > wdayThis )
1903 {
1904 // need to go to previous week
1905 diff = 7 - (weekday - wdayThis);
1906 }
1907 else // weekday < wdayThis
1908 {
1909 diff = wdayThis - weekday;
1910 }
1911
1912 return Subtract(wxDateSpan::Days(diff));
1913 }
1914
1915 bool wxDateTime::SetToWeekDay(WeekDay weekday,
1916 int n,
1917 Month month,
1918 int year)
1919 {
1920 wxCHECK_MSG( weekday != Inv_WeekDay, false, _T("invalid weekday") );
1921
1922 // we don't check explicitly that -5 <= n <= 5 because we will return false
1923 // anyhow in such case - but may be should still give an assert for it?
1924
1925 // take the current month/year if none specified
1926 ReplaceDefaultYearMonthWithCurrent(&year, &month);
1927
1928 wxDateTime dt;
1929
1930 // TODO this probably could be optimised somehow...
1931
1932 if ( n > 0 )
1933 {
1934 // get the first day of the month
1935 dt.Set(1, month, year);
1936
1937 // get its wday
1938 WeekDay wdayFirst = dt.GetWeekDay();
1939
1940 // go to the first weekday of the month
1941 int diff = weekday - wdayFirst;
1942 if ( diff < 0 )
1943 diff += 7;
1944
1945 // add advance n-1 weeks more
1946 diff += 7*(n - 1);
1947
1948 dt += wxDateSpan::Days(diff);
1949 }
1950 else // count from the end of the month
1951 {
1952 // get the last day of the month
1953 dt.SetToLastMonthDay(month, year);
1954
1955 // get its wday
1956 WeekDay wdayLast = dt.GetWeekDay();
1957
1958 // go to the last weekday of the month
1959 int diff = wdayLast - weekday;
1960 if ( diff < 0 )
1961 diff += 7;
1962
1963 // and rewind n-1 weeks from there
1964 diff += 7*(-n - 1);
1965
1966 dt -= wxDateSpan::Days(diff);
1967 }
1968
1969 // check that it is still in the same month
1970 if ( dt.GetMonth() == month )
1971 {
1972 *this = dt;
1973
1974 return true;
1975 }
1976 else
1977 {
1978 // no such day in this month
1979 return false;
1980 }
1981 }
1982
1983 static inline
1984 wxDateTime::wxDateTime_t GetDayOfYearFromTm(const wxDateTime::Tm& tm)
1985 {
1986 return (wxDateTime::wxDateTime_t)(gs_cumulatedDays[wxDateTime::IsLeapYear(tm.year)][tm.mon] + tm.mday);
1987 }
1988
1989 wxDateTime::wxDateTime_t wxDateTime::GetDayOfYear(const TimeZone& tz) const
1990 {
1991 return GetDayOfYearFromTm(GetTm(tz));
1992 }
1993
1994 wxDateTime::wxDateTime_t
1995 wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags, const TimeZone& tz) const
1996 {
1997 if ( flags == Default_First )
1998 {
1999 flags = GetCountry() == USA ? Sunday_First : Monday_First;
2000 }
2001
2002 Tm tm(GetTm(tz));
2003 wxDateTime_t nDayInYear = GetDayOfYearFromTm(tm);
2004
2005 int wdTarget = GetWeekDay(tz);
2006 int wdYearStart = wxDateTime(1, Jan, GetYear()).GetWeekDay();
2007 int week;
2008 if ( flags == Sunday_First )
2009 {
2010 // FIXME: First week is not calculated correctly.
2011 week = (nDayInYear - wdTarget + 7) / 7;
2012 if ( wdYearStart == Wed || wdYearStart == Thu )
2013 week++;
2014 }
2015 else // week starts with monday
2016 {
2017 // adjust the weekdays to non-US style.
2018 wdYearStart = ConvertWeekDayToMondayBase(wdYearStart);
2019 wdTarget = ConvertWeekDayToMondayBase(wdTarget);
2020
2021 // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html:
2022 //
2023 // Week 01 of a year is per definition the first week that has the
2024 // Thursday in this year, which is equivalent to the week that
2025 // contains the fourth day of January. In other words, the first
2026 // week of a new year is the week that has the majority of its
2027 // days in the new year. Week 01 might also contain days from the
2028 // previous year and the week before week 01 of a year is the last
2029 // week (52 or 53) of the previous year even if it contains days
2030 // from the new year. A week starts with Monday (day 1) and ends
2031 // with Sunday (day 7).
2032 //
2033
2034 // if Jan 1 is Thursday or less, it is in the first week of this year
2035 if ( wdYearStart < 4 )
2036 {
2037 // count the number of entire weeks between Jan 1 and this date
2038 week = (nDayInYear + wdYearStart + 6 - wdTarget)/7;
2039
2040 // be careful to check for overflow in the next year
2041 if ( week == 53 && tm.mday - wdTarget > 28 )
2042 week = 1;
2043 }
2044 else // Jan 1 is in the last week of the previous year
2045 {
2046 // check if we happen to be at the last week of previous year:
2047 if ( tm.mon == Jan && tm.mday < 8 - wdYearStart )
2048 week = wxDateTime(31, Dec, GetYear()-1).GetWeekOfYear();
2049 else
2050 week = (nDayInYear + wdYearStart - 1 - wdTarget)/7;
2051 }
2052 }
2053
2054 return (wxDateTime::wxDateTime_t)week;
2055 }
2056
2057 wxDateTime::wxDateTime_t wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags,
2058 const TimeZone& tz) const
2059 {
2060 Tm tm = GetTm(tz);
2061 wxDateTime dtMonthStart = wxDateTime(1, tm.mon, tm.year);
2062 int nWeek = GetWeekOfYear(flags) - dtMonthStart.GetWeekOfYear(flags) + 1;
2063 if ( nWeek < 0 )
2064 {
2065 // this may happen for January when Jan, 1 is the last week of the
2066 // previous year
2067 nWeek += IsLeapYear(tm.year - 1) ? 53 : 52;
2068 }
2069
2070 return (wxDateTime::wxDateTime_t)nWeek;
2071 }
2072
2073 wxDateTime& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday)
2074 {
2075 int year = GetYear();
2076 wxDATETIME_CHECK( (0 < yday) && (yday <= GetNumberOfDays(year)),
2077 _T("invalid year day") );
2078
2079 bool isLeap = IsLeapYear(year);
2080 for ( Month mon = Jan; mon < Inv_Month; wxNextMonth(mon) )
2081 {
2082 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
2083 // don't need it neither - because of the CHECK above we know that
2084 // yday lies in December then
2085 if ( (mon == Dec) || (yday <= gs_cumulatedDays[isLeap][mon + 1]) )
2086 {
2087 Set((wxDateTime::wxDateTime_t)(yday - gs_cumulatedDays[isLeap][mon]), mon, year);
2088
2089 break;
2090 }
2091 }
2092
2093 return *this;
2094 }
2095
2096 // ----------------------------------------------------------------------------
2097 // Julian day number conversion and related stuff
2098 // ----------------------------------------------------------------------------
2099
2100 double wxDateTime::GetJulianDayNumber() const
2101 {
2102 // JDN are always expressed for the UTC dates
2103 Tm tm(ToTimezone(UTC).GetTm(UTC));
2104
2105 double result = GetTruncatedJDN(tm.mday, tm.mon, tm.year);
2106
2107 // add the part GetTruncatedJDN() neglected
2108 result += 0.5;
2109
2110 // and now add the time: 86400 sec = 1 JDN
2111 return result + ((double)(60*(60*tm.hour + tm.min) + tm.sec)) / 86400;
2112 }
2113
2114 double wxDateTime::GetRataDie() const
2115 {
2116 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
2117 return GetJulianDayNumber() - 1721119.5 - 306;
2118 }
2119
2120 // ----------------------------------------------------------------------------
2121 // timezone and DST stuff
2122 // ----------------------------------------------------------------------------
2123
2124 int wxDateTime::IsDST(wxDateTime::Country country) const
2125 {
2126 wxCHECK_MSG( country == Country_Default, -1,
2127 _T("country support not implemented") );
2128
2129 // use the C RTL for the dates in the standard range
2130 time_t timet = GetTicks();
2131 if ( timet != (time_t)-1 )
2132 {
2133 tm *tm = localtime(&timet);
2134
2135 wxCHECK_MSG( tm, -1, _T("localtime() failed") );
2136
2137 return tm->tm_isdst;
2138 }
2139 else
2140 {
2141 int year = GetYear();
2142
2143 if ( !IsDSTApplicable(year, country) )
2144 {
2145 // no DST time in this year in this country
2146 return -1;
2147 }
2148
2149 return IsBetween(GetBeginDST(year, country), GetEndDST(year, country));
2150 }
2151 }
2152
2153 wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST)
2154 {
2155 long secDiff = GetTimeZone() + tz.GetOffset();
2156
2157 // we need to know whether DST is or not in effect for this date unless
2158 // the test disabled by the caller
2159 if ( !noDST && (IsDST() == 1) )
2160 {
2161 // FIXME we assume that the DST is always shifted by 1 hour
2162 secDiff -= 3600;
2163 }
2164
2165 return Subtract(wxTimeSpan::Seconds(secDiff));
2166 }
2167
2168 // ----------------------------------------------------------------------------
2169 // wxDateTime to/from text representations
2170 // ----------------------------------------------------------------------------
2171
2172 wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
2173 {
2174 wxCHECK_MSG( format, wxEmptyString, _T("NULL format in wxDateTime::Format") );
2175
2176 // we have to use our own implementation if the date is out of range of
2177 // strftime() or if we use non standard specificators
2178 time_t time = GetTicks();
2179 if ( (time != (time_t)-1) && !wxStrstr(format, _T("%l")) )
2180 {
2181 // use strftime()
2182 tm *tm;
2183 if ( tz.GetOffset() == -GetTimeZone() )
2184 {
2185 // we are working with local time
2186 tm = localtime(&time);
2187
2188 // should never happen
2189 wxCHECK_MSG( tm, wxEmptyString, _T("localtime() failed") );
2190 }
2191 else
2192 {
2193 time += (int)tz.GetOffset();
2194
2195 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
2196 int time2 = (int) time;
2197 if ( time2 >= 0 )
2198 #else
2199 if ( time >= 0 )
2200 #endif
2201 {
2202 tm = gmtime(&time);
2203
2204 // should never happen
2205 wxCHECK_MSG( tm, wxEmptyString, _T("gmtime() failed") );
2206 }
2207 else
2208 {
2209 tm = (struct tm *)NULL;
2210 }
2211 }
2212 #ifndef __WXWINCE__
2213 //Windows CE doesn't support strftime or wcsftime, so we use the generic implementation
2214 if ( tm )
2215 {
2216 return CallStrftime(format, tm);
2217 }
2218 #endif
2219 //else: use generic code below
2220 }
2221
2222 // we only parse ANSI C format specifications here, no POSIX 2
2223 // complications, no GNU extensions but we do add support for a "%l" format
2224 // specifier allowing to get the number of milliseconds
2225 Tm tm = GetTm(tz);
2226
2227 // used for calls to strftime() when we only deal with time
2228 struct tm tmTimeOnly;
2229 tmTimeOnly.tm_hour = tm.hour;
2230 tmTimeOnly.tm_min = tm.min;
2231 tmTimeOnly.tm_sec = tm.sec;
2232 tmTimeOnly.tm_wday = 0;
2233 tmTimeOnly.tm_yday = 0;
2234 tmTimeOnly.tm_mday = 1; // any date will do
2235 tmTimeOnly.tm_mon = 0;
2236 tmTimeOnly.tm_year = 76;
2237 tmTimeOnly.tm_isdst = 0; // no DST, we adjust for tz ourselves
2238
2239 wxString tmp, res, fmt;
2240 for ( const wxChar *p = format; *p; p++ )
2241 {
2242 if ( *p != _T('%') )
2243 {
2244 // copy as is
2245 res += *p;
2246
2247 continue;
2248 }
2249
2250 // set the default format
2251 switch ( *++p )
2252 {
2253 case _T('Y'): // year has 4 digits
2254 fmt = _T("%04d");
2255 break;
2256
2257 case _T('j'): // day of year has 3 digits
2258 case _T('l'): // milliseconds have 3 digits
2259 fmt = _T("%03d");
2260 break;
2261
2262 case _T('w'): // week day as number has only one
2263 fmt = _T("%d");
2264 break;
2265
2266 default:
2267 // it's either another valid format specifier in which case
2268 // the format is "%02d" (for all the rest) or we have the
2269 // field width preceding the format in which case it will
2270 // override the default format anyhow
2271 fmt = _T("%02d");
2272 }
2273
2274 bool restart = true;
2275 while ( restart )
2276 {
2277 restart = false;
2278
2279 // start of the format specification
2280 switch ( *p )
2281 {
2282 case _T('a'): // a weekday name
2283 case _T('A'):
2284 // second parameter should be true for abbreviated names
2285 res += GetWeekDayName(tm.GetWeekDay(),
2286 *p == _T('a') ? Name_Abbr : Name_Full);
2287 break;
2288
2289 case _T('b'): // a month name
2290 case _T('B'):
2291 res += GetMonthName(tm.mon,
2292 *p == _T('b') ? Name_Abbr : Name_Full);
2293 break;
2294
2295 case _T('c'): // locale default date and time representation
2296 case _T('x'): // locale default date representation
2297 #ifndef __WXWINCE__
2298 //
2299 // the problem: there is no way to know what do these format
2300 // specifications correspond to for the current locale.
2301 //
2302 // the solution: use a hack and still use strftime(): first
2303 // find the YEAR which is a year in the strftime() range (1970
2304 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
2305 // of the real year. Then make a copy of the format and
2306 // replace all occurences of YEAR in it with some unique
2307 // string not appearing anywhere else in it, then use
2308 // strftime() to format the date in year YEAR and then replace
2309 // YEAR back by the real year and the unique replacement
2310 // string back with YEAR. Notice that "all occurences of YEAR"
2311 // means all occurences of 4 digit as well as 2 digit form!
2312 //
2313 // the bugs: we assume that neither of %c nor %x contains any
2314 // fields which may change between the YEAR and real year. For
2315 // example, the week number (%U, %W) and the day number (%j)
2316 // will change if one of these years is leap and the other one
2317 // is not!
2318 {
2319 // find the YEAR: normally, for any year X, Jan 1 or the
2320 // year X + 28 is the same weekday as Jan 1 of X (because
2321 // the weekday advances by 1 for each normal X and by 2
2322 // for each leap X, hence by 5 every 4 years or by 35
2323 // which is 0 mod 7 every 28 years) but this rule breaks
2324 // down if there are years between X and Y which are
2325 // divisible by 4 but not leap (i.e. divisible by 100 but
2326 // not 400), hence the correction.
2327
2328 int yearReal = GetYear(tz);
2329 int mod28 = yearReal % 28;
2330
2331 // be careful to not go too far - we risk to leave the
2332 // supported range
2333 int year;
2334 if ( mod28 < 10 )
2335 {
2336 year = 1988 + mod28; // 1988 == 0 (mod 28)
2337 }
2338 else
2339 {
2340 year = 1970 + mod28 - 10; // 1970 == 10 (mod 28)
2341 }
2342
2343 int nCentury = year / 100,
2344 nCenturyReal = yearReal / 100;
2345
2346 // need to adjust for the years divisble by 400 which are
2347 // not leap but are counted like leap ones if we just take
2348 // the number of centuries in between for nLostWeekDays
2349 int nLostWeekDays = (nCentury - nCenturyReal) -
2350 (nCentury / 4 - nCenturyReal / 4);
2351
2352 // we have to gain back the "lost" weekdays: note that the
2353 // effect of this loop is to not do anything to
2354 // nLostWeekDays (which we won't use any more), but to
2355 // (indirectly) set the year correctly
2356 while ( (nLostWeekDays % 7) != 0 )
2357 {
2358 nLostWeekDays += year++ % 4 ? 1 : 2;
2359 }
2360
2361 // at any rate, we couldn't go further than 1988 + 9 + 28!
2362 wxASSERT_MSG( year < 2030,
2363 _T("logic error in wxDateTime::Format") );
2364
2365 wxString strYear, strYear2;
2366 strYear.Printf(_T("%d"), year);
2367 strYear2.Printf(_T("%d"), year % 100);
2368
2369 // find two strings not occuring in format (this is surely
2370 // not optimal way of doing it... improvements welcome!)
2371 wxString fmt = format;
2372 wxString replacement = (wxChar)-1;
2373 while ( fmt.Find(replacement) != wxNOT_FOUND )
2374 {
2375 replacement << (wxChar)-1;
2376 }
2377
2378 wxString replacement2 = (wxChar)-2;
2379 while ( fmt.Find(replacement) != wxNOT_FOUND )
2380 {
2381 replacement << (wxChar)-2;
2382 }
2383
2384 // replace all occurences of year with it
2385 bool wasReplaced = fmt.Replace(strYear, replacement) > 0;
2386 if ( !wasReplaced )
2387 wasReplaced = fmt.Replace(strYear2, replacement2) > 0;
2388
2389 // use strftime() to format the same date but in supported
2390 // year
2391 //
2392 // NB: we assume that strftime() doesn't check for the
2393 // date validity and will happily format the date
2394 // corresponding to Feb 29 of a non leap year (which
2395 // may happen if yearReal was leap and year is not)
2396 struct tm tmAdjusted;
2397 InitTm(tmAdjusted);
2398 tmAdjusted.tm_hour = tm.hour;
2399 tmAdjusted.tm_min = tm.min;
2400 tmAdjusted.tm_sec = tm.sec;
2401 tmAdjusted.tm_wday = tm.GetWeekDay();
2402 tmAdjusted.tm_yday = GetDayOfYear();
2403 tmAdjusted.tm_mday = tm.mday;
2404 tmAdjusted.tm_mon = tm.mon;
2405 tmAdjusted.tm_year = year - 1900;
2406 tmAdjusted.tm_isdst = 0; // no DST, already adjusted
2407 wxString str = CallStrftime(*p == _T('c') ? _T("%c")
2408 : _T("%x"),
2409 &tmAdjusted);
2410
2411 // now replace the occurence of 1999 with the real year
2412 wxString strYearReal, strYearReal2;
2413 strYearReal.Printf(_T("%04d"), yearReal);
2414 strYearReal2.Printf(_T("%02d"), yearReal % 100);
2415 str.Replace(strYear, strYearReal);
2416 str.Replace(strYear2, strYearReal2);
2417
2418 // and replace back all occurences of replacement string
2419 if ( wasReplaced )
2420 {
2421 str.Replace(replacement2, strYear2);
2422 str.Replace(replacement, strYear);
2423 }
2424
2425 res += str;
2426 }
2427 #else
2428 //Use "%m/%d/%y %H:%M:%S" format instead
2429 res += wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
2430 tm.mon+1,tm.mday, tm.year, tm.hour, tm.min, tm.sec);
2431 #endif
2432 break;
2433
2434 case _T('d'): // day of a month (01-31)
2435 res += wxString::Format(fmt, tm.mday);
2436 break;
2437
2438 case _T('H'): // hour in 24h format (00-23)
2439 res += wxString::Format(fmt, tm.hour);
2440 break;
2441
2442 case _T('I'): // hour in 12h format (01-12)
2443 {
2444 // 24h -> 12h, 0h -> 12h too
2445 int hour12 = tm.hour > 12 ? tm.hour - 12
2446 : tm.hour ? tm.hour : 12;
2447 res += wxString::Format(fmt, hour12);
2448 }
2449 break;
2450
2451 case _T('j'): // day of the year
2452 res += wxString::Format(fmt, GetDayOfYear(tz));
2453 break;
2454
2455 case _T('l'): // milliseconds (NOT STANDARD)
2456 res += wxString::Format(fmt, GetMillisecond(tz));
2457 break;
2458
2459 case _T('m'): // month as a number (01-12)
2460 res += wxString::Format(fmt, tm.mon + 1);
2461 break;
2462
2463 case _T('M'): // minute as a decimal number (00-59)
2464 res += wxString::Format(fmt, tm.min);
2465 break;
2466
2467 case _T('p'): // AM or PM string
2468 #ifndef __WXWINCE__
2469 res += CallStrftime(_T("%p"), &tmTimeOnly);
2470 #else
2471 res += (tmTimeOnly.tm_hour > 12) ? wxT("pm") : wxT("am");
2472 #endif
2473 break;
2474
2475 case _T('S'): // second as a decimal number (00-61)
2476 res += wxString::Format(fmt, tm.sec);
2477 break;
2478
2479 case _T('U'): // week number in the year (Sunday 1st week day)
2480 res += wxString::Format(fmt, GetWeekOfYear(Sunday_First, tz));
2481 break;
2482
2483 case _T('W'): // week number in the year (Monday 1st week day)
2484 res += wxString::Format(fmt, GetWeekOfYear(Monday_First, tz));
2485 break;
2486
2487 case _T('w'): // weekday as a number (0-6), Sunday = 0
2488 res += wxString::Format(fmt, tm.GetWeekDay());
2489 break;
2490
2491 // case _T('x'): -- handled with "%c"
2492
2493 case _T('X'): // locale default time representation
2494 // just use strftime() to format the time for us
2495 #ifndef __WXWINCE__
2496 res += CallStrftime(_T("%X"), &tmTimeOnly);
2497 #else
2498 res += wxString::Format(wxT("%02d:%02d:%02d"),tm.hour, tm.min, tm.sec);
2499 #endif
2500 break;
2501
2502 case _T('y'): // year without century (00-99)
2503 res += wxString::Format(fmt, tm.year % 100);
2504 break;
2505
2506 case _T('Y'): // year with century
2507 res += wxString::Format(fmt, tm.year);
2508 break;
2509
2510 case _T('Z'): // timezone name
2511 #ifndef __WXWINCE__
2512 res += CallStrftime(_T("%Z"), &tmTimeOnly);
2513 #endif
2514 break;
2515
2516 default:
2517 // is it the format width?
2518 fmt.Empty();
2519 while ( *p == _T('-') || *p == _T('+') ||
2520 *p == _T(' ') || wxIsdigit(*p) )
2521 {
2522 fmt += *p;
2523 }
2524
2525 if ( !fmt.empty() )
2526 {
2527 // we've only got the flags and width so far in fmt
2528 fmt.Prepend(_T('%'));
2529 fmt.Append(_T('d'));
2530
2531 restart = true;
2532
2533 break;
2534 }
2535
2536 // no, it wasn't the width
2537 wxFAIL_MSG(_T("unknown format specificator"));
2538
2539 // fall through and just copy it nevertheless
2540
2541 case _T('%'): // a percent sign
2542 res += *p;
2543 break;
2544
2545 case 0: // the end of string
2546 wxFAIL_MSG(_T("missing format at the end of string"));
2547
2548 // just put the '%' which was the last char in format
2549 res += _T('%');
2550 break;
2551 }
2552 }
2553 }
2554
2555 return res;
2556 }
2557
2558 // this function parses a string in (strict) RFC 822 format: see the section 5
2559 // of the RFC for the detailed description, but briefly it's something of the
2560 // form "Sat, 18 Dec 1999 00:48:30 +0100"
2561 //
2562 // this function is "strict" by design - it must reject anything except true
2563 // RFC822 time specs.
2564 //
2565 // TODO a great candidate for using reg exps
2566 const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date)
2567 {
2568 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
2569
2570 const wxChar *p = date;
2571 const wxChar *comma = wxStrchr(p, _T(','));
2572 if ( comma )
2573 {
2574 // the part before comma is the weekday
2575
2576 // skip it for now - we don't use but might check that it really
2577 // corresponds to the specfied date
2578 p = comma + 1;
2579
2580 if ( *p != _T(' ') )
2581 {
2582 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
2583
2584 return (wxChar *)NULL;
2585 }
2586
2587 p++; // skip space
2588 }
2589
2590 // the following 1 or 2 digits are the day number
2591 if ( !wxIsdigit(*p) )
2592 {
2593 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
2594
2595 return (wxChar *)NULL;
2596 }
2597
2598 wxDateTime_t day = (wxDateTime_t)(*p++ - _T('0'));
2599 if ( wxIsdigit(*p) )
2600 {
2601 day *= 10;
2602 day = (wxDateTime_t)(day + (*p++ - _T('0')));
2603 }
2604
2605 if ( *p++ != _T(' ') )
2606 {
2607 return (wxChar *)NULL;
2608 }
2609
2610 // the following 3 letters specify the month
2611 wxString monName(p, 3);
2612 Month mon;
2613 if ( monName == _T("Jan") )
2614 mon = Jan;
2615 else if ( monName == _T("Feb") )
2616 mon = Feb;
2617 else if ( monName == _T("Mar") )
2618 mon = Mar;
2619 else if ( monName == _T("Apr") )
2620 mon = Apr;
2621 else if ( monName == _T("May") )
2622 mon = May;
2623 else if ( monName == _T("Jun") )
2624 mon = Jun;
2625 else if ( monName == _T("Jul") )
2626 mon = Jul;
2627 else if ( monName == _T("Aug") )
2628 mon = Aug;
2629 else if ( monName == _T("Sep") )
2630 mon = Sep;
2631 else if ( monName == _T("Oct") )
2632 mon = Oct;
2633 else if ( monName == _T("Nov") )
2634 mon = Nov;
2635 else if ( monName == _T("Dec") )
2636 mon = Dec;
2637 else
2638 {
2639 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName.c_str());
2640
2641 return (wxChar *)NULL;
2642 }
2643
2644 p += 3;
2645
2646 if ( *p++ != _T(' ') )
2647 {
2648 return (wxChar *)NULL;
2649 }
2650
2651 // next is the year
2652 if ( !wxIsdigit(*p) )
2653 {
2654 // no year?
2655 return (wxChar *)NULL;
2656 }
2657
2658 int year = *p++ - _T('0');
2659
2660 if ( !wxIsdigit(*p) )
2661 {
2662 // should have at least 2 digits in the year
2663 return (wxChar *)NULL;
2664 }
2665
2666 year *= 10;
2667 year += *p++ - _T('0');
2668
2669 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
2670 if ( wxIsdigit(*p) )
2671 {
2672 year *= 10;
2673 year += *p++ - _T('0');
2674
2675 if ( !wxIsdigit(*p) )
2676 {
2677 // no 3 digit years please
2678 return (wxChar *)NULL;
2679 }
2680
2681 year *= 10;
2682 year += *p++ - _T('0');
2683 }
2684
2685 if ( *p++ != _T(' ') )
2686 {
2687 return (wxChar *)NULL;
2688 }
2689
2690 // time is in the format hh:mm:ss and seconds are optional
2691 if ( !wxIsdigit(*p) )
2692 {
2693 return (wxChar *)NULL;
2694 }
2695
2696 wxDateTime_t hour = (wxDateTime_t)(*p++ - _T('0'));
2697
2698 if ( !wxIsdigit(*p) )
2699 {
2700 return (wxChar *)NULL;
2701 }
2702
2703 hour *= 10;
2704 hour = (wxDateTime_t)(hour + (*p++ - _T('0')));
2705
2706 if ( *p++ != _T(':') )
2707 {
2708 return (wxChar *)NULL;
2709 }
2710
2711 if ( !wxIsdigit(*p) )
2712 {
2713 return (wxChar *)NULL;
2714 }
2715
2716 wxDateTime_t min = (wxDateTime_t)(*p++ - _T('0'));
2717
2718 if ( !wxIsdigit(*p) )
2719 {
2720 return (wxChar *)NULL;
2721 }
2722
2723 min *= 10;
2724 min = (wxDateTime_t)(min + *p++ - _T('0'));
2725
2726 wxDateTime_t sec = 0;
2727 if ( *p++ == _T(':') )
2728 {
2729 if ( !wxIsdigit(*p) )
2730 {
2731 return (wxChar *)NULL;
2732 }
2733
2734 sec = (wxDateTime_t)(*p++ - _T('0'));
2735
2736 if ( !wxIsdigit(*p) )
2737 {
2738 return (wxChar *)NULL;
2739 }
2740
2741 sec *= 10;
2742 sec = (wxDateTime_t)(sec + *p++ - _T('0'));
2743 }
2744
2745 if ( *p++ != _T(' ') )
2746 {
2747 return (wxChar *)NULL;
2748 }
2749
2750 // and now the interesting part: the timezone
2751 int offset wxDUMMY_INITIALIZE(0);
2752 if ( *p == _T('-') || *p == _T('+') )
2753 {
2754 // the explicit offset given: it has the form of hhmm
2755 bool plus = *p++ == _T('+');
2756
2757 if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) )
2758 {
2759 return (wxChar *)NULL;
2760 }
2761
2762 // hours
2763 offset = 60*(10*(*p - _T('0')) + (*(p + 1) - _T('0')));
2764
2765 p += 2;
2766
2767 if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) )
2768 {
2769 return (wxChar *)NULL;
2770 }
2771
2772 // minutes
2773 offset += 10*(*p - _T('0')) + (*(p + 1) - _T('0'));
2774
2775 if ( !plus )
2776 {
2777 offset = -offset;
2778 }
2779
2780 p += 2;
2781 }
2782 else
2783 {
2784 // the symbolic timezone given: may be either military timezone or one
2785 // of standard abbreviations
2786 if ( !*(p + 1) )
2787 {
2788 // military: Z = UTC, J unused, A = -1, ..., Y = +12
2789 static const int offsets[26] =
2790 {
2791 //A B C D E F G H I J K L M
2792 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
2793 //N O P R Q S T U V W Z Y Z
2794 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
2795 };
2796
2797 if ( *p < _T('A') || *p > _T('Z') || *p == _T('J') )
2798 {
2799 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p);
2800
2801 return (wxChar *)NULL;
2802 }
2803
2804 offset = offsets[*p++ - _T('A')];
2805 }
2806 else
2807 {
2808 // abbreviation
2809 wxString tz = p;
2810 if ( tz == _T("UT") || tz == _T("UTC") || tz == _T("GMT") )
2811 offset = 0;
2812 else if ( tz == _T("AST") )
2813 offset = AST - GMT0;
2814 else if ( tz == _T("ADT") )
2815 offset = ADT - GMT0;
2816 else if ( tz == _T("EST") )
2817 offset = EST - GMT0;
2818 else if ( tz == _T("EDT") )
2819 offset = EDT - GMT0;
2820 else if ( tz == _T("CST") )
2821 offset = CST - GMT0;
2822 else if ( tz == _T("CDT") )
2823 offset = CDT - GMT0;
2824 else if ( tz == _T("MST") )
2825 offset = MST - GMT0;
2826 else if ( tz == _T("MDT") )
2827 offset = MDT - GMT0;
2828 else if ( tz == _T("PST") )
2829 offset = PST - GMT0;
2830 else if ( tz == _T("PDT") )
2831 offset = PDT - GMT0;
2832 else
2833 {
2834 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p);
2835
2836 return (wxChar *)NULL;
2837 }
2838
2839 p += tz.length();
2840 }
2841
2842 // make it minutes
2843 offset *= 60;
2844 }
2845
2846 // the spec was correct
2847 Set(day, mon, year, hour, min, sec);
2848 MakeTimezone((wxDateTime_t)(60*offset));
2849
2850 return p;
2851 }
2852
2853 #ifdef __WINDOWS__
2854
2855 // Get's current locale's date formatting string and stores it in fmt if
2856 // the locale is set; otherwise or in case of failure, leaves fmt unchanged
2857 static void GetLocaleDateFormat(wxString *fmt)
2858 {
2859 // there is no setlocale() under Windows CE, so just always query the
2860 // system there
2861 #ifndef __WXWINCE__
2862 if ( strcmp(setlocale(LC_ALL, NULL), "C") != 0 )
2863 #endif
2864 {
2865 // The locale was programatically set to non-C. We assume that this was
2866 // done using wxLocale, in which case thread's current locale is also
2867 // set to correct LCID value and we can use GetLocaleInfo to determine
2868 // the correct formatting string:
2869 #ifdef __WXWINCE__
2870 LCID lcid = LOCALE_USER_DEFAULT;
2871 #else
2872 LCID lcid = GetThreadLocale();
2873 #endif
2874 wxChar delim[5]; // fields deliminer, 4 chars max
2875 if ( GetLocaleInfo(lcid, LOCALE_SDATE, delim, 5) )
2876 {
2877 wxChar centurybuf[2]; // use %y or %Y, 1 char max
2878 wxChar century = 'y';
2879 if ( GetLocaleInfo(lcid, LOCALE_ICENTURY, centurybuf, 2) )
2880 {
2881 if ( centurybuf[0] == _T('1') )
2882 century = 'Y';
2883 // else 'y' as above
2884 }
2885
2886 wxChar order[2]; // order code, 1 char max
2887 if ( GetLocaleInfo(lcid, LOCALE_IDATE, order, 2) )
2888 {
2889 if ( order[0] == _T('0') ) // M-D-Y
2890 {
2891 *fmt = wxString::Format(_T("%%m%s%%d%s%%%c"),
2892 delim, delim, century);
2893 }
2894 else if ( order[0] == _T('1') ) // D-M-Y
2895 {
2896 *fmt = wxString::Format(_T("%%d%s%%m%s%%%c"),
2897 delim, delim, century);
2898 }
2899 else if ( order[0] == _T('2') ) // Y-M-D
2900 {
2901 *fmt = wxString::Format(_T("%%%c%s%%m%s%%d"),
2902 century, delim, delim);
2903 }
2904 else
2905 {
2906 wxFAIL_MSG(_T("unexpected GetLocaleInfo return value"));
2907 }
2908 }
2909 }
2910 // if we failed, leave fmtDate value unchanged and
2911 // try our luck with the default set above
2912 }
2913 }
2914
2915 #endif // __WINDOWS__
2916
2917 const wxChar *wxDateTime::ParseFormat(const wxChar *date,
2918 const wxChar *format,
2919 const wxDateTime& dateDef)
2920 {
2921 wxCHECK_MSG( date && format, (wxChar *)NULL,
2922 _T("NULL pointer in wxDateTime::ParseFormat()") );
2923
2924 wxString str;
2925 unsigned long num;
2926
2927 // what fields have we found?
2928 bool haveWDay = false,
2929 haveYDay = false,
2930 haveDay = false,
2931 haveMon = false,
2932 haveYear = false,
2933 haveHour = false,
2934 haveMin = false,
2935 haveSec = false;
2936
2937 bool hourIsIn12hFormat = false, // or in 24h one?
2938 isPM = false; // AM by default
2939
2940 // and the value of the items we have (init them to get rid of warnings)
2941 wxDateTime_t sec = 0,
2942 min = 0,
2943 hour = 0;
2944 WeekDay wday = Inv_WeekDay;
2945 wxDateTime_t yday = 0,
2946 mday = 0;
2947 wxDateTime::Month mon = Inv_Month;
2948 int year = 0;
2949
2950 const wxChar *input = date;
2951 for ( const wxChar *fmt = format; *fmt; fmt++ )
2952 {
2953 if ( *fmt != _T('%') )
2954 {
2955 if ( wxIsspace(*fmt) )
2956 {
2957 // a white space in the format string matches 0 or more white
2958 // spaces in the input
2959 while ( wxIsspace(*input) )
2960 {
2961 input++;
2962 }
2963 }
2964 else // !space
2965 {
2966 // any other character (not whitespace, not '%') must be
2967 // matched by itself in the input
2968 if ( *input++ != *fmt )
2969 {
2970 // no match
2971 return (wxChar *)NULL;
2972 }
2973 }
2974
2975 // done with this format char
2976 continue;
2977 }
2978
2979 // start of a format specification
2980
2981 // parse the optional width
2982 size_t width = 0;
2983 while ( wxIsdigit(*++fmt) )
2984 {
2985 width *= 10;
2986 width += *fmt - _T('0');
2987 }
2988
2989 // the default widths for the various fields
2990 if ( !width )
2991 {
2992 switch ( *fmt )
2993 {
2994 case _T('Y'): // year has 4 digits
2995 width = 4;
2996 break;
2997
2998 case _T('j'): // day of year has 3 digits
2999 case _T('l'): // milliseconds have 3 digits
3000 width = 3;
3001 break;
3002
3003 case _T('w'): // week day as number has only one
3004 width = 1;
3005 break;
3006
3007 default:
3008 // default for all other fields
3009 width = 2;
3010 }
3011 }
3012
3013 // then the format itself
3014 switch ( *fmt )
3015 {
3016 case _T('a'): // a weekday name
3017 case _T('A'):
3018 {
3019 int flag = *fmt == _T('a') ? Name_Abbr : Name_Full;
3020 wday = GetWeekDayFromName(GetAlphaToken(input), flag);
3021 if ( wday == Inv_WeekDay )
3022 {
3023 // no match
3024 return (wxChar *)NULL;
3025 }
3026 }
3027 haveWDay = true;
3028 break;
3029
3030 case _T('b'): // a month name
3031 case _T('B'):
3032 {
3033 int flag = *fmt == _T('b') ? Name_Abbr : Name_Full;
3034 mon = GetMonthFromName(GetAlphaToken(input), flag);
3035 if ( mon == Inv_Month )
3036 {
3037 // no match
3038 return (wxChar *)NULL;
3039 }
3040 }
3041 haveMon = true;
3042 break;
3043
3044 case _T('c'): // locale default date and time representation
3045 {
3046 wxDateTime dt;
3047
3048 // this is the format which corresponds to ctime() output
3049 // and strptime("%c") should parse it, so try it first
3050 static const wxChar *fmtCtime = _T("%a %b %d %H:%M:%S %Y");
3051
3052 const wxChar *result = dt.ParseFormat(input, fmtCtime);
3053 if ( !result )
3054 {
3055 result = dt.ParseFormat(input, _T("%x %X"));
3056 }
3057
3058 if ( !result )
3059 {
3060 result = dt.ParseFormat(input, _T("%X %x"));
3061 }
3062
3063 if ( !result )
3064 {
3065 // we've tried everything and still no match
3066 return (wxChar *)NULL;
3067 }
3068
3069 Tm tm = dt.GetTm();
3070
3071 haveDay = haveMon = haveYear =
3072 haveHour = haveMin = haveSec = true;
3073
3074 hour = tm.hour;
3075 min = tm.min;
3076 sec = tm.sec;
3077
3078 year = tm.year;
3079 mon = tm.mon;
3080 mday = tm.mday;
3081
3082 input = result;
3083 }
3084 break;
3085
3086 case _T('d'): // day of a month (01-31)
3087 if ( !GetNumericToken(width, input, &num) ||
3088 (num > 31) || (num < 1) )
3089 {
3090 // no match
3091 return (wxChar *)NULL;
3092 }
3093
3094 // we can't check whether the day range is correct yet, will
3095 // do it later - assume ok for now
3096 haveDay = true;
3097 mday = (wxDateTime_t)num;
3098 break;
3099
3100 case _T('H'): // hour in 24h format (00-23)
3101 if ( !GetNumericToken(width, input, &num) || (num > 23) )
3102 {
3103 // no match
3104 return (wxChar *)NULL;
3105 }
3106
3107 haveHour = true;
3108 hour = (wxDateTime_t)num;
3109 break;
3110
3111 case _T('I'): // hour in 12h format (01-12)
3112 if ( !GetNumericToken(width, input, &num) || !num || (num > 12) )
3113 {
3114 // no match
3115 return (wxChar *)NULL;
3116 }
3117
3118 haveHour = true;
3119 hourIsIn12hFormat = true;
3120 hour = (wxDateTime_t)(num % 12); // 12 should be 0
3121 break;
3122
3123 case _T('j'): // day of the year
3124 if ( !GetNumericToken(width, input, &num) || !num || (num > 366) )
3125 {
3126 // no match
3127 return (wxChar *)NULL;
3128 }
3129
3130 haveYDay = true;
3131 yday = (wxDateTime_t)num;
3132 break;
3133
3134 case _T('m'): // month as a number (01-12)
3135 if ( !GetNumericToken(width, input, &num) || !num || (num > 12) )
3136 {
3137 // no match
3138 return (wxChar *)NULL;
3139 }
3140
3141 haveMon = true;
3142 mon = (Month)(num - 1);
3143 break;
3144
3145 case _T('M'): // minute as a decimal number (00-59)
3146 if ( !GetNumericToken(width, input, &num) || (num > 59) )
3147 {
3148 // no match
3149 return (wxChar *)NULL;
3150 }
3151
3152 haveMin = true;
3153 min = (wxDateTime_t)num;
3154 break;
3155
3156 case _T('p'): // AM or PM string
3157 {
3158 wxString am, pm, token = GetAlphaToken(input);
3159
3160 GetAmPmStrings(&am, &pm);
3161 if (am.empty() && pm.empty())
3162 return (wxChar *)NULL; // no am/pm strings defined
3163 if ( token.CmpNoCase(pm) == 0 )
3164 {
3165 isPM = true;
3166 }
3167 else if ( token.CmpNoCase(am) != 0 )
3168 {
3169 // no match
3170 return (wxChar *)NULL;
3171 }
3172 }
3173 break;
3174
3175 case _T('r'): // time as %I:%M:%S %p
3176 {
3177 wxDateTime dt;
3178 input = dt.ParseFormat(input, _T("%I:%M:%S %p"));
3179 if ( !input )
3180 {
3181 // no match
3182 return (wxChar *)NULL;
3183 }
3184
3185 haveHour = haveMin = haveSec = true;
3186
3187 Tm tm = dt.GetTm();
3188 hour = tm.hour;
3189 min = tm.min;
3190 sec = tm.sec;
3191 }
3192 break;
3193
3194 case _T('R'): // time as %H:%M
3195 {
3196 wxDateTime dt;
3197 input = dt.ParseFormat(input, _T("%H:%M"));
3198 if ( !input )
3199 {
3200 // no match
3201 return (wxChar *)NULL;
3202 }
3203
3204 haveHour = haveMin = true;
3205
3206 Tm tm = dt.GetTm();
3207 hour = tm.hour;
3208 min = tm.min;
3209 }
3210 break;
3211
3212 case _T('S'): // second as a decimal number (00-61)
3213 if ( !GetNumericToken(width, input, &num) || (num > 61) )
3214 {
3215 // no match
3216 return (wxChar *)NULL;
3217 }
3218
3219 haveSec = true;
3220 sec = (wxDateTime_t)num;
3221 break;
3222
3223 case _T('T'): // time as %H:%M:%S
3224 {
3225 wxDateTime dt;
3226 input = dt.ParseFormat(input, _T("%H:%M:%S"));
3227 if ( !input )
3228 {
3229 // no match
3230 return (wxChar *)NULL;
3231 }
3232
3233 haveHour = haveMin = haveSec = true;
3234
3235 Tm tm = dt.GetTm();
3236 hour = tm.hour;
3237 min = tm.min;
3238 sec = tm.sec;
3239 }
3240 break;
3241
3242 case _T('w'): // weekday as a number (0-6), Sunday = 0
3243 if ( !GetNumericToken(width, input, &num) || (wday > 6) )
3244 {
3245 // no match
3246 return (wxChar *)NULL;
3247 }
3248
3249 haveWDay = true;
3250 wday = (WeekDay)num;
3251 break;
3252
3253 case _T('x'): // locale default date representation
3254 #ifdef HAVE_STRPTIME
3255 // try using strptime() -- it may fail even if the input is
3256 // correct but the date is out of range, so we will fall back
3257 // to our generic code anyhow
3258 {
3259 struct tm tm;
3260
3261 const wxChar *result = CallStrptime(input, "%x", &tm);
3262 if ( result )
3263 {
3264 input = result;
3265
3266 haveDay = haveMon = haveYear = true;
3267
3268 year = 1900 + tm.tm_year;
3269 mon = (Month)tm.tm_mon;
3270 mday = tm.tm_mday;
3271
3272 break;
3273 }
3274 }
3275 #endif // HAVE_STRPTIME
3276
3277 {
3278 wxDateTime dt;
3279
3280 wxString fmtDate, fmtDateAlt;
3281
3282 if ( IsWestEuropeanCountry(GetCountry()) ||
3283 GetCountry() == Russia )
3284 {
3285 fmtDate = _T("%d/%m/%y");
3286 fmtDateAlt = _T("%m/%d/%y");
3287 }
3288 else // assume USA
3289 {
3290 fmtDate = _T("%m/%d/%y");
3291 fmtDateAlt = _T("%d/%m/%y");
3292 }
3293
3294 #ifdef __WINDOWS__
3295 // The above doesn't work for all locales, try to query
3296 // Windows for the right way of formatting the date:
3297 GetLocaleDateFormat(&fmtDate);
3298 #endif
3299
3300 const wxChar *result = dt.ParseFormat(input, fmtDate);
3301
3302 if ( !result )
3303 {
3304 // ok, be nice and try another one
3305 result = dt.ParseFormat(input, fmtDateAlt);
3306 }
3307
3308 if ( !result )
3309 {
3310 // bad luck
3311 return (wxChar *)NULL;
3312 }
3313
3314 Tm tm = dt.GetTm();
3315
3316 haveDay = haveMon = haveYear = true;
3317
3318 year = tm.year;
3319 mon = tm.mon;
3320 mday = tm.mday;
3321
3322 input = result;
3323 }
3324
3325 break;
3326
3327 case _T('X'): // locale default time representation
3328 #ifdef HAVE_STRPTIME
3329 {
3330 // use strptime() to do it for us (FIXME !Unicode friendly)
3331 struct tm tm;
3332 input = CallStrptime(input, "%X", &tm);
3333 if ( !input )
3334 {
3335 return (wxChar *)NULL;
3336 }
3337
3338 haveHour = haveMin = haveSec = true;
3339
3340 hour = tm.tm_hour;
3341 min = tm.tm_min;
3342 sec = tm.tm_sec;
3343 }
3344 #else // !HAVE_STRPTIME
3345 // TODO under Win32 we can query the LOCALE_ITIME system
3346 // setting which says whether the default time format is
3347 // 24 or 12 hour
3348 {
3349 // try to parse what follows as "%H:%M:%S" and, if this
3350 // fails, as "%I:%M:%S %p" - this should catch the most
3351 // common cases
3352 wxDateTime dt;
3353
3354 const wxChar *result = dt.ParseFormat(input, _T("%T"));
3355 if ( !result )
3356 {
3357 result = dt.ParseFormat(input, _T("%r"));
3358 }
3359
3360 if ( !result )
3361 {
3362 // no match
3363 return (wxChar *)NULL;
3364 }
3365
3366 haveHour = haveMin = haveSec = true;
3367
3368 Tm tm = dt.GetTm();
3369 hour = tm.hour;
3370 min = tm.min;
3371 sec = tm.sec;
3372
3373 input = result;
3374 }
3375 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
3376 break;
3377
3378 case _T('y'): // year without century (00-99)
3379 if ( !GetNumericToken(width, input, &num) || (num > 99) )
3380 {
3381 // no match
3382 return (wxChar *)NULL;
3383 }
3384
3385 haveYear = true;
3386
3387 // TODO should have an option for roll over date instead of
3388 // hard coding it here
3389 year = (num > 30 ? 1900 : 2000) + (wxDateTime_t)num;
3390 break;
3391
3392 case _T('Y'): // year with century
3393 if ( !GetNumericToken(width, input, &num) )
3394 {
3395 // no match
3396 return (wxChar *)NULL;
3397 }
3398
3399 haveYear = true;
3400 year = (wxDateTime_t)num;
3401 break;
3402
3403 case _T('Z'): // timezone name
3404 wxFAIL_MSG(_T("TODO"));
3405 break;
3406
3407 case _T('%'): // a percent sign
3408 if ( *input++ != _T('%') )
3409 {
3410 // no match
3411 return (wxChar *)NULL;
3412 }
3413 break;
3414
3415 case 0: // the end of string
3416 wxFAIL_MSG(_T("unexpected format end"));
3417
3418 // fall through
3419
3420 default: // not a known format spec
3421 return (wxChar *)NULL;
3422 }
3423 }
3424
3425 // format matched, try to construct a date from what we have now
3426 Tm tmDef;
3427 if ( dateDef.IsValid() )
3428 {
3429 // take this date as default
3430 tmDef = dateDef.GetTm();
3431 }
3432 else if ( IsValid() )
3433 {
3434 // if this date is valid, don't change it
3435 tmDef = GetTm();
3436 }
3437 else
3438 {
3439 // no default and this date is invalid - fall back to Today()
3440 tmDef = Today().GetTm();
3441 }
3442
3443 Tm tm = tmDef;
3444
3445 // set the date
3446 if ( haveYear )
3447 {
3448 tm.year = year;
3449 }
3450
3451 // TODO we don't check here that the values are consistent, if both year
3452 // day and month/day were found, we just ignore the year day and we
3453 // also always ignore the week day
3454 if ( haveMon && haveDay )
3455 {
3456 if ( mday > GetNumOfDaysInMonth(tm.year, mon) )
3457 {
3458 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
3459
3460 return (wxChar *)NULL;
3461 }
3462
3463 tm.mon = mon;
3464 tm.mday = mday;
3465 }
3466 else if ( haveYDay )
3467 {
3468 if ( yday > GetNumberOfDays(tm.year) )
3469 {
3470 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
3471
3472 return (wxChar *)NULL;
3473 }
3474
3475 Tm tm2 = wxDateTime(1, Jan, tm.year).SetToYearDay(yday).GetTm();
3476
3477 tm.mon = tm2.mon;
3478 tm.mday = tm2.mday;
3479 }
3480
3481 // deal with AM/PM
3482 if ( haveHour && hourIsIn12hFormat && isPM )
3483 {
3484 // translate to 24hour format
3485 hour += 12;
3486 }
3487 //else: either already in 24h format or no translation needed
3488
3489 // set the time
3490 if ( haveHour )
3491 {
3492 tm.hour = hour;
3493 }
3494
3495 if ( haveMin )
3496 {
3497 tm.min = min;
3498 }
3499
3500 if ( haveSec )
3501 {
3502 tm.sec = sec;
3503 }
3504
3505 Set(tm);
3506
3507 // finally check that the week day is consistent -- if we had it
3508 if ( haveWDay && GetWeekDay() != wday )
3509 {
3510 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
3511
3512 return NULL;
3513 }
3514
3515 return input;
3516 }
3517
3518 const wxChar *wxDateTime::ParseDateTime(const wxChar *date)
3519 {
3520 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
3521
3522 // Set to current day and hour, so strings like '14:00' becomes today at
3523 // 14, not some other random date
3524 wxDateTime dtDate = wxDateTime::Today();
3525 wxDateTime dtTime = wxDateTime::Today();
3526
3527 const wxChar* pchTime;
3528
3529 // Try to parse the beginning of the string as a date
3530 const wxChar* pchDate = dtDate.ParseDate(date);
3531
3532 // We got a date in the beginning, see if there is a time specified after the date
3533 if ( pchDate )
3534 {
3535 // Skip spaces, as the ParseTime() function fails on spaces
3536 while ( wxIsspace(*pchDate) )
3537 pchDate++;
3538
3539 pchTime = dtTime.ParseTime(pchDate);
3540 }
3541 else // no date in the beginning
3542 {
3543 // check and see if we have a time followed by a date
3544 pchTime = dtTime.ParseTime(date);
3545 if ( pchTime )
3546 {
3547 while ( wxIsspace(*pchTime) )
3548 pchTime++;
3549
3550 pchDate = dtDate.ParseDate(pchTime);
3551 }
3552 }
3553
3554 // If we have a date specified, set our own data to the same date
3555 if ( !pchDate || !pchTime )
3556 return NULL;
3557
3558 Set(dtDate.GetDay(), dtDate.GetMonth(), dtDate.GetYear(),
3559 dtTime.GetHour(), dtTime.GetMinute(), dtTime.GetSecond(),
3560 dtTime.GetMillisecond());
3561
3562 // Return endpoint of scan
3563 return pchDate > pchTime ? pchDate : pchTime;
3564 }
3565
3566 const wxChar *wxDateTime::ParseDate(const wxChar *date)
3567 {
3568 // this is a simplified version of ParseDateTime() which understands only
3569 // "today" (for wxDate compatibility) and digits only otherwise (and not
3570 // all esoteric constructions ParseDateTime() knows about)
3571
3572 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
3573
3574 const wxChar *p = date;
3575 while ( wxIsspace(*p) )
3576 p++;
3577
3578 // some special cases
3579 static struct
3580 {
3581 const wxChar *str;
3582 int dayDiffFromToday;
3583 } literalDates[] =
3584 {
3585 { wxTRANSLATE("today"), 0 },
3586 { wxTRANSLATE("yesterday"), -1 },
3587 { wxTRANSLATE("tomorrow"), 1 },
3588 };
3589
3590 for ( size_t n = 0; n < WXSIZEOF(literalDates); n++ )
3591 {
3592 wxString date = wxGetTranslation(literalDates[n].str);
3593 size_t len = date.length();
3594 if ( wxStrlen(p) >= len )
3595 {
3596 wxString str(p, len);
3597 if ( str.CmpNoCase(date) == 0 )
3598 {
3599 // nothing can follow this, so stop here
3600 p += len;
3601
3602 int dayDiffFromToday = literalDates[n].dayDiffFromToday;
3603 *this = Today();
3604 if ( dayDiffFromToday )
3605 {
3606 *this += wxDateSpan::Days(dayDiffFromToday);
3607 }
3608
3609 return p;
3610 }
3611 }
3612 }
3613
3614 // We try to guess what we have here: for each new (numeric) token, we
3615 // determine if it can be a month, day or a year. Of course, there is an
3616 // ambiguity as some numbers may be days as well as months, so we also
3617 // have the ability to back track.
3618
3619 // what do we have?
3620 bool haveDay = false, // the months day?
3621 haveWDay = false, // the day of week?
3622 haveMon = false, // the month?
3623 haveYear = false; // the year?
3624
3625 // and the value of the items we have (init them to get rid of warnings)
3626 WeekDay wday = Inv_WeekDay;
3627 wxDateTime_t day = 0;
3628 wxDateTime::Month mon = Inv_Month;
3629 int year = 0;
3630
3631 // tokenize the string
3632 size_t nPosCur = 0;
3633 static const wxChar *dateDelimiters = _T(".,/-\t\r\n ");
3634 wxStringTokenizer tok(p, dateDelimiters);
3635 while ( tok.HasMoreTokens() )
3636 {
3637 wxString token = tok.GetNextToken();
3638 if ( !token )
3639 continue;
3640
3641 // is it a number?
3642 unsigned long val;
3643 if ( token.ToULong(&val) )
3644 {
3645 // guess what this number is
3646
3647 bool isDay = false,
3648 isMonth = false,
3649 isYear = false;
3650
3651 if ( !haveMon && val > 0 && val <= 12 )
3652 {
3653 // assume it is month
3654 isMonth = true;
3655 }
3656 else // not the month
3657 {
3658 if ( haveDay )
3659 {
3660 // this can only be the year
3661 isYear = true;
3662 }
3663 else // may be either day or year
3664 {
3665 wxDateTime_t max_days = (wxDateTime_t)(
3666 haveMon
3667 ? GetNumOfDaysInMonth(haveYear ? year : Inv_Year, mon)
3668 : 31
3669 );
3670
3671 // can it be day?
3672 if ( (val == 0) || (val > (unsigned long)max_days) )
3673 {
3674 // no
3675 isYear = true;
3676 }
3677 else // yes, suppose it's the day
3678 {
3679 isDay = true;
3680 }
3681 }
3682 }
3683
3684 if ( isYear )
3685 {
3686 if ( haveYear )
3687 break;
3688
3689 haveYear = true;
3690
3691 year = (wxDateTime_t)val;
3692 }
3693 else if ( isDay )
3694 {
3695 if ( haveDay )
3696 break;
3697
3698 haveDay = true;
3699
3700 day = (wxDateTime_t)val;
3701 }
3702 else if ( isMonth )
3703 {
3704 haveMon = true;
3705
3706 mon = (Month)(val - 1);
3707 }
3708 }
3709 else // not a number
3710 {
3711 // be careful not to overwrite the current mon value
3712 Month mon2 = GetMonthFromName(token, Name_Full | Name_Abbr);
3713 if ( mon2 != Inv_Month )
3714 {
3715 // it's a month
3716 if ( haveMon )
3717 {
3718 // but we already have a month - maybe we guessed wrong?
3719 if ( !haveDay )
3720 {
3721 // no need to check in month range as always < 12, but
3722 // the days are counted from 1 unlike the months
3723 day = (wxDateTime_t)(mon + 1);
3724 haveDay = true;
3725 }
3726 else
3727 {
3728 // could possible be the year (doesn't the year come
3729 // before the month in the japanese format?) (FIXME)
3730 break;
3731 }
3732 }
3733
3734 mon = mon2;
3735
3736 haveMon = true;
3737 }
3738 else // not a valid month name
3739 {
3740 wday = GetWeekDayFromName(token, Name_Full | Name_Abbr);
3741 if ( wday != Inv_WeekDay )
3742 {
3743 // a week day
3744 if ( haveWDay )
3745 {
3746 break;
3747 }
3748
3749 haveWDay = true;
3750 }
3751 else // not a valid weekday name
3752 {
3753 // try the ordinals
3754 static const wxChar *ordinals[] =
3755 {
3756 wxTRANSLATE("first"),
3757 wxTRANSLATE("second"),
3758 wxTRANSLATE("third"),
3759 wxTRANSLATE("fourth"),
3760 wxTRANSLATE("fifth"),
3761 wxTRANSLATE("sixth"),
3762 wxTRANSLATE("seventh"),
3763 wxTRANSLATE("eighth"),
3764 wxTRANSLATE("ninth"),
3765 wxTRANSLATE("tenth"),
3766 wxTRANSLATE("eleventh"),
3767 wxTRANSLATE("twelfth"),
3768 wxTRANSLATE("thirteenth"),
3769 wxTRANSLATE("fourteenth"),
3770 wxTRANSLATE("fifteenth"),
3771 wxTRANSLATE("sixteenth"),
3772 wxTRANSLATE("seventeenth"),
3773 wxTRANSLATE("eighteenth"),
3774 wxTRANSLATE("nineteenth"),
3775 wxTRANSLATE("twentieth"),
3776 // that's enough - otherwise we'd have problems with
3777 // composite (or not) ordinals
3778 };
3779
3780 size_t n;
3781 for ( n = 0; n < WXSIZEOF(ordinals); n++ )
3782 {
3783 if ( token.CmpNoCase(ordinals[n]) == 0 )
3784 {
3785 break;
3786 }
3787 }
3788
3789 if ( n == WXSIZEOF(ordinals) )
3790 {
3791 // stop here - something unknown
3792 break;
3793 }
3794
3795 // it's a day
3796 if ( haveDay )
3797 {
3798 // don't try anything here (as in case of numeric day
3799 // above) - the symbolic day spec should always
3800 // precede the month/year
3801 break;
3802 }
3803
3804 haveDay = true;
3805
3806 day = (wxDateTime_t)(n + 1);
3807 }
3808 }
3809 }
3810
3811 nPosCur = tok.GetPosition();
3812 }
3813
3814 // either no more tokens or the scan was stopped by something we couldn't
3815 // parse - in any case, see if we can construct a date from what we have
3816 if ( !haveDay && !haveWDay )
3817 {
3818 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
3819
3820 return (wxChar *)NULL;
3821 }
3822
3823 if ( haveWDay && (haveMon || haveYear || haveDay) &&
3824 !(haveDay && haveMon && haveYear) )
3825 {
3826 // without adjectives (which we don't support here) the week day only
3827 // makes sense completely separately or with the full date
3828 // specification (what would "Wed 1999" mean?)
3829 return (wxChar *)NULL;
3830 }
3831
3832 if ( !haveWDay && haveYear && !(haveDay && haveMon) )
3833 {
3834 // may be we have month and day instead of day and year?
3835 if ( haveDay && !haveMon )
3836 {
3837 if ( day <= 12 )
3838 {
3839 // exchange day and month
3840 mon = (wxDateTime::Month)(day - 1);
3841
3842 // we're in the current year then
3843 if ( (year > 0) && (year <= (int)GetNumOfDaysInMonth(Inv_Year, mon)) )
3844 {
3845 day = (wxDateTime_t)year;
3846
3847 haveMon = true;
3848 haveYear = false;
3849 }
3850 //else: no, can't exchange, leave haveMon == false
3851 }
3852 }
3853
3854 if ( !haveMon )
3855 {
3856 // if we give the year, month and day must be given too
3857 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
3858
3859 return (wxChar *)NULL;
3860 }
3861 }
3862
3863 if ( !haveMon )
3864 {
3865 mon = GetCurrentMonth();
3866 }
3867
3868 if ( !haveYear )
3869 {
3870 year = GetCurrentYear();
3871 }
3872
3873 if ( haveDay )
3874 {
3875 Set(day, mon, year);
3876
3877 if ( haveWDay )
3878 {
3879 // check that it is really the same
3880 if ( GetWeekDay() != wday )
3881 {
3882 // inconsistency detected
3883 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
3884
3885 return (wxChar *)NULL;
3886 }
3887 }
3888 }
3889 else // haveWDay
3890 {
3891 *this = Today();
3892
3893 SetToWeekDayInSameWeek(wday);
3894 }
3895
3896 // return the pointer to the first unparsed char
3897 p += nPosCur;
3898 if ( nPosCur && wxStrchr(dateDelimiters, *(p - 1)) )
3899 {
3900 // if we couldn't parse the token after the delimiter, put back the
3901 // delimiter as well
3902 p--;
3903 }
3904
3905 return p;
3906 }
3907
3908 const wxChar *wxDateTime::ParseTime(const wxChar *time)
3909 {
3910 wxCHECK_MSG( time, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
3911
3912 // first try some extra things
3913 static const struct
3914 {
3915 const wxChar *name;
3916 wxDateTime_t hour;
3917 } stdTimes[] =
3918 {
3919 { wxTRANSLATE("noon"), 12 },
3920 { wxTRANSLATE("midnight"), 00 },
3921 // anything else?
3922 };
3923
3924 for ( size_t n = 0; n < WXSIZEOF(stdTimes); n++ )
3925 {
3926 wxString timeString = wxGetTranslation(stdTimes[n].name);
3927 size_t len = timeString.length();
3928 if ( timeString.CmpNoCase(wxString(time, len)) == 0 )
3929 {
3930 // casts required by DigitalMars
3931 Set(stdTimes[n].hour, wxDateTime_t(0), wxDateTime_t(0));
3932
3933 return time + len;
3934 }
3935 }
3936
3937 // try all time formats we may think about in the order from longest to
3938 // shortest
3939
3940 // 12hour with AM/PM?
3941 const wxChar *result = ParseFormat(time, _T("%I:%M:%S %p"));
3942
3943 if ( !result )
3944 {
3945 // normally, it's the same, but why not try it?
3946 result = ParseFormat(time, _T("%H:%M:%S"));
3947 }
3948
3949 if ( !result )
3950 {
3951 // 12hour with AM/PM but without seconds?
3952 result = ParseFormat(time, _T("%I:%M %p"));
3953 }
3954
3955 if ( !result )
3956 {
3957 // without seconds?
3958 result = ParseFormat(time, _T("%H:%M"));
3959 }
3960
3961 if ( !result )
3962 {
3963 // just the hour and AM/PM?
3964 result = ParseFormat(time, _T("%I %p"));
3965 }
3966
3967 if ( !result )
3968 {
3969 // just the hour?
3970 result = ParseFormat(time, _T("%H"));
3971 }
3972
3973 if ( !result )
3974 {
3975 // parse the standard format: normally it is one of the formats above
3976 // but it may be set to something completely different by the user
3977 result = ParseFormat(time, _T("%X"));
3978 }
3979
3980 // TODO: parse timezones
3981
3982 return result;
3983 }
3984
3985 // ----------------------------------------------------------------------------
3986 // Workdays and holidays support
3987 // ----------------------------------------------------------------------------
3988
3989 bool wxDateTime::IsWorkDay(Country WXUNUSED(country)) const
3990 {
3991 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
3992 }
3993
3994 // ============================================================================
3995 // wxDateSpan
3996 // ============================================================================
3997
3998 wxDateSpan WXDLLIMPEXP_BASE operator*(int n, const wxDateSpan& ds)
3999 {
4000 wxDateSpan ds1(ds);
4001 return ds1.Multiply(n);
4002 }
4003
4004 // ============================================================================
4005 // wxTimeSpan
4006 // ============================================================================
4007
4008 wxTimeSpan WXDLLIMPEXP_BASE operator*(int n, const wxTimeSpan& ts)
4009 {
4010 return wxTimeSpan(ts).Multiply(n);
4011 }
4012
4013 // this enum is only used in wxTimeSpan::Format() below but we can't declare
4014 // it locally to the method as it provokes an internal compiler error in egcs
4015 // 2.91.60 when building with -O2
4016 enum TimeSpanPart
4017 {
4018 Part_Week,
4019 Part_Day,
4020 Part_Hour,
4021 Part_Min,
4022 Part_Sec,
4023 Part_MSec
4024 };
4025
4026 // not all strftime(3) format specifiers make sense here because, for example,
4027 // a time span doesn't have a year nor a timezone
4028 //
4029 // Here are the ones which are supported (all of them are supported by strftime
4030 // as well):
4031 // %H hour in 24 hour format
4032 // %M minute (00 - 59)
4033 // %S second (00 - 59)
4034 // %% percent sign
4035 //
4036 // Also, for MFC CTimeSpan compatibility, we support
4037 // %D number of days
4038 //
4039 // And, to be better than MFC :-), we also have
4040 // %E number of wEeks
4041 // %l milliseconds (000 - 999)
4042 wxString wxTimeSpan::Format(const wxChar *format) const
4043 {
4044 wxCHECK_MSG( format, wxEmptyString, _T("NULL format in wxTimeSpan::Format") );
4045
4046 wxString str;
4047 str.Alloc(wxStrlen(format));
4048
4049 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
4050 //
4051 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
4052 // question is what should ts.Format("%S") do? The code here returns "3273"
4053 // in this case (i.e. the total number of seconds, not just seconds % 60)
4054 // because, for me, this call means "give me entire time interval in
4055 // seconds" and not "give me the seconds part of the time interval"
4056 //
4057 // If we agree that it should behave like this, it is clear that the
4058 // interpretation of each format specifier depends on the presence of the
4059 // other format specs in the string: if there was "%H" before "%M", we
4060 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
4061
4062 // we remember the most important unit found so far
4063 TimeSpanPart partBiggest = Part_MSec;
4064
4065 for ( const wxChar *pch = format; *pch; pch++ )
4066 {
4067 wxChar ch = *pch;
4068
4069 if ( ch == _T('%') )
4070 {
4071 // the start of the format specification of the printf() below
4072 wxString fmtPrefix = _T('%');
4073
4074 // the number
4075 long n;
4076
4077 ch = *++pch; // get the format spec char
4078 switch ( ch )
4079 {
4080 default:
4081 wxFAIL_MSG( _T("invalid format character") );
4082 // fall through
4083
4084 case _T('%'):
4085 str += ch;
4086
4087 // skip the part below switch
4088 continue;
4089
4090 case _T('D'):
4091 n = GetDays();
4092 if ( partBiggest < Part_Day )
4093 {
4094 n %= DAYS_PER_WEEK;
4095 }
4096 else
4097 {
4098 partBiggest = Part_Day;
4099 }
4100 break;
4101
4102 case _T('E'):
4103 partBiggest = Part_Week;
4104 n = GetWeeks();
4105 break;
4106
4107 case _T('H'):
4108 n = GetHours();
4109 if ( partBiggest < Part_Hour )
4110 {
4111 n %= HOURS_PER_DAY;
4112 }
4113 else
4114 {
4115 partBiggest = Part_Hour;
4116 }
4117
4118 fmtPrefix += _T("02");
4119 break;
4120
4121 case _T('l'):
4122 n = GetMilliseconds().ToLong();
4123 if ( partBiggest < Part_MSec )
4124 {
4125 n %= 1000;
4126 }
4127 //else: no need to reset partBiggest to Part_MSec, it is
4128 // the least significant one anyhow
4129
4130 fmtPrefix += _T("03");
4131 break;
4132
4133 case _T('M'):
4134 n = GetMinutes();
4135 if ( partBiggest < Part_Min )
4136 {
4137 n %= MIN_PER_HOUR;
4138 }
4139 else
4140 {
4141 partBiggest = Part_Min;
4142 }
4143
4144 fmtPrefix += _T("02");
4145 break;
4146
4147 case _T('S'):
4148 n = GetSeconds().ToLong();
4149 if ( partBiggest < Part_Sec )
4150 {
4151 n %= SEC_PER_MIN;
4152 }
4153 else
4154 {
4155 partBiggest = Part_Sec;
4156 }
4157
4158 fmtPrefix += _T("02");
4159 break;
4160 }
4161
4162 str += wxString::Format(fmtPrefix + _T("ld"), n);
4163 }
4164 else
4165 {
4166 // normal character, just copy
4167 str += ch;
4168 }
4169 }
4170
4171 return str;
4172 }
4173
4174 // ============================================================================
4175 // wxDateTimeHolidayAuthority and related classes
4176 // ============================================================================
4177
4178 #include "wx/arrimpl.cpp"
4179
4180 WX_DEFINE_OBJARRAY(wxDateTimeArray);
4181
4182 static int wxCMPFUNC_CONV
4183 wxDateTimeCompareFunc(wxDateTime **first, wxDateTime **second)
4184 {
4185 wxDateTime dt1 = **first,
4186 dt2 = **second;
4187
4188 return dt1 == dt2 ? 0 : dt1 < dt2 ? -1 : +1;
4189 }
4190
4191 // ----------------------------------------------------------------------------
4192 // wxDateTimeHolidayAuthority
4193 // ----------------------------------------------------------------------------
4194
4195 wxHolidayAuthoritiesArray wxDateTimeHolidayAuthority::ms_authorities;
4196
4197 /* static */
4198 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime& dt)
4199 {
4200 size_t count = ms_authorities.size();
4201 for ( size_t n = 0; n < count; n++ )
4202 {
4203 if ( ms_authorities[n]->DoIsHoliday(dt) )
4204 {
4205 return true;
4206 }
4207 }
4208
4209 return false;
4210 }
4211
4212 /* static */
4213 size_t
4214 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime& dtStart,
4215 const wxDateTime& dtEnd,
4216 wxDateTimeArray& holidays)
4217 {
4218 wxDateTimeArray hol;
4219
4220 holidays.Clear();
4221
4222 size_t count = ms_authorities.size();
4223 for ( size_t nAuth = 0; nAuth < count; nAuth++ )
4224 {
4225 ms_authorities[nAuth]->DoGetHolidaysInRange(dtStart, dtEnd, hol);
4226
4227 WX_APPEND_ARRAY(holidays, hol);
4228 }
4229
4230 holidays.Sort(wxDateTimeCompareFunc);
4231
4232 return holidays.size();
4233 }
4234
4235 /* static */
4236 void wxDateTimeHolidayAuthority::ClearAllAuthorities()
4237 {
4238 WX_CLEAR_ARRAY(ms_authorities);
4239 }
4240
4241 /* static */
4242 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority *auth)
4243 {
4244 ms_authorities.push_back(auth);
4245 }
4246
4247 wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
4248 {
4249 // required here for Darwin
4250 }
4251
4252 // ----------------------------------------------------------------------------
4253 // wxDateTimeWorkDays
4254 // ----------------------------------------------------------------------------
4255
4256 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime& dt) const
4257 {
4258 wxDateTime::WeekDay wd = dt.GetWeekDay();
4259
4260 return (wd == wxDateTime::Sun) || (wd == wxDateTime::Sat);
4261 }
4262
4263 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime& dtStart,
4264 const wxDateTime& dtEnd,
4265 wxDateTimeArray& holidays) const
4266 {
4267 if ( dtStart > dtEnd )
4268 {
4269 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
4270
4271 return 0u;
4272 }
4273
4274 holidays.Empty();
4275
4276 // instead of checking all days, start with the first Sat after dtStart and
4277 // end with the last Sun before dtEnd
4278 wxDateTime dtSatFirst = dtStart.GetNextWeekDay(wxDateTime::Sat),
4279 dtSatLast = dtEnd.GetPrevWeekDay(wxDateTime::Sat),
4280 dtSunFirst = dtStart.GetNextWeekDay(wxDateTime::Sun),
4281 dtSunLast = dtEnd.GetPrevWeekDay(wxDateTime::Sun),
4282 dt;
4283
4284 for ( dt = dtSatFirst; dt <= dtSatLast; dt += wxDateSpan::Week() )
4285 {
4286 holidays.Add(dt);
4287 }
4288
4289 for ( dt = dtSunFirst; dt <= dtSunLast; dt += wxDateSpan::Week() )
4290 {
4291 holidays.Add(dt);
4292 }
4293
4294 return holidays.GetCount();
4295 }
4296
4297 // ============================================================================
4298 // other helper functions
4299 // ============================================================================
4300
4301 // ----------------------------------------------------------------------------
4302 // iteration helpers: can be used to write a for loop over enum variable like
4303 // this:
4304 // for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
4305 // ----------------------------------------------------------------------------
4306
4307 WXDLLIMPEXP_BASE void wxNextMonth(wxDateTime::Month& m)
4308 {
4309 wxASSERT_MSG( m < wxDateTime::Inv_Month, _T("invalid month") );
4310
4311 // no wrapping or the for loop above would never end!
4312 m = (wxDateTime::Month)(m + 1);
4313 }
4314
4315 WXDLLIMPEXP_BASE void wxPrevMonth(wxDateTime::Month& m)
4316 {
4317 wxASSERT_MSG( m < wxDateTime::Inv_Month, _T("invalid month") );
4318
4319 m = m == wxDateTime::Jan ? wxDateTime::Inv_Month
4320 : (wxDateTime::Month)(m - 1);
4321 }
4322
4323 WXDLLIMPEXP_BASE void wxNextWDay(wxDateTime::WeekDay& wd)
4324 {
4325 wxASSERT_MSG( wd < wxDateTime::Inv_WeekDay, _T("invalid week day") );
4326
4327 // no wrapping or the for loop above would never end!
4328 wd = (wxDateTime::WeekDay)(wd + 1);
4329 }
4330
4331 WXDLLIMPEXP_BASE void wxPrevWDay(wxDateTime::WeekDay& wd)
4332 {
4333 wxASSERT_MSG( wd < wxDateTime::Inv_WeekDay, _T("invalid week day") );
4334
4335 wd = wd == wxDateTime::Sun ? wxDateTime::Inv_WeekDay
4336 : (wxDateTime::WeekDay)(wd - 1);
4337 }
4338
4339 #endif // wxUSE_DATETIME