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