1 ///////////////////////////////////////////////////////////////////////////////
3 // Purpose: implementation of time/date related classes
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // parts of code taken from sndcal library by Scott E. Lee:
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.
16 // Licence: wxWindows licence
17 ///////////////////////////////////////////////////////////////////////////////
20 * Implementation notes:
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
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
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
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
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
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
47 // ============================================================================
49 // ============================================================================
51 // ----------------------------------------------------------------------------
53 // ----------------------------------------------------------------------------
55 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
56 #pragma implementation "datetime.h"
59 // For compilers that support precompilation, includes "wx.h".
60 #include "wx/wxprec.h"
66 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
69 #include "wx/string.h"
74 #include "wx/thread.h"
75 #include "wx/tokenzr.h"
76 #include "wx/module.h"
80 #include "wx/datetime.h"
81 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
83 const long wxDateTime::TIME_T_FACTOR
= 1000l;
85 #if wxUSE_EXTENDED_RTTI
87 template<> void wxStringReadValue(const wxString
&s
, wxDateTime
&data
)
89 data
.ParseFormat(s
,wxT("%Y-%m-%d %H:%M:%S")) ;
92 template<> void wxStringWriteValue(wxString
&s
, const wxDateTime
&data
)
94 s
= data
.Format(wxT("%Y-%m-%d %H:%M:%S")) ;
97 wxCUSTOM_TYPE_INFO(wxDateTime
, wxToStringConverter
<wxDateTime
> , wxFromStringConverter
<wxDateTime
>)
102 // ----------------------------------------------------------------------------
103 // conditional compilation
104 // ----------------------------------------------------------------------------
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):
111 // strncpy(buf, "Tue Dec 21 20:25:40 1999", 128);
112 // strptime(buf, "%x", &tm);
116 #endif // broken strptime()
118 #if defined(__MWERKS__) && wxUSE_UNICODE
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>
131 static long wxGetTimeZone()
133 static long timezone
= MAXLONG
; // invalid timezone
134 if (timezone
== MAXLONG
)
138 timezone
= tb
.timezone
;
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
148 #endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
150 // ----------------------------------------------------------------------------
152 // ----------------------------------------------------------------------------
154 // debugging helper: just a convenient replacement of wxCHECK()
155 #define wxDATETIME_CHECK(expr, msg) \
159 *this = wxInvalidDateTime; \
163 // ----------------------------------------------------------------------------
165 // ----------------------------------------------------------------------------
167 class wxDateTimeHolidaysModule
: public wxModule
170 virtual bool OnInit()
172 wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays
);
177 virtual void OnExit()
179 wxDateTimeHolidayAuthority::ClearAllAuthorities();
180 wxDateTimeHolidayAuthority::ms_authorities
.clear();
184 DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule
)
187 IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule
, wxModule
)
189 // ----------------------------------------------------------------------------
191 // ----------------------------------------------------------------------------
194 static const int MONTHS_IN_YEAR
= 12;
196 static const int SEC_PER_MIN
= 60;
198 static const int MIN_PER_HOUR
= 60;
200 static const int HOURS_PER_DAY
= 24;
202 static const long SECONDS_PER_DAY
= 86400l;
204 static const int DAYS_PER_WEEK
= 7;
206 static const long MILLISECONDS_PER_DAY
= 86400000l;
208 // this is the integral part of JDN of the midnight of Jan 1, 1970
209 // (i.e. JDN(Jan 1, 1970) = 2440587.5)
210 static const long EPOCH_JDN
= 2440587l;
212 // the date of JDN -0.5 (as we don't work with fractional parts, this is the
213 // reference date for us) is Nov 24, 4714BC
214 static const int JDN_0_YEAR
= -4713;
215 static const int JDN_0_MONTH
= wxDateTime::Nov
;
216 static const int JDN_0_DAY
= 24;
218 // the constants used for JDN calculations
219 static const long JDN_OFFSET
= 32046l;
220 static const long DAYS_PER_5_MONTHS
= 153l;
221 static const long DAYS_PER_4_YEARS
= 1461l;
222 static const long DAYS_PER_400_YEARS
= 146097l;
224 // this array contains the cumulated number of days in all previous months for
225 // normal and leap years
226 static const wxDateTime::wxDateTime_t gs_cumulatedDays
[2][MONTHS_IN_YEAR
] =
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 }
232 // ----------------------------------------------------------------------------
234 // ----------------------------------------------------------------------------
236 // in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
237 // indicate an invalid wxDateTime object
238 const wxDateTime wxDefaultDateTime
;
240 wxDateTime::Country
wxDateTime::ms_country
= wxDateTime::Country_Unknown
;
242 // ----------------------------------------------------------------------------
244 // ----------------------------------------------------------------------------
246 // debugger helper: shows what the date really is
248 extern const wxChar
*wxDumpDate(const wxDateTime
* dt
)
250 static wxChar buf
[128];
252 wxStrcpy(buf
, dt
->Format(_T("%Y-%m-%d (%a) %H:%M:%S")));
258 // get the number of days in the given month of the given year
260 wxDateTime::wxDateTime_t
GetNumOfDaysInMonth(int year
, wxDateTime::Month month
)
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
] =
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 }
270 return daysInMonth
[wxDateTime::IsLeapYear(year
)][month
];
273 // returns the time zone in the C sense, i.e. the difference UTC - local
275 static int GetTimeZone()
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
282 // ensure that the timezone variable is set by calling localtime
283 if ( !s_timezoneSet
)
285 // just call localtime() instead of figuring out whether this system
286 // supports tzset(), _tzset() or something else
291 s_timezoneSet
= TRUE
;
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
;
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
305 // return the integral part of the JDN for the midnight of the given date (to
306 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
307 // noon of the previous day)
308 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day
,
309 wxDateTime::Month mon
,
312 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
314 // check the date validity
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")
322 // make the year positive to avoid problems with negative numbers division
325 // months are counted from March here
327 if ( mon
>= wxDateTime::Mar
)
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
345 // this function is a wrapper around strftime(3) adding error checking
346 static wxString
CallStrftime(const wxChar
*format
, const tm
* tm
)
349 if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) )
351 // buffer is too small?
352 wxFAIL_MSG(_T("strftime() failed"));
355 return wxString(buf
);
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
*);
366 // Unicode-friendly strptime() wrapper
367 static const wxChar
*
368 CallStrptime(const wxChar
*input
, const char *fmt
, tm
*tm
)
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
374 wxCharBuffer
inputMB(wxConvertWX2MB(input
));
376 const char * const inputMB
= input
;
377 #endif // Unicode/Ascii
379 const char *result
= strptime(inputMB
, fmt
, tm
);
384 // FIXME: this is wrong in presence of surrogates &c
385 return input
+ (result
- inputMB
.data());
388 #endif // Unicode/Ascii
391 #endif // HAVE_STRPTIME
393 // if year and/or month have invalid values, replace them with the current ones
394 static void ReplaceDefaultYearMonthWithCurrent(int *year
,
395 wxDateTime::Month
*month
)
397 struct tm
*tmNow
= NULL
;
399 if ( *year
== wxDateTime::Inv_Year
)
401 tmNow
= wxDateTime::GetTmNow();
403 *year
= 1900 + tmNow
->tm_year
;
406 if ( *month
== wxDateTime::Inv_Month
)
409 tmNow
= wxDateTime::GetTmNow();
411 *month
= (wxDateTime::Month
)tmNow
->tm_mon
;
415 // fll the struct tm with default values
416 static void InitTm(struct tm
& tm
)
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
));
422 tm
.tm_mday
= 1; // mday 0 is invalid
423 tm
.tm_year
= 76; // any valid year
424 tm
.tm_isdst
= -1; // auto determine
430 // return the month if the string is a month name or Inv_Month otherwise
431 static wxDateTime::Month
GetMonthFromName(const wxString
& name
, int flags
)
433 wxDateTime::Month mon
;
434 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
436 // case-insensitive comparison either one of or with both abbreviated
438 if ( flags
& wxDateTime::Name_Full
)
440 if ( name
.CmpNoCase(wxDateTime::
441 GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 )
447 if ( flags
& wxDateTime::Name_Abbr
)
449 if ( name
.CmpNoCase(wxDateTime::
450 GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 )
460 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
461 static wxDateTime::WeekDay
GetWeekDayFromName(const wxString
& name
, int flags
)
463 wxDateTime::WeekDay wd
;
464 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
466 // case-insensitive comparison either one of or with both abbreviated
468 if ( flags
& wxDateTime::Name_Full
)
470 if ( name
.CmpNoCase(wxDateTime::
471 GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 )
477 if ( flags
& wxDateTime::Name_Abbr
)
479 if ( name
.CmpNoCase(wxDateTime::
480 GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 )
490 // scans all digits (but no more than len) and returns the resulting number
491 static bool GetNumericToken(size_t len
, const wxChar
*& p
, unsigned long *number
)
495 while ( wxIsdigit(*p
) )
499 if ( len
&& ++n
> len
)
503 return !!s
&& s
.ToULong(number
);
506 // scans all alphabetic characters and returns the resulting string
507 static wxString
GetAlphaToken(const wxChar
*& p
)
510 while ( wxIsalpha(*p
) )
518 // ============================================================================
519 // implementation of wxDateTime
520 // ============================================================================
522 // ----------------------------------------------------------------------------
524 // ----------------------------------------------------------------------------
528 year
= (wxDateTime_t
)wxDateTime::Inv_Year
;
529 mon
= wxDateTime::Inv_Month
;
531 hour
= min
= sec
= msec
= 0;
532 wday
= wxDateTime::Inv_WeekDay
;
535 wxDateTime::Tm::Tm(const struct tm
& tm
, const TimeZone
& tz
)
543 mon
= (wxDateTime::Month
)tm
.tm_mon
;
544 year
= 1900 + tm
.tm_year
;
549 bool wxDateTime::Tm::IsValid() const
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);
557 void wxDateTime::Tm::ComputeWeekDay()
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;
565 void wxDateTime::Tm::AddMonths(int monDiff
)
567 // normalize the months field
568 while ( monDiff
< -mon
)
572 monDiff
+= MONTHS_IN_YEAR
;
575 while ( monDiff
+ mon
>= MONTHS_IN_YEAR
)
579 monDiff
-= MONTHS_IN_YEAR
;
582 mon
= (wxDateTime::Month
)(mon
+ monDiff
);
584 wxASSERT_MSG( mon
>= 0 && mon
< MONTHS_IN_YEAR
, _T("logic error") );
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
590 void wxDateTime::Tm::AddDays(int dayDiff
)
592 // normalize the days field
593 while ( dayDiff
+ mday
< 1 )
597 dayDiff
+= GetNumOfDaysInMonth(year
, mon
);
601 while ( mday
> GetNumOfDaysInMonth(year
, mon
) )
603 mday
-= GetNumOfDaysInMonth(year
, mon
);
608 wxASSERT_MSG( mday
> 0 && mday
<= GetNumOfDaysInMonth(year
, mon
),
612 // ----------------------------------------------------------------------------
614 // ----------------------------------------------------------------------------
616 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz
)
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();
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
);
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
);
657 case wxDateTime::A_CST
:
658 // Central Standard Time in use in Australia = UTC + 9.5
659 m_offset
= 60l*(9*60 + 30);
663 wxFAIL_MSG( _T("unknown time zone") );
667 // ----------------------------------------------------------------------------
669 // ----------------------------------------------------------------------------
672 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
)
674 if ( year
== Inv_Year
)
675 year
= GetCurrentYear();
677 if ( cal
== Gregorian
)
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));
685 else if ( cal
== Julian
)
687 // in Julian calendar the rule is simpler
688 return year
% 4 == 0;
692 wxFAIL_MSG(_T("unknown calendar"));
699 int wxDateTime::GetCentury(int year
)
701 return year
> 0 ? year
/ 100 : year
/ 100 - 1;
705 int wxDateTime::ConvertYearToBC(int year
)
708 return year
> 0 ? year
: year
- 1;
712 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
)
717 return Now().GetYear();
720 wxFAIL_MSG(_T("TODO"));
724 wxFAIL_MSG(_T("unsupported calendar"));
732 wxDateTime::Month
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
)
737 return Now().GetMonth();
740 wxFAIL_MSG(_T("TODO"));
744 wxFAIL_MSG(_T("unsupported calendar"));
752 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(int year
, Calendar cal
)
754 if ( year
== Inv_Year
)
756 // take the current year if none given
757 year
= GetCurrentYear();
764 return IsLeapYear(year
) ? 366 : 365;
767 wxFAIL_MSG(_T("unsupported calendar"));
775 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(wxDateTime::Month month
,
777 wxDateTime::Calendar cal
)
779 wxCHECK_MSG( month
< MONTHS_IN_YEAR
, 0, _T("invalid month") );
781 if ( cal
== Gregorian
|| cal
== Julian
)
783 if ( year
== Inv_Year
)
785 // take the current year if none given
786 year
= GetCurrentYear();
789 return GetNumOfDaysInMonth(year
, month
);
793 wxFAIL_MSG(_T("unsupported calendar"));
800 wxString
wxDateTime::GetMonthName(wxDateTime::Month month
,
801 wxDateTime::NameFlags flags
)
803 wxCHECK_MSG( month
!= Inv_Month
, _T(""), _T("invalid month") );
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)
811 return CallStrftime(flags
== Name_Abbr
? _T("%b") : _T("%B"), &tm
);
815 wxString
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
,
816 wxDateTime::NameFlags flags
)
818 wxCHECK_MSG( wday
!= Inv_WeekDay
, _T(""), _T("invalid weekday") );
820 // take some arbitrary Sunday
827 // and offset it by the number of days needed to get the correct wday
830 // call mktime() to normalize it...
833 // ... and call strftime()
834 return CallStrftime(flags
== Name_Abbr
? _T("%a") : _T("%A"), &tm
);
838 void wxDateTime::GetAmPmStrings(wxString
*am
, wxString
*pm
)
843 // @Note: Do not call 'CallStrftime' here! CallStrftime checks the return code
844 // and causes an assertion failed if the buffer is to small (which is good) - OR -
845 // if strftime does not return anything because the format string is invalid - OR -
846 // if there are no 'am' / 'pm' tokens defined for the current locale (which is not good).
847 // wxDateTime::ParseTime will try several different formats to parse the time.
848 // As a result, GetAmPmStrings might get called, even if the current locale
849 // does not define any 'am' / 'pm' tokens. In this case, wxStrftime would
850 // assert, even though it is a perfectly legal use.
853 if (wxStrftime(buffer
, sizeof buffer
, _T("%p"), &tm
) > 0)
854 *am
= wxString(buffer
);
861 if (wxStrftime(buffer
, sizeof buffer
, _T("%p"), &tm
) > 0)
862 *pm
= wxString(buffer
);
868 // ----------------------------------------------------------------------------
869 // Country stuff: date calculations depend on the country (DST, work days,
870 // ...), so we need to know which rules to follow.
871 // ----------------------------------------------------------------------------
874 wxDateTime::Country
wxDateTime::GetCountry()
876 // TODO use LOCALE_ICOUNTRY setting under Win32
878 if ( ms_country
== Country_Unknown
)
880 // try to guess from the time zone name
881 time_t t
= time(NULL
);
882 struct tm
*tm
= localtime(&t
);
884 wxString tz
= CallStrftime(_T("%Z"), tm
);
885 if ( tz
== _T("WET") || tz
== _T("WEST") )
889 else if ( tz
== _T("CET") || tz
== _T("CEST") )
891 ms_country
= Country_EEC
;
893 else if ( tz
== _T("MSK") || tz
== _T("MSD") )
897 else if ( tz
== _T("AST") || tz
== _T("ADT") ||
898 tz
== _T("EST") || tz
== _T("EDT") ||
899 tz
== _T("CST") || tz
== _T("CDT") ||
900 tz
== _T("MST") || tz
== _T("MDT") ||
901 tz
== _T("PST") || tz
== _T("PDT") )
907 // well, choose a default one
916 void wxDateTime::SetCountry(wxDateTime::Country country
)
918 ms_country
= country
;
922 bool wxDateTime::IsWestEuropeanCountry(Country country
)
924 if ( country
== Country_Default
)
926 country
= GetCountry();
929 return (Country_WesternEurope_Start
<= country
) &&
930 (country
<= Country_WesternEurope_End
);
933 // ----------------------------------------------------------------------------
934 // DST calculations: we use 3 different rules for the West European countries,
935 // USA and for the rest of the world. This is undoubtedly false for many
936 // countries, but I lack the necessary info (and the time to gather it),
937 // please add the other rules here!
938 // ----------------------------------------------------------------------------
941 bool wxDateTime::IsDSTApplicable(int year
, Country country
)
943 if ( year
== Inv_Year
)
945 // take the current year if none given
946 year
= GetCurrentYear();
949 if ( country
== Country_Default
)
951 country
= GetCountry();
958 // DST was first observed in the US and UK during WWI, reused
959 // during WWII and used again since 1966
960 return year
>= 1966 ||
961 (year
>= 1942 && year
<= 1945) ||
962 (year
== 1918 || year
== 1919);
965 // assume that it started after WWII
971 wxDateTime
wxDateTime::GetBeginDST(int year
, Country country
)
973 if ( year
== Inv_Year
)
975 // take the current year if none given
976 year
= GetCurrentYear();
979 if ( country
== Country_Default
)
981 country
= GetCountry();
984 if ( !IsDSTApplicable(year
, country
) )
986 return wxInvalidDateTime
;
991 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
993 // DST begins at 1 a.m. GMT on the last Sunday of March
994 if ( !dt
.SetToLastWeekDay(Sun
, Mar
, year
) )
997 wxFAIL_MSG( _T("no last Sunday in March?") );
1000 dt
+= wxTimeSpan::Hours(1);
1002 // disable DST tests because it could result in an infinite recursion!
1005 else switch ( country
)
1012 // don't know for sure - assume it was in effect all year
1017 dt
.Set(1, Jan
, year
);
1021 // DST was installed Feb 2, 1942 by the Congress
1022 dt
.Set(2, Feb
, year
);
1025 // Oil embargo changed the DST period in the US
1027 dt
.Set(6, Jan
, 1974);
1031 dt
.Set(23, Feb
, 1975);
1035 // before 1986, DST begun on the last Sunday of April, but
1036 // in 1986 Reagan changed it to begin at 2 a.m. of the
1037 // first Sunday in April
1040 if ( !dt
.SetToLastWeekDay(Sun
, Apr
, year
) )
1043 wxFAIL_MSG( _T("no first Sunday in April?") );
1048 if ( !dt
.SetToWeekDay(Sun
, 1, Apr
, year
) )
1051 wxFAIL_MSG( _T("no first Sunday in April?") );
1055 dt
+= wxTimeSpan::Hours(2);
1057 // TODO what about timezone??
1063 // assume Mar 30 as the start of the DST for the rest of the world
1064 // - totally bogus, of course
1065 dt
.Set(30, Mar
, year
);
1072 wxDateTime
wxDateTime::GetEndDST(int year
, Country country
)
1074 if ( year
== Inv_Year
)
1076 // take the current year if none given
1077 year
= GetCurrentYear();
1080 if ( country
== Country_Default
)
1082 country
= GetCountry();
1085 if ( !IsDSTApplicable(year
, country
) )
1087 return wxInvalidDateTime
;
1092 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1094 // DST ends at 1 a.m. GMT on the last Sunday of October
1095 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1097 // weirder and weirder...
1098 wxFAIL_MSG( _T("no last Sunday in October?") );
1101 dt
+= wxTimeSpan::Hours(1);
1103 // disable DST tests because it could result in an infinite recursion!
1106 else switch ( country
)
1113 // don't know for sure - assume it was in effect all year
1117 dt
.Set(31, Dec
, year
);
1121 // the time was reset after the end of the WWII
1122 dt
.Set(30, Sep
, year
);
1126 // DST ends at 2 a.m. on the last Sunday of October
1127 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1129 // weirder and weirder...
1130 wxFAIL_MSG( _T("no last Sunday in October?") );
1133 dt
+= wxTimeSpan::Hours(2);
1135 // TODO what about timezone??
1140 // assume October 26th as the end of the DST - totally bogus too
1141 dt
.Set(26, Oct
, year
);
1147 // ----------------------------------------------------------------------------
1148 // constructors and assignment operators
1149 // ----------------------------------------------------------------------------
1151 // return the current time with ms precision
1152 /* static */ wxDateTime
wxDateTime::UNow()
1154 return wxDateTime(wxGetLocalTimeMillis());
1157 // the values in the tm structure contain the local time
1158 wxDateTime
& wxDateTime::Set(const struct tm
& tm
)
1161 time_t timet
= mktime(&tm2
);
1163 if ( timet
== (time_t)-1 )
1165 // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
1166 // less than timezone - try to make it work for this case
1167 if ( tm2
.tm_year
== 70 && tm2
.tm_mon
== 0 && tm2
.tm_mday
== 1 )
1169 // add timezone to make sure that date is in range
1170 tm2
.tm_sec
-= GetTimeZone();
1172 timet
= mktime(&tm2
);
1173 if ( timet
!= (time_t)-1 )
1175 timet
+= GetTimeZone();
1181 wxFAIL_MSG( _T("mktime() failed") );
1183 *this = wxInvalidDateTime
;
1193 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
,
1194 wxDateTime_t minute
,
1195 wxDateTime_t second
,
1196 wxDateTime_t millisec
)
1198 // we allow seconds to be 61 to account for the leap seconds, even if we
1199 // don't use them really
1200 wxDATETIME_CHECK( hour
< 24 &&
1204 _T("Invalid time in wxDateTime::Set()") );
1206 // get the current date from system
1207 struct tm
*tm
= GetTmNow();
1209 wxDATETIME_CHECK( tm
, _T("localtime() failed") );
1213 tm
->tm_min
= minute
;
1214 tm
->tm_sec
= second
;
1218 // and finally adjust milliseconds
1219 return SetMillisecond(millisec
);
1222 wxDateTime
& wxDateTime::Set(wxDateTime_t day
,
1226 wxDateTime_t minute
,
1227 wxDateTime_t second
,
1228 wxDateTime_t millisec
)
1230 wxDATETIME_CHECK( hour
< 24 &&
1234 _T("Invalid time in wxDateTime::Set()") );
1236 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1238 wxDATETIME_CHECK( (0 < day
) && (day
<= GetNumberOfDays(month
, year
)),
1239 _T("Invalid date in wxDateTime::Set()") );
1241 // the range of time_t type (inclusive)
1242 static const int yearMinInRange
= 1970;
1243 static const int yearMaxInRange
= 2037;
1245 // test only the year instead of testing for the exact end of the Unix
1246 // time_t range - it doesn't bring anything to do more precise checks
1247 if ( year
>= yearMinInRange
&& year
<= yearMaxInRange
)
1249 // use the standard library version if the date is in range - this is
1250 // probably more efficient than our code
1252 tm
.tm_year
= year
- 1900;
1258 tm
.tm_isdst
= -1; // mktime() will guess it
1262 // and finally adjust milliseconds
1263 return SetMillisecond(millisec
);
1267 // do time calculations ourselves: we want to calculate the number of
1268 // milliseconds between the given date and the epoch
1270 // get the JDN for the midnight of this day
1271 m_time
= GetTruncatedJDN(day
, month
, year
);
1272 m_time
-= EPOCH_JDN
;
1273 m_time
*= SECONDS_PER_DAY
* TIME_T_FACTOR
;
1275 // JDN corresponds to GMT, we take localtime
1276 Add(wxTimeSpan(hour
, minute
, second
+ GetTimeZone(), millisec
));
1282 wxDateTime
& wxDateTime::Set(double jdn
)
1284 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1286 jdn
-= EPOCH_JDN
+ 0.5;
1288 jdn
*= MILLISECONDS_PER_DAY
;
1290 // JDNs always suppose an UTC date, so bring it back to local time zone
1291 // (also see GetJulianDayNumber() implementation)
1292 long tzDiff
= GetTimeZone();
1295 // FIXME: again, we suppose that DST is always one hour
1299 jdn
+= tzDiff
*1000; // tzDiff is in seconds
1306 wxDateTime
& wxDateTime::ResetTime()
1310 if ( tm
.hour
|| tm
.min
|| tm
.sec
|| tm
.msec
)
1323 // ----------------------------------------------------------------------------
1324 // DOS Date and Time Format functions
1325 // ----------------------------------------------------------------------------
1326 // the dos date and time value is an unsigned 32 bit value in the format:
1327 // YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
1329 // Y = year offset from 1980 (0-127)
1331 // D = day of month (1-31)
1333 // m = minute (0-59)
1334 // s = bisecond (0-29) each bisecond indicates two seconds
1335 // ----------------------------------------------------------------------------
1337 wxDateTime
& wxDateTime::SetFromDOS(unsigned long ddt
)
1342 long year
= ddt
& 0xFE000000;
1347 long month
= ddt
& 0x1E00000;
1352 long day
= ddt
& 0x1F0000;
1356 long hour
= ddt
& 0xF800;
1360 long minute
= ddt
& 0x7E0;
1364 long second
= ddt
& 0x1F;
1365 tm
.tm_sec
= second
* 2;
1367 return Set(mktime(&tm
));
1370 unsigned long wxDateTime::GetAsDOS() const
1373 time_t ticks
= GetTicks();
1374 struct tm
*tm
= localtime(&ticks
);
1376 long year
= tm
->tm_year
;
1380 long month
= tm
->tm_mon
;
1384 long day
= tm
->tm_mday
;
1387 long hour
= tm
->tm_hour
;
1390 long minute
= tm
->tm_min
;
1393 long second
= tm
->tm_sec
;
1396 ddt
= year
| month
| day
| hour
| minute
| second
;
1400 // ----------------------------------------------------------------------------
1401 // time_t <-> broken down time conversions
1402 // ----------------------------------------------------------------------------
1404 wxDateTime::Tm
wxDateTime::GetTm(const TimeZone
& tz
) const
1406 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1408 time_t time
= GetTicks();
1409 if ( time
!= (time_t)-1 )
1411 // use C RTL functions
1413 if ( tz
.GetOffset() == -GetTimeZone() )
1415 // we are working with local time
1416 tm
= localtime(&time
);
1418 // should never happen
1419 wxCHECK_MSG( tm
, Tm(), _T("localtime() failed") );
1423 time
+= (time_t)tz
.GetOffset();
1424 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
1425 int time2
= (int) time
;
1433 // should never happen
1434 wxCHECK_MSG( tm
, Tm(), _T("gmtime() failed") );
1438 tm
= (struct tm
*)NULL
;
1444 // adjust the milliseconds
1446 long timeOnly
= (m_time
% MILLISECONDS_PER_DAY
).ToLong();
1447 tm2
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1450 //else: use generic code below
1453 // remember the time and do the calculations with the date only - this
1454 // eliminates rounding errors of the floating point arithmetics
1456 wxLongLong timeMidnight
= m_time
+ tz
.GetOffset() * 1000;
1458 long timeOnly
= (timeMidnight
% MILLISECONDS_PER_DAY
).ToLong();
1460 // we want to always have positive time and timeMidnight to be really
1461 // the midnight before it
1464 timeOnly
= MILLISECONDS_PER_DAY
+ timeOnly
;
1467 timeMidnight
-= timeOnly
;
1469 // calculate the Gregorian date from JDN for the midnight of our date:
1470 // this will yield day, month (in 1..12 range) and year
1472 // actually, this is the JDN for the noon of the previous day
1473 long jdn
= (timeMidnight
/ MILLISECONDS_PER_DAY
).ToLong() + EPOCH_JDN
;
1475 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1477 wxASSERT_MSG( jdn
> -2, _T("JDN out of range") );
1479 // calculate the century
1480 long temp
= (jdn
+ JDN_OFFSET
) * 4 - 1;
1481 long century
= temp
/ DAYS_PER_400_YEARS
;
1483 // then the year and day of year (1 <= dayOfYear <= 366)
1484 temp
= ((temp
% DAYS_PER_400_YEARS
) / 4) * 4 + 3;
1485 long year
= (century
* 100) + (temp
/ DAYS_PER_4_YEARS
);
1486 long dayOfYear
= (temp
% DAYS_PER_4_YEARS
) / 4 + 1;
1488 // and finally the month and day of the month
1489 temp
= dayOfYear
* 5 - 3;
1490 long month
= temp
/ DAYS_PER_5_MONTHS
;
1491 long day
= (temp
% DAYS_PER_5_MONTHS
) / 5 + 1;
1493 // month is counted from March - convert to normal
1504 // year is offset by 4800
1507 // check that the algorithm gave us something reasonable
1508 wxASSERT_MSG( (0 < month
) && (month
<= 12), _T("invalid month") );
1509 wxASSERT_MSG( (1 <= day
) && (day
< 32), _T("invalid day") );
1511 // construct Tm from these values
1513 tm
.year
= (int)year
;
1514 tm
.mon
= (Month
)(month
- 1); // algorithm yields 1 for January, not 0
1515 tm
.mday
= (wxDateTime_t
)day
;
1516 tm
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1517 timeOnly
-= tm
.msec
;
1518 timeOnly
/= 1000; // now we have time in seconds
1520 tm
.sec
= (wxDateTime_t
)(timeOnly
% 60);
1522 timeOnly
/= 60; // now we have time in minutes
1524 tm
.min
= (wxDateTime_t
)(timeOnly
% 60);
1527 tm
.hour
= (wxDateTime_t
)(timeOnly
/ 60);
1532 wxDateTime
& wxDateTime::SetYear(int year
)
1534 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1543 wxDateTime
& wxDateTime::SetMonth(Month month
)
1545 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1554 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
)
1556 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1565 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
)
1567 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1576 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
)
1578 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1587 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
)
1589 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1598 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
)
1600 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1602 // we don't need to use GetTm() for this one
1603 m_time
-= m_time
% 1000l;
1604 m_time
+= millisecond
;
1609 // ----------------------------------------------------------------------------
1610 // wxDateTime arithmetics
1611 // ----------------------------------------------------------------------------
1613 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
)
1617 tm
.year
+= diff
.GetYears();
1618 tm
.AddMonths(diff
.GetMonths());
1620 // check that the resulting date is valid
1621 if ( tm
.mday
> GetNumOfDaysInMonth(tm
.year
, tm
.mon
) )
1623 // We suppose that when adding one month to Jan 31 we want to get Feb
1624 // 28 (or 29), i.e. adding a month to the last day of the month should
1625 // give the last day of the next month which is quite logical.
1627 // Unfortunately, there is no logic way to understand what should
1628 // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1629 // We make it Feb 28 (last day too), but it is highly questionable.
1630 tm
.mday
= GetNumOfDaysInMonth(tm
.year
, tm
.mon
);
1633 tm
.AddDays(diff
.GetTotalDays());
1637 wxASSERT_MSG( IsSameTime(tm
),
1638 _T("Add(wxDateSpan) shouldn't modify time") );
1643 // ----------------------------------------------------------------------------
1644 // Weekday and monthday stuff
1645 // ----------------------------------------------------------------------------
1647 bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek
,
1651 wxASSERT_MSG( numWeek
> 0,
1652 _T("invalid week number: weeks are counted from 1") );
1654 int year
= GetYear();
1656 // Jan 4 always lies in the 1st week of the year
1658 SetToWeekDayInSameWeek(weekday
, flags
) += wxDateSpan::Weeks(numWeek
- 1);
1660 if ( GetYear() != year
)
1662 // oops... numWeek was too big
1669 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
,
1672 // take the current month/year if none specified
1673 if ( year
== Inv_Year
)
1675 if ( month
== Inv_Month
)
1678 return Set(GetNumOfDaysInMonth(year
, month
), month
, year
);
1681 wxDateTime
& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday
, WeekFlags flags
)
1683 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1685 int wdayThis
= GetWeekDay();
1686 if ( weekday
== wdayThis
)
1692 if ( flags
== Default_First
)
1694 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
1697 // the logic below based on comparing weekday and wdayThis works if Sun (0)
1698 // is the first day in the week, but breaks down for Monday_First case so
1699 // we adjust the week days in this case
1700 if( flags
== Monday_First
)
1702 if ( wdayThis
== Sun
)
1705 //else: Sunday_First, nothing to do
1707 // go forward or back in time to the day we want
1708 if ( weekday
< wdayThis
)
1710 return Subtract(wxDateSpan::Days(wdayThis
- weekday
));
1712 else // weekday > wdayThis
1714 return Add(wxDateSpan::Days(weekday
- wdayThis
));
1718 wxDateTime
& wxDateTime::SetToNextWeekDay(WeekDay weekday
)
1720 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1723 WeekDay wdayThis
= GetWeekDay();
1724 if ( weekday
== wdayThis
)
1729 else if ( weekday
< wdayThis
)
1731 // need to advance a week
1732 diff
= 7 - (wdayThis
- weekday
);
1734 else // weekday > wdayThis
1736 diff
= weekday
- wdayThis
;
1739 return Add(wxDateSpan::Days(diff
));
1742 wxDateTime
& wxDateTime::SetToPrevWeekDay(WeekDay weekday
)
1744 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1747 WeekDay wdayThis
= GetWeekDay();
1748 if ( weekday
== wdayThis
)
1753 else if ( weekday
> wdayThis
)
1755 // need to go to previous week
1756 diff
= 7 - (weekday
- wdayThis
);
1758 else // weekday < wdayThis
1760 diff
= wdayThis
- weekday
;
1763 return Subtract(wxDateSpan::Days(diff
));
1766 bool wxDateTime::SetToWeekDay(WeekDay weekday
,
1771 wxCHECK_MSG( weekday
!= Inv_WeekDay
, FALSE
, _T("invalid weekday") );
1773 // we don't check explicitly that -5 <= n <= 5 because we will return FALSE
1774 // anyhow in such case - but may be should still give an assert for it?
1776 // take the current month/year if none specified
1777 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1781 // TODO this probably could be optimised somehow...
1785 // get the first day of the month
1786 dt
.Set(1, month
, year
);
1789 WeekDay wdayFirst
= dt
.GetWeekDay();
1791 // go to the first weekday of the month
1792 int diff
= weekday
- wdayFirst
;
1796 // add advance n-1 weeks more
1799 dt
+= wxDateSpan::Days(diff
);
1801 else // count from the end of the month
1803 // get the last day of the month
1804 dt
.SetToLastMonthDay(month
, year
);
1807 WeekDay wdayLast
= dt
.GetWeekDay();
1809 // go to the last weekday of the month
1810 int diff
= wdayLast
- weekday
;
1814 // and rewind n-1 weeks from there
1817 dt
-= wxDateSpan::Days(diff
);
1820 // check that it is still in the same month
1821 if ( dt
.GetMonth() == month
)
1829 // no such day in this month
1834 wxDateTime::wxDateTime_t
wxDateTime::GetDayOfYear(const TimeZone
& tz
) const
1838 return gs_cumulatedDays
[IsLeapYear(tm
.year
)][tm
.mon
] + tm
.mday
;
1841 wxDateTime::wxDateTime_t
wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags
,
1842 const TimeZone
& tz
) const
1844 if ( flags
== Default_First
)
1846 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
1849 wxDateTime_t nDayInYear
= GetDayOfYear(tz
);
1852 WeekDay wd
= GetWeekDay(tz
);
1853 if ( flags
== Sunday_First
)
1855 week
= (nDayInYear
- wd
+ 7) / 7;
1859 // have to shift the week days values
1860 week
= (nDayInYear
- (wd
- 1 + 7) % 7 + 7) / 7;
1863 // FIXME some more elegant way??
1864 WeekDay wdYearStart
= wxDateTime(1, Jan
, GetYear()).GetWeekDay();
1865 if ( wdYearStart
== Wed
|| wdYearStart
== Thu
)
1873 wxDateTime::wxDateTime_t
wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags
,
1874 const TimeZone
& tz
) const
1877 wxDateTime dtMonthStart
= wxDateTime(1, tm
.mon
, tm
.year
);
1878 int nWeek
= GetWeekOfYear(flags
) - dtMonthStart
.GetWeekOfYear(flags
) + 1;
1881 // this may happen for January when Jan, 1 is the last week of the
1883 nWeek
+= IsLeapYear(tm
.year
- 1) ? 53 : 52;
1886 return (wxDateTime::wxDateTime_t
)nWeek
;
1889 wxDateTime
& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday
)
1891 int year
= GetYear();
1892 wxDATETIME_CHECK( (0 < yday
) && (yday
<= GetNumberOfDays(year
)),
1893 _T("invalid year day") );
1895 bool isLeap
= IsLeapYear(year
);
1896 for ( Month mon
= Jan
; mon
< Inv_Month
; wxNextMonth(mon
) )
1898 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
1899 // don't need it neither - because of the CHECK above we know that
1900 // yday lies in December then
1901 if ( (mon
== Dec
) || (yday
<= gs_cumulatedDays
[isLeap
][mon
+ 1]) )
1903 Set(yday
- gs_cumulatedDays
[isLeap
][mon
], mon
, year
);
1912 // ----------------------------------------------------------------------------
1913 // Julian day number conversion and related stuff
1914 // ----------------------------------------------------------------------------
1916 double wxDateTime::GetJulianDayNumber() const
1918 // JDN are always expressed for the UTC dates
1919 Tm
tm(ToTimezone(UTC
).GetTm(UTC
));
1921 double result
= GetTruncatedJDN(tm
.mday
, tm
.mon
, tm
.year
);
1923 // add the part GetTruncatedJDN() neglected
1926 // and now add the time: 86400 sec = 1 JDN
1927 return result
+ ((double)(60*(60*tm
.hour
+ tm
.min
) + tm
.sec
)) / 86400;
1930 double wxDateTime::GetRataDie() const
1932 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
1933 return GetJulianDayNumber() - 1721119.5 - 306;
1936 // ----------------------------------------------------------------------------
1937 // timezone and DST stuff
1938 // ----------------------------------------------------------------------------
1940 int wxDateTime::IsDST(wxDateTime::Country country
) const
1942 wxCHECK_MSG( country
== Country_Default
, -1,
1943 _T("country support not implemented") );
1945 // use the C RTL for the dates in the standard range
1946 time_t timet
= GetTicks();
1947 if ( timet
!= (time_t)-1 )
1949 tm
*tm
= localtime(&timet
);
1951 wxCHECK_MSG( tm
, -1, _T("localtime() failed") );
1953 return tm
->tm_isdst
;
1957 int year
= GetYear();
1959 if ( !IsDSTApplicable(year
, country
) )
1961 // no DST time in this year in this country
1965 return IsBetween(GetBeginDST(year
, country
), GetEndDST(year
, country
));
1969 wxDateTime
& wxDateTime::MakeTimezone(const TimeZone
& tz
, bool noDST
)
1971 long secDiff
= GetTimeZone() + tz
.GetOffset();
1973 // we need to know whether DST is or not in effect for this date unless
1974 // the test disabled by the caller
1975 if ( !noDST
&& (IsDST() == 1) )
1977 // FIXME we assume that the DST is always shifted by 1 hour
1981 return Subtract(wxTimeSpan::Seconds(secDiff
));
1984 // ----------------------------------------------------------------------------
1985 // wxDateTime to/from text representations
1986 // ----------------------------------------------------------------------------
1988 wxString
wxDateTime::Format(const wxChar
*format
, const TimeZone
& tz
) const
1990 wxCHECK_MSG( format
, _T(""), _T("NULL format in wxDateTime::Format") );
1992 // we have to use our own implementation if the date is out of range of
1993 // strftime() or if we use non standard specificators
1994 time_t time
= GetTicks();
1995 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
1999 if ( tz
.GetOffset() == -GetTimeZone() )
2001 // we are working with local time
2002 tm
= localtime(&time
);
2004 // should never happen
2005 wxCHECK_MSG( tm
, wxEmptyString
, _T("localtime() failed") );
2009 time
+= (int)tz
.GetOffset();
2011 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
2012 int time2
= (int) time
;
2020 // should never happen
2021 wxCHECK_MSG( tm
, wxEmptyString
, _T("gmtime() failed") );
2025 tm
= (struct tm
*)NULL
;
2031 return CallStrftime(format
, tm
);
2033 //else: use generic code below
2036 // we only parse ANSI C format specifications here, no POSIX 2
2037 // complications, no GNU extensions but we do add support for a "%l" format
2038 // specifier allowing to get the number of milliseconds
2041 // used for calls to strftime() when we only deal with time
2042 struct tm tmTimeOnly
;
2043 tmTimeOnly
.tm_hour
= tm
.hour
;
2044 tmTimeOnly
.tm_min
= tm
.min
;
2045 tmTimeOnly
.tm_sec
= tm
.sec
;
2046 tmTimeOnly
.tm_wday
= 0;
2047 tmTimeOnly
.tm_yday
= 0;
2048 tmTimeOnly
.tm_mday
= 1; // any date will do
2049 tmTimeOnly
.tm_mon
= 0;
2050 tmTimeOnly
.tm_year
= 76;
2051 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
2053 wxString tmp
, res
, fmt
;
2054 for ( const wxChar
*p
= format
; *p
; p
++ )
2056 if ( *p
!= _T('%') )
2064 // set the default format
2067 case _T('Y'): // year has 4 digits
2071 case _T('j'): // day of year has 3 digits
2072 case _T('l'): // milliseconds have 3 digits
2076 case _T('w'): // week day as number has only one
2081 // it's either another valid format specifier in which case
2082 // the format is "%02d" (for all the rest) or we have the
2083 // field width preceding the format in which case it will
2084 // override the default format anyhow
2088 bool restart
= TRUE
;
2093 // start of the format specification
2096 case _T('a'): // a weekday name
2098 // second parameter should be TRUE for abbreviated names
2099 res
+= GetWeekDayName(tm
.GetWeekDay(),
2100 *p
== _T('a') ? Name_Abbr
: Name_Full
);
2103 case _T('b'): // a month name
2105 res
+= GetMonthName(tm
.mon
,
2106 *p
== _T('b') ? Name_Abbr
: Name_Full
);
2109 case _T('c'): // locale default date and time representation
2110 case _T('x'): // locale default date representation
2112 // the problem: there is no way to know what do these format
2113 // specifications correspond to for the current locale.
2115 // the solution: use a hack and still use strftime(): first
2116 // find the YEAR which is a year in the strftime() range (1970
2117 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
2118 // of the real year. Then make a copy of the format and
2119 // replace all occurences of YEAR in it with some unique
2120 // string not appearing anywhere else in it, then use
2121 // strftime() to format the date in year YEAR and then replace
2122 // YEAR back by the real year and the unique replacement
2123 // string back with YEAR. Notice that "all occurences of YEAR"
2124 // means all occurences of 4 digit as well as 2 digit form!
2126 // the bugs: we assume that neither of %c nor %x contains any
2127 // fields which may change between the YEAR and real year. For
2128 // example, the week number (%U, %W) and the day number (%j)
2129 // will change if one of these years is leap and the other one
2132 // find the YEAR: normally, for any year X, Jan 1 or the
2133 // year X + 28 is the same weekday as Jan 1 of X (because
2134 // the weekday advances by 1 for each normal X and by 2
2135 // for each leap X, hence by 5 every 4 years or by 35
2136 // which is 0 mod 7 every 28 years) but this rule breaks
2137 // down if there are years between X and Y which are
2138 // divisible by 4 but not leap (i.e. divisible by 100 but
2139 // not 400), hence the correction.
2141 int yearReal
= GetYear(tz
);
2142 int mod28
= yearReal
% 28;
2144 // be careful to not go too far - we risk to leave the
2149 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
2153 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
2156 int nCentury
= year
/ 100,
2157 nCenturyReal
= yearReal
/ 100;
2159 // need to adjust for the years divisble by 400 which are
2160 // not leap but are counted like leap ones if we just take
2161 // the number of centuries in between for nLostWeekDays
2162 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
2163 (nCentury
/ 4 - nCenturyReal
/ 4);
2165 // we have to gain back the "lost" weekdays: note that the
2166 // effect of this loop is to not do anything to
2167 // nLostWeekDays (which we won't use any more), but to
2168 // (indirectly) set the year correctly
2169 while ( (nLostWeekDays
% 7) != 0 )
2171 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
2174 // at any rate, we couldn't go further than 1988 + 9 + 28!
2175 wxASSERT_MSG( year
< 2030,
2176 _T("logic error in wxDateTime::Format") );
2178 wxString strYear
, strYear2
;
2179 strYear
.Printf(_T("%d"), year
);
2180 strYear2
.Printf(_T("%d"), year
% 100);
2182 // find two strings not occuring in format (this is surely
2183 // not optimal way of doing it... improvements welcome!)
2184 wxString fmt
= format
;
2185 wxString replacement
= (wxChar
)-1;
2186 while ( fmt
.Find(replacement
) != wxNOT_FOUND
)
2188 replacement
<< (wxChar
)-1;
2191 wxString replacement2
= (wxChar
)-2;
2192 while ( fmt
.Find(replacement
) != wxNOT_FOUND
)
2194 replacement
<< (wxChar
)-2;
2197 // replace all occurences of year with it
2198 bool wasReplaced
= fmt
.Replace(strYear
, replacement
) > 0;
2200 wasReplaced
= fmt
.Replace(strYear2
, replacement2
) > 0;
2202 // use strftime() to format the same date but in supported
2205 // NB: we assume that strftime() doesn't check for the
2206 // date validity and will happily format the date
2207 // corresponding to Feb 29 of a non leap year (which
2208 // may happen if yearReal was leap and year is not)
2209 struct tm tmAdjusted
;
2211 tmAdjusted
.tm_hour
= tm
.hour
;
2212 tmAdjusted
.tm_min
= tm
.min
;
2213 tmAdjusted
.tm_sec
= tm
.sec
;
2214 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
2215 tmAdjusted
.tm_yday
= GetDayOfYear();
2216 tmAdjusted
.tm_mday
= tm
.mday
;
2217 tmAdjusted
.tm_mon
= tm
.mon
;
2218 tmAdjusted
.tm_year
= year
- 1900;
2219 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
2220 wxString str
= CallStrftime(*p
== _T('c') ? _T("%c")
2224 // now replace the occurence of 1999 with the real year
2225 wxString strYearReal
, strYearReal2
;
2226 strYearReal
.Printf(_T("%04d"), yearReal
);
2227 strYearReal2
.Printf(_T("%02d"), yearReal
% 100);
2228 str
.Replace(strYear
, strYearReal
);
2229 str
.Replace(strYear2
, strYearReal2
);
2231 // and replace back all occurences of replacement string
2234 str
.Replace(replacement2
, strYear2
);
2235 str
.Replace(replacement
, strYear
);
2242 case _T('d'): // day of a month (01-31)
2243 res
+= wxString::Format(fmt
, tm
.mday
);
2246 case _T('H'): // hour in 24h format (00-23)
2247 res
+= wxString::Format(fmt
, tm
.hour
);
2250 case _T('I'): // hour in 12h format (01-12)
2252 // 24h -> 12h, 0h -> 12h too
2253 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
2254 : tm
.hour
? tm
.hour
: 12;
2255 res
+= wxString::Format(fmt
, hour12
);
2259 case _T('j'): // day of the year
2260 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
2263 case _T('l'): // milliseconds (NOT STANDARD)
2264 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
2267 case _T('m'): // month as a number (01-12)
2268 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
2271 case _T('M'): // minute as a decimal number (00-59)
2272 res
+= wxString::Format(fmt
, tm
.min
);
2275 case _T('p'): // AM or PM string
2276 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
2279 case _T('S'): // second as a decimal number (00-61)
2280 res
+= wxString::Format(fmt
, tm
.sec
);
2283 case _T('U'): // week number in the year (Sunday 1st week day)
2284 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
2287 case _T('W'): // week number in the year (Monday 1st week day)
2288 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
2291 case _T('w'): // weekday as a number (0-6), Sunday = 0
2292 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
2295 // case _T('x'): -- handled with "%c"
2297 case _T('X'): // locale default time representation
2298 // just use strftime() to format the time for us
2299 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
2302 case _T('y'): // year without century (00-99)
2303 res
+= wxString::Format(fmt
, tm
.year
% 100);
2306 case _T('Y'): // year with century
2307 res
+= wxString::Format(fmt
, tm
.year
);
2310 case _T('Z'): // timezone name
2311 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
2315 // is it the format width?
2317 while ( *p
== _T('-') || *p
== _T('+') ||
2318 *p
== _T(' ') || wxIsdigit(*p
) )
2323 if ( !fmt
.IsEmpty() )
2325 // we've only got the flags and width so far in fmt
2326 fmt
.Prepend(_T('%'));
2327 fmt
.Append(_T('d'));
2334 // no, it wasn't the width
2335 wxFAIL_MSG(_T("unknown format specificator"));
2337 // fall through and just copy it nevertheless
2339 case _T('%'): // a percent sign
2343 case 0: // the end of string
2344 wxFAIL_MSG(_T("missing format at the end of string"));
2346 // just put the '%' which was the last char in format
2356 // this function parses a string in (strict) RFC 822 format: see the section 5
2357 // of the RFC for the detailed description, but briefly it's something of the
2358 // form "Sat, 18 Dec 1999 00:48:30 +0100"
2360 // this function is "strict" by design - it must reject anything except true
2361 // RFC822 time specs.
2363 // TODO a great candidate for using reg exps
2364 const wxChar
*wxDateTime::ParseRfc822Date(const wxChar
* date
)
2366 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
2368 const wxChar
*p
= date
;
2369 const wxChar
*comma
= wxStrchr(p
, _T(','));
2372 // the part before comma is the weekday
2374 // skip it for now - we don't use but might check that it really
2375 // corresponds to the specfied date
2378 if ( *p
!= _T(' ') )
2380 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
2382 return (wxChar
*)NULL
;
2388 // the following 1 or 2 digits are the day number
2389 if ( !wxIsdigit(*p
) )
2391 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
2393 return (wxChar
*)NULL
;
2396 wxDateTime_t day
= *p
++ - _T('0');
2397 if ( wxIsdigit(*p
) )
2400 day
+= *p
++ - _T('0');
2403 if ( *p
++ != _T(' ') )
2405 return (wxChar
*)NULL
;
2408 // the following 3 letters specify the month
2409 wxString
monName(p
, 3);
2411 if ( monName
== _T("Jan") )
2413 else if ( monName
== _T("Feb") )
2415 else if ( monName
== _T("Mar") )
2417 else if ( monName
== _T("Apr") )
2419 else if ( monName
== _T("May") )
2421 else if ( monName
== _T("Jun") )
2423 else if ( monName
== _T("Jul") )
2425 else if ( monName
== _T("Aug") )
2427 else if ( monName
== _T("Sep") )
2429 else if ( monName
== _T("Oct") )
2431 else if ( monName
== _T("Nov") )
2433 else if ( monName
== _T("Dec") )
2437 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str());
2439 return (wxChar
*)NULL
;
2444 if ( *p
++ != _T(' ') )
2446 return (wxChar
*)NULL
;
2450 if ( !wxIsdigit(*p
) )
2453 return (wxChar
*)NULL
;
2456 int year
= *p
++ - _T('0');
2458 if ( !wxIsdigit(*p
) )
2460 // should have at least 2 digits in the year
2461 return (wxChar
*)NULL
;
2465 year
+= *p
++ - _T('0');
2467 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
2468 if ( wxIsdigit(*p
) )
2471 year
+= *p
++ - _T('0');
2473 if ( !wxIsdigit(*p
) )
2475 // no 3 digit years please
2476 return (wxChar
*)NULL
;
2480 year
+= *p
++ - _T('0');
2483 if ( *p
++ != _T(' ') )
2485 return (wxChar
*)NULL
;
2488 // time is in the format hh:mm:ss and seconds are optional
2489 if ( !wxIsdigit(*p
) )
2491 return (wxChar
*)NULL
;
2494 wxDateTime_t hour
= *p
++ - _T('0');
2496 if ( !wxIsdigit(*p
) )
2498 return (wxChar
*)NULL
;
2502 hour
+= *p
++ - _T('0');
2504 if ( *p
++ != _T(':') )
2506 return (wxChar
*)NULL
;
2509 if ( !wxIsdigit(*p
) )
2511 return (wxChar
*)NULL
;
2514 wxDateTime_t min
= *p
++ - _T('0');
2516 if ( !wxIsdigit(*p
) )
2518 return (wxChar
*)NULL
;
2522 min
+= *p
++ - _T('0');
2524 wxDateTime_t sec
= 0;
2525 if ( *p
++ == _T(':') )
2527 if ( !wxIsdigit(*p
) )
2529 return (wxChar
*)NULL
;
2532 sec
= *p
++ - _T('0');
2534 if ( !wxIsdigit(*p
) )
2536 return (wxChar
*)NULL
;
2540 sec
+= *p
++ - _T('0');
2543 if ( *p
++ != _T(' ') )
2545 return (wxChar
*)NULL
;
2548 // and now the interesting part: the timezone
2550 if ( *p
== _T('-') || *p
== _T('+') )
2552 // the explicit offset given: it has the form of hhmm
2553 bool plus
= *p
++ == _T('+');
2555 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2557 return (wxChar
*)NULL
;
2561 offset
= 60*(10*(*p
- _T('0')) + (*(p
+ 1) - _T('0')));
2565 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2567 return (wxChar
*)NULL
;
2571 offset
+= 10*(*p
- _T('0')) + (*(p
+ 1) - _T('0'));
2582 // the symbolic timezone given: may be either military timezone or one
2583 // of standard abbreviations
2586 // military: Z = UTC, J unused, A = -1, ..., Y = +12
2587 static const int offsets
[26] =
2589 //A B C D E F G H I J K L M
2590 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
2591 //N O P R Q S T U V W Z Y Z
2592 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
2595 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
2597 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
);
2599 return (wxChar
*)NULL
;
2602 offset
= offsets
[*p
++ - _T('A')];
2608 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
2610 else if ( tz
== _T("AST") )
2611 offset
= AST
- GMT0
;
2612 else if ( tz
== _T("ADT") )
2613 offset
= ADT
- GMT0
;
2614 else if ( tz
== _T("EST") )
2615 offset
= EST
- GMT0
;
2616 else if ( tz
== _T("EDT") )
2617 offset
= EDT
- GMT0
;
2618 else if ( tz
== _T("CST") )
2619 offset
= CST
- GMT0
;
2620 else if ( tz
== _T("CDT") )
2621 offset
= CDT
- GMT0
;
2622 else if ( tz
== _T("MST") )
2623 offset
= MST
- GMT0
;
2624 else if ( tz
== _T("MDT") )
2625 offset
= MDT
- GMT0
;
2626 else if ( tz
== _T("PST") )
2627 offset
= PST
- GMT0
;
2628 else if ( tz
== _T("PDT") )
2629 offset
= PDT
- GMT0
;
2632 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
);
2634 return (wxChar
*)NULL
;
2644 // the spec was correct
2645 Set(day
, mon
, year
, hour
, min
, sec
);
2646 MakeTimezone((wxDateTime_t
)(60*offset
));
2651 const wxChar
*wxDateTime::ParseFormat(const wxChar
*date
,
2652 const wxChar
*format
,
2653 const wxDateTime
& dateDef
)
2655 wxCHECK_MSG( date
&& format
, (wxChar
*)NULL
,
2656 _T("NULL pointer in wxDateTime::ParseFormat()") );
2661 // what fields have we found?
2662 bool haveWDay
= FALSE
,
2671 bool hourIsIn12hFormat
= FALSE
, // or in 24h one?
2672 isPM
= FALSE
; // AM by default
2674 // and the value of the items we have (init them to get rid of warnings)
2675 wxDateTime_t sec
= 0,
2678 WeekDay wday
= Inv_WeekDay
;
2679 wxDateTime_t yday
= 0,
2681 wxDateTime::Month mon
= Inv_Month
;
2684 const wxChar
*input
= date
;
2685 for ( const wxChar
*fmt
= format
; *fmt
; fmt
++ )
2687 if ( *fmt
!= _T('%') )
2689 if ( wxIsspace(*fmt
) )
2691 // a white space in the format string matches 0 or more white
2692 // spaces in the input
2693 while ( wxIsspace(*input
) )
2700 // any other character (not whitespace, not '%') must be
2701 // matched by itself in the input
2702 if ( *input
++ != *fmt
)
2705 return (wxChar
*)NULL
;
2709 // done with this format char
2713 // start of a format specification
2715 // parse the optional width
2717 while ( wxIsdigit(*++fmt
) )
2720 width
+= *fmt
- _T('0');
2723 // the default widths for the various fields
2728 case _T('Y'): // year has 4 digits
2732 case _T('j'): // day of year has 3 digits
2733 case _T('l'): // milliseconds have 3 digits
2737 case _T('w'): // week day as number has only one
2742 // default for all other fields
2747 // then the format itself
2750 case _T('a'): // a weekday name
2753 int flag
= *fmt
== _T('a') ? Name_Abbr
: Name_Full
;
2754 wday
= GetWeekDayFromName(GetAlphaToken(input
), flag
);
2755 if ( wday
== Inv_WeekDay
)
2758 return (wxChar
*)NULL
;
2764 case _T('b'): // a month name
2767 int flag
= *fmt
== _T('b') ? Name_Abbr
: Name_Full
;
2768 mon
= GetMonthFromName(GetAlphaToken(input
), flag
);
2769 if ( mon
== Inv_Month
)
2772 return (wxChar
*)NULL
;
2778 case _T('c'): // locale default date and time representation
2782 // this is the format which corresponds to ctime() output
2783 // and strptime("%c") should parse it, so try it first
2784 static const wxChar
*fmtCtime
= _T("%a %b %d %H:%M:%S %Y");
2786 const wxChar
*result
= dt
.ParseFormat(input
, fmtCtime
);
2789 result
= dt
.ParseFormat(input
, _T("%x %X"));
2794 result
= dt
.ParseFormat(input
, _T("%X %x"));
2799 // we've tried everything and still no match
2800 return (wxChar
*)NULL
;
2805 haveDay
= haveMon
= haveYear
=
2806 haveHour
= haveMin
= haveSec
= TRUE
;
2820 case _T('d'): // day of a month (01-31)
2821 if ( !GetNumericToken(width
, input
, &num
) ||
2822 (num
> 31) || (num
< 1) )
2825 return (wxChar
*)NULL
;
2828 // we can't check whether the day range is correct yet, will
2829 // do it later - assume ok for now
2831 mday
= (wxDateTime_t
)num
;
2834 case _T('H'): // hour in 24h format (00-23)
2835 if ( !GetNumericToken(width
, input
, &num
) || (num
> 23) )
2838 return (wxChar
*)NULL
;
2842 hour
= (wxDateTime_t
)num
;
2845 case _T('I'): // hour in 12h format (01-12)
2846 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
2849 return (wxChar
*)NULL
;
2853 hourIsIn12hFormat
= TRUE
;
2854 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
2857 case _T('j'): // day of the year
2858 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 366) )
2861 return (wxChar
*)NULL
;
2865 yday
= (wxDateTime_t
)num
;
2868 case _T('m'): // month as a number (01-12)
2869 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
2872 return (wxChar
*)NULL
;
2876 mon
= (Month
)(num
- 1);
2879 case _T('M'): // minute as a decimal number (00-59)
2880 if ( !GetNumericToken(width
, input
, &num
) || (num
> 59) )
2883 return (wxChar
*)NULL
;
2887 min
= (wxDateTime_t
)num
;
2890 case _T('p'): // AM or PM string
2892 wxString am
, pm
, token
= GetAlphaToken(input
);
2894 GetAmPmStrings(&am
, &pm
);
2895 if (am
.IsEmpty() && pm
.IsEmpty())
2896 return (wxChar
*)NULL
; // no am/pm strings defined
2897 if ( token
.CmpNoCase(pm
) == 0 )
2901 else if ( token
.CmpNoCase(am
) != 0 )
2904 return (wxChar
*)NULL
;
2909 case _T('r'): // time as %I:%M:%S %p
2912 input
= dt
.ParseFormat(input
, _T("%I:%M:%S %p"));
2916 return (wxChar
*)NULL
;
2919 haveHour
= haveMin
= haveSec
= TRUE
;
2928 case _T('R'): // time as %H:%M
2931 input
= dt
.ParseFormat(input
, _T("%H:%M"));
2935 return (wxChar
*)NULL
;
2938 haveHour
= haveMin
= TRUE
;
2945 case _T('S'): // second as a decimal number (00-61)
2946 if ( !GetNumericToken(width
, input
, &num
) || (num
> 61) )
2949 return (wxChar
*)NULL
;
2953 sec
= (wxDateTime_t
)num
;
2956 case _T('T'): // time as %H:%M:%S
2959 input
= dt
.ParseFormat(input
, _T("%H:%M:%S"));
2963 return (wxChar
*)NULL
;
2966 haveHour
= haveMin
= haveSec
= TRUE
;
2975 case _T('w'): // weekday as a number (0-6), Sunday = 0
2976 if ( !GetNumericToken(width
, input
, &num
) || (wday
> 6) )
2979 return (wxChar
*)NULL
;
2983 wday
= (WeekDay
)num
;
2986 case _T('x'): // locale default date representation
2987 #ifdef HAVE_STRPTIME
2988 // try using strptime() -- it may fail even if the input is
2989 // correct but the date is out of range, so we will fall back
2990 // to our generic code anyhow
2994 const wxChar
*result
= CallStrptime(input
, "%x", &tm
);
2999 haveDay
= haveMon
= haveYear
= TRUE
;
3001 year
= 1900 + tm
.tm_year
;
3002 mon
= (Month
)tm
.tm_mon
;
3008 #endif // HAVE_STRPTIME
3010 // TODO query the LOCALE_IDATE setting under Win32
3014 wxString fmtDate
, fmtDateAlt
;
3015 if ( IsWestEuropeanCountry(GetCountry()) ||
3016 GetCountry() == Russia
)
3018 fmtDate
= _T("%d/%m/%y");
3019 fmtDateAlt
= _T("%m/%d/%y");
3023 fmtDate
= _T("%m/%d/%y");
3024 fmtDateAlt
= _T("%d/%m/%y");
3027 const wxChar
*result
= dt
.ParseFormat(input
, fmtDate
);
3031 // ok, be nice and try another one
3032 result
= dt
.ParseFormat(input
, fmtDateAlt
);
3038 return (wxChar
*)NULL
;
3043 haveDay
= haveMon
= haveYear
= TRUE
;
3054 case _T('X'): // locale default time representation
3055 #ifdef HAVE_STRPTIME
3057 // use strptime() to do it for us (FIXME !Unicode friendly)
3059 input
= CallStrptime(input
, "%X", &tm
);
3062 return (wxChar
*)NULL
;
3065 haveHour
= haveMin
= haveSec
= TRUE
;
3071 #else // !HAVE_STRPTIME
3072 // TODO under Win32 we can query the LOCALE_ITIME system
3073 // setting which says whether the default time format is
3076 // try to parse what follows as "%H:%M:%S" and, if this
3077 // fails, as "%I:%M:%S %p" - this should catch the most
3081 const wxChar
*result
= dt
.ParseFormat(input
, _T("%T"));
3084 result
= dt
.ParseFormat(input
, _T("%r"));
3090 return (wxChar
*)NULL
;
3093 haveHour
= haveMin
= haveSec
= TRUE
;
3102 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
3105 case _T('y'): // year without century (00-99)
3106 if ( !GetNumericToken(width
, input
, &num
) || (num
> 99) )
3109 return (wxChar
*)NULL
;
3114 // TODO should have an option for roll over date instead of
3115 // hard coding it here
3116 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
3119 case _T('Y'): // year with century
3120 if ( !GetNumericToken(width
, input
, &num
) )
3123 return (wxChar
*)NULL
;
3127 year
= (wxDateTime_t
)num
;
3130 case _T('Z'): // timezone name
3131 wxFAIL_MSG(_T("TODO"));
3134 case _T('%'): // a percent sign
3135 if ( *input
++ != _T('%') )
3138 return (wxChar
*)NULL
;
3142 case 0: // the end of string
3143 wxFAIL_MSG(_T("unexpected format end"));
3147 default: // not a known format spec
3148 return (wxChar
*)NULL
;
3152 // format matched, try to construct a date from what we have now
3154 if ( dateDef
.IsValid() )
3156 // take this date as default
3157 tmDef
= dateDef
.GetTm();
3159 else if ( IsValid() )
3161 // if this date is valid, don't change it
3166 // no default and this date is invalid - fall back to Today()
3167 tmDef
= Today().GetTm();
3178 // TODO we don't check here that the values are consistent, if both year
3179 // day and month/day were found, we just ignore the year day and we
3180 // also always ignore the week day
3181 if ( haveMon
&& haveDay
)
3183 if ( mday
> GetNumOfDaysInMonth(tm
.year
, mon
) )
3185 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
3187 return (wxChar
*)NULL
;
3193 else if ( haveYDay
)
3195 if ( yday
> GetNumberOfDays(tm
.year
) )
3197 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
3199 return (wxChar
*)NULL
;
3202 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
3209 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
3211 // translate to 24hour format
3214 //else: either already in 24h format or no translation needed
3234 // finally check that the week day is consistent -- if we had it
3235 if ( haveWDay
&& GetWeekDay() != wday
)
3237 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
3245 const wxChar
*wxDateTime::ParseDateTime(const wxChar
*date
)
3247 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3249 // Set to current day and hour, so strings like '14:00' becomes today at
3250 // 14, not some other random date
3251 wxDateTime dtDate
= wxDateTime::Today();
3252 wxDateTime dtTime
= wxDateTime::Today();
3254 const wxChar
* pchTime
;
3256 // Try to parse the beginning of the string as a date
3257 const wxChar
* pchDate
= dtDate
.ParseDate(date
);
3259 // We got a date in the beginning, see if there is a time specified after the date
3262 // Skip spaces, as the ParseTime() function fails on spaces
3263 while ( wxIsspace(*pchDate
) )
3266 pchTime
= dtTime
.ParseTime(pchDate
);
3268 else // no date in the beginning
3270 // check and see if we have a time followed by a date
3271 pchTime
= dtTime
.ParseTime(date
);
3274 while ( wxIsspace(*pchTime
) )
3277 pchDate
= dtDate
.ParseDate(pchTime
);
3281 // If we have a date specified, set our own data to the same date
3282 if ( !pchDate
|| !pchTime
)
3285 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
3286 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
3287 dtTime
.GetMillisecond());
3289 // Return endpoint of scan
3290 return pchDate
> pchTime
? pchDate
: pchTime
;
3293 const wxChar
*wxDateTime::ParseDate(const wxChar
*date
)
3295 // this is a simplified version of ParseDateTime() which understands only
3296 // "today" (for wxDate compatibility) and digits only otherwise (and not
3297 // all esoteric constructions ParseDateTime() knows about)
3299 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3301 const wxChar
*p
= date
;
3302 while ( wxIsspace(*p
) )
3305 // some special cases
3309 int dayDiffFromToday
;
3312 { wxTRANSLATE("today"), 0 },
3313 { wxTRANSLATE("yesterday"), -1 },
3314 { wxTRANSLATE("tomorrow"), 1 },
3317 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
3319 wxString date
= wxGetTranslation(literalDates
[n
].str
);
3320 size_t len
= date
.length();
3321 if ( wxStrlen(p
) >= len
&& (wxString(p
, len
).CmpNoCase(date
) == 0) )
3323 // nothing can follow this, so stop here
3326 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
3328 if ( dayDiffFromToday
)
3330 *this += wxDateSpan::Days(dayDiffFromToday
);
3337 // We try to guess what we have here: for each new (numeric) token, we
3338 // determine if it can be a month, day or a year. Of course, there is an
3339 // ambiguity as some numbers may be days as well as months, so we also
3340 // have the ability to back track.
3343 bool haveDay
= FALSE
, // the months day?
3344 haveWDay
= FALSE
, // the day of week?
3345 haveMon
= FALSE
, // the month?
3346 haveYear
= FALSE
; // the year?
3348 // and the value of the items we have (init them to get rid of warnings)
3349 WeekDay wday
= Inv_WeekDay
;
3350 wxDateTime_t day
= 0;
3351 wxDateTime::Month mon
= Inv_Month
;
3354 // tokenize the string
3356 static const wxChar
*dateDelimiters
= _T(".,/-\t\r\n ");
3357 wxStringTokenizer
tok(p
, dateDelimiters
);
3358 while ( tok
.HasMoreTokens() )
3360 wxString token
= tok
.GetNextToken();
3366 if ( token
.ToULong(&val
) )
3368 // guess what this number is
3374 if ( !haveMon
&& val
> 0 && val
<= 12 )
3376 // assume it is month
3379 else // not the month
3383 // this can only be the year
3386 else // may be either day or year
3388 wxDateTime_t maxDays
= haveMon
3389 ? GetNumOfDaysInMonth(haveYear
? year
: Inv_Year
, mon
)
3393 if ( (val
== 0) || (val
> (unsigned long)maxDays
) )
3398 else // yes, suppose it's the day
3412 year
= (wxDateTime_t
)val
;
3421 day
= (wxDateTime_t
)val
;
3427 mon
= (Month
)(val
- 1);
3430 else // not a number
3432 // be careful not to overwrite the current mon value
3433 Month mon2
= GetMonthFromName(token
, Name_Full
| Name_Abbr
);
3434 if ( mon2
!= Inv_Month
)
3439 // but we already have a month - maybe we guessed wrong?
3442 // no need to check in month range as always < 12, but
3443 // the days are counted from 1 unlike the months
3444 day
= (wxDateTime_t
)mon
+ 1;
3449 // could possible be the year (doesn't the year come
3450 // before the month in the japanese format?) (FIXME)
3459 else // not a valid month name
3461 wday
= GetWeekDayFromName(token
, Name_Full
| Name_Abbr
);
3462 if ( wday
!= Inv_WeekDay
)
3472 else // not a valid weekday name
3475 static const wxChar
*ordinals
[] =
3477 wxTRANSLATE("first"),
3478 wxTRANSLATE("second"),
3479 wxTRANSLATE("third"),
3480 wxTRANSLATE("fourth"),
3481 wxTRANSLATE("fifth"),
3482 wxTRANSLATE("sixth"),
3483 wxTRANSLATE("seventh"),
3484 wxTRANSLATE("eighth"),
3485 wxTRANSLATE("ninth"),
3486 wxTRANSLATE("tenth"),
3487 wxTRANSLATE("eleventh"),
3488 wxTRANSLATE("twelfth"),
3489 wxTRANSLATE("thirteenth"),
3490 wxTRANSLATE("fourteenth"),
3491 wxTRANSLATE("fifteenth"),
3492 wxTRANSLATE("sixteenth"),
3493 wxTRANSLATE("seventeenth"),
3494 wxTRANSLATE("eighteenth"),
3495 wxTRANSLATE("nineteenth"),
3496 wxTRANSLATE("twentieth"),
3497 // that's enough - otherwise we'd have problems with
3498 // composite (or not) ordinals
3502 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
3504 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
3510 if ( n
== WXSIZEOF(ordinals
) )
3512 // stop here - something unknown
3519 // don't try anything here (as in case of numeric day
3520 // above) - the symbolic day spec should always
3521 // precede the month/year
3527 day
= (wxDateTime_t
)(n
+ 1);
3532 nPosCur
= tok
.GetPosition();
3535 // either no more tokens or the scan was stopped by something we couldn't
3536 // parse - in any case, see if we can construct a date from what we have
3537 if ( !haveDay
&& !haveWDay
)
3539 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
3541 return (wxChar
*)NULL
;
3544 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
3545 !(haveDay
&& haveMon
&& haveYear
) )
3547 // without adjectives (which we don't support here) the week day only
3548 // makes sense completely separately or with the full date
3549 // specification (what would "Wed 1999" mean?)
3550 return (wxChar
*)NULL
;
3553 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
3555 // may be we have month and day instead of day and year?
3556 if ( haveDay
&& !haveMon
)
3560 // exchange day and month
3561 mon
= (wxDateTime::Month
)(day
- 1);
3563 // we're in the current year then
3564 if ( (year
> 0) && (year
<= (int)GetNumOfDaysInMonth(Inv_Year
, mon
)) )
3571 //else: no, can't exchange, leave haveMon == FALSE
3577 // if we give the year, month and day must be given too
3578 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
3580 return (wxChar
*)NULL
;
3586 mon
= GetCurrentMonth();
3591 year
= GetCurrentYear();
3596 Set(day
, mon
, year
);
3600 // check that it is really the same
3601 if ( GetWeekDay() != wday
)
3603 // inconsistency detected
3604 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
3606 return (wxChar
*)NULL
;
3614 SetToWeekDayInSameWeek(wday
);
3617 // return the pointer to the first unparsed char
3619 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
3621 // if we couldn't parse the token after the delimiter, put back the
3622 // delimiter as well
3629 const wxChar
*wxDateTime::ParseTime(const wxChar
*time
)
3631 wxCHECK_MSG( time
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3633 // first try some extra things
3640 { wxTRANSLATE("noon"), 12 },
3641 { wxTRANSLATE("midnight"), 00 },
3645 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
3647 wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
3648 size_t len
= timeString
.length();
3649 if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 )
3651 // casts required by DigitalMars
3652 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
3658 // try all time formats we may think about in the order from longest to
3661 // 12hour with AM/PM?
3662 const wxChar
*result
= ParseFormat(time
, _T("%I:%M:%S %p"));
3666 // normally, it's the same, but why not try it?
3667 result
= ParseFormat(time
, _T("%H:%M:%S"));
3672 // 12hour with AM/PM but without seconds?
3673 result
= ParseFormat(time
, _T("%I:%M %p"));
3679 result
= ParseFormat(time
, _T("%H:%M"));
3684 // just the hour and AM/PM?
3685 result
= ParseFormat(time
, _T("%I %p"));
3691 result
= ParseFormat(time
, _T("%H"));
3696 // parse the standard format: normally it is one of the formats above
3697 // but it may be set to something completely different by the user
3698 result
= ParseFormat(time
, _T("%X"));
3701 // TODO: parse timezones
3706 // ----------------------------------------------------------------------------
3707 // Workdays and holidays support
3708 // ----------------------------------------------------------------------------
3710 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
3712 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
3715 // ============================================================================
3717 // ============================================================================
3719 // this enum is only used in wxTimeSpan::Format() below but we can't declare
3720 // it locally to the method as it provokes an internal compiler error in egcs
3721 // 2.91.60 when building with -O2
3732 // not all strftime(3) format specifiers make sense here because, for example,
3733 // a time span doesn't have a year nor a timezone
3735 // Here are the ones which are supported (all of them are supported by strftime
3737 // %H hour in 24 hour format
3738 // %M minute (00 - 59)
3739 // %S second (00 - 59)
3742 // Also, for MFC CTimeSpan compatibility, we support
3743 // %D number of days
3745 // And, to be better than MFC :-), we also have
3746 // %E number of wEeks
3747 // %l milliseconds (000 - 999)
3748 wxString
wxTimeSpan::Format(const wxChar
*format
) const
3750 wxCHECK_MSG( format
, _T(""), _T("NULL format in wxTimeSpan::Format") );
3753 str
.Alloc(wxStrlen(format
));
3755 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
3757 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
3758 // question is what should ts.Format("%S") do? The code here returns "3273"
3759 // in this case (i.e. the total number of seconds, not just seconds % 60)
3760 // because, for me, this call means "give me entire time interval in
3761 // seconds" and not "give me the seconds part of the time interval"
3763 // If we agree that it should behave like this, it is clear that the
3764 // interpretation of each format specifier depends on the presence of the
3765 // other format specs in the string: if there was "%H" before "%M", we
3766 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
3768 // we remember the most important unit found so far
3769 TimeSpanPart partBiggest
= Part_MSec
;
3771 for ( const wxChar
*pch
= format
; *pch
; pch
++ )
3775 if ( ch
== _T('%') )
3777 // the start of the format specification of the printf() below
3778 wxString fmtPrefix
= _T('%');
3783 ch
= *++pch
; // get the format spec char
3787 wxFAIL_MSG( _T("invalid format character") );
3793 // skip the part below switch
3798 if ( partBiggest
< Part_Day
)
3804 partBiggest
= Part_Day
;
3809 partBiggest
= Part_Week
;
3815 if ( partBiggest
< Part_Hour
)
3821 partBiggest
= Part_Hour
;
3824 fmtPrefix
+= _T("02");
3828 n
= GetMilliseconds().ToLong();
3829 if ( partBiggest
< Part_MSec
)
3833 //else: no need to reset partBiggest to Part_MSec, it is
3834 // the least significant one anyhow
3836 fmtPrefix
+= _T("03");
3841 if ( partBiggest
< Part_Min
)
3847 partBiggest
= Part_Min
;
3850 fmtPrefix
+= _T("02");
3854 n
= GetSeconds().ToLong();
3855 if ( partBiggest
< Part_Sec
)
3861 partBiggest
= Part_Sec
;
3864 fmtPrefix
+= _T("02");
3868 str
+= wxString::Format(fmtPrefix
+ _T("ld"), n
);
3872 // normal character, just copy
3880 // ============================================================================
3881 // wxDateTimeHolidayAuthority and related classes
3882 // ============================================================================
3884 #include "wx/arrimpl.cpp"
3886 WX_DEFINE_OBJARRAY(wxDateTimeArray
);
3888 static int wxCMPFUNC_CONV
3889 wxDateTimeCompareFunc(wxDateTime
**first
, wxDateTime
**second
)
3891 wxDateTime dt1
= **first
,
3894 return dt1
== dt2
? 0 : dt1
< dt2
? -1 : +1;
3897 // ----------------------------------------------------------------------------
3898 // wxDateTimeHolidayAuthority
3899 // ----------------------------------------------------------------------------
3901 wxHolidayAuthoritiesArray
wxDateTimeHolidayAuthority::ms_authorities
;
3904 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime
& dt
)
3906 size_t count
= ms_authorities
.size();
3907 for ( size_t n
= 0; n
< count
; n
++ )
3909 if ( ms_authorities
[n
]->DoIsHoliday(dt
) )
3920 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime
& dtStart
,
3921 const wxDateTime
& dtEnd
,
3922 wxDateTimeArray
& holidays
)
3924 wxDateTimeArray hol
;
3928 size_t count
= ms_authorities
.size();
3929 for ( size_t nAuth
= 0; nAuth
< count
; nAuth
++ )
3931 ms_authorities
[nAuth
]->DoGetHolidaysInRange(dtStart
, dtEnd
, hol
);
3933 WX_APPEND_ARRAY(holidays
, hol
);
3936 holidays
.Sort(wxDateTimeCompareFunc
);
3938 return holidays
.size();
3942 void wxDateTimeHolidayAuthority::ClearAllAuthorities()
3944 WX_CLEAR_ARRAY(ms_authorities
);
3948 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority
*auth
)
3950 ms_authorities
.push_back(auth
);
3953 wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
3955 // nothing to do here
3958 // ----------------------------------------------------------------------------
3959 // wxDateTimeWorkDays
3960 // ----------------------------------------------------------------------------
3962 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime
& dt
) const
3964 wxDateTime::WeekDay wd
= dt
.GetWeekDay();
3966 return (wd
== wxDateTime::Sun
) || (wd
== wxDateTime::Sat
);
3969 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime
& dtStart
,
3970 const wxDateTime
& dtEnd
,
3971 wxDateTimeArray
& holidays
) const
3973 if ( dtStart
> dtEnd
)
3975 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
3982 // instead of checking all days, start with the first Sat after dtStart and
3983 // end with the last Sun before dtEnd
3984 wxDateTime dtSatFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sat
),
3985 dtSatLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sat
),
3986 dtSunFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sun
),
3987 dtSunLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sun
),
3990 for ( dt
= dtSatFirst
; dt
<= dtSatLast
; dt
+= wxDateSpan::Week() )
3995 for ( dt
= dtSunFirst
; dt
<= dtSunLast
; dt
+= wxDateSpan::Week() )
4000 return holidays
.GetCount();
4003 #endif // wxUSE_DATETIME