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