]> git.saurik.com Git - wxWidgets.git/blob - src/common/datetime.cpp
Applied [ 821234 ] Fix: erroneous assertion failed wxListBox::SetSelection
[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 m_time.Assign(jdn);
1291
1292 return *this;
1293 }
1294
1295 wxDateTime& wxDateTime::ResetTime()
1296 {
1297 Tm tm = GetTm();
1298
1299 if ( tm.hour || tm.min || tm.sec || tm.msec )
1300 {
1301 tm.msec =
1302 tm.sec =
1303 tm.min =
1304 tm.hour = 0;
1305
1306 Set(tm);
1307 }
1308
1309 return *this;
1310 }
1311
1312 // ----------------------------------------------------------------------------
1313 // DOS Date and Time Format functions
1314 // ----------------------------------------------------------------------------
1315 // the dos date and time value is an unsigned 32 bit value in the format:
1316 // YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
1317 //
1318 // Y = year offset from 1980 (0-127)
1319 // M = month (1-12)
1320 // D = day of month (1-31)
1321 // h = hour (0-23)
1322 // m = minute (0-59)
1323 // s = bisecond (0-29) each bisecond indicates two seconds
1324 // ----------------------------------------------------------------------------
1325
1326 wxDateTime& wxDateTime::SetFromDOS(unsigned long ddt)
1327 {
1328 struct tm tm;
1329
1330 long year = ddt & 0xFE000000;
1331 year >>= 25;
1332 year += 80;
1333 tm.tm_year = year;
1334
1335 long month = ddt & 0x1E00000;
1336 month >>= 21;
1337 month -= 1;
1338 tm.tm_mon = month;
1339
1340 long day = ddt & 0x1F0000;
1341 day >>= 16;
1342 tm.tm_mday = day;
1343
1344 long hour = ddt & 0xF800;
1345 hour >>= 11;
1346 tm.tm_hour = hour;
1347
1348 long minute = ddt & 0x7E0;
1349 minute >>= 5;
1350 tm.tm_min = minute;
1351
1352 long second = ddt & 0x1F;
1353 tm.tm_sec = second * 2;
1354
1355 return Set(mktime(&tm));
1356 }
1357
1358 unsigned long wxDateTime::GetAsDOS() const
1359 {
1360 unsigned long ddt;
1361 time_t ticks = GetTicks();
1362 struct tm *tm = localtime(&ticks);
1363
1364 long year = tm->tm_year;
1365 year -= 80;
1366 year <<= 25;
1367
1368 long month = tm->tm_mon;
1369 month += 1;
1370 month <<= 21;
1371
1372 long day = tm->tm_mday;
1373 day <<= 16;
1374
1375 long hour = tm->tm_hour;
1376 hour <<= 11;
1377
1378 long minute = tm->tm_min;
1379 minute <<= 5;
1380
1381 long second = tm->tm_sec;
1382 second /= 2;
1383
1384 ddt = year | month | day | hour | minute | second;
1385 return ddt;
1386 }
1387
1388 // ----------------------------------------------------------------------------
1389 // time_t <-> broken down time conversions
1390 // ----------------------------------------------------------------------------
1391
1392 wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
1393 {
1394 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1395
1396 time_t time = GetTicks();
1397 if ( time != (time_t)-1 )
1398 {
1399 // use C RTL functions
1400 tm *tm;
1401 if ( tz.GetOffset() == -GetTimeZone() )
1402 {
1403 // we are working with local time
1404 tm = localtime(&time);
1405
1406 // should never happen
1407 wxCHECK_MSG( tm, Tm(), _T("localtime() failed") );
1408 }
1409 else
1410 {
1411 time += (time_t)tz.GetOffset();
1412 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
1413 int time2 = (int) time;
1414 if ( time2 >= 0 )
1415 #else
1416 if ( time >= 0 )
1417 #endif
1418 {
1419 tm = gmtime(&time);
1420
1421 // should never happen
1422 wxCHECK_MSG( tm, Tm(), _T("gmtime() failed") );
1423 }
1424 else
1425 {
1426 tm = (struct tm *)NULL;
1427 }
1428 }
1429
1430 if ( tm )
1431 {
1432 // adjust the milliseconds
1433 Tm tm2(*tm, tz);
1434 long timeOnly = (m_time % MILLISECONDS_PER_DAY).ToLong();
1435 tm2.msec = (wxDateTime_t)(timeOnly % 1000);
1436 return tm2;
1437 }
1438 //else: use generic code below
1439 }
1440
1441 // remember the time and do the calculations with the date only - this
1442 // eliminates rounding errors of the floating point arithmetics
1443
1444 wxLongLong timeMidnight = m_time + tz.GetOffset() * 1000;
1445
1446 long timeOnly = (timeMidnight % MILLISECONDS_PER_DAY).ToLong();
1447
1448 // we want to always have positive time and timeMidnight to be really
1449 // the midnight before it
1450 if ( timeOnly < 0 )
1451 {
1452 timeOnly = MILLISECONDS_PER_DAY + timeOnly;
1453 }
1454
1455 timeMidnight -= timeOnly;
1456
1457 // calculate the Gregorian date from JDN for the midnight of our date:
1458 // this will yield day, month (in 1..12 range) and year
1459
1460 // actually, this is the JDN for the noon of the previous day
1461 long jdn = (timeMidnight / MILLISECONDS_PER_DAY).ToLong() + EPOCH_JDN;
1462
1463 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1464
1465 wxASSERT_MSG( jdn > -2, _T("JDN out of range") );
1466
1467 // calculate the century
1468 long temp = (jdn + JDN_OFFSET) * 4 - 1;
1469 long century = temp / DAYS_PER_400_YEARS;
1470
1471 // then the year and day of year (1 <= dayOfYear <= 366)
1472 temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3;
1473 long year = (century * 100) + (temp / DAYS_PER_4_YEARS);
1474 long dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
1475
1476 // and finally the month and day of the month
1477 temp = dayOfYear * 5 - 3;
1478 long month = temp / DAYS_PER_5_MONTHS;
1479 long day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
1480
1481 // month is counted from March - convert to normal
1482 if ( month < 10 )
1483 {
1484 month += 3;
1485 }
1486 else
1487 {
1488 year += 1;
1489 month -= 9;
1490 }
1491
1492 // year is offset by 4800
1493 year -= 4800;
1494
1495 // check that the algorithm gave us something reasonable
1496 wxASSERT_MSG( (0 < month) && (month <= 12), _T("invalid month") );
1497 wxASSERT_MSG( (1 <= day) && (day < 32), _T("invalid day") );
1498
1499 // construct Tm from these values
1500 Tm tm;
1501 tm.year = (int)year;
1502 tm.mon = (Month)(month - 1); // algorithm yields 1 for January, not 0
1503 tm.mday = (wxDateTime_t)day;
1504 tm.msec = (wxDateTime_t)(timeOnly % 1000);
1505 timeOnly -= tm.msec;
1506 timeOnly /= 1000; // now we have time in seconds
1507
1508 tm.sec = (wxDateTime_t)(timeOnly % 60);
1509 timeOnly -= tm.sec;
1510 timeOnly /= 60; // now we have time in minutes
1511
1512 tm.min = (wxDateTime_t)(timeOnly % 60);
1513 timeOnly -= tm.min;
1514
1515 tm.hour = (wxDateTime_t)(timeOnly / 60);
1516
1517 return tm;
1518 }
1519
1520 wxDateTime& wxDateTime::SetYear(int year)
1521 {
1522 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1523
1524 Tm tm(GetTm());
1525 tm.year = year;
1526 Set(tm);
1527
1528 return *this;
1529 }
1530
1531 wxDateTime& wxDateTime::SetMonth(Month month)
1532 {
1533 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1534
1535 Tm tm(GetTm());
1536 tm.mon = month;
1537 Set(tm);
1538
1539 return *this;
1540 }
1541
1542 wxDateTime& wxDateTime::SetDay(wxDateTime_t mday)
1543 {
1544 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1545
1546 Tm tm(GetTm());
1547 tm.mday = mday;
1548 Set(tm);
1549
1550 return *this;
1551 }
1552
1553 wxDateTime& wxDateTime::SetHour(wxDateTime_t hour)
1554 {
1555 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1556
1557 Tm tm(GetTm());
1558 tm.hour = hour;
1559 Set(tm);
1560
1561 return *this;
1562 }
1563
1564 wxDateTime& wxDateTime::SetMinute(wxDateTime_t min)
1565 {
1566 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1567
1568 Tm tm(GetTm());
1569 tm.min = min;
1570 Set(tm);
1571
1572 return *this;
1573 }
1574
1575 wxDateTime& wxDateTime::SetSecond(wxDateTime_t sec)
1576 {
1577 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1578
1579 Tm tm(GetTm());
1580 tm.sec = sec;
1581 Set(tm);
1582
1583 return *this;
1584 }
1585
1586 wxDateTime& wxDateTime::SetMillisecond(wxDateTime_t millisecond)
1587 {
1588 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1589
1590 // we don't need to use GetTm() for this one
1591 m_time -= m_time % 1000l;
1592 m_time += millisecond;
1593
1594 return *this;
1595 }
1596
1597 // ----------------------------------------------------------------------------
1598 // wxDateTime arithmetics
1599 // ----------------------------------------------------------------------------
1600
1601 wxDateTime& wxDateTime::Add(const wxDateSpan& diff)
1602 {
1603 Tm tm(GetTm());
1604
1605 tm.year += diff.GetYears();
1606 tm.AddMonths(diff.GetMonths());
1607
1608 // check that the resulting date is valid
1609 if ( tm.mday > GetNumOfDaysInMonth(tm.year, tm.mon) )
1610 {
1611 // We suppose that when adding one month to Jan 31 we want to get Feb
1612 // 28 (or 29), i.e. adding a month to the last day of the month should
1613 // give the last day of the next month which is quite logical.
1614 //
1615 // Unfortunately, there is no logic way to understand what should
1616 // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1617 // We make it Feb 28 (last day too), but it is highly questionable.
1618 tm.mday = GetNumOfDaysInMonth(tm.year, tm.mon);
1619 }
1620
1621 tm.AddDays(diff.GetTotalDays());
1622
1623 Set(tm);
1624
1625 wxASSERT_MSG( IsSameTime(tm),
1626 _T("Add(wxDateSpan) shouldn't modify time") );
1627
1628 return *this;
1629 }
1630
1631 // ----------------------------------------------------------------------------
1632 // Weekday and monthday stuff
1633 // ----------------------------------------------------------------------------
1634
1635 bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek,
1636 WeekDay weekday,
1637 WeekFlags flags)
1638 {
1639 wxASSERT_MSG( numWeek > 0,
1640 _T("invalid week number: weeks are counted from 1") );
1641
1642 int year = GetYear();
1643
1644 // Jan 4 always lies in the 1st week of the year
1645 Set(4, Jan, year);
1646 SetToWeekDayInSameWeek(weekday, flags) += wxDateSpan::Weeks(numWeek - 1);
1647
1648 if ( GetYear() != year )
1649 {
1650 // oops... numWeek was too big
1651 return FALSE;
1652 }
1653
1654 return TRUE;
1655 }
1656
1657 wxDateTime& wxDateTime::SetToLastMonthDay(Month month,
1658 int year)
1659 {
1660 // take the current month/year if none specified
1661 if ( year == Inv_Year )
1662 year = GetYear();
1663 if ( month == Inv_Month )
1664 month = GetMonth();
1665
1666 return Set(GetNumOfDaysInMonth(year, month), month, year);
1667 }
1668
1669 wxDateTime& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday, WeekFlags flags)
1670 {
1671 wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
1672
1673 int wdayThis = GetWeekDay();
1674 if ( weekday == wdayThis )
1675 {
1676 // nothing to do
1677 return *this;
1678 }
1679
1680 if ( flags == Default_First )
1681 {
1682 flags = GetCountry() == USA ? Sunday_First : Monday_First;
1683 }
1684
1685 // the logic below based on comparing weekday and wdayThis works if Sun (0)
1686 // is the first day in the week, but breaks down for Monday_First case so
1687 // we adjust the week days in this case
1688 if( flags == Monday_First )
1689 {
1690 if ( wdayThis == Sun )
1691 wdayThis += 7;
1692 }
1693 //else: Sunday_First, nothing to do
1694
1695 // go forward or back in time to the day we want
1696 if ( weekday < wdayThis )
1697 {
1698 return Subtract(wxDateSpan::Days(wdayThis - weekday));
1699 }
1700 else // weekday > wdayThis
1701 {
1702 return Add(wxDateSpan::Days(weekday - wdayThis));
1703 }
1704 }
1705
1706 wxDateTime& wxDateTime::SetToNextWeekDay(WeekDay weekday)
1707 {
1708 wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
1709
1710 int diff;
1711 WeekDay wdayThis = GetWeekDay();
1712 if ( weekday == wdayThis )
1713 {
1714 // nothing to do
1715 return *this;
1716 }
1717 else if ( weekday < wdayThis )
1718 {
1719 // need to advance a week
1720 diff = 7 - (wdayThis - weekday);
1721 }
1722 else // weekday > wdayThis
1723 {
1724 diff = weekday - wdayThis;
1725 }
1726
1727 return Add(wxDateSpan::Days(diff));
1728 }
1729
1730 wxDateTime& wxDateTime::SetToPrevWeekDay(WeekDay weekday)
1731 {
1732 wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
1733
1734 int diff;
1735 WeekDay wdayThis = GetWeekDay();
1736 if ( weekday == wdayThis )
1737 {
1738 // nothing to do
1739 return *this;
1740 }
1741 else if ( weekday > wdayThis )
1742 {
1743 // need to go to previous week
1744 diff = 7 - (weekday - wdayThis);
1745 }
1746 else // weekday < wdayThis
1747 {
1748 diff = wdayThis - weekday;
1749 }
1750
1751 return Subtract(wxDateSpan::Days(diff));
1752 }
1753
1754 bool wxDateTime::SetToWeekDay(WeekDay weekday,
1755 int n,
1756 Month month,
1757 int year)
1758 {
1759 wxCHECK_MSG( weekday != Inv_WeekDay, FALSE, _T("invalid weekday") );
1760
1761 // we don't check explicitly that -5 <= n <= 5 because we will return FALSE
1762 // anyhow in such case - but may be should still give an assert for it?
1763
1764 // take the current month/year if none specified
1765 ReplaceDefaultYearMonthWithCurrent(&year, &month);
1766
1767 wxDateTime dt;
1768
1769 // TODO this probably could be optimised somehow...
1770
1771 if ( n > 0 )
1772 {
1773 // get the first day of the month
1774 dt.Set(1, month, year);
1775
1776 // get its wday
1777 WeekDay wdayFirst = dt.GetWeekDay();
1778
1779 // go to the first weekday of the month
1780 int diff = weekday - wdayFirst;
1781 if ( diff < 0 )
1782 diff += 7;
1783
1784 // add advance n-1 weeks more
1785 diff += 7*(n - 1);
1786
1787 dt += wxDateSpan::Days(diff);
1788 }
1789 else // count from the end of the month
1790 {
1791 // get the last day of the month
1792 dt.SetToLastMonthDay(month, year);
1793
1794 // get its wday
1795 WeekDay wdayLast = dt.GetWeekDay();
1796
1797 // go to the last weekday of the month
1798 int diff = wdayLast - weekday;
1799 if ( diff < 0 )
1800 diff += 7;
1801
1802 // and rewind n-1 weeks from there
1803 diff += 7*(-n - 1);
1804
1805 dt -= wxDateSpan::Days(diff);
1806 }
1807
1808 // check that it is still in the same month
1809 if ( dt.GetMonth() == month )
1810 {
1811 *this = dt;
1812
1813 return TRUE;
1814 }
1815 else
1816 {
1817 // no such day in this month
1818 return FALSE;
1819 }
1820 }
1821
1822 wxDateTime::wxDateTime_t wxDateTime::GetDayOfYear(const TimeZone& tz) const
1823 {
1824 Tm tm(GetTm(tz));
1825
1826 return gs_cumulatedDays[IsLeapYear(tm.year)][tm.mon] + tm.mday;
1827 }
1828
1829 wxDateTime::wxDateTime_t wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags,
1830 const TimeZone& tz) const
1831 {
1832 if ( flags == Default_First )
1833 {
1834 flags = GetCountry() == USA ? Sunday_First : Monday_First;
1835 }
1836
1837 wxDateTime_t nDayInYear = GetDayOfYear(tz);
1838 wxDateTime_t week;
1839
1840 WeekDay wd = GetWeekDay(tz);
1841 if ( flags == Sunday_First )
1842 {
1843 week = (nDayInYear - wd + 7) / 7;
1844 }
1845 else
1846 {
1847 // have to shift the week days values
1848 week = (nDayInYear - (wd - 1 + 7) % 7 + 7) / 7;
1849 }
1850
1851 // FIXME some more elegant way??
1852 WeekDay wdYearStart = wxDateTime(1, Jan, GetYear()).GetWeekDay();
1853 if ( wdYearStart == Wed || wdYearStart == Thu )
1854 {
1855 week++;
1856 }
1857
1858 return week;
1859 }
1860
1861 wxDateTime::wxDateTime_t wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags,
1862 const TimeZone& tz) const
1863 {
1864 Tm tm = GetTm(tz);
1865 wxDateTime dtMonthStart = wxDateTime(1, tm.mon, tm.year);
1866 int nWeek = GetWeekOfYear(flags) - dtMonthStart.GetWeekOfYear(flags) + 1;
1867 if ( nWeek < 0 )
1868 {
1869 // this may happen for January when Jan, 1 is the last week of the
1870 // previous year
1871 nWeek += IsLeapYear(tm.year - 1) ? 53 : 52;
1872 }
1873
1874 return (wxDateTime::wxDateTime_t)nWeek;
1875 }
1876
1877 wxDateTime& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday)
1878 {
1879 int year = GetYear();
1880 wxDATETIME_CHECK( (0 < yday) && (yday <= GetNumberOfDays(year)),
1881 _T("invalid year day") );
1882
1883 bool isLeap = IsLeapYear(year);
1884 for ( Month mon = Jan; mon < Inv_Month; wxNextMonth(mon) )
1885 {
1886 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
1887 // don't need it neither - because of the CHECK above we know that
1888 // yday lies in December then
1889 if ( (mon == Dec) || (yday <= gs_cumulatedDays[isLeap][mon + 1]) )
1890 {
1891 Set(yday - gs_cumulatedDays[isLeap][mon], mon, year);
1892
1893 break;
1894 }
1895 }
1896
1897 return *this;
1898 }
1899
1900 // ----------------------------------------------------------------------------
1901 // Julian day number conversion and related stuff
1902 // ----------------------------------------------------------------------------
1903
1904 double wxDateTime::GetJulianDayNumber() const
1905 {
1906 // JDN are always expressed for the GMT dates
1907 Tm tm(ToTimezone(GMT0).GetTm(GMT0));
1908
1909 double result = GetTruncatedJDN(tm.mday, tm.mon, tm.year);
1910
1911 // add the part GetTruncatedJDN() neglected
1912 result += 0.5;
1913
1914 // and now add the time: 86400 sec = 1 JDN
1915 return result + ((double)(60*(60*tm.hour + tm.min) + tm.sec)) / 86400;
1916 }
1917
1918 double wxDateTime::GetRataDie() const
1919 {
1920 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
1921 return GetJulianDayNumber() - 1721119.5 - 306;
1922 }
1923
1924 // ----------------------------------------------------------------------------
1925 // timezone and DST stuff
1926 // ----------------------------------------------------------------------------
1927
1928 int wxDateTime::IsDST(wxDateTime::Country country) const
1929 {
1930 wxCHECK_MSG( country == Country_Default, -1,
1931 _T("country support not implemented") );
1932
1933 // use the C RTL for the dates in the standard range
1934 time_t timet = GetTicks();
1935 if ( timet != (time_t)-1 )
1936 {
1937 tm *tm = localtime(&timet);
1938
1939 wxCHECK_MSG( tm, -1, _T("localtime() failed") );
1940
1941 return tm->tm_isdst;
1942 }
1943 else
1944 {
1945 int year = GetYear();
1946
1947 if ( !IsDSTApplicable(year, country) )
1948 {
1949 // no DST time in this year in this country
1950 return -1;
1951 }
1952
1953 return IsBetween(GetBeginDST(year, country), GetEndDST(year, country));
1954 }
1955 }
1956
1957 wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST)
1958 {
1959 long secDiff = GetTimeZone() + tz.GetOffset();
1960
1961 // we need to know whether DST is or not in effect for this date unless
1962 // the test disabled by the caller
1963 if ( !noDST && (IsDST() == 1) )
1964 {
1965 // FIXME we assume that the DST is always shifted by 1 hour
1966 secDiff -= 3600;
1967 }
1968
1969 return Subtract(wxTimeSpan::Seconds(secDiff));
1970 }
1971
1972 // ----------------------------------------------------------------------------
1973 // wxDateTime to/from text representations
1974 // ----------------------------------------------------------------------------
1975
1976 wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
1977 {
1978 wxCHECK_MSG( format, _T(""), _T("NULL format in wxDateTime::Format") );
1979
1980 // we have to use our own implementation if the date is out of range of
1981 // strftime() or if we use non standard specificators
1982 time_t time = GetTicks();
1983 if ( (time != (time_t)-1) && !wxStrstr(format, _T("%l")) )
1984 {
1985 // use strftime()
1986 tm *tm;
1987 if ( tz.GetOffset() == -GetTimeZone() )
1988 {
1989 // we are working with local time
1990 tm = localtime(&time);
1991
1992 // should never happen
1993 wxCHECK_MSG( tm, wxEmptyString, _T("localtime() failed") );
1994 }
1995 else
1996 {
1997 time += (int)tz.GetOffset();
1998
1999 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
2000 int time2 = (int) time;
2001 if ( time2 >= 0 )
2002 #else
2003 if ( time >= 0 )
2004 #endif
2005 {
2006 tm = gmtime(&time);
2007
2008 // should never happen
2009 wxCHECK_MSG( tm, wxEmptyString, _T("gmtime() failed") );
2010 }
2011 else
2012 {
2013 tm = (struct tm *)NULL;
2014 }
2015 }
2016
2017 if ( tm )
2018 {
2019 return CallStrftime(format, tm);
2020 }
2021 //else: use generic code below
2022 }
2023
2024 // we only parse ANSI C format specifications here, no POSIX 2
2025 // complications, no GNU extensions but we do add support for a "%l" format
2026 // specifier allowing to get the number of milliseconds
2027 Tm tm = GetTm(tz);
2028
2029 // used for calls to strftime() when we only deal with time
2030 struct tm tmTimeOnly;
2031 tmTimeOnly.tm_hour = tm.hour;
2032 tmTimeOnly.tm_min = tm.min;
2033 tmTimeOnly.tm_sec = tm.sec;
2034 tmTimeOnly.tm_wday = 0;
2035 tmTimeOnly.tm_yday = 0;
2036 tmTimeOnly.tm_mday = 1; // any date will do
2037 tmTimeOnly.tm_mon = 0;
2038 tmTimeOnly.tm_year = 76;
2039 tmTimeOnly.tm_isdst = 0; // no DST, we adjust for tz ourselves
2040
2041 wxString tmp, res, fmt;
2042 for ( const wxChar *p = format; *p; p++ )
2043 {
2044 if ( *p != _T('%') )
2045 {
2046 // copy as is
2047 res += *p;
2048
2049 continue;
2050 }
2051
2052 // set the default format
2053 switch ( *++p )
2054 {
2055 case _T('Y'): // year has 4 digits
2056 fmt = _T("%04d");
2057 break;
2058
2059 case _T('j'): // day of year has 3 digits
2060 case _T('l'): // milliseconds have 3 digits
2061 fmt = _T("%03d");
2062 break;
2063
2064 case _T('w'): // week day as number has only one
2065 fmt = _T("%d");
2066 break;
2067
2068 default:
2069 // it's either another valid format specifier in which case
2070 // the format is "%02d" (for all the rest) or we have the
2071 // field width preceding the format in which case it will
2072 // override the default format anyhow
2073 fmt = _T("%02d");
2074 }
2075
2076 bool restart = TRUE;
2077 while ( restart )
2078 {
2079 restart = FALSE;
2080
2081 // start of the format specification
2082 switch ( *p )
2083 {
2084 case _T('a'): // a weekday name
2085 case _T('A'):
2086 // second parameter should be TRUE for abbreviated names
2087 res += GetWeekDayName(tm.GetWeekDay(),
2088 *p == _T('a') ? Name_Abbr : Name_Full);
2089 break;
2090
2091 case _T('b'): // a month name
2092 case _T('B'):
2093 res += GetMonthName(tm.mon,
2094 *p == _T('b') ? Name_Abbr : Name_Full);
2095 break;
2096
2097 case _T('c'): // locale default date and time representation
2098 case _T('x'): // locale default date representation
2099 //
2100 // the problem: there is no way to know what do these format
2101 // specifications correspond to for the current locale.
2102 //
2103 // the solution: use a hack and still use strftime(): first
2104 // find the YEAR which is a year in the strftime() range (1970
2105 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
2106 // of the real year. Then make a copy of the format and
2107 // replace all occurences of YEAR in it with some unique
2108 // string not appearing anywhere else in it, then use
2109 // strftime() to format the date in year YEAR and then replace
2110 // YEAR back by the real year and the unique replacement
2111 // string back with YEAR. Notice that "all occurences of YEAR"
2112 // means all occurences of 4 digit as well as 2 digit form!
2113 //
2114 // the bugs: we assume that neither of %c nor %x contains any
2115 // fields which may change between the YEAR and real year. For
2116 // example, the week number (%U, %W) and the day number (%j)
2117 // will change if one of these years is leap and the other one
2118 // is not!
2119 {
2120 // find the YEAR: normally, for any year X, Jan 1 or the
2121 // year X + 28 is the same weekday as Jan 1 of X (because
2122 // the weekday advances by 1 for each normal X and by 2
2123 // for each leap X, hence by 5 every 4 years or by 35
2124 // which is 0 mod 7 every 28 years) but this rule breaks
2125 // down if there are years between X and Y which are
2126 // divisible by 4 but not leap (i.e. divisible by 100 but
2127 // not 400), hence the correction.
2128
2129 int yearReal = GetYear(tz);
2130 int mod28 = yearReal % 28;
2131
2132 // be careful to not go too far - we risk to leave the
2133 // supported range
2134 int year;
2135 if ( mod28 < 10 )
2136 {
2137 year = 1988 + mod28; // 1988 == 0 (mod 28)
2138 }
2139 else
2140 {
2141 year = 1970 + mod28 - 10; // 1970 == 10 (mod 28)
2142 }
2143
2144 int nCentury = year / 100,
2145 nCenturyReal = yearReal / 100;
2146
2147 // need to adjust for the years divisble by 400 which are
2148 // not leap but are counted like leap ones if we just take
2149 // the number of centuries in between for nLostWeekDays
2150 int nLostWeekDays = (nCentury - nCenturyReal) -
2151 (nCentury / 4 - nCenturyReal / 4);
2152
2153 // we have to gain back the "lost" weekdays: note that the
2154 // effect of this loop is to not do anything to
2155 // nLostWeekDays (which we won't use any more), but to
2156 // (indirectly) set the year correctly
2157 while ( (nLostWeekDays % 7) != 0 )
2158 {
2159 nLostWeekDays += year++ % 4 ? 1 : 2;
2160 }
2161
2162 // at any rate, we couldn't go further than 1988 + 9 + 28!
2163 wxASSERT_MSG( year < 2030,
2164 _T("logic error in wxDateTime::Format") );
2165
2166 wxString strYear, strYear2;
2167 strYear.Printf(_T("%d"), year);
2168 strYear2.Printf(_T("%d"), year % 100);
2169
2170 // find two strings not occuring in format (this is surely
2171 // not optimal way of doing it... improvements welcome!)
2172 wxString fmt = format;
2173 wxString replacement = (wxChar)-1;
2174 while ( fmt.Find(replacement) != wxNOT_FOUND )
2175 {
2176 replacement << (wxChar)-1;
2177 }
2178
2179 wxString replacement2 = (wxChar)-2;
2180 while ( fmt.Find(replacement) != wxNOT_FOUND )
2181 {
2182 replacement << (wxChar)-2;
2183 }
2184
2185 // replace all occurences of year with it
2186 bool wasReplaced = fmt.Replace(strYear, replacement) > 0;
2187 if ( !wasReplaced )
2188 wasReplaced = fmt.Replace(strYear2, replacement2) > 0;
2189
2190 // use strftime() to format the same date but in supported
2191 // year
2192 //
2193 // NB: we assume that strftime() doesn't check for the
2194 // date validity and will happily format the date
2195 // corresponding to Feb 29 of a non leap year (which
2196 // may happen if yearReal was leap and year is not)
2197 struct tm tmAdjusted;
2198 InitTm(tmAdjusted);
2199 tmAdjusted.tm_hour = tm.hour;
2200 tmAdjusted.tm_min = tm.min;
2201 tmAdjusted.tm_sec = tm.sec;
2202 tmAdjusted.tm_wday = tm.GetWeekDay();
2203 tmAdjusted.tm_yday = GetDayOfYear();
2204 tmAdjusted.tm_mday = tm.mday;
2205 tmAdjusted.tm_mon = tm.mon;
2206 tmAdjusted.tm_year = year - 1900;
2207 tmAdjusted.tm_isdst = 0; // no DST, already adjusted
2208 wxString str = CallStrftime(*p == _T('c') ? _T("%c")
2209 : _T("%x"),
2210 &tmAdjusted);
2211
2212 // now replace the occurence of 1999 with the real year
2213 wxString strYearReal, strYearReal2;
2214 strYearReal.Printf(_T("%04d"), yearReal);
2215 strYearReal2.Printf(_T("%02d"), yearReal % 100);
2216 str.Replace(strYear, strYearReal);
2217 str.Replace(strYear2, strYearReal2);
2218
2219 // and replace back all occurences of replacement string
2220 if ( wasReplaced )
2221 {
2222 str.Replace(replacement2, strYear2);
2223 str.Replace(replacement, strYear);
2224 }
2225
2226 res += str;
2227 }
2228 break;
2229
2230 case _T('d'): // day of a month (01-31)
2231 res += wxString::Format(fmt, tm.mday);
2232 break;
2233
2234 case _T('H'): // hour in 24h format (00-23)
2235 res += wxString::Format(fmt, tm.hour);
2236 break;
2237
2238 case _T('I'): // hour in 12h format (01-12)
2239 {
2240 // 24h -> 12h, 0h -> 12h too
2241 int hour12 = tm.hour > 12 ? tm.hour - 12
2242 : tm.hour ? tm.hour : 12;
2243 res += wxString::Format(fmt, hour12);
2244 }
2245 break;
2246
2247 case _T('j'): // day of the year
2248 res += wxString::Format(fmt, GetDayOfYear(tz));
2249 break;
2250
2251 case _T('l'): // milliseconds (NOT STANDARD)
2252 res += wxString::Format(fmt, GetMillisecond(tz));
2253 break;
2254
2255 case _T('m'): // month as a number (01-12)
2256 res += wxString::Format(fmt, tm.mon + 1);
2257 break;
2258
2259 case _T('M'): // minute as a decimal number (00-59)
2260 res += wxString::Format(fmt, tm.min);
2261 break;
2262
2263 case _T('p'): // AM or PM string
2264 res += CallStrftime(_T("%p"), &tmTimeOnly);
2265 break;
2266
2267 case _T('S'): // second as a decimal number (00-61)
2268 res += wxString::Format(fmt, tm.sec);
2269 break;
2270
2271 case _T('U'): // week number in the year (Sunday 1st week day)
2272 res += wxString::Format(fmt, GetWeekOfYear(Sunday_First, tz));
2273 break;
2274
2275 case _T('W'): // week number in the year (Monday 1st week day)
2276 res += wxString::Format(fmt, GetWeekOfYear(Monday_First, tz));
2277 break;
2278
2279 case _T('w'): // weekday as a number (0-6), Sunday = 0
2280 res += wxString::Format(fmt, tm.GetWeekDay());
2281 break;
2282
2283 // case _T('x'): -- handled with "%c"
2284
2285 case _T('X'): // locale default time representation
2286 // just use strftime() to format the time for us
2287 res += CallStrftime(_T("%X"), &tmTimeOnly);
2288 break;
2289
2290 case _T('y'): // year without century (00-99)
2291 res += wxString::Format(fmt, tm.year % 100);
2292 break;
2293
2294 case _T('Y'): // year with century
2295 res += wxString::Format(fmt, tm.year);
2296 break;
2297
2298 case _T('Z'): // timezone name
2299 res += CallStrftime(_T("%Z"), &tmTimeOnly);
2300 break;
2301
2302 default:
2303 // is it the format width?
2304 fmt.Empty();
2305 while ( *p == _T('-') || *p == _T('+') ||
2306 *p == _T(' ') || wxIsdigit(*p) )
2307 {
2308 fmt += *p;
2309 }
2310
2311 if ( !fmt.IsEmpty() )
2312 {
2313 // we've only got the flags and width so far in fmt
2314 fmt.Prepend(_T('%'));
2315 fmt.Append(_T('d'));
2316
2317 restart = TRUE;
2318
2319 break;
2320 }
2321
2322 // no, it wasn't the width
2323 wxFAIL_MSG(_T("unknown format specificator"));
2324
2325 // fall through and just copy it nevertheless
2326
2327 case _T('%'): // a percent sign
2328 res += *p;
2329 break;
2330
2331 case 0: // the end of string
2332 wxFAIL_MSG(_T("missing format at the end of string"));
2333
2334 // just put the '%' which was the last char in format
2335 res += _T('%');
2336 break;
2337 }
2338 }
2339 }
2340
2341 return res;
2342 }
2343
2344 // this function parses a string in (strict) RFC 822 format: see the section 5
2345 // of the RFC for the detailed description, but briefly it's something of the
2346 // form "Sat, 18 Dec 1999 00:48:30 +0100"
2347 //
2348 // this function is "strict" by design - it must reject anything except true
2349 // RFC822 time specs.
2350 //
2351 // TODO a great candidate for using reg exps
2352 const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date)
2353 {
2354 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
2355
2356 const wxChar *p = date;
2357 const wxChar *comma = wxStrchr(p, _T(','));
2358 if ( comma )
2359 {
2360 // the part before comma is the weekday
2361
2362 // skip it for now - we don't use but might check that it really
2363 // corresponds to the specfied date
2364 p = comma + 1;
2365
2366 if ( *p != _T(' ') )
2367 {
2368 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
2369
2370 return (wxChar *)NULL;
2371 }
2372
2373 p++; // skip space
2374 }
2375
2376 // the following 1 or 2 digits are the day number
2377 if ( !wxIsdigit(*p) )
2378 {
2379 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
2380
2381 return (wxChar *)NULL;
2382 }
2383
2384 wxDateTime_t day = *p++ - _T('0');
2385 if ( wxIsdigit(*p) )
2386 {
2387 day *= 10;
2388 day += *p++ - _T('0');
2389 }
2390
2391 if ( *p++ != _T(' ') )
2392 {
2393 return (wxChar *)NULL;
2394 }
2395
2396 // the following 3 letters specify the month
2397 wxString monName(p, 3);
2398 Month mon;
2399 if ( monName == _T("Jan") )
2400 mon = Jan;
2401 else if ( monName == _T("Feb") )
2402 mon = Feb;
2403 else if ( monName == _T("Mar") )
2404 mon = Mar;
2405 else if ( monName == _T("Apr") )
2406 mon = Apr;
2407 else if ( monName == _T("May") )
2408 mon = May;
2409 else if ( monName == _T("Jun") )
2410 mon = Jun;
2411 else if ( monName == _T("Jul") )
2412 mon = Jul;
2413 else if ( monName == _T("Aug") )
2414 mon = Aug;
2415 else if ( monName == _T("Sep") )
2416 mon = Sep;
2417 else if ( monName == _T("Oct") )
2418 mon = Oct;
2419 else if ( monName == _T("Nov") )
2420 mon = Nov;
2421 else if ( monName == _T("Dec") )
2422 mon = Dec;
2423 else
2424 {
2425 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName.c_str());
2426
2427 return (wxChar *)NULL;
2428 }
2429
2430 p += 3;
2431
2432 if ( *p++ != _T(' ') )
2433 {
2434 return (wxChar *)NULL;
2435 }
2436
2437 // next is the year
2438 if ( !wxIsdigit(*p) )
2439 {
2440 // no year?
2441 return (wxChar *)NULL;
2442 }
2443
2444 int year = *p++ - _T('0');
2445
2446 if ( !wxIsdigit(*p) )
2447 {
2448 // should have at least 2 digits in the year
2449 return (wxChar *)NULL;
2450 }
2451
2452 year *= 10;
2453 year += *p++ - _T('0');
2454
2455 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
2456 if ( wxIsdigit(*p) )
2457 {
2458 year *= 10;
2459 year += *p++ - _T('0');
2460
2461 if ( !wxIsdigit(*p) )
2462 {
2463 // no 3 digit years please
2464 return (wxChar *)NULL;
2465 }
2466
2467 year *= 10;
2468 year += *p++ - _T('0');
2469 }
2470
2471 if ( *p++ != _T(' ') )
2472 {
2473 return (wxChar *)NULL;
2474 }
2475
2476 // time is in the format hh:mm:ss and seconds are optional
2477 if ( !wxIsdigit(*p) )
2478 {
2479 return (wxChar *)NULL;
2480 }
2481
2482 wxDateTime_t hour = *p++ - _T('0');
2483
2484 if ( !wxIsdigit(*p) )
2485 {
2486 return (wxChar *)NULL;
2487 }
2488
2489 hour *= 10;
2490 hour += *p++ - _T('0');
2491
2492 if ( *p++ != _T(':') )
2493 {
2494 return (wxChar *)NULL;
2495 }
2496
2497 if ( !wxIsdigit(*p) )
2498 {
2499 return (wxChar *)NULL;
2500 }
2501
2502 wxDateTime_t min = *p++ - _T('0');
2503
2504 if ( !wxIsdigit(*p) )
2505 {
2506 return (wxChar *)NULL;
2507 }
2508
2509 min *= 10;
2510 min += *p++ - _T('0');
2511
2512 wxDateTime_t sec = 0;
2513 if ( *p++ == _T(':') )
2514 {
2515 if ( !wxIsdigit(*p) )
2516 {
2517 return (wxChar *)NULL;
2518 }
2519
2520 sec = *p++ - _T('0');
2521
2522 if ( !wxIsdigit(*p) )
2523 {
2524 return (wxChar *)NULL;
2525 }
2526
2527 sec *= 10;
2528 sec += *p++ - _T('0');
2529 }
2530
2531 if ( *p++ != _T(' ') )
2532 {
2533 return (wxChar *)NULL;
2534 }
2535
2536 // and now the interesting part: the timezone
2537 int offset;
2538 if ( *p == _T('-') || *p == _T('+') )
2539 {
2540 // the explicit offset given: it has the form of hhmm
2541 bool plus = *p++ == _T('+');
2542
2543 if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) )
2544 {
2545 return (wxChar *)NULL;
2546 }
2547
2548 // hours
2549 offset = 60*(10*(*p - _T('0')) + (*(p + 1) - _T('0')));
2550
2551 p += 2;
2552
2553 if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) )
2554 {
2555 return (wxChar *)NULL;
2556 }
2557
2558 // minutes
2559 offset += 10*(*p - _T('0')) + (*(p + 1) - _T('0'));
2560
2561 if ( !plus )
2562 {
2563 offset = -offset;
2564 }
2565
2566 p += 2;
2567 }
2568 else
2569 {
2570 // the symbolic timezone given: may be either military timezone or one
2571 // of standard abbreviations
2572 if ( !*(p + 1) )
2573 {
2574 // military: Z = UTC, J unused, A = -1, ..., Y = +12
2575 static const int offsets[26] =
2576 {
2577 //A B C D E F G H I J K L M
2578 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
2579 //N O P R Q S T U V W Z Y Z
2580 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
2581 };
2582
2583 if ( *p < _T('A') || *p > _T('Z') || *p == _T('J') )
2584 {
2585 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p);
2586
2587 return (wxChar *)NULL;
2588 }
2589
2590 offset = offsets[*p++ - _T('A')];
2591 }
2592 else
2593 {
2594 // abbreviation
2595 wxString tz = p;
2596 if ( tz == _T("UT") || tz == _T("UTC") || tz == _T("GMT") )
2597 offset = 0;
2598 else if ( tz == _T("AST") )
2599 offset = AST - GMT0;
2600 else if ( tz == _T("ADT") )
2601 offset = ADT - GMT0;
2602 else if ( tz == _T("EST") )
2603 offset = EST - GMT0;
2604 else if ( tz == _T("EDT") )
2605 offset = EDT - GMT0;
2606 else if ( tz == _T("CST") )
2607 offset = CST - GMT0;
2608 else if ( tz == _T("CDT") )
2609 offset = CDT - GMT0;
2610 else if ( tz == _T("MST") )
2611 offset = MST - GMT0;
2612 else if ( tz == _T("MDT") )
2613 offset = MDT - GMT0;
2614 else if ( tz == _T("PST") )
2615 offset = PST - GMT0;
2616 else if ( tz == _T("PDT") )
2617 offset = PDT - GMT0;
2618 else
2619 {
2620 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p);
2621
2622 return (wxChar *)NULL;
2623 }
2624
2625 p += tz.length();
2626 }
2627
2628 // make it minutes
2629 offset *= 60;
2630 }
2631
2632 // the spec was correct
2633 Set(day, mon, year, hour, min, sec);
2634 MakeTimezone((wxDateTime_t)(60*offset));
2635
2636 return p;
2637 }
2638
2639 const wxChar *wxDateTime::ParseFormat(const wxChar *date,
2640 const wxChar *format,
2641 const wxDateTime& dateDef)
2642 {
2643 wxCHECK_MSG( date && format, (wxChar *)NULL,
2644 _T("NULL pointer in wxDateTime::ParseFormat()") );
2645
2646 wxString str;
2647 unsigned long num;
2648
2649 // what fields have we found?
2650 bool haveWDay = FALSE,
2651 haveYDay = FALSE,
2652 haveDay = FALSE,
2653 haveMon = FALSE,
2654 haveYear = FALSE,
2655 haveHour = FALSE,
2656 haveMin = FALSE,
2657 haveSec = FALSE;
2658
2659 bool hourIsIn12hFormat = FALSE, // or in 24h one?
2660 isPM = FALSE; // AM by default
2661
2662 // and the value of the items we have (init them to get rid of warnings)
2663 wxDateTime_t sec = 0,
2664 min = 0,
2665 hour = 0;
2666 WeekDay wday = Inv_WeekDay;
2667 wxDateTime_t yday = 0,
2668 mday = 0;
2669 wxDateTime::Month mon = Inv_Month;
2670 int year = 0;
2671
2672 const wxChar *input = date;
2673 for ( const wxChar *fmt = format; *fmt; fmt++ )
2674 {
2675 if ( *fmt != _T('%') )
2676 {
2677 if ( wxIsspace(*fmt) )
2678 {
2679 // a white space in the format string matches 0 or more white
2680 // spaces in the input
2681 while ( wxIsspace(*input) )
2682 {
2683 input++;
2684 }
2685 }
2686 else // !space
2687 {
2688 // any other character (not whitespace, not '%') must be
2689 // matched by itself in the input
2690 if ( *input++ != *fmt )
2691 {
2692 // no match
2693 return (wxChar *)NULL;
2694 }
2695 }
2696
2697 // done with this format char
2698 continue;
2699 }
2700
2701 // start of a format specification
2702
2703 // parse the optional width
2704 size_t width = 0;
2705 while ( wxIsdigit(*++fmt) )
2706 {
2707 width *= 10;
2708 width += *fmt - _T('0');
2709 }
2710
2711 // the default widths for the various fields
2712 if ( !width )
2713 {
2714 switch ( *fmt )
2715 {
2716 case _T('Y'): // year has 4 digits
2717 width = 4;
2718 break;
2719
2720 case _T('j'): // day of year has 3 digits
2721 case _T('l'): // milliseconds have 3 digits
2722 width = 3;
2723 break;
2724
2725 case _T('w'): // week day as number has only one
2726 width = 1;
2727 break;
2728
2729 default:
2730 // default for all other fields
2731 width = 2;
2732 }
2733 }
2734
2735 // then the format itself
2736 switch ( *fmt )
2737 {
2738 case _T('a'): // a weekday name
2739 case _T('A'):
2740 {
2741 int flag = *fmt == _T('a') ? Name_Abbr : Name_Full;
2742 wday = GetWeekDayFromName(GetAlphaToken(input), flag);
2743 if ( wday == Inv_WeekDay )
2744 {
2745 // no match
2746 return (wxChar *)NULL;
2747 }
2748 }
2749 haveWDay = TRUE;
2750 break;
2751
2752 case _T('b'): // a month name
2753 case _T('B'):
2754 {
2755 int flag = *fmt == _T('b') ? Name_Abbr : Name_Full;
2756 mon = GetMonthFromName(GetAlphaToken(input), flag);
2757 if ( mon == Inv_Month )
2758 {
2759 // no match
2760 return (wxChar *)NULL;
2761 }
2762 }
2763 haveMon = TRUE;
2764 break;
2765
2766 case _T('c'): // locale default date and time representation
2767 {
2768 wxDateTime dt;
2769
2770 // this is the format which corresponds to ctime() output
2771 // and strptime("%c") should parse it, so try it first
2772 static const wxChar *fmtCtime = _T("%a %b %d %H:%M:%S %Y");
2773
2774 const wxChar *result = dt.ParseFormat(input, fmtCtime);
2775 if ( !result )
2776 {
2777 result = dt.ParseFormat(input, _T("%x %X"));
2778 }
2779
2780 if ( !result )
2781 {
2782 result = dt.ParseFormat(input, _T("%X %x"));
2783 }
2784
2785 if ( !result )
2786 {
2787 // we've tried everything and still no match
2788 return (wxChar *)NULL;
2789 }
2790
2791 Tm tm = dt.GetTm();
2792
2793 haveDay = haveMon = haveYear =
2794 haveHour = haveMin = haveSec = TRUE;
2795
2796 hour = tm.hour;
2797 min = tm.min;
2798 sec = tm.sec;
2799
2800 year = tm.year;
2801 mon = tm.mon;
2802 mday = tm.mday;
2803
2804 input = result;
2805 }
2806 break;
2807
2808 case _T('d'): // day of a month (01-31)
2809 if ( !GetNumericToken(width, input, &num) ||
2810 (num > 31) || (num < 1) )
2811 {
2812 // no match
2813 return (wxChar *)NULL;
2814 }
2815
2816 // we can't check whether the day range is correct yet, will
2817 // do it later - assume ok for now
2818 haveDay = TRUE;
2819 mday = (wxDateTime_t)num;
2820 break;
2821
2822 case _T('H'): // hour in 24h format (00-23)
2823 if ( !GetNumericToken(width, input, &num) || (num > 23) )
2824 {
2825 // no match
2826 return (wxChar *)NULL;
2827 }
2828
2829 haveHour = TRUE;
2830 hour = (wxDateTime_t)num;
2831 break;
2832
2833 case _T('I'): // hour in 12h format (01-12)
2834 if ( !GetNumericToken(width, input, &num) || !num || (num > 12) )
2835 {
2836 // no match
2837 return (wxChar *)NULL;
2838 }
2839
2840 haveHour = TRUE;
2841 hourIsIn12hFormat = TRUE;
2842 hour = (wxDateTime_t)(num % 12); // 12 should be 0
2843 break;
2844
2845 case _T('j'): // day of the year
2846 if ( !GetNumericToken(width, input, &num) || !num || (num > 366) )
2847 {
2848 // no match
2849 return (wxChar *)NULL;
2850 }
2851
2852 haveYDay = TRUE;
2853 yday = (wxDateTime_t)num;
2854 break;
2855
2856 case _T('m'): // month as a number (01-12)
2857 if ( !GetNumericToken(width, input, &num) || !num || (num > 12) )
2858 {
2859 // no match
2860 return (wxChar *)NULL;
2861 }
2862
2863 haveMon = TRUE;
2864 mon = (Month)(num - 1);
2865 break;
2866
2867 case _T('M'): // minute as a decimal number (00-59)
2868 if ( !GetNumericToken(width, input, &num) || (num > 59) )
2869 {
2870 // no match
2871 return (wxChar *)NULL;
2872 }
2873
2874 haveMin = TRUE;
2875 min = (wxDateTime_t)num;
2876 break;
2877
2878 case _T('p'): // AM or PM string
2879 {
2880 wxString am, pm, token = GetAlphaToken(input);
2881
2882 GetAmPmStrings(&am, &pm);
2883 if (am.IsEmpty() && pm.IsEmpty())
2884 return (wxChar *)NULL; // no am/pm strings defined
2885 if ( token.CmpNoCase(pm) == 0 )
2886 {
2887 isPM = TRUE;
2888 }
2889 else if ( token.CmpNoCase(am) != 0 )
2890 {
2891 // no match
2892 return (wxChar *)NULL;
2893 }
2894 }
2895 break;
2896
2897 case _T('r'): // time as %I:%M:%S %p
2898 {
2899 wxDateTime dt;
2900 input = dt.ParseFormat(input, _T("%I:%M:%S %p"));
2901 if ( !input )
2902 {
2903 // no match
2904 return (wxChar *)NULL;
2905 }
2906
2907 haveHour = haveMin = haveSec = TRUE;
2908
2909 Tm tm = dt.GetTm();
2910 hour = tm.hour;
2911 min = tm.min;
2912 sec = tm.sec;
2913 }
2914 break;
2915
2916 case _T('R'): // time as %H:%M
2917 {
2918 wxDateTime dt;
2919 input = dt.ParseFormat(input, _T("%H:%M"));
2920 if ( !input )
2921 {
2922 // no match
2923 return (wxChar *)NULL;
2924 }
2925
2926 haveHour = haveMin = TRUE;
2927
2928 Tm tm = dt.GetTm();
2929 hour = tm.hour;
2930 min = tm.min;
2931 }
2932
2933 case _T('S'): // second as a decimal number (00-61)
2934 if ( !GetNumericToken(width, input, &num) || (num > 61) )
2935 {
2936 // no match
2937 return (wxChar *)NULL;
2938 }
2939
2940 haveSec = TRUE;
2941 sec = (wxDateTime_t)num;
2942 break;
2943
2944 case _T('T'): // time as %H:%M:%S
2945 {
2946 wxDateTime dt;
2947 input = dt.ParseFormat(input, _T("%H:%M:%S"));
2948 if ( !input )
2949 {
2950 // no match
2951 return (wxChar *)NULL;
2952 }
2953
2954 haveHour = haveMin = haveSec = TRUE;
2955
2956 Tm tm = dt.GetTm();
2957 hour = tm.hour;
2958 min = tm.min;
2959 sec = tm.sec;
2960 }
2961 break;
2962
2963 case _T('w'): // weekday as a number (0-6), Sunday = 0
2964 if ( !GetNumericToken(width, input, &num) || (wday > 6) )
2965 {
2966 // no match
2967 return (wxChar *)NULL;
2968 }
2969
2970 haveWDay = TRUE;
2971 wday = (WeekDay)num;
2972 break;
2973
2974 case _T('x'): // locale default date representation
2975 #ifdef HAVE_STRPTIME
2976 // try using strptime() -- it may fail even if the input is
2977 // correct but the date is out of range, so we will fall back
2978 // to our generic code anyhow
2979 {
2980 struct tm tm;
2981
2982 const wxChar *result = CallStrptime(input, "%x", &tm);
2983 if ( result )
2984 {
2985 input = result;
2986
2987 haveDay = haveMon = haveYear = TRUE;
2988
2989 year = 1900 + tm.tm_year;
2990 mon = (Month)tm.tm_mon;
2991 mday = tm.tm_mday;
2992
2993 break;
2994 }
2995 }
2996 #endif // HAVE_STRPTIME
2997
2998 // TODO query the LOCALE_IDATE setting under Win32
2999 {
3000 wxDateTime dt;
3001
3002 wxString fmtDate, fmtDateAlt;
3003 if ( IsWestEuropeanCountry(GetCountry()) ||
3004 GetCountry() == Russia )
3005 {
3006 fmtDate = _T("%d/%m/%y");
3007 fmtDateAlt = _T("%m/%d/%y");
3008 }
3009 else // assume USA
3010 {
3011 fmtDate = _T("%m/%d/%y");
3012 fmtDateAlt = _T("%d/%m/%y");
3013 }
3014
3015 const wxChar *result = dt.ParseFormat(input, fmtDate);
3016
3017 if ( !result )
3018 {
3019 // ok, be nice and try another one
3020 result = dt.ParseFormat(input, fmtDateAlt);
3021 }
3022
3023 if ( !result )
3024 {
3025 // bad luck
3026 return (wxChar *)NULL;
3027 }
3028
3029 Tm tm = dt.GetTm();
3030
3031 haveDay = haveMon = haveYear = TRUE;
3032
3033 year = tm.year;
3034 mon = tm.mon;
3035 mday = tm.mday;
3036
3037 input = result;
3038 }
3039
3040 break;
3041
3042 case _T('X'): // locale default time representation
3043 #ifdef HAVE_STRPTIME
3044 {
3045 // use strptime() to do it for us (FIXME !Unicode friendly)
3046 struct tm tm;
3047 input = CallStrptime(input, "%X", &tm);
3048 if ( !input )
3049 {
3050 return (wxChar *)NULL;
3051 }
3052
3053 haveHour = haveMin = haveSec = TRUE;
3054
3055 hour = tm.tm_hour;
3056 min = tm.tm_min;
3057 sec = tm.tm_sec;
3058 }
3059 #else // !HAVE_STRPTIME
3060 // TODO under Win32 we can query the LOCALE_ITIME system
3061 // setting which says whether the default time format is
3062 // 24 or 12 hour
3063 {
3064 // try to parse what follows as "%H:%M:%S" and, if this
3065 // fails, as "%I:%M:%S %p" - this should catch the most
3066 // common cases
3067 wxDateTime dt;
3068
3069 const wxChar *result = dt.ParseFormat(input, _T("%T"));
3070 if ( !result )
3071 {
3072 result = dt.ParseFormat(input, _T("%r"));
3073 }
3074
3075 if ( !result )
3076 {
3077 // no match
3078 return (wxChar *)NULL;
3079 }
3080
3081 haveHour = haveMin = haveSec = TRUE;
3082
3083 Tm tm = dt.GetTm();
3084 hour = tm.hour;
3085 min = tm.min;
3086 sec = tm.sec;
3087
3088 input = result;
3089 }
3090 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
3091 break;
3092
3093 case _T('y'): // year without century (00-99)
3094 if ( !GetNumericToken(width, input, &num) || (num > 99) )
3095 {
3096 // no match
3097 return (wxChar *)NULL;
3098 }
3099
3100 haveYear = TRUE;
3101
3102 // TODO should have an option for roll over date instead of
3103 // hard coding it here
3104 year = (num > 30 ? 1900 : 2000) + (wxDateTime_t)num;
3105 break;
3106
3107 case _T('Y'): // year with century
3108 if ( !GetNumericToken(width, input, &num) )
3109 {
3110 // no match
3111 return (wxChar *)NULL;
3112 }
3113
3114 haveYear = TRUE;
3115 year = (wxDateTime_t)num;
3116 break;
3117
3118 case _T('Z'): // timezone name
3119 wxFAIL_MSG(_T("TODO"));
3120 break;
3121
3122 case _T('%'): // a percent sign
3123 if ( *input++ != _T('%') )
3124 {
3125 // no match
3126 return (wxChar *)NULL;
3127 }
3128 break;
3129
3130 case 0: // the end of string
3131 wxFAIL_MSG(_T("unexpected format end"));
3132
3133 // fall through
3134
3135 default: // not a known format spec
3136 return (wxChar *)NULL;
3137 }
3138 }
3139
3140 // format matched, try to construct a date from what we have now
3141 Tm tmDef;
3142 if ( dateDef.IsValid() )
3143 {
3144 // take this date as default
3145 tmDef = dateDef.GetTm();
3146 }
3147 else if ( IsValid() )
3148 {
3149 // if this date is valid, don't change it
3150 tmDef = GetTm();
3151 }
3152 else
3153 {
3154 // no default and this date is invalid - fall back to Today()
3155 tmDef = Today().GetTm();
3156 }
3157
3158 Tm tm = tmDef;
3159
3160 // set the date
3161 if ( haveYear )
3162 {
3163 tm.year = year;
3164 }
3165
3166 // TODO we don't check here that the values are consistent, if both year
3167 // day and month/day were found, we just ignore the year day and we
3168 // also always ignore the week day
3169 if ( haveMon && haveDay )
3170 {
3171 if ( mday > GetNumOfDaysInMonth(tm.year, mon) )
3172 {
3173 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
3174
3175 return (wxChar *)NULL;
3176 }
3177
3178 tm.mon = mon;
3179 tm.mday = mday;
3180 }
3181 else if ( haveYDay )
3182 {
3183 if ( yday > GetNumberOfDays(tm.year) )
3184 {
3185 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
3186
3187 return (wxChar *)NULL;
3188 }
3189
3190 Tm tm2 = wxDateTime(1, Jan, tm.year).SetToYearDay(yday).GetTm();
3191
3192 tm.mon = tm2.mon;
3193 tm.mday = tm2.mday;
3194 }
3195
3196 // deal with AM/PM
3197 if ( haveHour && hourIsIn12hFormat && isPM )
3198 {
3199 // translate to 24hour format
3200 hour += 12;
3201 }
3202 //else: either already in 24h format or no translation needed
3203
3204 // set the time
3205 if ( haveHour )
3206 {
3207 tm.hour = hour;
3208 }
3209
3210 if ( haveMin )
3211 {
3212 tm.min = min;
3213 }
3214
3215 if ( haveSec )
3216 {
3217 tm.sec = sec;
3218 }
3219
3220 Set(tm);
3221
3222 // finally check that the week day is consistent -- if we had it
3223 if ( haveWDay && GetWeekDay() != wday )
3224 {
3225 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
3226
3227 return NULL;
3228 }
3229
3230 return input;
3231 }
3232
3233 const wxChar *wxDateTime::ParseDateTime(const wxChar *date)
3234 {
3235 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
3236
3237 // Set to current day and hour, so strings like '14:00' becomes today at
3238 // 14, not some other random date
3239 wxDateTime dtDate = wxDateTime::Today();
3240 wxDateTime dtTime = wxDateTime::Today();
3241
3242 const wxChar* pchTime;
3243
3244 // Try to parse the beginning of the string as a date
3245 const wxChar* pchDate = dtDate.ParseDate(date);
3246
3247 // We got a date in the beginning, see if there is a time specified after the date
3248 if ( pchDate )
3249 {
3250 // Skip spaces, as the ParseTime() function fails on spaces
3251 while ( wxIsspace(*pchDate) )
3252 pchDate++;
3253
3254 pchTime = dtTime.ParseTime(pchDate);
3255 }
3256 else // no date in the beginning
3257 {
3258 // check and see if we have a time followed by a date
3259 pchTime = dtTime.ParseTime(date);
3260 if ( pchTime )
3261 {
3262 while ( wxIsspace(*pchTime) )
3263 pchTime++;
3264
3265 pchDate = dtDate.ParseDate(pchTime);
3266 }
3267 }
3268
3269 // If we have a date specified, set our own data to the same date
3270 if ( !pchDate || !pchTime )
3271 return NULL;
3272
3273 Set(dtDate.GetDay(), dtDate.GetMonth(), dtDate.GetYear(),
3274 dtTime.GetHour(), dtTime.GetMinute(), dtTime.GetSecond(),
3275 dtTime.GetMillisecond());
3276
3277 // Return endpoint of scan
3278 return pchDate > pchTime ? pchDate : pchTime;
3279 }
3280
3281 const wxChar *wxDateTime::ParseDate(const wxChar *date)
3282 {
3283 // this is a simplified version of ParseDateTime() which understands only
3284 // "today" (for wxDate compatibility) and digits only otherwise (and not
3285 // all esoteric constructions ParseDateTime() knows about)
3286
3287 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
3288
3289 const wxChar *p = date;
3290 while ( wxIsspace(*p) )
3291 p++;
3292
3293 // some special cases
3294 static struct
3295 {
3296 const wxChar *str;
3297 int dayDiffFromToday;
3298 } literalDates[] =
3299 {
3300 { wxTRANSLATE("today"), 0 },
3301 { wxTRANSLATE("yesterday"), -1 },
3302 { wxTRANSLATE("tomorrow"), 1 },
3303 };
3304
3305 for ( size_t n = 0; n < WXSIZEOF(literalDates); n++ )
3306 {
3307 wxString date = wxGetTranslation(literalDates[n].str);
3308 size_t len = date.length();
3309 if ( wxStrlen(p) >= len && (wxString(p, len).CmpNoCase(date) == 0) )
3310 {
3311 // nothing can follow this, so stop here
3312 p += len;
3313
3314 int dayDiffFromToday = literalDates[n].dayDiffFromToday;
3315 *this = Today();
3316 if ( dayDiffFromToday )
3317 {
3318 *this += wxDateSpan::Days(dayDiffFromToday);
3319 }
3320
3321 return p;
3322 }
3323 }
3324
3325 // We try to guess what we have here: for each new (numeric) token, we
3326 // determine if it can be a month, day or a year. Of course, there is an
3327 // ambiguity as some numbers may be days as well as months, so we also
3328 // have the ability to back track.
3329
3330 // what do we have?
3331 bool haveDay = FALSE, // the months day?
3332 haveWDay = FALSE, // the day of week?
3333 haveMon = FALSE, // the month?
3334 haveYear = FALSE; // the year?
3335
3336 // and the value of the items we have (init them to get rid of warnings)
3337 WeekDay wday = Inv_WeekDay;
3338 wxDateTime_t day = 0;
3339 wxDateTime::Month mon = Inv_Month;
3340 int year = 0;
3341
3342 // tokenize the string
3343 size_t nPosCur = 0;
3344 static const wxChar *dateDelimiters = _T(".,/-\t\r\n ");
3345 wxStringTokenizer tok(p, dateDelimiters);
3346 while ( tok.HasMoreTokens() )
3347 {
3348 wxString token = tok.GetNextToken();
3349 if ( !token )
3350 continue;
3351
3352 // is it a number?
3353 unsigned long val;
3354 if ( token.ToULong(&val) )
3355 {
3356 // guess what this number is
3357
3358 bool isDay = FALSE,
3359 isMonth = FALSE,
3360 isYear = FALSE;
3361
3362 if ( !haveMon && val > 0 && val <= 12 )
3363 {
3364 // assume it is month
3365 isMonth = TRUE;
3366 }
3367 else // not the month
3368 {
3369 if ( haveDay )
3370 {
3371 // this can only be the year
3372 isYear = TRUE;
3373 }
3374 else // may be either day or year
3375 {
3376 wxDateTime_t maxDays = haveMon
3377 ? GetNumOfDaysInMonth(haveYear ? year : Inv_Year, mon)
3378 : 31;
3379
3380 // can it be day?
3381 if ( (val == 0) || (val > (unsigned long)maxDays) )
3382 {
3383 // no
3384 isYear = TRUE;
3385 }
3386 else // yes, suppose it's the day
3387 {
3388 isDay = TRUE;
3389 }
3390 }
3391 }
3392
3393 if ( isYear )
3394 {
3395 if ( haveYear )
3396 break;
3397
3398 haveYear = TRUE;
3399
3400 year = (wxDateTime_t)val;
3401 }
3402 else if ( isDay )
3403 {
3404 if ( haveDay )
3405 break;
3406
3407 haveDay = TRUE;
3408
3409 day = (wxDateTime_t)val;
3410 }
3411 else if ( isMonth )
3412 {
3413 haveMon = TRUE;
3414
3415 mon = (Month)(val - 1);
3416 }
3417 }
3418 else // not a number
3419 {
3420 // be careful not to overwrite the current mon value
3421 Month mon2 = GetMonthFromName(token, Name_Full | Name_Abbr);
3422 if ( mon2 != Inv_Month )
3423 {
3424 // it's a month
3425 if ( haveMon )
3426 {
3427 // but we already have a month - maybe we guessed wrong?
3428 if ( !haveDay )
3429 {
3430 // no need to check in month range as always < 12, but
3431 // the days are counted from 1 unlike the months
3432 day = (wxDateTime_t)mon + 1;
3433 haveDay = TRUE;
3434 }
3435 else
3436 {
3437 // could possible be the year (doesn't the year come
3438 // before the month in the japanese format?) (FIXME)
3439 break;
3440 }
3441 }
3442
3443 mon = mon2;
3444
3445 haveMon = TRUE;
3446 }
3447 else // not a valid month name
3448 {
3449 wday = GetWeekDayFromName(token, Name_Full | Name_Abbr);
3450 if ( wday != Inv_WeekDay )
3451 {
3452 // a week day
3453 if ( haveWDay )
3454 {
3455 break;
3456 }
3457
3458 haveWDay = TRUE;
3459 }
3460 else // not a valid weekday name
3461 {
3462 // try the ordinals
3463 static const wxChar *ordinals[] =
3464 {
3465 wxTRANSLATE("first"),
3466 wxTRANSLATE("second"),
3467 wxTRANSLATE("third"),
3468 wxTRANSLATE("fourth"),
3469 wxTRANSLATE("fifth"),
3470 wxTRANSLATE("sixth"),
3471 wxTRANSLATE("seventh"),
3472 wxTRANSLATE("eighth"),
3473 wxTRANSLATE("ninth"),
3474 wxTRANSLATE("tenth"),
3475 wxTRANSLATE("eleventh"),
3476 wxTRANSLATE("twelfth"),
3477 wxTRANSLATE("thirteenth"),
3478 wxTRANSLATE("fourteenth"),
3479 wxTRANSLATE("fifteenth"),
3480 wxTRANSLATE("sixteenth"),
3481 wxTRANSLATE("seventeenth"),
3482 wxTRANSLATE("eighteenth"),
3483 wxTRANSLATE("nineteenth"),
3484 wxTRANSLATE("twentieth"),
3485 // that's enough - otherwise we'd have problems with
3486 // composite (or not) ordinals
3487 };
3488
3489 size_t n;
3490 for ( n = 0; n < WXSIZEOF(ordinals); n++ )
3491 {
3492 if ( token.CmpNoCase(ordinals[n]) == 0 )
3493 {
3494 break;
3495 }
3496 }
3497
3498 if ( n == WXSIZEOF(ordinals) )
3499 {
3500 // stop here - something unknown
3501 break;
3502 }
3503
3504 // it's a day
3505 if ( haveDay )
3506 {
3507 // don't try anything here (as in case of numeric day
3508 // above) - the symbolic day spec should always
3509 // precede the month/year
3510 break;
3511 }
3512
3513 haveDay = TRUE;
3514
3515 day = (wxDateTime_t)(n + 1);
3516 }
3517 }
3518 }
3519
3520 nPosCur = tok.GetPosition();
3521 }
3522
3523 // either no more tokens or the scan was stopped by something we couldn't
3524 // parse - in any case, see if we can construct a date from what we have
3525 if ( !haveDay && !haveWDay )
3526 {
3527 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
3528
3529 return (wxChar *)NULL;
3530 }
3531
3532 if ( haveWDay && (haveMon || haveYear || haveDay) &&
3533 !(haveDay && haveMon && haveYear) )
3534 {
3535 // without adjectives (which we don't support here) the week day only
3536 // makes sense completely separately or with the full date
3537 // specification (what would "Wed 1999" mean?)
3538 return (wxChar *)NULL;
3539 }
3540
3541 if ( !haveWDay && haveYear && !(haveDay && haveMon) )
3542 {
3543 // may be we have month and day instead of day and year?
3544 if ( haveDay && !haveMon )
3545 {
3546 if ( day <= 12 )
3547 {
3548 // exchange day and month
3549 mon = (wxDateTime::Month)(day - 1);
3550
3551 // we're in the current year then
3552 if ( (year > 0) && (year <= (int)GetNumOfDaysInMonth(Inv_Year, mon)) )
3553 {
3554 day = year;
3555
3556 haveMon = TRUE;
3557 haveYear = FALSE;
3558 }
3559 //else: no, can't exchange, leave haveMon == FALSE
3560 }
3561 }
3562
3563 if ( !haveMon )
3564 {
3565 // if we give the year, month and day must be given too
3566 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
3567
3568 return (wxChar *)NULL;
3569 }
3570 }
3571
3572 if ( !haveMon )
3573 {
3574 mon = GetCurrentMonth();
3575 }
3576
3577 if ( !haveYear )
3578 {
3579 year = GetCurrentYear();
3580 }
3581
3582 if ( haveDay )
3583 {
3584 Set(day, mon, year);
3585
3586 if ( haveWDay )
3587 {
3588 // check that it is really the same
3589 if ( GetWeekDay() != wday )
3590 {
3591 // inconsistency detected
3592 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
3593
3594 return (wxChar *)NULL;
3595 }
3596 }
3597 }
3598 else // haveWDay
3599 {
3600 *this = Today();
3601
3602 SetToWeekDayInSameWeek(wday);
3603 }
3604
3605 // return the pointer to the first unparsed char
3606 p += nPosCur;
3607 if ( nPosCur && wxStrchr(dateDelimiters, *(p - 1)) )
3608 {
3609 // if we couldn't parse the token after the delimiter, put back the
3610 // delimiter as well
3611 p--;
3612 }
3613
3614 return p;
3615 }
3616
3617 const wxChar *wxDateTime::ParseTime(const wxChar *time)
3618 {
3619 wxCHECK_MSG( time, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
3620
3621 // first try some extra things
3622 static const struct
3623 {
3624 const wxChar *name;
3625 wxDateTime_t hour;
3626 } stdTimes[] =
3627 {
3628 { wxTRANSLATE("noon"), 12 },
3629 { wxTRANSLATE("midnight"), 00 },
3630 // anything else?
3631 };
3632
3633 for ( size_t n = 0; n < WXSIZEOF(stdTimes); n++ )
3634 {
3635 wxString timeString = wxGetTranslation(stdTimes[n].name);
3636 size_t len = timeString.length();
3637 if ( timeString.CmpNoCase(wxString(time, len)) == 0 )
3638 {
3639 // casts required by DigitalMars
3640 Set(stdTimes[n].hour, wxDateTime_t(0), wxDateTime_t(0));
3641
3642 return time + len;
3643 }
3644 }
3645
3646 // try all time formats we may think about in the order from longest to
3647 // shortest
3648
3649 // 12hour with AM/PM?
3650 const wxChar *result = ParseFormat(time, _T("%I:%M:%S %p"));
3651
3652 if ( !result )
3653 {
3654 // normally, it's the same, but why not try it?
3655 result = ParseFormat(time, _T("%H:%M:%S"));
3656 }
3657
3658 if ( !result )
3659 {
3660 // 12hour with AM/PM but without seconds?
3661 result = ParseFormat(time, _T("%I:%M %p"));
3662 }
3663
3664 if ( !result )
3665 {
3666 // without seconds?
3667 result = ParseFormat(time, _T("%H:%M"));
3668 }
3669
3670 if ( !result )
3671 {
3672 // just the hour and AM/PM?
3673 result = ParseFormat(time, _T("%I %p"));
3674 }
3675
3676 if ( !result )
3677 {
3678 // just the hour?
3679 result = ParseFormat(time, _T("%H"));
3680 }
3681
3682 if ( !result )
3683 {
3684 // parse the standard format: normally it is one of the formats above
3685 // but it may be set to something completely different by the user
3686 result = ParseFormat(time, _T("%X"));
3687 }
3688
3689 // TODO: parse timezones
3690
3691 return result;
3692 }
3693
3694 // ----------------------------------------------------------------------------
3695 // Workdays and holidays support
3696 // ----------------------------------------------------------------------------
3697
3698 bool wxDateTime::IsWorkDay(Country WXUNUSED(country)) const
3699 {
3700 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
3701 }
3702
3703 // ============================================================================
3704 // wxTimeSpan
3705 // ============================================================================
3706
3707 // this enum is only used in wxTimeSpan::Format() below but we can't declare
3708 // it locally to the method as it provokes an internal compiler error in egcs
3709 // 2.91.60 when building with -O2
3710 enum TimeSpanPart
3711 {
3712 Part_Week,
3713 Part_Day,
3714 Part_Hour,
3715 Part_Min,
3716 Part_Sec,
3717 Part_MSec
3718 };
3719
3720 // not all strftime(3) format specifiers make sense here because, for example,
3721 // a time span doesn't have a year nor a timezone
3722 //
3723 // Here are the ones which are supported (all of them are supported by strftime
3724 // as well):
3725 // %H hour in 24 hour format
3726 // %M minute (00 - 59)
3727 // %S second (00 - 59)
3728 // %% percent sign
3729 //
3730 // Also, for MFC CTimeSpan compatibility, we support
3731 // %D number of days
3732 //
3733 // And, to be better than MFC :-), we also have
3734 // %E number of wEeks
3735 // %l milliseconds (000 - 999)
3736 wxString wxTimeSpan::Format(const wxChar *format) const
3737 {
3738 wxCHECK_MSG( format, _T(""), _T("NULL format in wxTimeSpan::Format") );
3739
3740 wxString str;
3741 str.Alloc(wxStrlen(format));
3742
3743 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
3744 //
3745 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
3746 // question is what should ts.Format("%S") do? The code here returns "3273"
3747 // in this case (i.e. the total number of seconds, not just seconds % 60)
3748 // because, for me, this call means "give me entire time interval in
3749 // seconds" and not "give me the seconds part of the time interval"
3750 //
3751 // If we agree that it should behave like this, it is clear that the
3752 // interpretation of each format specifier depends on the presence of the
3753 // other format specs in the string: if there was "%H" before "%M", we
3754 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
3755
3756 // we remember the most important unit found so far
3757 TimeSpanPart partBiggest = Part_MSec;
3758
3759 for ( const wxChar *pch = format; *pch; pch++ )
3760 {
3761 wxChar ch = *pch;
3762
3763 if ( ch == _T('%') )
3764 {
3765 // the start of the format specification of the printf() below
3766 wxString fmtPrefix = _T('%');
3767
3768 // the number
3769 long n;
3770
3771 ch = *++pch; // get the format spec char
3772 switch ( ch )
3773 {
3774 default:
3775 wxFAIL_MSG( _T("invalid format character") );
3776 // fall through
3777
3778 case _T('%'):
3779 str += ch;
3780
3781 // skip the part below switch
3782 continue;
3783
3784 case _T('D'):
3785 n = GetDays();
3786 if ( partBiggest < Part_Day )
3787 {
3788 n %= DAYS_PER_WEEK;
3789 }
3790 else
3791 {
3792 partBiggest = Part_Day;
3793 }
3794 break;
3795
3796 case _T('E'):
3797 partBiggest = Part_Week;
3798 n = GetWeeks();
3799 break;
3800
3801 case _T('H'):
3802 n = GetHours();
3803 if ( partBiggest < Part_Hour )
3804 {
3805 n %= HOURS_PER_DAY;
3806 }
3807 else
3808 {
3809 partBiggest = Part_Hour;
3810 }
3811
3812 fmtPrefix += _T("02");
3813 break;
3814
3815 case _T('l'):
3816 n = GetMilliseconds().ToLong();
3817 if ( partBiggest < Part_MSec )
3818 {
3819 n %= 1000;
3820 }
3821 //else: no need to reset partBiggest to Part_MSec, it is
3822 // the least significant one anyhow
3823
3824 fmtPrefix += _T("03");
3825 break;
3826
3827 case _T('M'):
3828 n = GetMinutes();
3829 if ( partBiggest < Part_Min )
3830 {
3831 n %= MIN_PER_HOUR;
3832 }
3833 else
3834 {
3835 partBiggest = Part_Min;
3836 }
3837
3838 fmtPrefix += _T("02");
3839 break;
3840
3841 case _T('S'):
3842 n = GetSeconds().ToLong();
3843 if ( partBiggest < Part_Sec )
3844 {
3845 n %= SEC_PER_MIN;
3846 }
3847 else
3848 {
3849 partBiggest = Part_Sec;
3850 }
3851
3852 fmtPrefix += _T("02");
3853 break;
3854 }
3855
3856 str += wxString::Format(fmtPrefix + _T("ld"), n);
3857 }
3858 else
3859 {
3860 // normal character, just copy
3861 str += ch;
3862 }
3863 }
3864
3865 return str;
3866 }
3867
3868 // ============================================================================
3869 // wxDateTimeHolidayAuthority and related classes
3870 // ============================================================================
3871
3872 #include "wx/arrimpl.cpp"
3873
3874 WX_DEFINE_OBJARRAY(wxDateTimeArray);
3875
3876 static int wxCMPFUNC_CONV
3877 wxDateTimeCompareFunc(wxDateTime **first, wxDateTime **second)
3878 {
3879 wxDateTime dt1 = **first,
3880 dt2 = **second;
3881
3882 return dt1 == dt2 ? 0 : dt1 < dt2 ? -1 : +1;
3883 }
3884
3885 // ----------------------------------------------------------------------------
3886 // wxDateTimeHolidayAuthority
3887 // ----------------------------------------------------------------------------
3888
3889 wxHolidayAuthoritiesArray wxDateTimeHolidayAuthority::ms_authorities;
3890
3891 /* static */
3892 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime& dt)
3893 {
3894 size_t count = ms_authorities.size();
3895 for ( size_t n = 0; n < count; n++ )
3896 {
3897 if ( ms_authorities[n]->DoIsHoliday(dt) )
3898 {
3899 return TRUE;
3900 }
3901 }
3902
3903 return FALSE;
3904 }
3905
3906 /* static */
3907 size_t
3908 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime& dtStart,
3909 const wxDateTime& dtEnd,
3910 wxDateTimeArray& holidays)
3911 {
3912 wxDateTimeArray hol;
3913
3914 holidays.Clear();
3915
3916 size_t count = ms_authorities.size();
3917 for ( size_t nAuth = 0; nAuth < count; nAuth++ )
3918 {
3919 ms_authorities[nAuth]->DoGetHolidaysInRange(dtStart, dtEnd, hol);
3920
3921 WX_APPEND_ARRAY(holidays, hol);
3922 }
3923
3924 holidays.Sort(wxDateTimeCompareFunc);
3925
3926 return holidays.size();
3927 }
3928
3929 /* static */
3930 void wxDateTimeHolidayAuthority::ClearAllAuthorities()
3931 {
3932 WX_CLEAR_ARRAY(ms_authorities);
3933 }
3934
3935 /* static */
3936 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority *auth)
3937 {
3938 ms_authorities.push_back(auth);
3939 }
3940
3941 wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
3942 {
3943 // nothing to do here
3944 }
3945
3946 // ----------------------------------------------------------------------------
3947 // wxDateTimeWorkDays
3948 // ----------------------------------------------------------------------------
3949
3950 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime& dt) const
3951 {
3952 wxDateTime::WeekDay wd = dt.GetWeekDay();
3953
3954 return (wd == wxDateTime::Sun) || (wd == wxDateTime::Sat);
3955 }
3956
3957 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime& dtStart,
3958 const wxDateTime& dtEnd,
3959 wxDateTimeArray& holidays) const
3960 {
3961 if ( dtStart > dtEnd )
3962 {
3963 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
3964
3965 return 0u;
3966 }
3967
3968 holidays.Empty();
3969
3970 // instead of checking all days, start with the first Sat after dtStart and
3971 // end with the last Sun before dtEnd
3972 wxDateTime dtSatFirst = dtStart.GetNextWeekDay(wxDateTime::Sat),
3973 dtSatLast = dtEnd.GetPrevWeekDay(wxDateTime::Sat),
3974 dtSunFirst = dtStart.GetNextWeekDay(wxDateTime::Sun),
3975 dtSunLast = dtEnd.GetPrevWeekDay(wxDateTime::Sun),
3976 dt;
3977
3978 for ( dt = dtSatFirst; dt <= dtSatLast; dt += wxDateSpan::Week() )
3979 {
3980 holidays.Add(dt);
3981 }
3982
3983 for ( dt = dtSunFirst; dt <= dtSunLast; dt += wxDateSpan::Week() )
3984 {
3985 holidays.Add(dt);
3986 }
3987
3988 return holidays.GetCount();
3989 }
3990
3991 #endif // wxUSE_DATETIME