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