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