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