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