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