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