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