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 license
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 // ----------------------------------------------------------------------------
56 #pragma implementation "datetime.h"
59 // For compilers that support precompilation, includes "wx.h".
60 #include "wx/wxprec.h"
67 #include "wx/string.h"
72 #include "wx/thread.h"
73 #include "wx/tokenzr.h"
74 #include "wx/module.h"
76 #define wxDEFINE_TIME_CONSTANTS // before including datetime.h
80 #include "wx/datetime.h"
81 #include "wx/timer.h" // for wxGetLocalTimeMillis()
83 // ----------------------------------------------------------------------------
84 // conditional compilation
85 // ----------------------------------------------------------------------------
87 #if defined(HAVE_STRPTIME) && defined(__LINUX__)
88 // glibc 2.0.7 strptime() is broken - the following snippet causes it to
89 // crash (instead of just failing):
91 // strncpy(buf, "Tue Dec 21 20:25:40 1999", 128);
92 // strptime(buf, "%x", &tm);
96 #endif // broken strptime()
99 #if defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
100 #define WX_TIMEZONE _timezone
101 #elif defined(__MWERKS__)
102 long wxmw_timezone
= 28800;
103 #define WX_TIMEZONE wxmw_timezone;
104 #else // unknown platform - try timezone
105 #define WX_TIMEZONE timezone
107 #endif // !WX_TIMEZONE
109 // ----------------------------------------------------------------------------
111 // ----------------------------------------------------------------------------
113 // debugging helper: just a convenient replacement of wxCHECK()
114 #define wxDATETIME_CHECK(expr, msg) \
118 *this = wxInvalidDateTime; \
122 // ----------------------------------------------------------------------------
124 // ----------------------------------------------------------------------------
126 class wxDateTimeHolidaysModule
: public wxModule
129 virtual bool OnInit()
131 wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays
);
136 virtual void OnExit()
138 wxDateTimeHolidayAuthority::ClearAllAuthorities();
139 wxDateTimeHolidayAuthority::ms_authorities
.Clear();
143 DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule
)
146 IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule
, wxModule
)
148 // ----------------------------------------------------------------------------
150 // ----------------------------------------------------------------------------
153 static const int MONTHS_IN_YEAR
= 12;
155 static const int SECONDS_IN_MINUTE
= 60;
157 static const long SECONDS_PER_DAY
= 86400l;
159 static const long MILLISECONDS_PER_DAY
= 86400000l;
161 // this is the integral part of JDN of the midnight of Jan 1, 1970
162 // (i.e. JDN(Jan 1, 1970) = 2440587.5)
163 static const long EPOCH_JDN
= 2440587l;
165 // the date of JDN -0.5 (as we don't work with fractional parts, this is the
166 // reference date for us) is Nov 24, 4714BC
167 static const int JDN_0_YEAR
= -4713;
168 static const int JDN_0_MONTH
= wxDateTime::Nov
;
169 static const int JDN_0_DAY
= 24;
171 // the constants used for JDN calculations
172 static const long JDN_OFFSET
= 32046l;
173 static const long DAYS_PER_5_MONTHS
= 153l;
174 static const long DAYS_PER_4_YEARS
= 1461l;
175 static const long DAYS_PER_400_YEARS
= 146097l;
177 // this array contains the cumulated number of days in all previous months for
178 // normal and leap years
179 static const wxDateTime::wxDateTime_t gs_cumulatedDays
[2][MONTHS_IN_YEAR
] =
181 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
182 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
185 // ----------------------------------------------------------------------------
187 // ----------------------------------------------------------------------------
189 // in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
190 // indicate an invalid wxDateTime object
191 static const wxDateTime gs_dtDefault
= wxLongLong((long)ULONG_MAX
, ULONG_MAX
);
193 const wxDateTime
& wxDefaultDateTime
= gs_dtDefault
;
195 wxDateTime::Country
wxDateTime::ms_country
= wxDateTime::Country_Unknown
;
197 // ----------------------------------------------------------------------------
199 // ----------------------------------------------------------------------------
201 // a critical section is needed to protect GetTimeZone() static
202 // variable in MT case
204 static wxCriticalSection gs_critsectTimezone
;
205 #endif // wxUSE_THREADS
207 // ----------------------------------------------------------------------------
209 // ----------------------------------------------------------------------------
211 // debugger helper: shows what the date really is
213 extern const wxChar
*wxDumpDate(const wxDateTime
* dt
)
215 static wxChar buf
[128];
217 wxStrcpy(buf
, dt
->Format(_T("%Y-%m-%d (%a) %H:%M:%S")));
223 // get the number of days in the given month of the given year
225 wxDateTime::wxDateTime_t
GetNumOfDaysInMonth(int year
, wxDateTime::Month month
)
227 // the number of days in month in Julian/Gregorian calendar: the first line
228 // is for normal years, the second one is for the leap ones
229 static wxDateTime::wxDateTime_t daysInMonth
[2][MONTHS_IN_YEAR
] =
231 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
232 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
235 return daysInMonth
[wxDateTime::IsLeapYear(year
)][month
];
238 // ensure that the timezone variable is set by calling localtime
239 static int GetTimeZone()
241 // set to TRUE when the timezone is set
242 static bool s_timezoneSet
= FALSE
;
244 wxCRIT_SECT_LOCKER(lock
, gs_critsectTimezone
);
246 if ( !s_timezoneSet
)
248 // just call localtime() instead of figuring out whether this system
249 // supports tzset(), _tzset() or something else
253 s_timezoneSet
= TRUE
;
256 return (int)WX_TIMEZONE
;
259 // return the integral part of the JDN for the midnight of the given date (to
260 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
261 // noon of the previous day)
262 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day
,
263 wxDateTime::Month mon
,
266 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
268 // check the date validity
270 (year
> JDN_0_YEAR
) ||
271 ((year
== JDN_0_YEAR
) && (mon
> JDN_0_MONTH
)) ||
272 ((year
== JDN_0_YEAR
) && (mon
== JDN_0_MONTH
) && (day
>= JDN_0_DAY
)),
273 _T("date out of range - can't convert to JDN")
276 // make the year positive to avoid problems with negative numbers division
279 // months are counted from March here
281 if ( mon
>= wxDateTime::Mar
)
291 // now we can simply add all the contributions together
292 return ((year
/ 100) * DAYS_PER_400_YEARS
) / 4
293 + ((year
% 100) * DAYS_PER_4_YEARS
) / 4
294 + (month
* DAYS_PER_5_MONTHS
+ 2) / 5
299 // this function is a wrapper around strftime(3)
300 static wxString
CallStrftime(const wxChar
*format
, const tm
* tm
)
303 if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) )
305 // buffer is too small?
306 wxFAIL_MSG(_T("strftime() failed"));
309 return wxString(buf
);
312 // if year and/or month have invalid values, replace them with the current ones
313 static void ReplaceDefaultYearMonthWithCurrent(int *year
,
314 wxDateTime::Month
*month
)
316 struct tm
*tmNow
= NULL
;
318 if ( *year
== wxDateTime::Inv_Year
)
320 tmNow
= wxDateTime::GetTmNow();
322 *year
= 1900 + tmNow
->tm_year
;
325 if ( *month
== wxDateTime::Inv_Month
)
328 tmNow
= wxDateTime::GetTmNow();
330 *month
= (wxDateTime::Month
)tmNow
->tm_mon
;
334 // fll the struct tm with default values
335 static void InitTm(struct tm
& tm
)
337 // struct tm may have etxra fields (undocumented and with unportable
338 // names) which, nevertheless, must be set to 0
339 memset(&tm
, 0, sizeof(struct tm
));
341 tm
.tm_mday
= 1; // mday 0 is invalid
342 tm
.tm_year
= 76; // any valid year
343 tm
.tm_isdst
= -1; // auto determine
349 // return the month if the string is a month name or Inv_Month otherwise
350 static wxDateTime::Month
GetMonthFromName(const wxString
& name
, int flags
)
352 wxDateTime::Month mon
;
353 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
355 // case-insensitive comparison either one of or with both abbreviated
357 if ( flags
& wxDateTime::Name_Full
)
359 if ( name
.CmpNoCase(wxDateTime::
360 GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 )
366 if ( flags
& wxDateTime::Name_Abbr
)
368 if ( name
.CmpNoCase(wxDateTime::
369 GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 )
379 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
380 static wxDateTime::WeekDay
GetWeekDayFromName(const wxString
& name
, int flags
)
382 wxDateTime::WeekDay wd
;
383 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
385 // case-insensitive comparison either one of or with both abbreviated
387 if ( flags
& wxDateTime::Name_Full
)
389 if ( name
.CmpNoCase(wxDateTime::
390 GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 )
396 if ( flags
& wxDateTime::Name_Abbr
)
398 if ( name
.CmpNoCase(wxDateTime::
399 GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 )
409 // scans all digits (but no more than len) and returns the resulting number
410 static bool GetNumericToken(size_t len
, const wxChar
*& p
, unsigned long *number
)
414 while ( wxIsdigit(*p
) )
418 if ( len
&& ++n
> len
)
422 return !!s
&& s
.ToULong(number
);
425 // scans all alphabetic characters and returns the resulting string
426 static wxString
GetAlphaToken(const wxChar
*& p
)
429 while ( wxIsalpha(*p
) )
437 // ============================================================================
438 // implementation of wxDateTime
439 // ============================================================================
441 // ----------------------------------------------------------------------------
443 // ----------------------------------------------------------------------------
447 year
= (wxDateTime_t
)wxDateTime::Inv_Year
;
448 mon
= wxDateTime::Inv_Month
;
450 hour
= min
= sec
= msec
= 0;
451 wday
= wxDateTime::Inv_WeekDay
;
454 wxDateTime::Tm::Tm(const struct tm
& tm
, const TimeZone
& tz
)
462 mon
= (wxDateTime::Month
)tm
.tm_mon
;
463 year
= 1900 + tm
.tm_year
;
468 bool wxDateTime::Tm::IsValid() const
470 // we allow for the leap seconds, although we don't use them (yet)
471 return (year
!= wxDateTime::Inv_Year
) && (mon
!= wxDateTime::Inv_Month
) &&
472 (mday
<= GetNumOfDaysInMonth(year
, mon
)) &&
473 (hour
< 24) && (min
< 60) && (sec
< 62) && (msec
< 1000);
476 void wxDateTime::Tm::ComputeWeekDay()
478 // compute the week day from day/month/year: we use the dumbest algorithm
479 // possible: just compute our JDN and then use the (simple to derive)
480 // formula: weekday = (JDN + 1.5) % 7
481 wday
= (wxDateTime::WeekDay
)(GetTruncatedJDN(mday
, mon
, year
) + 2) % 7;
484 void wxDateTime::Tm::AddMonths(int monDiff
)
486 // normalize the months field
487 while ( monDiff
< -mon
)
491 monDiff
+= MONTHS_IN_YEAR
;
494 while ( monDiff
+ mon
>= MONTHS_IN_YEAR
)
498 monDiff
-= MONTHS_IN_YEAR
;
501 mon
= (wxDateTime::Month
)(mon
+ monDiff
);
503 wxASSERT_MSG( mon
>= 0 && mon
< MONTHS_IN_YEAR
, _T("logic error") );
505 // NB: we don't check here that the resulting date is valid, this function
506 // is private and the caller must check it if needed
509 void wxDateTime::Tm::AddDays(int dayDiff
)
511 // normalize the days field
512 while ( dayDiff
+ mday
< 1 )
516 dayDiff
+= GetNumOfDaysInMonth(year
, mon
);
520 while ( mday
> GetNumOfDaysInMonth(year
, mon
) )
522 mday
-= GetNumOfDaysInMonth(year
, mon
);
527 wxASSERT_MSG( mday
> 0 && mday
<= GetNumOfDaysInMonth(year
, mon
),
531 // ----------------------------------------------------------------------------
533 // ----------------------------------------------------------------------------
535 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz
)
539 case wxDateTime::Local
:
540 // get the offset from C RTL: it returns the difference GMT-local
541 // while we want to have the offset _from_ GMT, hence the '-'
542 m_offset
= -GetTimeZone();
545 case wxDateTime::GMT_12
:
546 case wxDateTime::GMT_11
:
547 case wxDateTime::GMT_10
:
548 case wxDateTime::GMT_9
:
549 case wxDateTime::GMT_8
:
550 case wxDateTime::GMT_7
:
551 case wxDateTime::GMT_6
:
552 case wxDateTime::GMT_5
:
553 case wxDateTime::GMT_4
:
554 case wxDateTime::GMT_3
:
555 case wxDateTime::GMT_2
:
556 case wxDateTime::GMT_1
:
557 m_offset
= -3600*(wxDateTime::GMT0
- tz
);
560 case wxDateTime::GMT0
:
561 case wxDateTime::GMT1
:
562 case wxDateTime::GMT2
:
563 case wxDateTime::GMT3
:
564 case wxDateTime::GMT4
:
565 case wxDateTime::GMT5
:
566 case wxDateTime::GMT6
:
567 case wxDateTime::GMT7
:
568 case wxDateTime::GMT8
:
569 case wxDateTime::GMT9
:
570 case wxDateTime::GMT10
:
571 case wxDateTime::GMT11
:
572 case wxDateTime::GMT12
:
573 m_offset
= 3600*(tz
- wxDateTime::GMT0
);
576 case wxDateTime::A_CST
:
577 // Central Standard Time in use in Australia = UTC + 9.5
578 m_offset
= 60l*(9*60 + 30);
582 wxFAIL_MSG( _T("unknown time zone") );
586 // ----------------------------------------------------------------------------
588 // ----------------------------------------------------------------------------
591 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
)
593 if ( year
== Inv_Year
)
594 year
= GetCurrentYear();
596 if ( cal
== Gregorian
)
598 // in Gregorian calendar leap years are those divisible by 4 except
599 // those divisible by 100 unless they're also divisible by 400
600 // (in some countries, like Russia and Greece, additional corrections
601 // exist, but they won't manifest themselves until 2700)
602 return (year
% 4 == 0) && ((year
% 100 != 0) || (year
% 400 == 0));
604 else if ( cal
== Julian
)
606 // in Julian calendar the rule is simpler
607 return year
% 4 == 0;
611 wxFAIL_MSG(_T("unknown calendar"));
618 int wxDateTime::GetCentury(int year
)
620 return year
> 0 ? year
/ 100 : year
/ 100 - 1;
624 int wxDateTime::ConvertYearToBC(int year
)
627 return year
> 0 ? year
: year
- 1;
631 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
)
636 return Now().GetYear();
639 wxFAIL_MSG(_T("TODO"));
643 wxFAIL_MSG(_T("unsupported calendar"));
651 wxDateTime::Month
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
)
656 return Now().GetMonth();
659 wxFAIL_MSG(_T("TODO"));
663 wxFAIL_MSG(_T("unsupported calendar"));
671 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(int year
, Calendar cal
)
673 if ( year
== Inv_Year
)
675 // take the current year if none given
676 year
= GetCurrentYear();
683 return IsLeapYear(year
) ? 366 : 365;
686 wxFAIL_MSG(_T("unsupported calendar"));
694 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(wxDateTime::Month month
,
696 wxDateTime::Calendar cal
)
698 wxCHECK_MSG( month
< MONTHS_IN_YEAR
, 0, _T("invalid month") );
700 if ( cal
== Gregorian
|| cal
== Julian
)
702 if ( year
== Inv_Year
)
704 // take the current year if none given
705 year
= GetCurrentYear();
708 return GetNumOfDaysInMonth(year
, month
);
712 wxFAIL_MSG(_T("unsupported calendar"));
719 wxString
wxDateTime::GetMonthName(wxDateTime::Month month
,
720 wxDateTime::NameFlags flags
)
722 wxCHECK_MSG( month
!= Inv_Month
, _T(""), _T("invalid month") );
724 // notice that we must set all the fields to avoid confusing libc (GNU one
725 // gets confused to a crash if we don't do this)
730 return CallStrftime(flags
== Name_Abbr
? _T("%b") : _T("%B"), &tm
);
734 wxString
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
,
735 wxDateTime::NameFlags flags
)
737 wxCHECK_MSG( wday
!= Inv_WeekDay
, _T(""), _T("invalid weekday") );
739 // take some arbitrary Sunday
746 // and offset it by the number of days needed to get the correct wday
749 // call mktime() to normalize it...
752 // ... and call strftime()
753 return CallStrftime(flags
== Name_Abbr
? _T("%a") : _T("%A"), &tm
);
757 void wxDateTime::GetAmPmStrings(wxString
*am
, wxString
*pm
)
763 *am
= CallStrftime(_T("%p"), &tm
);
768 *pm
= CallStrftime(_T("%p"), &tm
);
772 // ----------------------------------------------------------------------------
773 // Country stuff: date calculations depend on the country (DST, work days,
774 // ...), so we need to know which rules to follow.
775 // ----------------------------------------------------------------------------
778 wxDateTime::Country
wxDateTime::GetCountry()
780 // TODO use LOCALE_ICOUNTRY setting under Win32
782 if ( ms_country
== Country_Unknown
)
784 // try to guess from the time zone name
785 time_t t
= time(NULL
);
786 struct tm
*tm
= localtime(&t
);
788 wxString tz
= CallStrftime(_T("%Z"), tm
);
789 if ( tz
== _T("WET") || tz
== _T("WEST") )
793 else if ( tz
== _T("CET") || tz
== _T("CEST") )
795 ms_country
= Country_EEC
;
797 else if ( tz
== _T("MSK") || tz
== _T("MSD") )
801 else if ( tz
== _T("AST") || tz
== _T("ADT") ||
802 tz
== _T("EST") || tz
== _T("EDT") ||
803 tz
== _T("CST") || tz
== _T("CDT") ||
804 tz
== _T("MST") || tz
== _T("MDT") ||
805 tz
== _T("PST") || tz
== _T("PDT") )
811 // well, choose a default one
820 void wxDateTime::SetCountry(wxDateTime::Country country
)
822 ms_country
= country
;
826 bool wxDateTime::IsWestEuropeanCountry(Country country
)
828 if ( country
== Country_Default
)
830 country
= GetCountry();
833 return (Country_WesternEurope_Start
<= country
) &&
834 (country
<= Country_WesternEurope_End
);
837 // ----------------------------------------------------------------------------
838 // DST calculations: we use 3 different rules for the West European countries,
839 // USA and for the rest of the world. This is undoubtedly false for many
840 // countries, but I lack the necessary info (and the time to gather it),
841 // please add the other rules here!
842 // ----------------------------------------------------------------------------
845 bool wxDateTime::IsDSTApplicable(int year
, Country country
)
847 if ( year
== Inv_Year
)
849 // take the current year if none given
850 year
= GetCurrentYear();
853 if ( country
== Country_Default
)
855 country
= GetCountry();
862 // DST was first observed in the US and UK during WWI, reused
863 // during WWII and used again since 1966
864 return year
>= 1966 ||
865 (year
>= 1942 && year
<= 1945) ||
866 (year
== 1918 || year
== 1919);
869 // assume that it started after WWII
875 wxDateTime
wxDateTime::GetBeginDST(int year
, Country country
)
877 if ( year
== Inv_Year
)
879 // take the current year if none given
880 year
= GetCurrentYear();
883 if ( country
== Country_Default
)
885 country
= GetCountry();
888 if ( !IsDSTApplicable(year
, country
) )
890 return wxInvalidDateTime
;
895 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
897 // DST begins at 1 a.m. GMT on the last Sunday of March
898 if ( !dt
.SetToLastWeekDay(Sun
, Mar
, year
) )
901 wxFAIL_MSG( _T("no last Sunday in March?") );
904 dt
+= wxTimeSpan::Hours(1);
906 // disable DST tests because it could result in an infinite recursion!
909 else switch ( country
)
916 // don't know for sure - assume it was in effect all year
921 dt
.Set(1, Jan
, year
);
925 // DST was installed Feb 2, 1942 by the Congress
926 dt
.Set(2, Feb
, year
);
929 // Oil embargo changed the DST period in the US
931 dt
.Set(6, Jan
, 1974);
935 dt
.Set(23, Feb
, 1975);
939 // before 1986, DST begun on the last Sunday of April, but
940 // in 1986 Reagan changed it to begin at 2 a.m. of the
941 // first Sunday in April
944 if ( !dt
.SetToLastWeekDay(Sun
, Apr
, year
) )
947 wxFAIL_MSG( _T("no first Sunday in April?") );
952 if ( !dt
.SetToWeekDay(Sun
, 1, Apr
, year
) )
955 wxFAIL_MSG( _T("no first Sunday in April?") );
959 dt
+= wxTimeSpan::Hours(2);
961 // TODO what about timezone??
967 // assume Mar 30 as the start of the DST for the rest of the world
968 // - totally bogus, of course
969 dt
.Set(30, Mar
, year
);
976 wxDateTime
wxDateTime::GetEndDST(int year
, Country country
)
978 if ( year
== Inv_Year
)
980 // take the current year if none given
981 year
= GetCurrentYear();
984 if ( country
== Country_Default
)
986 country
= GetCountry();
989 if ( !IsDSTApplicable(year
, country
) )
991 return wxInvalidDateTime
;
996 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
998 // DST ends at 1 a.m. GMT on the last Sunday of October
999 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1001 // weirder and weirder...
1002 wxFAIL_MSG( _T("no last Sunday in October?") );
1005 dt
+= wxTimeSpan::Hours(1);
1007 // disable DST tests because it could result in an infinite recursion!
1010 else switch ( country
)
1017 // don't know for sure - assume it was in effect all year
1021 dt
.Set(31, Dec
, year
);
1025 // the time was reset after the end of the WWII
1026 dt
.Set(30, Sep
, year
);
1030 // DST ends at 2 a.m. on the last Sunday of October
1031 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1033 // weirder and weirder...
1034 wxFAIL_MSG( _T("no last Sunday in October?") );
1037 dt
+= wxTimeSpan::Hours(2);
1039 // TODO what about timezone??
1044 // assume October 26th as the end of the DST - totally bogus too
1045 dt
.Set(26, Oct
, year
);
1051 // ----------------------------------------------------------------------------
1052 // constructors and assignment operators
1053 // ----------------------------------------------------------------------------
1055 // return the current time with ms precision
1056 /* static */ wxDateTime
wxDateTime::UNow()
1058 return wxDateTime(wxGetLocalTimeMillis());
1061 // the values in the tm structure contain the local time
1062 wxDateTime
& wxDateTime::Set(const struct tm
& tm
)
1064 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1067 time_t timet
= mktime(&tm2
);
1069 if ( timet
== (time_t)-1 )
1071 // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
1072 // less than timezone - try to make it work for this case
1073 if ( tm2
.tm_year
== 70 && tm2
.tm_mon
== 0 && tm2
.tm_mday
== 1 )
1075 // add timezone to make sure that date is in range
1076 tm2
.tm_sec
-= GetTimeZone();
1078 timet
= mktime(&tm2
);
1079 if ( timet
!= (time_t)-1 )
1081 timet
+= GetTimeZone();
1087 wxFAIL_MSG( _T("mktime() failed") );
1089 *this = wxInvalidDateTime
;
1099 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
,
1100 wxDateTime_t minute
,
1101 wxDateTime_t second
,
1102 wxDateTime_t millisec
)
1104 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1106 // we allow seconds to be 61 to account for the leap seconds, even if we
1107 // don't use them really
1108 wxDATETIME_CHECK( hour
< 24 &&
1112 _T("Invalid time in wxDateTime::Set()") );
1114 // get the current date from system
1115 struct tm
*tm
= GetTmNow();
1117 wxDATETIME_CHECK( tm
, _T("localtime() failed") );
1121 tm
->tm_min
= minute
;
1122 tm
->tm_sec
= second
;
1126 // and finally adjust milliseconds
1127 return SetMillisecond(millisec
);
1130 wxDateTime
& wxDateTime::Set(wxDateTime_t day
,
1134 wxDateTime_t minute
,
1135 wxDateTime_t second
,
1136 wxDateTime_t millisec
)
1138 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1140 wxDATETIME_CHECK( hour
< 24 &&
1144 _T("Invalid time in wxDateTime::Set()") );
1146 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1148 wxDATETIME_CHECK( (0 < day
) && (day
<= GetNumberOfDays(month
, year
)),
1149 _T("Invalid date in wxDateTime::Set()") );
1151 // the range of time_t type (inclusive)
1152 static const int yearMinInRange
= 1970;
1153 static const int yearMaxInRange
= 2037;
1155 // test only the year instead of testing for the exact end of the Unix
1156 // time_t range - it doesn't bring anything to do more precise checks
1157 if ( year
>= yearMinInRange
&& year
<= yearMaxInRange
)
1159 // use the standard library version if the date is in range - this is
1160 // probably more efficient than our code
1162 tm
.tm_year
= year
- 1900;
1168 tm
.tm_isdst
= -1; // mktime() will guess it
1172 // and finally adjust milliseconds
1173 return SetMillisecond(millisec
);
1177 // do time calculations ourselves: we want to calculate the number of
1178 // milliseconds between the given date and the epoch
1180 // get the JDN for the midnight of this day
1181 m_time
= GetTruncatedJDN(day
, month
, year
);
1182 m_time
-= EPOCH_JDN
;
1183 m_time
*= SECONDS_PER_DAY
* TIME_T_FACTOR
;
1185 // JDN corresponds to GMT, we take localtime
1186 Add(wxTimeSpan(hour
, minute
, second
+ GetTimeZone(), millisec
));
1192 wxDateTime
& wxDateTime::Set(double jdn
)
1194 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1196 jdn
-= EPOCH_JDN
+ 0.5;
1198 jdn
*= MILLISECONDS_PER_DAY
;
1205 wxDateTime
& wxDateTime::ResetTime()
1209 if ( tm
.hour
|| tm
.min
|| tm
.sec
|| tm
.msec
)
1222 // ----------------------------------------------------------------------------
1223 // time_t <-> broken down time conversions
1224 // ----------------------------------------------------------------------------
1226 wxDateTime::Tm
wxDateTime::GetTm(const TimeZone
& tz
) const
1228 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1230 time_t time
= GetTicks();
1231 if ( time
!= (time_t)-1 )
1233 // use C RTL functions
1235 if ( tz
.GetOffset() == -GetTimeZone() )
1237 // we are working with local time
1238 tm
= localtime(&time
);
1240 // should never happen
1241 wxCHECK_MSG( tm
, Tm(), _T("localtime() failed") );
1245 time
+= (time_t)tz
.GetOffset();
1246 #ifdef __VMS__ // time is unsigned so avoid warning
1247 int time2
= (int) time
;
1255 // should never happen
1256 wxCHECK_MSG( tm
, Tm(), _T("gmtime() failed") );
1260 tm
= (struct tm
*)NULL
;
1266 // adjust the milliseconds
1268 long timeOnly
= (m_time
% MILLISECONDS_PER_DAY
).ToLong();
1269 tm2
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1272 //else: use generic code below
1275 // remember the time and do the calculations with the date only - this
1276 // eliminates rounding errors of the floating point arithmetics
1278 wxLongLong timeMidnight
= m_time
+ tz
.GetOffset() * 1000;
1280 long timeOnly
= (timeMidnight
% MILLISECONDS_PER_DAY
).ToLong();
1282 // we want to always have positive time and timeMidnight to be really
1283 // the midnight before it
1286 timeOnly
= MILLISECONDS_PER_DAY
+ timeOnly
;
1289 timeMidnight
-= timeOnly
;
1291 // calculate the Gregorian date from JDN for the midnight of our date:
1292 // this will yield day, month (in 1..12 range) and year
1294 // actually, this is the JDN for the noon of the previous day
1295 long jdn
= (timeMidnight
/ MILLISECONDS_PER_DAY
).ToLong() + EPOCH_JDN
;
1297 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1299 wxASSERT_MSG( jdn
> -2, _T("JDN out of range") );
1301 // calculate the century
1302 long temp
= (jdn
+ JDN_OFFSET
) * 4 - 1;
1303 long century
= temp
/ DAYS_PER_400_YEARS
;
1305 // then the year and day of year (1 <= dayOfYear <= 366)
1306 temp
= ((temp
% DAYS_PER_400_YEARS
) / 4) * 4 + 3;
1307 long year
= (century
* 100) + (temp
/ DAYS_PER_4_YEARS
);
1308 long dayOfYear
= (temp
% DAYS_PER_4_YEARS
) / 4 + 1;
1310 // and finally the month and day of the month
1311 temp
= dayOfYear
* 5 - 3;
1312 long month
= temp
/ DAYS_PER_5_MONTHS
;
1313 long day
= (temp
% DAYS_PER_5_MONTHS
) / 5 + 1;
1315 // month is counted from March - convert to normal
1326 // year is offset by 4800
1329 // check that the algorithm gave us something reasonable
1330 wxASSERT_MSG( (0 < month
) && (month
<= 12), _T("invalid month") );
1331 wxASSERT_MSG( (1 <= day
) && (day
< 32), _T("invalid day") );
1332 wxASSERT_MSG( (INT_MIN
<= year
) && (year
<= INT_MAX
),
1333 _T("year range overflow") );
1335 // construct Tm from these values
1337 tm
.year
= (int)year
;
1338 tm
.mon
= (Month
)(month
- 1); // algorithm yields 1 for January, not 0
1339 tm
.mday
= (wxDateTime_t
)day
;
1340 tm
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1341 timeOnly
-= tm
.msec
;
1342 timeOnly
/= 1000; // now we have time in seconds
1344 tm
.sec
= (wxDateTime_t
)(timeOnly
% 60);
1346 timeOnly
/= 60; // now we have time in minutes
1348 tm
.min
= (wxDateTime_t
)(timeOnly
% 60);
1351 tm
.hour
= (wxDateTime_t
)(timeOnly
/ 60);
1356 wxDateTime
& wxDateTime::SetYear(int year
)
1358 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1367 wxDateTime
& wxDateTime::SetMonth(Month month
)
1369 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1378 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
)
1380 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1389 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
)
1391 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1400 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
)
1402 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1411 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
)
1413 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1422 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
)
1424 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1426 // we don't need to use GetTm() for this one
1427 m_time
-= m_time
% 1000l;
1428 m_time
+= millisecond
;
1433 // ----------------------------------------------------------------------------
1434 // wxDateTime arithmetics
1435 // ----------------------------------------------------------------------------
1437 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
)
1441 tm
.year
+= diff
.GetYears();
1442 tm
.AddMonths(diff
.GetMonths());
1444 // check that the resulting date is valid
1445 if ( tm
.mday
> GetNumOfDaysInMonth(tm
.year
, tm
.mon
) )
1447 // We suppose that when adding one month to Jan 31 we want to get Feb
1448 // 28 (or 29), i.e. adding a month to the last day of the month should
1449 // give the last day of the next month which is quite logical.
1451 // Unfortunately, there is no logic way to understand what should
1452 // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1453 // We make it Feb 28 (last day too), but it is highly questionable.
1454 tm
.mday
= GetNumOfDaysInMonth(tm
.year
, tm
.mon
);
1457 tm
.AddDays(diff
.GetTotalDays());
1461 wxASSERT_MSG( IsSameTime(tm
),
1462 _T("Add(wxDateSpan) shouldn't modify time") );
1467 // ----------------------------------------------------------------------------
1468 // Weekday and monthday stuff
1469 // ----------------------------------------------------------------------------
1471 bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek
, WeekDay weekday
)
1473 int year
= GetYear();
1475 // Jan 4 always lies in the 1st week of the year
1477 SetToWeekDayInSameWeek(weekday
) += wxDateSpan::Weeks(numWeek
);
1479 if ( GetYear() != year
)
1481 // oops... numWeek was too big
1488 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
,
1491 // take the current month/year if none specified
1492 if ( year
== Inv_Year
)
1494 if ( month
== Inv_Month
)
1497 return Set(GetNumOfDaysInMonth(year
, month
), month
, year
);
1500 wxDateTime
& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday
)
1502 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1504 WeekDay wdayThis
= GetWeekDay();
1505 if ( weekday
== wdayThis
)
1510 else if ( weekday
< wdayThis
)
1512 return Subtract(wxDateSpan::Days(wdayThis
- weekday
));
1514 else // weekday > wdayThis
1516 return Add(wxDateSpan::Days(weekday
- wdayThis
));
1520 wxDateTime
& wxDateTime::SetToNextWeekDay(WeekDay weekday
)
1522 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1525 WeekDay wdayThis
= GetWeekDay();
1526 if ( weekday
== wdayThis
)
1531 else if ( weekday
< wdayThis
)
1533 // need to advance a week
1534 diff
= 7 - (wdayThis
- weekday
);
1536 else // weekday > wdayThis
1538 diff
= weekday
- wdayThis
;
1541 return Add(wxDateSpan::Days(diff
));
1544 wxDateTime
& wxDateTime::SetToPrevWeekDay(WeekDay weekday
)
1546 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1549 WeekDay wdayThis
= GetWeekDay();
1550 if ( weekday
== wdayThis
)
1555 else if ( weekday
> wdayThis
)
1557 // need to go to previous week
1558 diff
= 7 - (weekday
- wdayThis
);
1560 else // weekday < wdayThis
1562 diff
= wdayThis
- weekday
;
1565 return Subtract(wxDateSpan::Days(diff
));
1568 bool wxDateTime::SetToWeekDay(WeekDay weekday
,
1573 wxCHECK_MSG( weekday
!= Inv_WeekDay
, FALSE
, _T("invalid weekday") );
1575 // we don't check explicitly that -5 <= n <= 5 because we will return FALSE
1576 // anyhow in such case - but may be should still give an assert for it?
1578 // take the current month/year if none specified
1579 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1583 // TODO this probably could be optimised somehow...
1587 // get the first day of the month
1588 dt
.Set(1, month
, year
);
1591 WeekDay wdayFirst
= dt
.GetWeekDay();
1593 // go to the first weekday of the month
1594 int diff
= weekday
- wdayFirst
;
1598 // add advance n-1 weeks more
1601 dt
+= wxDateSpan::Days(diff
);
1603 else // count from the end of the month
1605 // get the last day of the month
1606 dt
.SetToLastMonthDay(month
, year
);
1609 WeekDay wdayLast
= dt
.GetWeekDay();
1611 // go to the last weekday of the month
1612 int diff
= wdayLast
- weekday
;
1616 // and rewind n-1 weeks from there
1619 dt
-= wxDateSpan::Days(diff
);
1622 // check that it is still in the same month
1623 if ( dt
.GetMonth() == month
)
1631 // no such day in this month
1636 wxDateTime::wxDateTime_t
wxDateTime::GetDayOfYear(const TimeZone
& tz
) const
1640 return gs_cumulatedDays
[IsLeapYear(tm
.year
)][tm
.mon
] + tm
.mday
;
1643 wxDateTime::wxDateTime_t
wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags
,
1644 const TimeZone
& tz
) const
1646 if ( flags
== Default_First
)
1648 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
1651 wxDateTime_t nDayInYear
= GetDayOfYear(tz
);
1654 WeekDay wd
= GetWeekDay(tz
);
1655 if ( flags
== Sunday_First
)
1657 week
= (nDayInYear
- wd
+ 7) / 7;
1661 // have to shift the week days values
1662 week
= (nDayInYear
- (wd
- 1 + 7) % 7 + 7) / 7;
1665 // FIXME some more elegant way??
1666 WeekDay wdYearStart
= wxDateTime(1, Jan
, GetYear()).GetWeekDay();
1667 if ( wdYearStart
== Wed
|| wdYearStart
== Thu
)
1675 wxDateTime::wxDateTime_t
wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags
,
1676 const TimeZone
& tz
) const
1679 wxDateTime dtMonthStart
= wxDateTime(1, tm
.mon
, tm
.year
);
1680 int nWeek
= GetWeekOfYear(flags
) - dtMonthStart
.GetWeekOfYear(flags
) + 1;
1683 // this may happen for January when Jan, 1 is the last week of the
1685 nWeek
+= IsLeapYear(tm
.year
- 1) ? 53 : 52;
1688 return (wxDateTime::wxDateTime_t
)nWeek
;
1691 wxDateTime
& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday
)
1693 int year
= GetYear();
1694 wxDATETIME_CHECK( (0 < yday
) && (yday
<= GetNumberOfDays(year
)),
1695 _T("invalid year day") );
1697 bool isLeap
= IsLeapYear(year
);
1698 for ( Month mon
= Jan
; mon
< Inv_Month
; wxNextMonth(mon
) )
1700 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
1701 // don't need it neither - because of the CHECK above we know that
1702 // yday lies in December then
1703 if ( (mon
== Dec
) || (yday
< gs_cumulatedDays
[isLeap
][mon
+ 1]) )
1705 Set(yday
- gs_cumulatedDays
[isLeap
][mon
], mon
, year
);
1714 // ----------------------------------------------------------------------------
1715 // Julian day number conversion and related stuff
1716 // ----------------------------------------------------------------------------
1718 double wxDateTime::GetJulianDayNumber() const
1720 // JDN are always expressed for the GMT dates
1721 Tm
tm(ToTimezone(GMT0
).GetTm(GMT0
));
1723 double result
= GetTruncatedJDN(tm
.mday
, tm
.mon
, tm
.year
);
1725 // add the part GetTruncatedJDN() neglected
1728 // and now add the time: 86400 sec = 1 JDN
1729 return result
+ ((double)(60*(60*tm
.hour
+ tm
.min
) + tm
.sec
)) / 86400;
1732 double wxDateTime::GetRataDie() const
1734 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
1735 return GetJulianDayNumber() - 1721119.5 - 306;
1738 // ----------------------------------------------------------------------------
1739 // timezone and DST stuff
1740 // ----------------------------------------------------------------------------
1742 int wxDateTime::IsDST(wxDateTime::Country country
) const
1744 wxCHECK_MSG( country
== Country_Default
, -1,
1745 _T("country support not implemented") );
1747 // use the C RTL for the dates in the standard range
1748 time_t timet
= GetTicks();
1749 if ( timet
!= (time_t)-1 )
1751 tm
*tm
= localtime(&timet
);
1753 wxCHECK_MSG( tm
, -1, _T("localtime() failed") );
1755 return tm
->tm_isdst
;
1759 int year
= GetYear();
1761 if ( !IsDSTApplicable(year
, country
) )
1763 // no DST time in this year in this country
1767 return IsBetween(GetBeginDST(year
, country
), GetEndDST(year
, country
));
1771 wxDateTime
& wxDateTime::MakeTimezone(const TimeZone
& tz
, bool noDST
)
1773 long secDiff
= GetTimeZone() + tz
.GetOffset();
1775 // we need to know whether DST is or not in effect for this date unless
1776 // the test disabled by the caller
1777 if ( !noDST
&& (IsDST() == 1) )
1779 // FIXME we assume that the DST is always shifted by 1 hour
1783 return Subtract(wxTimeSpan::Seconds(secDiff
));
1786 // ----------------------------------------------------------------------------
1787 // wxDateTime to/from text representations
1788 // ----------------------------------------------------------------------------
1790 wxString
wxDateTime::Format(const wxChar
*format
, const TimeZone
& tz
) const
1792 wxCHECK_MSG( format
, _T(""), _T("NULL format in wxDateTime::Format") );
1794 // we have to use our own implementation if the date is out of range of
1795 // strftime() or if we use non standard specificators
1796 time_t time
= GetTicks();
1797 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
1801 if ( tz
.GetOffset() == -GetTimeZone() )
1803 // we are working with local time
1804 tm
= localtime(&time
);
1806 // should never happen
1807 wxCHECK_MSG( tm
, wxEmptyString
, _T("localtime() failed") );
1811 time
+= (int)tz
.GetOffset();
1813 #ifdef __VMS__ // time is unsigned so avoid the warning
1814 int time2
= (int) time
;
1822 // should never happen
1823 wxCHECK_MSG( tm
, wxEmptyString
, _T("gmtime() failed") );
1827 tm
= (struct tm
*)NULL
;
1833 return CallStrftime(format
, tm
);
1835 //else: use generic code below
1838 // we only parse ANSI C format specifications here, no POSIX 2
1839 // complications, no GNU extensions but we do add support for a "%l" format
1840 // specifier allowing to get the number of milliseconds
1843 // used for calls to strftime() when we only deal with time
1844 struct tm tmTimeOnly
;
1845 tmTimeOnly
.tm_hour
= tm
.hour
;
1846 tmTimeOnly
.tm_min
= tm
.min
;
1847 tmTimeOnly
.tm_sec
= tm
.sec
;
1848 tmTimeOnly
.tm_wday
= 0;
1849 tmTimeOnly
.tm_yday
= 0;
1850 tmTimeOnly
.tm_mday
= 1; // any date will do
1851 tmTimeOnly
.tm_mon
= 0;
1852 tmTimeOnly
.tm_year
= 76;
1853 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
1855 wxString tmp
, res
, fmt
;
1856 for ( const wxChar
*p
= format
; *p
; p
++ )
1858 if ( *p
!= _T('%') )
1866 // set the default format
1869 case _T('Y'): // year has 4 digits
1873 case _T('j'): // day of year has 3 digits
1874 case _T('l'): // milliseconds have 3 digits
1879 // it's either another valid format specifier in which case
1880 // the format is "%02d" (for all the rest) or we have the
1881 // field width preceding the format in which case it will
1882 // override the default format anyhow
1886 bool restart
= TRUE
;
1891 // start of the format specification
1894 case _T('a'): // a weekday name
1896 // second parameter should be TRUE for abbreviated names
1897 res
+= GetWeekDayName(tm
.GetWeekDay(),
1898 *p
== _T('a') ? Name_Abbr
: Name_Full
);
1901 case _T('b'): // a month name
1903 res
+= GetMonthName(tm
.mon
,
1904 *p
== _T('b') ? Name_Abbr
: Name_Full
);
1907 case _T('c'): // locale default date and time representation
1908 case _T('x'): // locale default date representation
1910 // the problem: there is no way to know what do these format
1911 // specifications correspond to for the current locale.
1913 // the solution: use a hack and still use strftime(): first
1914 // find the YEAR which is a year in the strftime() range (1970
1915 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
1916 // of the real year. Then make a copy of the format and
1917 // replace all occurences of YEAR in it with some unique
1918 // string not appearing anywhere else in it, then use
1919 // strftime() to format the date in year YEAR and then replace
1920 // YEAR back by the real year and the unique replacement
1921 // string back with YEAR. Notice that "all occurences of YEAR"
1922 // means all occurences of 4 digit as well as 2 digit form!
1924 // the bugs: we assume that neither of %c nor %x contains any
1925 // fields which may change between the YEAR and real year. For
1926 // example, the week number (%U, %W) and the day number (%j)
1927 // will change if one of these years is leap and the other one
1930 // find the YEAR: normally, for any year X, Jan 1 or the
1931 // year X + 28 is the same weekday as Jan 1 of X (because
1932 // the weekday advances by 1 for each normal X and by 2
1933 // for each leap X, hence by 5 every 4 years or by 35
1934 // which is 0 mod 7 every 28 years) but this rule breaks
1935 // down if there are years between X and Y which are
1936 // divisible by 4 but not leap (i.e. divisible by 100 but
1937 // not 400), hence the correction.
1939 int yearReal
= GetYear(tz
);
1940 int mod28
= yearReal
% 28;
1942 // be careful to not go too far - we risk to leave the
1947 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
1951 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
1954 int nCentury
= year
/ 100,
1955 nCenturyReal
= yearReal
/ 100;
1957 // need to adjust for the years divisble by 400 which are
1958 // not leap but are counted like leap ones if we just take
1959 // the number of centuries in between for nLostWeekDays
1960 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
1961 (nCentury
/ 4 - nCenturyReal
/ 4);
1963 // we have to gain back the "lost" weekdays: note that the
1964 // effect of this loop is to not do anything to
1965 // nLostWeekDays (which we won't use any more), but to
1966 // (indirectly) set the year correctly
1967 while ( (nLostWeekDays
% 7) != 0 )
1969 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
1972 // at any rate, we couldn't go further than 1988 + 9 + 28!
1973 wxASSERT_MSG( year
< 2030,
1974 _T("logic error in wxDateTime::Format") );
1976 wxString strYear
, strYear2
;
1977 strYear
.Printf(_T("%d"), year
);
1978 strYear2
.Printf(_T("%d"), year
% 100);
1980 // find two strings not occuring in format (this is surely
1981 // not optimal way of doing it... improvements welcome!)
1982 wxString fmt
= format
;
1983 wxString replacement
= (wxChar
)-1;
1984 while ( fmt
.Find(replacement
) != wxNOT_FOUND
)
1986 replacement
<< (wxChar
)-1;
1989 wxString replacement2
= (wxChar
)-2;
1990 while ( fmt
.Find(replacement
) != wxNOT_FOUND
)
1992 replacement
<< (wxChar
)-2;
1995 // replace all occurences of year with it
1996 bool wasReplaced
= fmt
.Replace(strYear
, replacement
) > 0;
1998 wasReplaced
= fmt
.Replace(strYear2
, replacement2
) > 0;
2000 // use strftime() to format the same date but in supported
2003 // NB: we assume that strftime() doesn't check for the
2004 // date validity and will happily format the date
2005 // corresponding to Feb 29 of a non leap year (which
2006 // may happen if yearReal was leap and year is not)
2007 struct tm tmAdjusted
;
2009 tmAdjusted
.tm_hour
= tm
.hour
;
2010 tmAdjusted
.tm_min
= tm
.min
;
2011 tmAdjusted
.tm_sec
= tm
.sec
;
2012 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
2013 tmAdjusted
.tm_yday
= GetDayOfYear();
2014 tmAdjusted
.tm_mday
= tm
.mday
;
2015 tmAdjusted
.tm_mon
= tm
.mon
;
2016 tmAdjusted
.tm_year
= year
- 1900;
2017 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
2018 wxString str
= CallStrftime(*p
== _T('c') ? _T("%c")
2022 // now replace the occurence of 1999 with the real year
2023 wxString strYearReal
, strYearReal2
;
2024 strYearReal
.Printf(_T("%04d"), yearReal
);
2025 strYearReal2
.Printf(_T("%02d"), yearReal
% 100);
2026 str
.Replace(strYear
, strYearReal
);
2027 str
.Replace(strYear2
, strYearReal2
);
2029 // and replace back all occurences of replacement string
2032 str
.Replace(replacement2
, strYear2
);
2033 str
.Replace(replacement
, strYear
);
2040 case _T('d'): // day of a month (01-31)
2041 res
+= wxString::Format(fmt
, tm
.mday
);
2044 case _T('H'): // hour in 24h format (00-23)
2045 res
+= wxString::Format(fmt
, tm
.hour
);
2048 case _T('I'): // hour in 12h format (01-12)
2050 // 24h -> 12h, 0h -> 12h too
2051 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
2052 : tm
.hour
? tm
.hour
: 12;
2053 res
+= wxString::Format(fmt
, hour12
);
2057 case _T('j'): // day of the year
2058 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
2061 case _T('l'): // milliseconds (NOT STANDARD)
2062 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
2065 case _T('m'): // month as a number (01-12)
2066 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
2069 case _T('M'): // minute as a decimal number (00-59)
2070 res
+= wxString::Format(fmt
, tm
.min
);
2073 case _T('p'): // AM or PM string
2074 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
2077 case _T('S'): // second as a decimal number (00-61)
2078 res
+= wxString::Format(fmt
, tm
.sec
);
2081 case _T('U'): // week number in the year (Sunday 1st week day)
2082 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
2085 case _T('W'): // week number in the year (Monday 1st week day)
2086 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
2089 case _T('w'): // weekday as a number (0-6), Sunday = 0
2090 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
2093 // case _T('x'): -- handled with "%c"
2095 case _T('X'): // locale default time representation
2096 // just use strftime() to format the time for us
2097 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
2100 case _T('y'): // year without century (00-99)
2101 res
+= wxString::Format(fmt
, tm
.year
% 100);
2104 case _T('Y'): // year with century
2105 res
+= wxString::Format(fmt
, tm
.year
);
2108 case _T('Z'): // timezone name
2109 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
2113 // is it the format width?
2115 while ( *p
== _T('-') || *p
== _T('+') ||
2116 *p
== _T(' ') || wxIsdigit(*p
) )
2121 if ( !fmt
.IsEmpty() )
2123 // we've only got the flags and width so far in fmt
2124 fmt
.Prepend(_T('%'));
2125 fmt
.Append(_T('d'));
2132 // no, it wasn't the width
2133 wxFAIL_MSG(_T("unknown format specificator"));
2135 // fall through and just copy it nevertheless
2137 case _T('%'): // a percent sign
2141 case 0: // the end of string
2142 wxFAIL_MSG(_T("missing format at the end of string"));
2144 // just put the '%' which was the last char in format
2154 // this function parses a string in (strict) RFC 822 format: see the section 5
2155 // of the RFC for the detailed description, but briefly it's something of the
2156 // form "Sat, 18 Dec 1999 00:48:30 +0100"
2158 // this function is "strict" by design - it must reject anything except true
2159 // RFC822 time specs.
2161 // TODO a great candidate for using reg exps
2162 const wxChar
*wxDateTime::ParseRfc822Date(const wxChar
* date
)
2164 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
2166 const wxChar
*p
= date
;
2167 const wxChar
*comma
= wxStrchr(p
, _T(','));
2170 // the part before comma is the weekday
2172 // skip it for now - we don't use but might check that it really
2173 // corresponds to the specfied date
2176 if ( *p
!= _T(' ') )
2178 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
2180 return (wxChar
*)NULL
;
2186 // the following 1 or 2 digits are the day number
2187 if ( !wxIsdigit(*p
) )
2189 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
2191 return (wxChar
*)NULL
;
2194 wxDateTime_t day
= *p
++ - _T('0');
2195 if ( wxIsdigit(*p
) )
2198 day
+= *p
++ - _T('0');
2201 if ( *p
++ != _T(' ') )
2203 return (wxChar
*)NULL
;
2206 // the following 3 letters specify the month
2207 wxString
monName(p
, 3);
2209 if ( monName
== _T("Jan") )
2211 else if ( monName
== _T("Feb") )
2213 else if ( monName
== _T("Mar") )
2215 else if ( monName
== _T("Apr") )
2217 else if ( monName
== _T("May") )
2219 else if ( monName
== _T("Jun") )
2221 else if ( monName
== _T("Jul") )
2223 else if ( monName
== _T("Aug") )
2225 else if ( monName
== _T("Sep") )
2227 else if ( monName
== _T("Oct") )
2229 else if ( monName
== _T("Nov") )
2231 else if ( monName
== _T("Dec") )
2235 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str());
2237 return (wxChar
*)NULL
;
2242 if ( *p
++ != _T(' ') )
2244 return (wxChar
*)NULL
;
2248 if ( !wxIsdigit(*p
) )
2251 return (wxChar
*)NULL
;
2254 int year
= *p
++ - _T('0');
2256 if ( !wxIsdigit(*p
) )
2258 // should have at least 2 digits in the year
2259 return (wxChar
*)NULL
;
2263 year
+= *p
++ - _T('0');
2265 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
2266 if ( wxIsdigit(*p
) )
2269 year
+= *p
++ - _T('0');
2271 if ( !wxIsdigit(*p
) )
2273 // no 3 digit years please
2274 return (wxChar
*)NULL
;
2278 year
+= *p
++ - _T('0');
2281 if ( *p
++ != _T(' ') )
2283 return (wxChar
*)NULL
;
2286 // time is in the format hh:mm:ss and seconds are optional
2287 if ( !wxIsdigit(*p
) )
2289 return (wxChar
*)NULL
;
2292 wxDateTime_t hour
= *p
++ - _T('0');
2294 if ( !wxIsdigit(*p
) )
2296 return (wxChar
*)NULL
;
2300 hour
+= *p
++ - _T('0');
2302 if ( *p
++ != _T(':') )
2304 return (wxChar
*)NULL
;
2307 if ( !wxIsdigit(*p
) )
2309 return (wxChar
*)NULL
;
2312 wxDateTime_t min
= *p
++ - _T('0');
2314 if ( !wxIsdigit(*p
) )
2316 return (wxChar
*)NULL
;
2320 min
+= *p
++ - _T('0');
2322 wxDateTime_t sec
= 0;
2323 if ( *p
++ == _T(':') )
2325 if ( !wxIsdigit(*p
) )
2327 return (wxChar
*)NULL
;
2330 sec
= *p
++ - _T('0');
2332 if ( !wxIsdigit(*p
) )
2334 return (wxChar
*)NULL
;
2338 sec
+= *p
++ - _T('0');
2341 if ( *p
++ != _T(' ') )
2343 return (wxChar
*)NULL
;
2346 // and now the interesting part: the timezone
2348 if ( *p
== _T('-') || *p
== _T('+') )
2350 // the explicit offset given: it has the form of hhmm
2351 bool plus
= *p
++ == _T('+');
2353 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2355 return (wxChar
*)NULL
;
2359 offset
= 60*(10*(*p
- _T('0')) + (*(p
+ 1) - _T('0')));
2363 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2365 return (wxChar
*)NULL
;
2369 offset
+= 10*(*p
- _T('0')) + (*(p
+ 1) - _T('0'));
2380 // the symbolic timezone given: may be either military timezone or one
2381 // of standard abbreviations
2384 // military: Z = UTC, J unused, A = -1, ..., Y = +12
2385 static const int offsets
[26] =
2387 //A B C D E F G H I J K L M
2388 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
2389 //N O P R Q S T U V W Z Y Z
2390 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
2393 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
2395 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
);
2397 return (wxChar
*)NULL
;
2400 offset
= offsets
[*p
++ - _T('A')];
2406 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
2408 else if ( tz
== _T("AST") )
2409 offset
= AST
- GMT0
;
2410 else if ( tz
== _T("ADT") )
2411 offset
= ADT
- GMT0
;
2412 else if ( tz
== _T("EST") )
2413 offset
= EST
- GMT0
;
2414 else if ( tz
== _T("EDT") )
2415 offset
= EDT
- GMT0
;
2416 else if ( tz
== _T("CST") )
2417 offset
= CST
- GMT0
;
2418 else if ( tz
== _T("CDT") )
2419 offset
= CDT
- GMT0
;
2420 else if ( tz
== _T("MST") )
2421 offset
= MST
- GMT0
;
2422 else if ( tz
== _T("MDT") )
2423 offset
= MDT
- GMT0
;
2424 else if ( tz
== _T("PST") )
2425 offset
= PST
- GMT0
;
2426 else if ( tz
== _T("PDT") )
2427 offset
= PDT
- GMT0
;
2430 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
);
2432 return (wxChar
*)NULL
;
2442 // the spec was correct
2443 Set(day
, mon
, year
, hour
, min
, sec
);
2444 MakeTimezone((wxDateTime_t
)(60*offset
));
2449 const wxChar
*wxDateTime::ParseFormat(const wxChar
*date
,
2450 const wxChar
*format
,
2451 const wxDateTime
& dateDef
)
2453 wxCHECK_MSG( date
&& format
, (wxChar
*)NULL
,
2454 _T("NULL pointer in wxDateTime::ParseFormat()") );
2459 // what fields have we found?
2460 bool haveWDay
= FALSE
,
2469 bool hourIsIn12hFormat
= FALSE
, // or in 24h one?
2470 isPM
= FALSE
; // AM by default
2472 // and the value of the items we have (init them to get rid of warnings)
2473 wxDateTime_t sec
= 0,
2476 WeekDay wday
= Inv_WeekDay
;
2477 wxDateTime_t yday
= 0,
2479 wxDateTime::Month mon
= Inv_Month
;
2482 const wxChar
*input
= date
;
2483 for ( const wxChar
*fmt
= format
; *fmt
; fmt
++ )
2485 if ( *fmt
!= _T('%') )
2487 if ( wxIsspace(*fmt
) )
2489 // a white space in the format string matches 0 or more white
2490 // spaces in the input
2491 while ( wxIsspace(*input
) )
2498 // any other character (not whitespace, not '%') must be
2499 // matched by itself in the input
2500 if ( *input
++ != *fmt
)
2503 return (wxChar
*)NULL
;
2507 // done with this format char
2511 // start of a format specification
2513 // parse the optional width
2515 while ( isdigit(*++fmt
) )
2518 width
+= *fmt
- _T('0');
2521 // then the format itself
2524 case _T('a'): // a weekday name
2527 int flag
= *fmt
== _T('a') ? Name_Abbr
: Name_Full
;
2528 wday
= GetWeekDayFromName(GetAlphaToken(input
), flag
);
2529 if ( wday
== Inv_WeekDay
)
2532 return (wxChar
*)NULL
;
2538 case _T('b'): // a month name
2541 int flag
= *fmt
== _T('b') ? Name_Abbr
: Name_Full
;
2542 mon
= GetMonthFromName(GetAlphaToken(input
), flag
);
2543 if ( mon
== Inv_Month
)
2546 return (wxChar
*)NULL
;
2552 case _T('c'): // locale default date and time representation
2556 // this is the format which corresponds to ctime() output
2557 // and strptime("%c") should parse it, so try it first
2558 static const wxChar
*fmtCtime
= _T("%a %b %d %H:%M:%S %Y");
2560 const wxChar
*result
= dt
.ParseFormat(input
, fmtCtime
);
2563 result
= dt
.ParseFormat(input
, _T("%x %X"));
2568 result
= dt
.ParseFormat(input
, _T("%X %x"));
2573 // we've tried everything and still no match
2574 return (wxChar
*)NULL
;
2579 haveDay
= haveMon
= haveYear
=
2580 haveHour
= haveMin
= haveSec
= TRUE
;
2594 case _T('d'): // day of a month (01-31)
2595 if ( !GetNumericToken(width
, input
, &num
) ||
2596 (num
> 31) || (num
< 1) )
2599 return (wxChar
*)NULL
;
2602 // we can't check whether the day range is correct yet, will
2603 // do it later - assume ok for now
2605 mday
= (wxDateTime_t
)num
;
2608 case _T('H'): // hour in 24h format (00-23)
2609 if ( !GetNumericToken(width
, input
, &num
) || (num
> 23) )
2612 return (wxChar
*)NULL
;
2616 hour
= (wxDateTime_t
)num
;
2619 case _T('I'): // hour in 12h format (01-12)
2620 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
2623 return (wxChar
*)NULL
;
2627 hourIsIn12hFormat
= TRUE
;
2628 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
2631 case _T('j'): // day of the year
2632 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 366) )
2635 return (wxChar
*)NULL
;
2639 yday
= (wxDateTime_t
)num
;
2642 case _T('m'): // month as a number (01-12)
2643 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
2646 return (wxChar
*)NULL
;
2650 mon
= (Month
)(num
- 1);
2653 case _T('M'): // minute as a decimal number (00-59)
2654 if ( !GetNumericToken(width
, input
, &num
) || (num
> 59) )
2657 return (wxChar
*)NULL
;
2661 min
= (wxDateTime_t
)num
;
2664 case _T('p'): // AM or PM string
2666 wxString am
, pm
, token
= GetAlphaToken(input
);
2668 GetAmPmStrings(&am
, &pm
);
2669 if ( token
.CmpNoCase(pm
) == 0 )
2673 else if ( token
.CmpNoCase(am
) != 0 )
2676 return (wxChar
*)NULL
;
2681 case _T('r'): // time as %I:%M:%S %p
2684 input
= dt
.ParseFormat(input
, _T("%I:%M:%S %p"));
2688 return (wxChar
*)NULL
;
2691 haveHour
= haveMin
= haveSec
= TRUE
;
2700 case _T('R'): // time as %H:%M
2703 input
= dt
.ParseFormat(input
, _T("%H:%M"));
2707 return (wxChar
*)NULL
;
2710 haveHour
= haveMin
= TRUE
;
2717 case _T('S'): // second as a decimal number (00-61)
2718 if ( !GetNumericToken(width
, input
, &num
) || (num
> 61) )
2721 return (wxChar
*)NULL
;
2725 sec
= (wxDateTime_t
)num
;
2728 case _T('T'): // time as %H:%M:%S
2731 input
= dt
.ParseFormat(input
, _T("%H:%M:%S"));
2735 return (wxChar
*)NULL
;
2738 haveHour
= haveMin
= haveSec
= TRUE
;
2747 case _T('w'): // weekday as a number (0-6), Sunday = 0
2748 if ( !GetNumericToken(width
, input
, &num
) || (wday
> 6) )
2751 return (wxChar
*)NULL
;
2755 wday
= (WeekDay
)num
;
2758 case _T('x'): // locale default date representation
2759 #ifdef HAVE_STRPTIME
2760 // try using strptime() - it may fail even if the input is
2761 // correct but the date is out of range, so we will fall back
2762 // to our generic code anyhow (FIXME !Unicode friendly)
2765 const wxChar
*result
= strptime(input
, "%x", &tm
);
2770 haveDay
= haveMon
= haveYear
= TRUE
;
2772 year
= 1900 + tm
.tm_year
;
2773 mon
= (Month
)tm
.tm_mon
;
2779 #endif // HAVE_STRPTIME
2781 // TODO query the LOCALE_IDATE setting under Win32
2785 wxString fmtDate
, fmtDateAlt
;
2786 if ( IsWestEuropeanCountry(GetCountry()) ||
2787 GetCountry() == Russia
)
2789 fmtDate
= _T("%d/%m/%y");
2790 fmtDateAlt
= _T("%m/%d/%y");
2794 fmtDate
= _T("%m/%d/%y");
2795 fmtDateAlt
= _T("%d/%m/%y");
2798 const wxChar
*result
= dt
.ParseFormat(input
, fmtDate
);
2802 // ok, be nice and try another one
2803 result
= dt
.ParseFormat(input
, fmtDateAlt
);
2809 return (wxChar
*)NULL
;
2814 haveDay
= haveMon
= haveYear
= TRUE
;
2825 case _T('X'): // locale default time representation
2826 #ifdef HAVE_STRPTIME
2828 // use strptime() to do it for us (FIXME !Unicode friendly)
2830 input
= strptime(input
, "%X", &tm
);
2833 return (wxChar
*)NULL
;
2836 haveHour
= haveMin
= haveSec
= TRUE
;
2842 #else // !HAVE_STRPTIME
2843 // TODO under Win32 we can query the LOCALE_ITIME system
2844 // setting which says whether the default time format is
2847 // try to parse what follows as "%H:%M:%S" and, if this
2848 // fails, as "%I:%M:%S %p" - this should catch the most
2852 const wxChar
*result
= dt
.ParseFormat(input
, _T("%T"));
2855 result
= dt
.ParseFormat(input
, _T("%r"));
2861 return (wxChar
*)NULL
;
2864 haveHour
= haveMin
= haveSec
= TRUE
;
2873 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
2876 case _T('y'): // year without century (00-99)
2877 if ( !GetNumericToken(width
, input
, &num
) || (num
> 99) )
2880 return (wxChar
*)NULL
;
2885 // TODO should have an option for roll over date instead of
2886 // hard coding it here
2887 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
2890 case _T('Y'): // year with century
2891 if ( !GetNumericToken(width
, input
, &num
) )
2894 return (wxChar
*)NULL
;
2898 year
= (wxDateTime_t
)num
;
2901 case _T('Z'): // timezone name
2902 wxFAIL_MSG(_T("TODO"));
2905 case _T('%'): // a percent sign
2906 if ( *input
++ != _T('%') )
2909 return (wxChar
*)NULL
;
2913 case 0: // the end of string
2914 wxFAIL_MSG(_T("unexpected format end"));
2918 default: // not a known format spec
2919 return (wxChar
*)NULL
;
2923 // format matched, try to construct a date from what we have now
2925 if ( dateDef
.IsValid() )
2927 // take this date as default
2928 tmDef
= dateDef
.GetTm();
2931 else if ( m_time
!= 0 )
2933 else if ( m_time
!= wxLongLong(0) )
2936 // if this date is valid, don't change it
2941 // no default and this date is invalid - fall back to Today()
2942 tmDef
= Today().GetTm();
2953 // TODO we don't check here that the values are consistent, if both year
2954 // day and month/day were found, we just ignore the year day and we
2955 // also always ignore the week day
2956 if ( haveMon
&& haveDay
)
2958 if ( mday
> GetNumOfDaysInMonth(tm
.year
, mon
) )
2960 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
2962 return (wxChar
*)NULL
;
2968 else if ( haveYDay
)
2970 if ( yday
> GetNumberOfDays(tm
.year
) )
2972 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
2974 return (wxChar
*)NULL
;
2977 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
2984 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
2986 // translate to 24hour format
2989 //else: either already in 24h format or no translation needed
3012 const wxChar
*wxDateTime::ParseDateTime(const wxChar
*date
)
3014 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3016 // there is a public domain version of getdate.y, but it only works for
3018 wxFAIL_MSG(_T("TODO"));
3020 return (wxChar
*)NULL
;
3023 const wxChar
*wxDateTime::ParseDate(const wxChar
*date
)
3025 // this is a simplified version of ParseDateTime() which understands only
3026 // "today" (for wxDate compatibility) and digits only otherwise (and not
3027 // all esoteric constructions ParseDateTime() knows about)
3029 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3031 const wxChar
*p
= date
;
3032 while ( wxIsspace(*p
) )
3035 // some special cases
3039 int dayDiffFromToday
;
3042 { wxTRANSLATE("today"), 0 },
3043 { wxTRANSLATE("yesterday"), -1 },
3044 { wxTRANSLATE("tomorrow"), 1 },
3047 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
3049 wxString date
= wxGetTranslation(literalDates
[n
].str
);
3050 size_t len
= date
.length();
3051 if ( wxStrlen(p
) >= len
&& (wxString(p
, len
).CmpNoCase(date
) == 0) )
3053 // nothing can follow this, so stop here
3056 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
3058 if ( dayDiffFromToday
)
3060 *this += wxDateSpan::Days(dayDiffFromToday
);
3067 // We try to guess what we have here: for each new (numeric) token, we
3068 // determine if it can be a month, day or a year. Of course, there is an
3069 // ambiguity as some numbers may be days as well as months, so we also
3070 // have the ability to back track.
3073 bool haveDay
= FALSE
, // the months day?
3074 haveWDay
= FALSE
, // the day of week?
3075 haveMon
= FALSE
, // the month?
3076 haveYear
= FALSE
; // the year?
3078 // and the value of the items we have (init them to get rid of warnings)
3079 WeekDay wday
= Inv_WeekDay
;
3080 wxDateTime_t day
= 0;
3081 wxDateTime::Month mon
= Inv_Month
;
3084 // tokenize the string
3086 static const wxChar
*dateDelimiters
= _T(".,/-\t\n ");
3087 wxStringTokenizer
tok(p
, dateDelimiters
);
3088 while ( tok
.HasMoreTokens() )
3090 wxString token
= tok
.GetNextToken();
3096 if ( token
.ToULong(&val
) )
3098 // guess what this number is
3104 if ( !haveMon
&& val
> 0 && val
<= 12 )
3106 // assume it is month
3109 else // not the month
3111 wxDateTime_t maxDays
= haveMon
3112 ? GetNumOfDaysInMonth(haveYear
? year
: Inv_Year
, mon
)
3116 if ( (val
== 0) || (val
> maxDays
) )
3133 year
= (wxDateTime_t
)val
;
3142 day
= (wxDateTime_t
)val
;
3148 mon
= (Month
)(val
- 1);
3151 else // not a number
3153 // be careful not to overwrite the current mon value
3154 Month mon2
= GetMonthFromName(token
, Name_Full
| Name_Abbr
);
3155 if ( mon2
!= Inv_Month
)
3160 // but we already have a month - maybe we guessed wrong?
3163 // no need to check in month range as always < 12, but
3164 // the days are counted from 1 unlike the months
3165 day
= (wxDateTime_t
)mon
+ 1;
3170 // could possible be the year (doesn't the year come
3171 // before the month in the japanese format?) (FIXME)
3180 else // not a valid month name
3182 wday
= GetWeekDayFromName(token
, Name_Full
| Name_Abbr
);
3183 if ( wday
!= Inv_WeekDay
)
3193 else // not a valid weekday name
3196 static const wxChar
*ordinals
[] =
3198 wxTRANSLATE("first"),
3199 wxTRANSLATE("second"),
3200 wxTRANSLATE("third"),
3201 wxTRANSLATE("fourth"),
3202 wxTRANSLATE("fifth"),
3203 wxTRANSLATE("sixth"),
3204 wxTRANSLATE("seventh"),
3205 wxTRANSLATE("eighth"),
3206 wxTRANSLATE("ninth"),
3207 wxTRANSLATE("tenth"),
3208 wxTRANSLATE("eleventh"),
3209 wxTRANSLATE("twelfth"),
3210 wxTRANSLATE("thirteenth"),
3211 wxTRANSLATE("fourteenth"),
3212 wxTRANSLATE("fifteenth"),
3213 wxTRANSLATE("sixteenth"),
3214 wxTRANSLATE("seventeenth"),
3215 wxTRANSLATE("eighteenth"),
3216 wxTRANSLATE("nineteenth"),
3217 wxTRANSLATE("twentieth"),
3218 // that's enough - otherwise we'd have problems with
3219 // composite (or not) ordinals
3223 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
3225 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
3231 if ( n
== WXSIZEOF(ordinals
) )
3233 // stop here - something unknown
3240 // don't try anything here (as in case of numeric day
3241 // above) - the symbolic day spec should always
3242 // precede the month/year
3253 nPosCur
= tok
.GetPosition();
3256 // either no more tokens or the scan was stopped by something we couldn't
3257 // parse - in any case, see if we can construct a date from what we have
3258 if ( !haveDay
&& !haveWDay
)
3260 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
3262 return (wxChar
*)NULL
;
3265 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
3266 !(haveDay
&& haveMon
&& haveYear
) )
3268 // without adjectives (which we don't support here) the week day only
3269 // makes sense completely separately or with the full date
3270 // specification (what would "Wed 1999" mean?)
3271 return (wxChar
*)NULL
;
3274 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
3276 // may be we have month and day instead of day and year?
3277 if ( haveDay
&& !haveMon
)
3281 // exchange day and month
3282 mon
= (wxDateTime::Month
)(day
- 1);
3284 // we're in the current year then
3285 if ( year
<= GetNumOfDaysInMonth(Inv_Year
, mon
) )
3292 //else: no, can't exchange, leave haveMon == FALSE
3298 // if we give the year, month and day must be given too
3299 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
3301 return (wxChar
*)NULL
;
3307 mon
= GetCurrentMonth();
3312 year
= GetCurrentYear();
3317 Set(day
, mon
, year
);
3321 // check that it is really the same
3322 if ( GetWeekDay() != wday
)
3324 // inconsistency detected
3325 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
3327 return (wxChar
*)NULL
;
3335 SetToWeekDayInSameWeek(wday
);
3338 // return the pointer to the first unparsed char
3340 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
3342 // if we couldn't parse the token after the delimiter, put back the
3343 // delimiter as well
3350 const wxChar
*wxDateTime::ParseTime(const wxChar
*time
)
3352 wxCHECK_MSG( time
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3354 // first try some extra things
3361 { wxTRANSLATE("noon"), 12 },
3362 { wxTRANSLATE("midnight"), 00 },
3366 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
3368 wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
3369 size_t len
= timeString
.length();
3370 if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 )
3372 Set(stdTimes
[n
].hour
, 0, 0);
3378 // try all time formats we may think about starting with the standard one
3379 const wxChar
*result
= ParseFormat(time
, _T("%X"));
3382 // normally, it's the same, but why not try it?
3383 result
= ParseFormat(time
, _T("%H:%M:%S"));
3388 // 12hour with AM/PM?
3389 result
= ParseFormat(time
, _T("%I:%M:%S %p"));
3395 result
= ParseFormat(time
, _T("%H:%M"));
3400 // 12hour with AM/PM but without seconds?
3401 result
= ParseFormat(time
, _T("%I:%M %p"));
3407 result
= ParseFormat(time
, _T("%H"));
3412 // just the hour and AM/PM?
3413 result
= ParseFormat(time
, _T("%I %p"));
3416 // TODO: parse timezones
3421 // ----------------------------------------------------------------------------
3422 // Workdays and holidays support
3423 // ----------------------------------------------------------------------------
3425 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
3427 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
3430 // ============================================================================
3432 // ============================================================================
3434 // not all strftime(3) format specifiers make sense here because, for example,
3435 // a time span doesn't have a year nor a timezone
3437 // Here are the ones which are supported (all of them are supported by strftime
3439 // %H hour in 24 hour format
3440 // %M minute (00 - 59)
3441 // %S second (00 - 59)
3444 // Also, for MFC CTimeSpan compatibility, we support
3445 // %D number of days
3447 // And, to be better than MFC :-), we also have
3448 // %E number of wEeks
3449 // %l milliseconds (000 - 999)
3450 wxString
wxTimeSpan::Format(const wxChar
*format
) const
3452 wxCHECK_MSG( format
, _T(""), _T("NULL format in wxTimeSpan::Format") );
3455 str
.Alloc(wxStrlen(format
));
3457 for ( const wxChar
*pch
= format
; *pch
; pch
++ )
3461 if ( ch
== _T('%') )
3465 ch
= *++pch
; // get the format spec char
3469 wxFAIL_MSG( _T("invalid format character") );
3473 // will get to str << ch below
3477 tmp
.Printf(_T("%d"), GetDays());
3481 tmp
.Printf(_T("%d"), GetWeeks());
3485 tmp
.Printf(_T("%02d"), GetHours());
3489 tmp
.Printf(_T("%03ld"), GetMilliseconds().ToLong());
3493 tmp
.Printf(_T("%02d"), GetMinutes());
3497 tmp
.Printf(_T("%02ld"), GetSeconds().ToLong());
3505 // skip str += ch below
3516 // ============================================================================
3517 // wxDateTimeHolidayAuthority and related classes
3518 // ============================================================================
3520 #include "wx/arrimpl.cpp"
3522 WX_DEFINE_OBJARRAY(wxDateTimeArray
);
3524 static int wxCMPFUNC_CONV
3525 wxDateTimeCompareFunc(wxDateTime
**first
, wxDateTime
**second
)
3527 wxDateTime dt1
= **first
,
3530 return dt1
== dt2
? 0 : dt1
< dt2
? -1 : +1;
3533 // ----------------------------------------------------------------------------
3534 // wxDateTimeHolidayAuthority
3535 // ----------------------------------------------------------------------------
3537 wxHolidayAuthoritiesArray
wxDateTimeHolidayAuthority::ms_authorities
;
3540 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime
& dt
)
3542 size_t count
= ms_authorities
.GetCount();
3543 for ( size_t n
= 0; n
< count
; n
++ )
3545 if ( ms_authorities
[n
]->DoIsHoliday(dt
) )
3556 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime
& dtStart
,
3557 const wxDateTime
& dtEnd
,
3558 wxDateTimeArray
& holidays
)
3560 wxDateTimeArray hol
;
3564 size_t count
= ms_authorities
.GetCount();
3565 for ( size_t nAuth
= 0; nAuth
< count
; nAuth
++ )
3567 ms_authorities
[nAuth
]->DoGetHolidaysInRange(dtStart
, dtEnd
, hol
);
3569 WX_APPEND_ARRAY(holidays
, hol
);
3572 holidays
.Sort(wxDateTimeCompareFunc
);
3574 return holidays
.GetCount();
3578 void wxDateTimeHolidayAuthority::ClearAllAuthorities()
3580 WX_CLEAR_ARRAY(ms_authorities
);
3584 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority
*auth
)
3586 ms_authorities
.Add(auth
);
3589 // ----------------------------------------------------------------------------
3590 // wxDateTimeWorkDays
3591 // ----------------------------------------------------------------------------
3593 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime
& dt
) const
3595 wxDateTime::WeekDay wd
= dt
.GetWeekDay();
3597 return (wd
== wxDateTime::Sun
) || (wd
== wxDateTime::Sat
);
3600 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime
& dtStart
,
3601 const wxDateTime
& dtEnd
,
3602 wxDateTimeArray
& holidays
) const
3604 if ( dtStart
> dtEnd
)
3606 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
3613 // instead of checking all days, start with the first Sat after dtStart and
3614 // end with the last Sun before dtEnd
3615 wxDateTime dtSatFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sat
),
3616 dtSatLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sat
),
3617 dtSunFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sun
),
3618 dtSunLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sun
),
3621 for ( dt
= dtSatFirst
; dt
<= dtSatLast
; dt
+= wxDateSpan::Week() )
3626 for ( dt
= dtSunFirst
; dt
<= dtSunLast
; dt
+= wxDateSpan::Week() )
3631 return holidays
.GetCount();