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