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 // ----------------------------------------------------------------------------
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"
78 #define wxDEFINE_TIME_CONSTANTS // before including datetime.h
82 #include "wx/datetime.h"
83 #include "wx/timer.h" // for wxGetLocalTimeMillis()
85 // ----------------------------------------------------------------------------
86 // conditional compilation
87 // ----------------------------------------------------------------------------
89 #if defined(HAVE_STRPTIME) && defined(__LINUX__)
90 // glibc 2.0.7 strptime() is broken - the following snippet causes it to
91 // crash (instead of just failing):
93 // strncpy(buf, "Tue Dec 21 20:25:40 1999", 128);
94 // strptime(buf, "%x", &tm);
98 #endif // broken strptime()
100 #if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM)
101 #if defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
102 #define WX_TIMEZONE _timezone
103 #elif defined(__MWERKS__)
104 long wxmw_timezone
= 28800;
105 #define WX_TIMEZONE wxmw_timezone
106 #elif defined(__DJGPP__) || defined(__WINE__)
107 #include <sys/timeb.h>
109 static long wxGetTimeZone()
111 static long timezone
= MAXLONG
; // invalid timezone
112 if (timezone
== MAXLONG
)
116 timezone
= tb
.timezone
;
120 #define WX_TIMEZONE wxGetTimeZone()
121 #elif defined(__DARWIN__)
122 #define WX_GMTOFF_IN_TM
123 #else // unknown platform - try timezone
124 #define WX_TIMEZONE timezone
126 #endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
128 // ----------------------------------------------------------------------------
130 // ----------------------------------------------------------------------------
132 // debugging helper: just a convenient replacement of wxCHECK()
133 #define wxDATETIME_CHECK(expr, msg) \
137 *this = wxInvalidDateTime; \
141 // ----------------------------------------------------------------------------
143 // ----------------------------------------------------------------------------
145 class wxDateTimeHolidaysModule
: public wxModule
148 virtual bool OnInit()
150 wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays
);
155 virtual void OnExit()
157 wxDateTimeHolidayAuthority::ClearAllAuthorities();
158 wxDateTimeHolidayAuthority::ms_authorities
.Clear();
162 DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule
)
165 IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule
, wxModule
)
167 // ----------------------------------------------------------------------------
169 // ----------------------------------------------------------------------------
172 static const int MONTHS_IN_YEAR
= 12;
174 static const int SEC_PER_MIN
= 60;
176 static const int MIN_PER_HOUR
= 60;
178 static const int HOURS_PER_DAY
= 24;
180 static const long SECONDS_PER_DAY
= 86400l;
182 static const int DAYS_PER_WEEK
= 7;
184 static const long MILLISECONDS_PER_DAY
= 86400000l;
186 // this is the integral part of JDN of the midnight of Jan 1, 1970
187 // (i.e. JDN(Jan 1, 1970) = 2440587.5)
188 static const long EPOCH_JDN
= 2440587l;
190 // the date of JDN -0.5 (as we don't work with fractional parts, this is the
191 // reference date for us) is Nov 24, 4714BC
192 static const int JDN_0_YEAR
= -4713;
193 static const int JDN_0_MONTH
= wxDateTime::Nov
;
194 static const int JDN_0_DAY
= 24;
196 // the constants used for JDN calculations
197 static const long JDN_OFFSET
= 32046l;
198 static const long DAYS_PER_5_MONTHS
= 153l;
199 static const long DAYS_PER_4_YEARS
= 1461l;
200 static const long DAYS_PER_400_YEARS
= 146097l;
202 // this array contains the cumulated number of days in all previous months for
203 // normal and leap years
204 static const wxDateTime::wxDateTime_t gs_cumulatedDays
[2][MONTHS_IN_YEAR
] =
206 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
207 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
210 // ----------------------------------------------------------------------------
212 // ----------------------------------------------------------------------------
214 // in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
215 // indicate an invalid wxDateTime object
216 const wxDateTime wxDefaultDateTime
;
218 wxDateTime::Country
wxDateTime::ms_country
= wxDateTime::Country_Unknown
;
220 // ----------------------------------------------------------------------------
222 // ----------------------------------------------------------------------------
224 // a critical section is needed to protect GetTimeZone() static
225 // variable in MT case
227 static wxCriticalSection gs_critsectTimezone
;
228 #endif // wxUSE_THREADS
230 // ----------------------------------------------------------------------------
232 // ----------------------------------------------------------------------------
234 // debugger helper: shows what the date really is
236 extern const wxChar
*wxDumpDate(const wxDateTime
* dt
)
238 static wxChar buf
[128];
240 wxStrcpy(buf
, dt
->Format(_T("%Y-%m-%d (%a) %H:%M:%S")));
246 // get the number of days in the given month of the given year
248 wxDateTime::wxDateTime_t
GetNumOfDaysInMonth(int year
, wxDateTime::Month month
)
250 // the number of days in month in Julian/Gregorian calendar: the first line
251 // is for normal years, the second one is for the leap ones
252 static wxDateTime::wxDateTime_t daysInMonth
[2][MONTHS_IN_YEAR
] =
254 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
255 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
258 return daysInMonth
[wxDateTime::IsLeapYear(year
)][month
];
261 // returns the time zone in the C sense, i.e. the difference UTC - local
263 static int GetTimeZone()
265 // set to TRUE when the timezone is set
266 static bool s_timezoneSet
= FALSE
;
267 #ifdef WX_GMTOFF_IN_TM
268 static long gmtoffset
= LONG_MAX
; // invalid timezone
271 wxCRIT_SECT_LOCKER(lock
, gs_critsectTimezone
);
273 // ensure that the timezone variable is set by calling localtime
274 if ( !s_timezoneSet
)
276 // just call localtime() instead of figuring out whether this system
277 // supports tzset(), _tzset() or something else
282 s_timezoneSet
= TRUE
;
284 #ifdef WX_GMTOFF_IN_TM
285 // note that GMT offset is the opposite of time zone and so to return
286 // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM
287 // cases we have to negate it
288 gmtoffset
= -tm
->tm_gmtoff
;
292 #ifdef WX_GMTOFF_IN_TM
293 return (int)gmtoffset
;
295 return (int)WX_TIMEZONE
;
299 // return the integral part of the JDN for the midnight of the given date (to
300 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
301 // noon of the previous day)
302 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day
,
303 wxDateTime::Month mon
,
306 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
308 // check the date validity
310 (year
> JDN_0_YEAR
) ||
311 ((year
== JDN_0_YEAR
) && (mon
> JDN_0_MONTH
)) ||
312 ((year
== JDN_0_YEAR
) && (mon
== JDN_0_MONTH
) && (day
>= JDN_0_DAY
)),
313 _T("date out of range - can't convert to JDN")
316 // make the year positive to avoid problems with negative numbers division
319 // months are counted from March here
321 if ( mon
>= wxDateTime::Mar
)
331 // now we can simply add all the contributions together
332 return ((year
/ 100) * DAYS_PER_400_YEARS
) / 4
333 + ((year
% 100) * DAYS_PER_4_YEARS
) / 4
334 + (month
* DAYS_PER_5_MONTHS
+ 2) / 5
339 // this function is a wrapper around strftime(3)
340 static wxString
CallStrftime(const wxChar
*format
, const tm
* tm
)
343 if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) )
345 // buffer is too small?
346 wxFAIL_MSG(_T("strftime() failed"));
349 return wxString(buf
);
352 // if year and/or month have invalid values, replace them with the current ones
353 static void ReplaceDefaultYearMonthWithCurrent(int *year
,
354 wxDateTime::Month
*month
)
356 struct tm
*tmNow
= NULL
;
358 if ( *year
== wxDateTime::Inv_Year
)
360 tmNow
= wxDateTime::GetTmNow();
362 *year
= 1900 + tmNow
->tm_year
;
365 if ( *month
== wxDateTime::Inv_Month
)
368 tmNow
= wxDateTime::GetTmNow();
370 *month
= (wxDateTime::Month
)tmNow
->tm_mon
;
374 // fll the struct tm with default values
375 static void InitTm(struct tm
& tm
)
377 // struct tm may have etxra fields (undocumented and with unportable
378 // names) which, nevertheless, must be set to 0
379 memset(&tm
, 0, sizeof(struct tm
));
381 tm
.tm_mday
= 1; // mday 0 is invalid
382 tm
.tm_year
= 76; // any valid year
383 tm
.tm_isdst
= -1; // auto determine
389 // return the month if the string is a month name or Inv_Month otherwise
390 static wxDateTime::Month
GetMonthFromName(const wxString
& name
, int flags
)
392 wxDateTime::Month mon
;
393 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
395 // case-insensitive comparison either one of or with both abbreviated
397 if ( flags
& wxDateTime::Name_Full
)
399 if ( name
.CmpNoCase(wxDateTime::
400 GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 )
406 if ( flags
& wxDateTime::Name_Abbr
)
408 if ( name
.CmpNoCase(wxDateTime::
409 GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 )
419 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
420 static wxDateTime::WeekDay
GetWeekDayFromName(const wxString
& name
, int flags
)
422 wxDateTime::WeekDay wd
;
423 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
425 // case-insensitive comparison either one of or with both abbreviated
427 if ( flags
& wxDateTime::Name_Full
)
429 if ( name
.CmpNoCase(wxDateTime::
430 GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 )
436 if ( flags
& wxDateTime::Name_Abbr
)
438 if ( name
.CmpNoCase(wxDateTime::
439 GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 )
449 // scans all digits (but no more than len) and returns the resulting number
450 static bool GetNumericToken(size_t len
, const wxChar
*& p
, unsigned long *number
)
454 while ( wxIsdigit(*p
) )
458 if ( len
&& ++n
> len
)
462 return !!s
&& s
.ToULong(number
);
465 // scans all alphabetic characters and returns the resulting string
466 static wxString
GetAlphaToken(const wxChar
*& p
)
469 while ( wxIsalpha(*p
) )
477 // ============================================================================
478 // implementation of wxDateTime
479 // ============================================================================
481 // ----------------------------------------------------------------------------
483 // ----------------------------------------------------------------------------
487 year
= (wxDateTime_t
)wxDateTime::Inv_Year
;
488 mon
= wxDateTime::Inv_Month
;
490 hour
= min
= sec
= msec
= 0;
491 wday
= wxDateTime::Inv_WeekDay
;
494 wxDateTime::Tm::Tm(const struct tm
& tm
, const TimeZone
& tz
)
502 mon
= (wxDateTime::Month
)tm
.tm_mon
;
503 year
= 1900 + tm
.tm_year
;
508 bool wxDateTime::Tm::IsValid() const
510 // we allow for the leap seconds, although we don't use them (yet)
511 return (year
!= wxDateTime::Inv_Year
) && (mon
!= wxDateTime::Inv_Month
) &&
512 (mday
<= GetNumOfDaysInMonth(year
, mon
)) &&
513 (hour
< 24) && (min
< 60) && (sec
< 62) && (msec
< 1000);
516 void wxDateTime::Tm::ComputeWeekDay()
518 // compute the week day from day/month/year: we use the dumbest algorithm
519 // possible: just compute our JDN and then use the (simple to derive)
520 // formula: weekday = (JDN + 1.5) % 7
521 wday
= (wxDateTime::WeekDay
)(GetTruncatedJDN(mday
, mon
, year
) + 2) % 7;
524 void wxDateTime::Tm::AddMonths(int monDiff
)
526 // normalize the months field
527 while ( monDiff
< -mon
)
531 monDiff
+= MONTHS_IN_YEAR
;
534 while ( monDiff
+ mon
>= MONTHS_IN_YEAR
)
538 monDiff
-= MONTHS_IN_YEAR
;
541 mon
= (wxDateTime::Month
)(mon
+ monDiff
);
543 wxASSERT_MSG( mon
>= 0 && mon
< MONTHS_IN_YEAR
, _T("logic error") );
545 // NB: we don't check here that the resulting date is valid, this function
546 // is private and the caller must check it if needed
549 void wxDateTime::Tm::AddDays(int dayDiff
)
551 // normalize the days field
552 while ( dayDiff
+ mday
< 1 )
556 dayDiff
+= GetNumOfDaysInMonth(year
, mon
);
560 while ( mday
> GetNumOfDaysInMonth(year
, mon
) )
562 mday
-= GetNumOfDaysInMonth(year
, mon
);
567 wxASSERT_MSG( mday
> 0 && mday
<= GetNumOfDaysInMonth(year
, mon
),
571 // ----------------------------------------------------------------------------
573 // ----------------------------------------------------------------------------
575 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz
)
579 case wxDateTime::Local
:
580 // get the offset from C RTL: it returns the difference GMT-local
581 // while we want to have the offset _from_ GMT, hence the '-'
582 m_offset
= -GetTimeZone();
585 case wxDateTime::GMT_12
:
586 case wxDateTime::GMT_11
:
587 case wxDateTime::GMT_10
:
588 case wxDateTime::GMT_9
:
589 case wxDateTime::GMT_8
:
590 case wxDateTime::GMT_7
:
591 case wxDateTime::GMT_6
:
592 case wxDateTime::GMT_5
:
593 case wxDateTime::GMT_4
:
594 case wxDateTime::GMT_3
:
595 case wxDateTime::GMT_2
:
596 case wxDateTime::GMT_1
:
597 m_offset
= -3600*(wxDateTime::GMT0
- tz
);
600 case wxDateTime::GMT0
:
601 case wxDateTime::GMT1
:
602 case wxDateTime::GMT2
:
603 case wxDateTime::GMT3
:
604 case wxDateTime::GMT4
:
605 case wxDateTime::GMT5
:
606 case wxDateTime::GMT6
:
607 case wxDateTime::GMT7
:
608 case wxDateTime::GMT8
:
609 case wxDateTime::GMT9
:
610 case wxDateTime::GMT10
:
611 case wxDateTime::GMT11
:
612 case wxDateTime::GMT12
:
613 m_offset
= 3600*(tz
- wxDateTime::GMT0
);
616 case wxDateTime::A_CST
:
617 // Central Standard Time in use in Australia = UTC + 9.5
618 m_offset
= 60l*(9*60 + 30);
622 wxFAIL_MSG( _T("unknown time zone") );
626 // ----------------------------------------------------------------------------
628 // ----------------------------------------------------------------------------
631 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
)
633 if ( year
== Inv_Year
)
634 year
= GetCurrentYear();
636 if ( cal
== Gregorian
)
638 // in Gregorian calendar leap years are those divisible by 4 except
639 // those divisible by 100 unless they're also divisible by 400
640 // (in some countries, like Russia and Greece, additional corrections
641 // exist, but they won't manifest themselves until 2700)
642 return (year
% 4 == 0) && ((year
% 100 != 0) || (year
% 400 == 0));
644 else if ( cal
== Julian
)
646 // in Julian calendar the rule is simpler
647 return year
% 4 == 0;
651 wxFAIL_MSG(_T("unknown calendar"));
658 int wxDateTime::GetCentury(int year
)
660 return year
> 0 ? year
/ 100 : year
/ 100 - 1;
664 int wxDateTime::ConvertYearToBC(int year
)
667 return year
> 0 ? year
: year
- 1;
671 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
)
676 return Now().GetYear();
679 wxFAIL_MSG(_T("TODO"));
683 wxFAIL_MSG(_T("unsupported calendar"));
691 wxDateTime::Month
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
)
696 return Now().GetMonth();
699 wxFAIL_MSG(_T("TODO"));
703 wxFAIL_MSG(_T("unsupported calendar"));
711 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(int year
, Calendar cal
)
713 if ( year
== Inv_Year
)
715 // take the current year if none given
716 year
= GetCurrentYear();
723 return IsLeapYear(year
) ? 366 : 365;
726 wxFAIL_MSG(_T("unsupported calendar"));
734 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(wxDateTime::Month month
,
736 wxDateTime::Calendar cal
)
738 wxCHECK_MSG( month
< MONTHS_IN_YEAR
, 0, _T("invalid month") );
740 if ( cal
== Gregorian
|| cal
== Julian
)
742 if ( year
== Inv_Year
)
744 // take the current year if none given
745 year
= GetCurrentYear();
748 return GetNumOfDaysInMonth(year
, month
);
752 wxFAIL_MSG(_T("unsupported calendar"));
759 wxString
wxDateTime::GetMonthName(wxDateTime::Month month
,
760 wxDateTime::NameFlags flags
)
762 wxCHECK_MSG( month
!= Inv_Month
, _T(""), _T("invalid month") );
764 // notice that we must set all the fields to avoid confusing libc (GNU one
765 // gets confused to a crash if we don't do this)
770 return CallStrftime(flags
== Name_Abbr
? _T("%b") : _T("%B"), &tm
);
774 wxString
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
,
775 wxDateTime::NameFlags flags
)
777 wxCHECK_MSG( wday
!= Inv_WeekDay
, _T(""), _T("invalid weekday") );
779 // take some arbitrary Sunday
786 // and offset it by the number of days needed to get the correct wday
789 // call mktime() to normalize it...
792 // ... and call strftime()
793 return CallStrftime(flags
== Name_Abbr
? _T("%a") : _T("%A"), &tm
);
797 void wxDateTime::GetAmPmStrings(wxString
*am
, wxString
*pm
)
803 *am
= CallStrftime(_T("%p"), &tm
);
808 *pm
= CallStrftime(_T("%p"), &tm
);
812 // ----------------------------------------------------------------------------
813 // Country stuff: date calculations depend on the country (DST, work days,
814 // ...), so we need to know which rules to follow.
815 // ----------------------------------------------------------------------------
818 wxDateTime::Country
wxDateTime::GetCountry()
820 // TODO use LOCALE_ICOUNTRY setting under Win32
822 if ( ms_country
== Country_Unknown
)
824 // try to guess from the time zone name
825 time_t t
= time(NULL
);
826 struct tm
*tm
= localtime(&t
);
828 wxString tz
= CallStrftime(_T("%Z"), tm
);
829 if ( tz
== _T("WET") || tz
== _T("WEST") )
833 else if ( tz
== _T("CET") || tz
== _T("CEST") )
835 ms_country
= Country_EEC
;
837 else if ( tz
== _T("MSK") || tz
== _T("MSD") )
841 else if ( tz
== _T("AST") || tz
== _T("ADT") ||
842 tz
== _T("EST") || tz
== _T("EDT") ||
843 tz
== _T("CST") || tz
== _T("CDT") ||
844 tz
== _T("MST") || tz
== _T("MDT") ||
845 tz
== _T("PST") || tz
== _T("PDT") )
851 // well, choose a default one
860 void wxDateTime::SetCountry(wxDateTime::Country country
)
862 ms_country
= country
;
866 bool wxDateTime::IsWestEuropeanCountry(Country country
)
868 if ( country
== Country_Default
)
870 country
= GetCountry();
873 return (Country_WesternEurope_Start
<= country
) &&
874 (country
<= Country_WesternEurope_End
);
877 // ----------------------------------------------------------------------------
878 // DST calculations: we use 3 different rules for the West European countries,
879 // USA and for the rest of the world. This is undoubtedly false for many
880 // countries, but I lack the necessary info (and the time to gather it),
881 // please add the other rules here!
882 // ----------------------------------------------------------------------------
885 bool wxDateTime::IsDSTApplicable(int year
, Country country
)
887 if ( year
== Inv_Year
)
889 // take the current year if none given
890 year
= GetCurrentYear();
893 if ( country
== Country_Default
)
895 country
= GetCountry();
902 // DST was first observed in the US and UK during WWI, reused
903 // during WWII and used again since 1966
904 return year
>= 1966 ||
905 (year
>= 1942 && year
<= 1945) ||
906 (year
== 1918 || year
== 1919);
909 // assume that it started after WWII
915 wxDateTime
wxDateTime::GetBeginDST(int year
, Country country
)
917 if ( year
== Inv_Year
)
919 // take the current year if none given
920 year
= GetCurrentYear();
923 if ( country
== Country_Default
)
925 country
= GetCountry();
928 if ( !IsDSTApplicable(year
, country
) )
930 return wxInvalidDateTime
;
935 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
937 // DST begins at 1 a.m. GMT on the last Sunday of March
938 if ( !dt
.SetToLastWeekDay(Sun
, Mar
, year
) )
941 wxFAIL_MSG( _T("no last Sunday in March?") );
944 dt
+= wxTimeSpan::Hours(1);
946 // disable DST tests because it could result in an infinite recursion!
949 else switch ( country
)
956 // don't know for sure - assume it was in effect all year
961 dt
.Set(1, Jan
, year
);
965 // DST was installed Feb 2, 1942 by the Congress
966 dt
.Set(2, Feb
, year
);
969 // Oil embargo changed the DST period in the US
971 dt
.Set(6, Jan
, 1974);
975 dt
.Set(23, Feb
, 1975);
979 // before 1986, DST begun on the last Sunday of April, but
980 // in 1986 Reagan changed it to begin at 2 a.m. of the
981 // first Sunday in April
984 if ( !dt
.SetToLastWeekDay(Sun
, Apr
, year
) )
987 wxFAIL_MSG( _T("no first Sunday in April?") );
992 if ( !dt
.SetToWeekDay(Sun
, 1, Apr
, year
) )
995 wxFAIL_MSG( _T("no first Sunday in April?") );
999 dt
+= wxTimeSpan::Hours(2);
1001 // TODO what about timezone??
1007 // assume Mar 30 as the start of the DST for the rest of the world
1008 // - totally bogus, of course
1009 dt
.Set(30, Mar
, year
);
1016 wxDateTime
wxDateTime::GetEndDST(int year
, Country country
)
1018 if ( year
== Inv_Year
)
1020 // take the current year if none given
1021 year
= GetCurrentYear();
1024 if ( country
== Country_Default
)
1026 country
= GetCountry();
1029 if ( !IsDSTApplicable(year
, country
) )
1031 return wxInvalidDateTime
;
1036 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1038 // DST ends at 1 a.m. GMT on the last Sunday of October
1039 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1041 // weirder and weirder...
1042 wxFAIL_MSG( _T("no last Sunday in October?") );
1045 dt
+= wxTimeSpan::Hours(1);
1047 // disable DST tests because it could result in an infinite recursion!
1050 else switch ( country
)
1057 // don't know for sure - assume it was in effect all year
1061 dt
.Set(31, Dec
, year
);
1065 // the time was reset after the end of the WWII
1066 dt
.Set(30, Sep
, year
);
1070 // DST ends at 2 a.m. on the last Sunday of October
1071 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1073 // weirder and weirder...
1074 wxFAIL_MSG( _T("no last Sunday in October?") );
1077 dt
+= wxTimeSpan::Hours(2);
1079 // TODO what about timezone??
1084 // assume October 26th as the end of the DST - totally bogus too
1085 dt
.Set(26, Oct
, year
);
1091 // ----------------------------------------------------------------------------
1092 // constructors and assignment operators
1093 // ----------------------------------------------------------------------------
1095 // return the current time with ms precision
1096 /* static */ wxDateTime
wxDateTime::UNow()
1098 return wxDateTime(wxGetLocalTimeMillis());
1101 // the values in the tm structure contain the local time
1102 wxDateTime
& wxDateTime::Set(const struct tm
& tm
)
1105 time_t timet
= mktime(&tm2
);
1107 if ( timet
== (time_t)-1 )
1109 // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
1110 // less than timezone - try to make it work for this case
1111 if ( tm2
.tm_year
== 70 && tm2
.tm_mon
== 0 && tm2
.tm_mday
== 1 )
1113 // add timezone to make sure that date is in range
1114 tm2
.tm_sec
-= GetTimeZone();
1116 timet
= mktime(&tm2
);
1117 if ( timet
!= (time_t)-1 )
1119 timet
+= GetTimeZone();
1125 wxFAIL_MSG( _T("mktime() failed") );
1127 *this = wxInvalidDateTime
;
1137 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
,
1138 wxDateTime_t minute
,
1139 wxDateTime_t second
,
1140 wxDateTime_t millisec
)
1142 // we allow seconds to be 61 to account for the leap seconds, even if we
1143 // don't use them really
1144 wxDATETIME_CHECK( hour
< 24 &&
1148 _T("Invalid time in wxDateTime::Set()") );
1150 // get the current date from system
1151 struct tm
*tm
= GetTmNow();
1153 wxDATETIME_CHECK( tm
, _T("localtime() failed") );
1157 tm
->tm_min
= minute
;
1158 tm
->tm_sec
= second
;
1162 // and finally adjust milliseconds
1163 return SetMillisecond(millisec
);
1166 wxDateTime
& wxDateTime::Set(wxDateTime_t day
,
1170 wxDateTime_t minute
,
1171 wxDateTime_t second
,
1172 wxDateTime_t millisec
)
1174 wxDATETIME_CHECK( hour
< 24 &&
1178 _T("Invalid time in wxDateTime::Set()") );
1180 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1182 wxDATETIME_CHECK( (0 < day
) && (day
<= GetNumberOfDays(month
, year
)),
1183 _T("Invalid date in wxDateTime::Set()") );
1185 // the range of time_t type (inclusive)
1186 static const int yearMinInRange
= 1970;
1187 static const int yearMaxInRange
= 2037;
1189 // test only the year instead of testing for the exact end of the Unix
1190 // time_t range - it doesn't bring anything to do more precise checks
1191 if ( year
>= yearMinInRange
&& year
<= yearMaxInRange
)
1193 // use the standard library version if the date is in range - this is
1194 // probably more efficient than our code
1196 tm
.tm_year
= year
- 1900;
1202 tm
.tm_isdst
= -1; // mktime() will guess it
1206 // and finally adjust milliseconds
1207 return SetMillisecond(millisec
);
1211 // do time calculations ourselves: we want to calculate the number of
1212 // milliseconds between the given date and the epoch
1214 // get the JDN for the midnight of this day
1215 m_time
= GetTruncatedJDN(day
, month
, year
);
1216 m_time
-= EPOCH_JDN
;
1217 m_time
*= SECONDS_PER_DAY
* TIME_T_FACTOR
;
1219 // JDN corresponds to GMT, we take localtime
1220 Add(wxTimeSpan(hour
, minute
, second
+ GetTimeZone(), millisec
));
1226 wxDateTime
& wxDateTime::Set(double jdn
)
1228 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1230 jdn
-= EPOCH_JDN
+ 0.5;
1232 jdn
*= MILLISECONDS_PER_DAY
;
1239 wxDateTime
& wxDateTime::ResetTime()
1243 if ( tm
.hour
|| tm
.min
|| tm
.sec
|| tm
.msec
)
1256 // ----------------------------------------------------------------------------
1257 // DOS Date and Time Format functions
1258 // ----------------------------------------------------------------------------
1259 // the dos date and time value is an unsigned 32 bit value in the format:
1260 // YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
1262 // Y = year offset from 1980 (0-127)
1264 // D = day of month (1-31)
1266 // m = minute (0-59)
1267 // s = bisecond (0-29) each bisecond indicates two seconds
1268 // ----------------------------------------------------------------------------
1270 wxDateTime
& wxDateTime::SetFromDOS(unsigned long ddt
)
1274 long year
= ddt
& 0xFE000000;
1279 long month
= ddt
& 0x1E00000;
1284 long day
= ddt
& 0x1F0000;
1288 long hour
= ddt
& 0xF800;
1292 long minute
= ddt
& 0x7E0;
1296 long second
= ddt
& 0x1F;
1297 tm
.tm_sec
= second
* 2;
1299 return Set(mktime(&tm
));
1302 unsigned long wxDateTime::GetAsDOS() const
1305 time_t ticks
= GetTicks();
1306 struct tm
*tm
= localtime(&ticks
);
1308 long year
= tm
->tm_year
;
1312 long month
= tm
->tm_mon
;
1316 long day
= tm
->tm_mday
;
1319 long hour
= tm
->tm_hour
;
1322 long minute
= tm
->tm_min
;
1325 long second
= tm
->tm_sec
;
1328 ddt
= year
| month
| day
| hour
| minute
| second
;
1332 // ----------------------------------------------------------------------------
1333 // time_t <-> broken down time conversions
1334 // ----------------------------------------------------------------------------
1336 wxDateTime::Tm
wxDateTime::GetTm(const TimeZone
& tz
) const
1338 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1340 time_t time
= GetTicks();
1341 if ( time
!= (time_t)-1 )
1343 // use C RTL functions
1345 if ( tz
.GetOffset() == -GetTimeZone() )
1347 // we are working with local time
1348 tm
= localtime(&time
);
1350 // should never happen
1351 wxCHECK_MSG( tm
, Tm(), _T("localtime() failed") );
1355 time
+= (time_t)tz
.GetOffset();
1356 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
1357 int time2
= (int) time
;
1365 // should never happen
1366 wxCHECK_MSG( tm
, Tm(), _T("gmtime() failed") );
1370 tm
= (struct tm
*)NULL
;
1376 // adjust the milliseconds
1378 long timeOnly
= (m_time
% MILLISECONDS_PER_DAY
).ToLong();
1379 tm2
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1382 //else: use generic code below
1385 // remember the time and do the calculations with the date only - this
1386 // eliminates rounding errors of the floating point arithmetics
1388 wxLongLong timeMidnight
= m_time
+ tz
.GetOffset() * 1000;
1390 long timeOnly
= (timeMidnight
% MILLISECONDS_PER_DAY
).ToLong();
1392 // we want to always have positive time and timeMidnight to be really
1393 // the midnight before it
1396 timeOnly
= MILLISECONDS_PER_DAY
+ timeOnly
;
1399 timeMidnight
-= timeOnly
;
1401 // calculate the Gregorian date from JDN for the midnight of our date:
1402 // this will yield day, month (in 1..12 range) and year
1404 // actually, this is the JDN for the noon of the previous day
1405 long jdn
= (timeMidnight
/ MILLISECONDS_PER_DAY
).ToLong() + EPOCH_JDN
;
1407 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1409 wxASSERT_MSG( jdn
> -2, _T("JDN out of range") );
1411 // calculate the century
1412 long temp
= (jdn
+ JDN_OFFSET
) * 4 - 1;
1413 long century
= temp
/ DAYS_PER_400_YEARS
;
1415 // then the year and day of year (1 <= dayOfYear <= 366)
1416 temp
= ((temp
% DAYS_PER_400_YEARS
) / 4) * 4 + 3;
1417 long year
= (century
* 100) + (temp
/ DAYS_PER_4_YEARS
);
1418 long dayOfYear
= (temp
% DAYS_PER_4_YEARS
) / 4 + 1;
1420 // and finally the month and day of the month
1421 temp
= dayOfYear
* 5 - 3;
1422 long month
= temp
/ DAYS_PER_5_MONTHS
;
1423 long day
= (temp
% DAYS_PER_5_MONTHS
) / 5 + 1;
1425 // month is counted from March - convert to normal
1436 // year is offset by 4800
1439 // check that the algorithm gave us something reasonable
1440 wxASSERT_MSG( (0 < month
) && (month
<= 12), _T("invalid month") );
1441 wxASSERT_MSG( (1 <= day
) && (day
< 32), _T("invalid day") );
1443 // construct Tm from these values
1445 tm
.year
= (int)year
;
1446 tm
.mon
= (Month
)(month
- 1); // algorithm yields 1 for January, not 0
1447 tm
.mday
= (wxDateTime_t
)day
;
1448 tm
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1449 timeOnly
-= tm
.msec
;
1450 timeOnly
/= 1000; // now we have time in seconds
1452 tm
.sec
= (wxDateTime_t
)(timeOnly
% 60);
1454 timeOnly
/= 60; // now we have time in minutes
1456 tm
.min
= (wxDateTime_t
)(timeOnly
% 60);
1459 tm
.hour
= (wxDateTime_t
)(timeOnly
/ 60);
1464 wxDateTime
& wxDateTime::SetYear(int year
)
1466 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1475 wxDateTime
& wxDateTime::SetMonth(Month month
)
1477 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1486 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
)
1488 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1497 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
)
1499 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1508 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
)
1510 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1519 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
)
1521 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1530 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
)
1532 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1534 // we don't need to use GetTm() for this one
1535 m_time
-= m_time
% 1000l;
1536 m_time
+= millisecond
;
1541 // ----------------------------------------------------------------------------
1542 // wxDateTime arithmetics
1543 // ----------------------------------------------------------------------------
1545 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
)
1549 tm
.year
+= diff
.GetYears();
1550 tm
.AddMonths(diff
.GetMonths());
1552 // check that the resulting date is valid
1553 if ( tm
.mday
> GetNumOfDaysInMonth(tm
.year
, tm
.mon
) )
1555 // We suppose that when adding one month to Jan 31 we want to get Feb
1556 // 28 (or 29), i.e. adding a month to the last day of the month should
1557 // give the last day of the next month which is quite logical.
1559 // Unfortunately, there is no logic way to understand what should
1560 // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1561 // We make it Feb 28 (last day too), but it is highly questionable.
1562 tm
.mday
= GetNumOfDaysInMonth(tm
.year
, tm
.mon
);
1565 tm
.AddDays(diff
.GetTotalDays());
1569 wxASSERT_MSG( IsSameTime(tm
),
1570 _T("Add(wxDateSpan) shouldn't modify time") );
1575 // ----------------------------------------------------------------------------
1576 // Weekday and monthday stuff
1577 // ----------------------------------------------------------------------------
1579 bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek
,
1583 wxASSERT_MSG( numWeek
> 0,
1584 _T("invalid week number: weeks are counted from 1") );
1586 int year
= GetYear();
1588 // Jan 4 always lies in the 1st week of the year
1590 SetToWeekDayInSameWeek(weekday
, flags
) += wxDateSpan::Weeks(numWeek
- 1);
1592 if ( GetYear() != year
)
1594 // oops... numWeek was too big
1601 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
,
1604 // take the current month/year if none specified
1605 if ( year
== Inv_Year
)
1607 if ( month
== Inv_Month
)
1610 return Set(GetNumOfDaysInMonth(year
, month
), month
, year
);
1613 wxDateTime
& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday
, WeekFlags flags
)
1615 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1617 int wdayThis
= GetWeekDay();
1618 if ( weekday
== wdayThis
)
1624 if ( flags
== Default_First
)
1626 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
1629 // the logic below based on comparing weekday and wdayThis works if Sun (0)
1630 // is the first day in the week, but breaks down for Monday_First case so
1631 // we adjust the week days in this case
1632 if( flags
== Monday_First
)
1634 if ( wdayThis
== Sun
)
1637 //else: Sunday_First, nothing to do
1639 // go forward or back in time to the day we want
1640 if ( weekday
< wdayThis
)
1642 return Subtract(wxDateSpan::Days(wdayThis
- weekday
));
1644 else // weekday > wdayThis
1646 return Add(wxDateSpan::Days(weekday
- wdayThis
));
1650 wxDateTime
& wxDateTime::SetToNextWeekDay(WeekDay weekday
)
1652 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1655 WeekDay wdayThis
= GetWeekDay();
1656 if ( weekday
== wdayThis
)
1661 else if ( weekday
< wdayThis
)
1663 // need to advance a week
1664 diff
= 7 - (wdayThis
- weekday
);
1666 else // weekday > wdayThis
1668 diff
= weekday
- wdayThis
;
1671 return Add(wxDateSpan::Days(diff
));
1674 wxDateTime
& wxDateTime::SetToPrevWeekDay(WeekDay weekday
)
1676 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1679 WeekDay wdayThis
= GetWeekDay();
1680 if ( weekday
== wdayThis
)
1685 else if ( weekday
> wdayThis
)
1687 // need to go to previous week
1688 diff
= 7 - (weekday
- wdayThis
);
1690 else // weekday < wdayThis
1692 diff
= wdayThis
- weekday
;
1695 return Subtract(wxDateSpan::Days(diff
));
1698 bool wxDateTime::SetToWeekDay(WeekDay weekday
,
1703 wxCHECK_MSG( weekday
!= Inv_WeekDay
, FALSE
, _T("invalid weekday") );
1705 // we don't check explicitly that -5 <= n <= 5 because we will return FALSE
1706 // anyhow in such case - but may be should still give an assert for it?
1708 // take the current month/year if none specified
1709 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1713 // TODO this probably could be optimised somehow...
1717 // get the first day of the month
1718 dt
.Set(1, month
, year
);
1721 WeekDay wdayFirst
= dt
.GetWeekDay();
1723 // go to the first weekday of the month
1724 int diff
= weekday
- wdayFirst
;
1728 // add advance n-1 weeks more
1731 dt
+= wxDateSpan::Days(diff
);
1733 else // count from the end of the month
1735 // get the last day of the month
1736 dt
.SetToLastMonthDay(month
, year
);
1739 WeekDay wdayLast
= dt
.GetWeekDay();
1741 // go to the last weekday of the month
1742 int diff
= wdayLast
- weekday
;
1746 // and rewind n-1 weeks from there
1749 dt
-= wxDateSpan::Days(diff
);
1752 // check that it is still in the same month
1753 if ( dt
.GetMonth() == month
)
1761 // no such day in this month
1766 wxDateTime::wxDateTime_t
wxDateTime::GetDayOfYear(const TimeZone
& tz
) const
1770 return gs_cumulatedDays
[IsLeapYear(tm
.year
)][tm
.mon
] + tm
.mday
;
1773 wxDateTime::wxDateTime_t
wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags
,
1774 const TimeZone
& tz
) const
1776 if ( flags
== Default_First
)
1778 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
1781 wxDateTime_t nDayInYear
= GetDayOfYear(tz
);
1784 WeekDay wd
= GetWeekDay(tz
);
1785 if ( flags
== Sunday_First
)
1787 week
= (nDayInYear
- wd
+ 7) / 7;
1791 // have to shift the week days values
1792 week
= (nDayInYear
- (wd
- 1 + 7) % 7 + 7) / 7;
1795 // FIXME some more elegant way??
1796 WeekDay wdYearStart
= wxDateTime(1, Jan
, GetYear()).GetWeekDay();
1797 if ( wdYearStart
== Wed
|| wdYearStart
== Thu
)
1805 wxDateTime::wxDateTime_t
wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags
,
1806 const TimeZone
& tz
) const
1809 wxDateTime dtMonthStart
= wxDateTime(1, tm
.mon
, tm
.year
);
1810 int nWeek
= GetWeekOfYear(flags
) - dtMonthStart
.GetWeekOfYear(flags
) + 1;
1813 // this may happen for January when Jan, 1 is the last week of the
1815 nWeek
+= IsLeapYear(tm
.year
- 1) ? 53 : 52;
1818 return (wxDateTime::wxDateTime_t
)nWeek
;
1821 wxDateTime
& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday
)
1823 int year
= GetYear();
1824 wxDATETIME_CHECK( (0 < yday
) && (yday
<= GetNumberOfDays(year
)),
1825 _T("invalid year day") );
1827 bool isLeap
= IsLeapYear(year
);
1828 for ( Month mon
= Jan
; mon
< Inv_Month
; wxNextMonth(mon
) )
1830 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
1831 // don't need it neither - because of the CHECK above we know that
1832 // yday lies in December then
1833 if ( (mon
== Dec
) || (yday
< gs_cumulatedDays
[isLeap
][mon
+ 1]) )
1835 Set(yday
- gs_cumulatedDays
[isLeap
][mon
], mon
, year
);
1844 // ----------------------------------------------------------------------------
1845 // Julian day number conversion and related stuff
1846 // ----------------------------------------------------------------------------
1848 double wxDateTime::GetJulianDayNumber() const
1850 // JDN are always expressed for the GMT dates
1851 Tm
tm(ToTimezone(GMT0
).GetTm(GMT0
));
1853 double result
= GetTruncatedJDN(tm
.mday
, tm
.mon
, tm
.year
);
1855 // add the part GetTruncatedJDN() neglected
1858 // and now add the time: 86400 sec = 1 JDN
1859 return result
+ ((double)(60*(60*tm
.hour
+ tm
.min
) + tm
.sec
)) / 86400;
1862 double wxDateTime::GetRataDie() const
1864 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
1865 return GetJulianDayNumber() - 1721119.5 - 306;
1868 // ----------------------------------------------------------------------------
1869 // timezone and DST stuff
1870 // ----------------------------------------------------------------------------
1872 int wxDateTime::IsDST(wxDateTime::Country country
) const
1874 wxCHECK_MSG( country
== Country_Default
, -1,
1875 _T("country support not implemented") );
1877 // use the C RTL for the dates in the standard range
1878 time_t timet
= GetTicks();
1879 if ( timet
!= (time_t)-1 )
1881 tm
*tm
= localtime(&timet
);
1883 wxCHECK_MSG( tm
, -1, _T("localtime() failed") );
1885 return tm
->tm_isdst
;
1889 int year
= GetYear();
1891 if ( !IsDSTApplicable(year
, country
) )
1893 // no DST time in this year in this country
1897 return IsBetween(GetBeginDST(year
, country
), GetEndDST(year
, country
));
1901 wxDateTime
& wxDateTime::MakeTimezone(const TimeZone
& tz
, bool noDST
)
1903 long secDiff
= GetTimeZone() + tz
.GetOffset();
1905 // we need to know whether DST is or not in effect for this date unless
1906 // the test disabled by the caller
1907 if ( !noDST
&& (IsDST() == 1) )
1909 // FIXME we assume that the DST is always shifted by 1 hour
1913 return Subtract(wxTimeSpan::Seconds(secDiff
));
1916 // ----------------------------------------------------------------------------
1917 // wxDateTime to/from text representations
1918 // ----------------------------------------------------------------------------
1920 wxString
wxDateTime::Format(const wxChar
*format
, const TimeZone
& tz
) const
1922 wxCHECK_MSG( format
, _T(""), _T("NULL format in wxDateTime::Format") );
1924 // we have to use our own implementation if the date is out of range of
1925 // strftime() or if we use non standard specificators
1926 time_t time
= GetTicks();
1927 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
1931 if ( tz
.GetOffset() == -GetTimeZone() )
1933 // we are working with local time
1934 tm
= localtime(&time
);
1936 // should never happen
1937 wxCHECK_MSG( tm
, wxEmptyString
, _T("localtime() failed") );
1941 time
+= (int)tz
.GetOffset();
1943 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
1944 int time2
= (int) time
;
1952 // should never happen
1953 wxCHECK_MSG( tm
, wxEmptyString
, _T("gmtime() failed") );
1957 tm
= (struct tm
*)NULL
;
1963 return CallStrftime(format
, tm
);
1965 //else: use generic code below
1968 // we only parse ANSI C format specifications here, no POSIX 2
1969 // complications, no GNU extensions but we do add support for a "%l" format
1970 // specifier allowing to get the number of milliseconds
1973 // used for calls to strftime() when we only deal with time
1974 struct tm tmTimeOnly
;
1975 tmTimeOnly
.tm_hour
= tm
.hour
;
1976 tmTimeOnly
.tm_min
= tm
.min
;
1977 tmTimeOnly
.tm_sec
= tm
.sec
;
1978 tmTimeOnly
.tm_wday
= 0;
1979 tmTimeOnly
.tm_yday
= 0;
1980 tmTimeOnly
.tm_mday
= 1; // any date will do
1981 tmTimeOnly
.tm_mon
= 0;
1982 tmTimeOnly
.tm_year
= 76;
1983 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
1985 wxString tmp
, res
, fmt
;
1986 for ( const wxChar
*p
= format
; *p
; p
++ )
1988 if ( *p
!= _T('%') )
1996 // set the default format
1999 case _T('Y'): // year has 4 digits
2003 case _T('j'): // day of year has 3 digits
2004 case _T('l'): // milliseconds have 3 digits
2008 case _T('w'): // week day as number has only one
2013 // it's either another valid format specifier in which case
2014 // the format is "%02d" (for all the rest) or we have the
2015 // field width preceding the format in which case it will
2016 // override the default format anyhow
2020 bool restart
= TRUE
;
2025 // start of the format specification
2028 case _T('a'): // a weekday name
2030 // second parameter should be TRUE for abbreviated names
2031 res
+= GetWeekDayName(tm
.GetWeekDay(),
2032 *p
== _T('a') ? Name_Abbr
: Name_Full
);
2035 case _T('b'): // a month name
2037 res
+= GetMonthName(tm
.mon
,
2038 *p
== _T('b') ? Name_Abbr
: Name_Full
);
2041 case _T('c'): // locale default date and time representation
2042 case _T('x'): // locale default date representation
2044 // the problem: there is no way to know what do these format
2045 // specifications correspond to for the current locale.
2047 // the solution: use a hack and still use strftime(): first
2048 // find the YEAR which is a year in the strftime() range (1970
2049 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
2050 // of the real year. Then make a copy of the format and
2051 // replace all occurences of YEAR in it with some unique
2052 // string not appearing anywhere else in it, then use
2053 // strftime() to format the date in year YEAR and then replace
2054 // YEAR back by the real year and the unique replacement
2055 // string back with YEAR. Notice that "all occurences of YEAR"
2056 // means all occurences of 4 digit as well as 2 digit form!
2058 // the bugs: we assume that neither of %c nor %x contains any
2059 // fields which may change between the YEAR and real year. For
2060 // example, the week number (%U, %W) and the day number (%j)
2061 // will change if one of these years is leap and the other one
2064 // find the YEAR: normally, for any year X, Jan 1 or the
2065 // year X + 28 is the same weekday as Jan 1 of X (because
2066 // the weekday advances by 1 for each normal X and by 2
2067 // for each leap X, hence by 5 every 4 years or by 35
2068 // which is 0 mod 7 every 28 years) but this rule breaks
2069 // down if there are years between X and Y which are
2070 // divisible by 4 but not leap (i.e. divisible by 100 but
2071 // not 400), hence the correction.
2073 int yearReal
= GetYear(tz
);
2074 int mod28
= yearReal
% 28;
2076 // be careful to not go too far - we risk to leave the
2081 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
2085 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
2088 int nCentury
= year
/ 100,
2089 nCenturyReal
= yearReal
/ 100;
2091 // need to adjust for the years divisble by 400 which are
2092 // not leap but are counted like leap ones if we just take
2093 // the number of centuries in between for nLostWeekDays
2094 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
2095 (nCentury
/ 4 - nCenturyReal
/ 4);
2097 // we have to gain back the "lost" weekdays: note that the
2098 // effect of this loop is to not do anything to
2099 // nLostWeekDays (which we won't use any more), but to
2100 // (indirectly) set the year correctly
2101 while ( (nLostWeekDays
% 7) != 0 )
2103 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
2106 // at any rate, we couldn't go further than 1988 + 9 + 28!
2107 wxASSERT_MSG( year
< 2030,
2108 _T("logic error in wxDateTime::Format") );
2110 wxString strYear
, strYear2
;
2111 strYear
.Printf(_T("%d"), year
);
2112 strYear2
.Printf(_T("%d"), year
% 100);
2114 // find two strings not occuring in format (this is surely
2115 // not optimal way of doing it... improvements welcome!)
2116 wxString fmt
= format
;
2117 wxString replacement
= (wxChar
)-1;
2118 while ( fmt
.Find(replacement
) != wxNOT_FOUND
)
2120 replacement
<< (wxChar
)-1;
2123 wxString replacement2
= (wxChar
)-2;
2124 while ( fmt
.Find(replacement
) != wxNOT_FOUND
)
2126 replacement
<< (wxChar
)-2;
2129 // replace all occurences of year with it
2130 bool wasReplaced
= fmt
.Replace(strYear
, replacement
) > 0;
2132 wasReplaced
= fmt
.Replace(strYear2
, replacement2
) > 0;
2134 // use strftime() to format the same date but in supported
2137 // NB: we assume that strftime() doesn't check for the
2138 // date validity and will happily format the date
2139 // corresponding to Feb 29 of a non leap year (which
2140 // may happen if yearReal was leap and year is not)
2141 struct tm tmAdjusted
;
2143 tmAdjusted
.tm_hour
= tm
.hour
;
2144 tmAdjusted
.tm_min
= tm
.min
;
2145 tmAdjusted
.tm_sec
= tm
.sec
;
2146 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
2147 tmAdjusted
.tm_yday
= GetDayOfYear();
2148 tmAdjusted
.tm_mday
= tm
.mday
;
2149 tmAdjusted
.tm_mon
= tm
.mon
;
2150 tmAdjusted
.tm_year
= year
- 1900;
2151 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
2152 wxString str
= CallStrftime(*p
== _T('c') ? _T("%c")
2156 // now replace the occurence of 1999 with the real year
2157 wxString strYearReal
, strYearReal2
;
2158 strYearReal
.Printf(_T("%04d"), yearReal
);
2159 strYearReal2
.Printf(_T("%02d"), yearReal
% 100);
2160 str
.Replace(strYear
, strYearReal
);
2161 str
.Replace(strYear2
, strYearReal2
);
2163 // and replace back all occurences of replacement string
2166 str
.Replace(replacement2
, strYear2
);
2167 str
.Replace(replacement
, strYear
);
2174 case _T('d'): // day of a month (01-31)
2175 res
+= wxString::Format(fmt
, tm
.mday
);
2178 case _T('H'): // hour in 24h format (00-23)
2179 res
+= wxString::Format(fmt
, tm
.hour
);
2182 case _T('I'): // hour in 12h format (01-12)
2184 // 24h -> 12h, 0h -> 12h too
2185 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
2186 : tm
.hour
? tm
.hour
: 12;
2187 res
+= wxString::Format(fmt
, hour12
);
2191 case _T('j'): // day of the year
2192 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
2195 case _T('l'): // milliseconds (NOT STANDARD)
2196 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
2199 case _T('m'): // month as a number (01-12)
2200 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
2203 case _T('M'): // minute as a decimal number (00-59)
2204 res
+= wxString::Format(fmt
, tm
.min
);
2207 case _T('p'): // AM or PM string
2208 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
2211 case _T('S'): // second as a decimal number (00-61)
2212 res
+= wxString::Format(fmt
, tm
.sec
);
2215 case _T('U'): // week number in the year (Sunday 1st week day)
2216 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
2219 case _T('W'): // week number in the year (Monday 1st week day)
2220 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
2223 case _T('w'): // weekday as a number (0-6), Sunday = 0
2224 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
2227 // case _T('x'): -- handled with "%c"
2229 case _T('X'): // locale default time representation
2230 // just use strftime() to format the time for us
2231 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
2234 case _T('y'): // year without century (00-99)
2235 res
+= wxString::Format(fmt
, tm
.year
% 100);
2238 case _T('Y'): // year with century
2239 res
+= wxString::Format(fmt
, tm
.year
);
2242 case _T('Z'): // timezone name
2243 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
2247 // is it the format width?
2249 while ( *p
== _T('-') || *p
== _T('+') ||
2250 *p
== _T(' ') || wxIsdigit(*p
) )
2255 if ( !fmt
.IsEmpty() )
2257 // we've only got the flags and width so far in fmt
2258 fmt
.Prepend(_T('%'));
2259 fmt
.Append(_T('d'));
2266 // no, it wasn't the width
2267 wxFAIL_MSG(_T("unknown format specificator"));
2269 // fall through and just copy it nevertheless
2271 case _T('%'): // a percent sign
2275 case 0: // the end of string
2276 wxFAIL_MSG(_T("missing format at the end of string"));
2278 // just put the '%' which was the last char in format
2288 // this function parses a string in (strict) RFC 822 format: see the section 5
2289 // of the RFC for the detailed description, but briefly it's something of the
2290 // form "Sat, 18 Dec 1999 00:48:30 +0100"
2292 // this function is "strict" by design - it must reject anything except true
2293 // RFC822 time specs.
2295 // TODO a great candidate for using reg exps
2296 const wxChar
*wxDateTime::ParseRfc822Date(const wxChar
* date
)
2298 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
2300 const wxChar
*p
= date
;
2301 const wxChar
*comma
= wxStrchr(p
, _T(','));
2304 // the part before comma is the weekday
2306 // skip it for now - we don't use but might check that it really
2307 // corresponds to the specfied date
2310 if ( *p
!= _T(' ') )
2312 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
2314 return (wxChar
*)NULL
;
2320 // the following 1 or 2 digits are the day number
2321 if ( !wxIsdigit(*p
) )
2323 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
2325 return (wxChar
*)NULL
;
2328 wxDateTime_t day
= *p
++ - _T('0');
2329 if ( wxIsdigit(*p
) )
2332 day
+= *p
++ - _T('0');
2335 if ( *p
++ != _T(' ') )
2337 return (wxChar
*)NULL
;
2340 // the following 3 letters specify the month
2341 wxString
monName(p
, 3);
2343 if ( monName
== _T("Jan") )
2345 else if ( monName
== _T("Feb") )
2347 else if ( monName
== _T("Mar") )
2349 else if ( monName
== _T("Apr") )
2351 else if ( monName
== _T("May") )
2353 else if ( monName
== _T("Jun") )
2355 else if ( monName
== _T("Jul") )
2357 else if ( monName
== _T("Aug") )
2359 else if ( monName
== _T("Sep") )
2361 else if ( monName
== _T("Oct") )
2363 else if ( monName
== _T("Nov") )
2365 else if ( monName
== _T("Dec") )
2369 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str());
2371 return (wxChar
*)NULL
;
2376 if ( *p
++ != _T(' ') )
2378 return (wxChar
*)NULL
;
2382 if ( !wxIsdigit(*p
) )
2385 return (wxChar
*)NULL
;
2388 int year
= *p
++ - _T('0');
2390 if ( !wxIsdigit(*p
) )
2392 // should have at least 2 digits in the year
2393 return (wxChar
*)NULL
;
2397 year
+= *p
++ - _T('0');
2399 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
2400 if ( wxIsdigit(*p
) )
2403 year
+= *p
++ - _T('0');
2405 if ( !wxIsdigit(*p
) )
2407 // no 3 digit years please
2408 return (wxChar
*)NULL
;
2412 year
+= *p
++ - _T('0');
2415 if ( *p
++ != _T(' ') )
2417 return (wxChar
*)NULL
;
2420 // time is in the format hh:mm:ss and seconds are optional
2421 if ( !wxIsdigit(*p
) )
2423 return (wxChar
*)NULL
;
2426 wxDateTime_t hour
= *p
++ - _T('0');
2428 if ( !wxIsdigit(*p
) )
2430 return (wxChar
*)NULL
;
2434 hour
+= *p
++ - _T('0');
2436 if ( *p
++ != _T(':') )
2438 return (wxChar
*)NULL
;
2441 if ( !wxIsdigit(*p
) )
2443 return (wxChar
*)NULL
;
2446 wxDateTime_t min
= *p
++ - _T('0');
2448 if ( !wxIsdigit(*p
) )
2450 return (wxChar
*)NULL
;
2454 min
+= *p
++ - _T('0');
2456 wxDateTime_t sec
= 0;
2457 if ( *p
++ == _T(':') )
2459 if ( !wxIsdigit(*p
) )
2461 return (wxChar
*)NULL
;
2464 sec
= *p
++ - _T('0');
2466 if ( !wxIsdigit(*p
) )
2468 return (wxChar
*)NULL
;
2472 sec
+= *p
++ - _T('0');
2475 if ( *p
++ != _T(' ') )
2477 return (wxChar
*)NULL
;
2480 // and now the interesting part: the timezone
2482 if ( *p
== _T('-') || *p
== _T('+') )
2484 // the explicit offset given: it has the form of hhmm
2485 bool plus
= *p
++ == _T('+');
2487 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2489 return (wxChar
*)NULL
;
2493 offset
= 60*(10*(*p
- _T('0')) + (*(p
+ 1) - _T('0')));
2497 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2499 return (wxChar
*)NULL
;
2503 offset
+= 10*(*p
- _T('0')) + (*(p
+ 1) - _T('0'));
2514 // the symbolic timezone given: may be either military timezone or one
2515 // of standard abbreviations
2518 // military: Z = UTC, J unused, A = -1, ..., Y = +12
2519 static const int offsets
[26] =
2521 //A B C D E F G H I J K L M
2522 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
2523 //N O P R Q S T U V W Z Y Z
2524 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
2527 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
2529 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
);
2531 return (wxChar
*)NULL
;
2534 offset
= offsets
[*p
++ - _T('A')];
2540 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
2542 else if ( tz
== _T("AST") )
2543 offset
= AST
- GMT0
;
2544 else if ( tz
== _T("ADT") )
2545 offset
= ADT
- GMT0
;
2546 else if ( tz
== _T("EST") )
2547 offset
= EST
- GMT0
;
2548 else if ( tz
== _T("EDT") )
2549 offset
= EDT
- GMT0
;
2550 else if ( tz
== _T("CST") )
2551 offset
= CST
- GMT0
;
2552 else if ( tz
== _T("CDT") )
2553 offset
= CDT
- GMT0
;
2554 else if ( tz
== _T("MST") )
2555 offset
= MST
- GMT0
;
2556 else if ( tz
== _T("MDT") )
2557 offset
= MDT
- GMT0
;
2558 else if ( tz
== _T("PST") )
2559 offset
= PST
- GMT0
;
2560 else if ( tz
== _T("PDT") )
2561 offset
= PDT
- GMT0
;
2564 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
);
2566 return (wxChar
*)NULL
;
2576 // the spec was correct
2577 Set(day
, mon
, year
, hour
, min
, sec
);
2578 MakeTimezone((wxDateTime_t
)(60*offset
));
2583 const wxChar
*wxDateTime::ParseFormat(const wxChar
*date
,
2584 const wxChar
*format
,
2585 const wxDateTime
& dateDef
)
2587 wxCHECK_MSG( date
&& format
, (wxChar
*)NULL
,
2588 _T("NULL pointer in wxDateTime::ParseFormat()") );
2593 // what fields have we found?
2594 bool haveWDay
= FALSE
,
2603 bool hourIsIn12hFormat
= FALSE
, // or in 24h one?
2604 isPM
= FALSE
; // AM by default
2606 // and the value of the items we have (init them to get rid of warnings)
2607 wxDateTime_t sec
= 0,
2610 WeekDay wday
= Inv_WeekDay
;
2611 wxDateTime_t yday
= 0,
2613 wxDateTime::Month mon
= Inv_Month
;
2616 const wxChar
*input
= date
;
2617 for ( const wxChar
*fmt
= format
; *fmt
; fmt
++ )
2619 if ( *fmt
!= _T('%') )
2621 if ( wxIsspace(*fmt
) )
2623 // a white space in the format string matches 0 or more white
2624 // spaces in the input
2625 while ( wxIsspace(*input
) )
2632 // any other character (not whitespace, not '%') must be
2633 // matched by itself in the input
2634 if ( *input
++ != *fmt
)
2637 return (wxChar
*)NULL
;
2641 // done with this format char
2645 // start of a format specification
2647 // parse the optional width
2649 while ( isdigit(*++fmt
) )
2652 width
+= *fmt
- _T('0');
2655 // the default widths for the various fields
2660 case _T('Y'): // year has 4 digits
2664 case _T('j'): // day of year has 3 digits
2665 case _T('l'): // milliseconds have 3 digits
2669 case _T('w'): // week day as number has only one
2674 // default for all other fields
2679 // then the format itself
2682 case _T('a'): // a weekday name
2685 int flag
= *fmt
== _T('a') ? Name_Abbr
: Name_Full
;
2686 wday
= GetWeekDayFromName(GetAlphaToken(input
), flag
);
2687 if ( wday
== Inv_WeekDay
)
2690 return (wxChar
*)NULL
;
2696 case _T('b'): // a month name
2699 int flag
= *fmt
== _T('b') ? Name_Abbr
: Name_Full
;
2700 mon
= GetMonthFromName(GetAlphaToken(input
), flag
);
2701 if ( mon
== Inv_Month
)
2704 return (wxChar
*)NULL
;
2710 case _T('c'): // locale default date and time representation
2714 // this is the format which corresponds to ctime() output
2715 // and strptime("%c") should parse it, so try it first
2716 static const wxChar
*fmtCtime
= _T("%a %b %d %H:%M:%S %Y");
2718 const wxChar
*result
= dt
.ParseFormat(input
, fmtCtime
);
2721 result
= dt
.ParseFormat(input
, _T("%x %X"));
2726 result
= dt
.ParseFormat(input
, _T("%X %x"));
2731 // we've tried everything and still no match
2732 return (wxChar
*)NULL
;
2737 haveDay
= haveMon
= haveYear
=
2738 haveHour
= haveMin
= haveSec
= TRUE
;
2752 case _T('d'): // day of a month (01-31)
2753 if ( !GetNumericToken(width
, input
, &num
) ||
2754 (num
> 31) || (num
< 1) )
2757 return (wxChar
*)NULL
;
2760 // we can't check whether the day range is correct yet, will
2761 // do it later - assume ok for now
2763 mday
= (wxDateTime_t
)num
;
2766 case _T('H'): // hour in 24h format (00-23)
2767 if ( !GetNumericToken(width
, input
, &num
) || (num
> 23) )
2770 return (wxChar
*)NULL
;
2774 hour
= (wxDateTime_t
)num
;
2777 case _T('I'): // hour in 12h format (01-12)
2778 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
2781 return (wxChar
*)NULL
;
2785 hourIsIn12hFormat
= TRUE
;
2786 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
2789 case _T('j'): // day of the year
2790 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 366) )
2793 return (wxChar
*)NULL
;
2797 yday
= (wxDateTime_t
)num
;
2800 case _T('m'): // month as a number (01-12)
2801 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
2804 return (wxChar
*)NULL
;
2808 mon
= (Month
)(num
- 1);
2811 case _T('M'): // minute as a decimal number (00-59)
2812 if ( !GetNumericToken(width
, input
, &num
) || (num
> 59) )
2815 return (wxChar
*)NULL
;
2819 min
= (wxDateTime_t
)num
;
2822 case _T('p'): // AM or PM string
2824 wxString am
, pm
, token
= GetAlphaToken(input
);
2826 GetAmPmStrings(&am
, &pm
);
2827 if ( token
.CmpNoCase(pm
) == 0 )
2831 else if ( token
.CmpNoCase(am
) != 0 )
2834 return (wxChar
*)NULL
;
2839 case _T('r'): // time as %I:%M:%S %p
2842 input
= dt
.ParseFormat(input
, _T("%I:%M:%S %p"));
2846 return (wxChar
*)NULL
;
2849 haveHour
= haveMin
= haveSec
= TRUE
;
2858 case _T('R'): // time as %H:%M
2861 input
= dt
.ParseFormat(input
, _T("%H:%M"));
2865 return (wxChar
*)NULL
;
2868 haveHour
= haveMin
= TRUE
;
2875 case _T('S'): // second as a decimal number (00-61)
2876 if ( !GetNumericToken(width
, input
, &num
) || (num
> 61) )
2879 return (wxChar
*)NULL
;
2883 sec
= (wxDateTime_t
)num
;
2886 case _T('T'): // time as %H:%M:%S
2889 input
= dt
.ParseFormat(input
, _T("%H:%M:%S"));
2893 return (wxChar
*)NULL
;
2896 haveHour
= haveMin
= haveSec
= TRUE
;
2905 case _T('w'): // weekday as a number (0-6), Sunday = 0
2906 if ( !GetNumericToken(width
, input
, &num
) || (wday
> 6) )
2909 return (wxChar
*)NULL
;
2913 wday
= (WeekDay
)num
;
2916 case _T('x'): // locale default date representation
2917 #ifdef HAVE_STRPTIME
2918 // try using strptime() - it may fail even if the input is
2919 // correct but the date is out of range, so we will fall back
2920 // to our generic code anyhow (FIXME !Unicode friendly)
2923 const wxChar
*result
= strptime(input
, "%x", &tm
);
2928 haveDay
= haveMon
= haveYear
= TRUE
;
2930 year
= 1900 + tm
.tm_year
;
2931 mon
= (Month
)tm
.tm_mon
;
2937 #endif // HAVE_STRPTIME
2939 // TODO query the LOCALE_IDATE setting under Win32
2943 wxString fmtDate
, fmtDateAlt
;
2944 if ( IsWestEuropeanCountry(GetCountry()) ||
2945 GetCountry() == Russia
)
2947 fmtDate
= _T("%d/%m/%y");
2948 fmtDateAlt
= _T("%m/%d/%y");
2952 fmtDate
= _T("%m/%d/%y");
2953 fmtDateAlt
= _T("%d/%m/%y");
2956 const wxChar
*result
= dt
.ParseFormat(input
, fmtDate
);
2960 // ok, be nice and try another one
2961 result
= dt
.ParseFormat(input
, fmtDateAlt
);
2967 return (wxChar
*)NULL
;
2972 haveDay
= haveMon
= haveYear
= TRUE
;
2983 case _T('X'): // locale default time representation
2984 #ifdef HAVE_STRPTIME
2986 // use strptime() to do it for us (FIXME !Unicode friendly)
2988 input
= strptime(input
, "%X", &tm
);
2991 return (wxChar
*)NULL
;
2994 haveHour
= haveMin
= haveSec
= TRUE
;
3000 #else // !HAVE_STRPTIME
3001 // TODO under Win32 we can query the LOCALE_ITIME system
3002 // setting which says whether the default time format is
3005 // try to parse what follows as "%H:%M:%S" and, if this
3006 // fails, as "%I:%M:%S %p" - this should catch the most
3010 const wxChar
*result
= dt
.ParseFormat(input
, _T("%T"));
3013 result
= dt
.ParseFormat(input
, _T("%r"));
3019 return (wxChar
*)NULL
;
3022 haveHour
= haveMin
= haveSec
= TRUE
;
3031 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
3034 case _T('y'): // year without century (00-99)
3035 if ( !GetNumericToken(width
, input
, &num
) || (num
> 99) )
3038 return (wxChar
*)NULL
;
3043 // TODO should have an option for roll over date instead of
3044 // hard coding it here
3045 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
3048 case _T('Y'): // year with century
3049 if ( !GetNumericToken(width
, input
, &num
) )
3052 return (wxChar
*)NULL
;
3056 year
= (wxDateTime_t
)num
;
3059 case _T('Z'): // timezone name
3060 wxFAIL_MSG(_T("TODO"));
3063 case _T('%'): // a percent sign
3064 if ( *input
++ != _T('%') )
3067 return (wxChar
*)NULL
;
3071 case 0: // the end of string
3072 wxFAIL_MSG(_T("unexpected format end"));
3076 default: // not a known format spec
3077 return (wxChar
*)NULL
;
3081 // format matched, try to construct a date from what we have now
3083 if ( dateDef
.IsValid() )
3085 // take this date as default
3086 tmDef
= dateDef
.GetTm();
3088 else if ( IsValid() )
3090 // if this date is valid, don't change it
3095 // no default and this date is invalid - fall back to Today()
3096 tmDef
= Today().GetTm();
3107 // TODO we don't check here that the values are consistent, if both year
3108 // day and month/day were found, we just ignore the year day and we
3109 // also always ignore the week day
3110 if ( haveMon
&& haveDay
)
3112 if ( mday
> GetNumOfDaysInMonth(tm
.year
, mon
) )
3114 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
3116 return (wxChar
*)NULL
;
3122 else if ( haveYDay
)
3124 if ( yday
> GetNumberOfDays(tm
.year
) )
3126 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
3128 return (wxChar
*)NULL
;
3131 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
3138 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
3140 // translate to 24hour format
3143 //else: either already in 24h format or no translation needed
3166 const wxChar
*wxDateTime::ParseDateTime(const wxChar
*date
)
3168 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3170 // there is a public domain version of getdate.y, but it only works for
3172 wxFAIL_MSG(_T("TODO"));
3174 return (wxChar
*)NULL
;
3177 const wxChar
*wxDateTime::ParseDate(const wxChar
*date
)
3179 // this is a simplified version of ParseDateTime() which understands only
3180 // "today" (for wxDate compatibility) and digits only otherwise (and not
3181 // all esoteric constructions ParseDateTime() knows about)
3183 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3185 const wxChar
*p
= date
;
3186 while ( wxIsspace(*p
) )
3189 // some special cases
3193 int dayDiffFromToday
;
3196 { wxTRANSLATE("today"), 0 },
3197 { wxTRANSLATE("yesterday"), -1 },
3198 { wxTRANSLATE("tomorrow"), 1 },
3201 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
3203 wxString date
= wxGetTranslation(literalDates
[n
].str
);
3204 size_t len
= date
.length();
3205 if ( wxStrlen(p
) >= len
&& (wxString(p
, len
).CmpNoCase(date
) == 0) )
3207 // nothing can follow this, so stop here
3210 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
3212 if ( dayDiffFromToday
)
3214 *this += wxDateSpan::Days(dayDiffFromToday
);
3221 // We try to guess what we have here: for each new (numeric) token, we
3222 // determine if it can be a month, day or a year. Of course, there is an
3223 // ambiguity as some numbers may be days as well as months, so we also
3224 // have the ability to back track.
3227 bool haveDay
= FALSE
, // the months day?
3228 haveWDay
= FALSE
, // the day of week?
3229 haveMon
= FALSE
, // the month?
3230 haveYear
= FALSE
; // the year?
3232 // and the value of the items we have (init them to get rid of warnings)
3233 WeekDay wday
= Inv_WeekDay
;
3234 wxDateTime_t day
= 0;
3235 wxDateTime::Month mon
= Inv_Month
;
3238 // tokenize the string
3240 static const wxChar
*dateDelimiters
= _T(".,/-\t\r\n ");
3241 wxStringTokenizer
tok(p
, dateDelimiters
);
3242 while ( tok
.HasMoreTokens() )
3244 wxString token
= tok
.GetNextToken();
3250 if ( token
.ToULong(&val
) )
3252 // guess what this number is
3258 if ( !haveMon
&& val
> 0 && val
<= 12 )
3260 // assume it is month
3263 else // not the month
3265 wxDateTime_t maxDays
= haveMon
3266 ? GetNumOfDaysInMonth(haveYear
? year
: Inv_Year
, mon
)
3270 if ( (val
== 0) || (val
> (unsigned long)maxDays
) ) // cast to shut up compiler warning in BCC
3287 year
= (wxDateTime_t
)val
;
3296 day
= (wxDateTime_t
)val
;
3302 mon
= (Month
)(val
- 1);
3305 else // not a number
3307 // be careful not to overwrite the current mon value
3308 Month mon2
= GetMonthFromName(token
, Name_Full
| Name_Abbr
);
3309 if ( mon2
!= Inv_Month
)
3314 // but we already have a month - maybe we guessed wrong?
3317 // no need to check in month range as always < 12, but
3318 // the days are counted from 1 unlike the months
3319 day
= (wxDateTime_t
)mon
+ 1;
3324 // could possible be the year (doesn't the year come
3325 // before the month in the japanese format?) (FIXME)
3334 else // not a valid month name
3336 wday
= GetWeekDayFromName(token
, Name_Full
| Name_Abbr
);
3337 if ( wday
!= Inv_WeekDay
)
3347 else // not a valid weekday name
3350 static const wxChar
*ordinals
[] =
3352 wxTRANSLATE("first"),
3353 wxTRANSLATE("second"),
3354 wxTRANSLATE("third"),
3355 wxTRANSLATE("fourth"),
3356 wxTRANSLATE("fifth"),
3357 wxTRANSLATE("sixth"),
3358 wxTRANSLATE("seventh"),
3359 wxTRANSLATE("eighth"),
3360 wxTRANSLATE("ninth"),
3361 wxTRANSLATE("tenth"),
3362 wxTRANSLATE("eleventh"),
3363 wxTRANSLATE("twelfth"),
3364 wxTRANSLATE("thirteenth"),
3365 wxTRANSLATE("fourteenth"),
3366 wxTRANSLATE("fifteenth"),
3367 wxTRANSLATE("sixteenth"),
3368 wxTRANSLATE("seventeenth"),
3369 wxTRANSLATE("eighteenth"),
3370 wxTRANSLATE("nineteenth"),
3371 wxTRANSLATE("twentieth"),
3372 // that's enough - otherwise we'd have problems with
3373 // composite (or not) ordinals
3377 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
3379 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
3385 if ( n
== WXSIZEOF(ordinals
) )
3387 // stop here - something unknown
3394 // don't try anything here (as in case of numeric day
3395 // above) - the symbolic day spec should always
3396 // precede the month/year
3402 day
= (wxDateTime_t
)(n
+ 1);
3407 nPosCur
= tok
.GetPosition();
3410 // either no more tokens or the scan was stopped by something we couldn't
3411 // parse - in any case, see if we can construct a date from what we have
3412 if ( !haveDay
&& !haveWDay
)
3414 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
3416 return (wxChar
*)NULL
;
3419 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
3420 !(haveDay
&& haveMon
&& haveYear
) )
3422 // without adjectives (which we don't support here) the week day only
3423 // makes sense completely separately or with the full date
3424 // specification (what would "Wed 1999" mean?)
3425 return (wxChar
*)NULL
;
3428 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
3430 // may be we have month and day instead of day and year?
3431 if ( haveDay
&& !haveMon
)
3435 // exchange day and month
3436 mon
= (wxDateTime::Month
)(day
- 1);
3438 // we're in the current year then
3440 (unsigned)year
<= GetNumOfDaysInMonth(Inv_Year
, mon
) )
3447 //else: no, can't exchange, leave haveMon == FALSE
3453 // if we give the year, month and day must be given too
3454 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
3456 return (wxChar
*)NULL
;
3462 mon
= GetCurrentMonth();
3467 year
= GetCurrentYear();
3472 Set(day
, mon
, year
);
3476 // check that it is really the same
3477 if ( GetWeekDay() != wday
)
3479 // inconsistency detected
3480 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
3482 return (wxChar
*)NULL
;
3490 SetToWeekDayInSameWeek(wday
);
3493 // return the pointer to the first unparsed char
3495 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
3497 // if we couldn't parse the token after the delimiter, put back the
3498 // delimiter as well
3505 const wxChar
*wxDateTime::ParseTime(const wxChar
*time
)
3507 wxCHECK_MSG( time
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3509 // first try some extra things
3516 { wxTRANSLATE("noon"), 12 },
3517 { wxTRANSLATE("midnight"), 00 },
3521 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
3523 wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
3524 size_t len
= timeString
.length();
3525 if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 )
3527 Set(stdTimes
[n
].hour
, 0, 0);
3533 // try all time formats we may think about in the order from longest to
3536 // 12hour with AM/PM?
3537 const wxChar
*result
= ParseFormat(time
, _T("%I:%M:%S %p"));
3541 // normally, it's the same, but why not try it?
3542 result
= ParseFormat(time
, _T("%H:%M:%S"));
3547 // 12hour with AM/PM but without seconds?
3548 result
= ParseFormat(time
, _T("%I:%M %p"));
3554 result
= ParseFormat(time
, _T("%H:%M"));
3559 // just the hour and AM/PM?
3560 result
= ParseFormat(time
, _T("%I %p"));
3566 result
= ParseFormat(time
, _T("%H"));
3571 // parse the standard format: normally it is one of the formats above
3572 // but it may be set to something completely different by the user
3573 result
= ParseFormat(time
, _T("%X"));
3576 // TODO: parse timezones
3581 // ----------------------------------------------------------------------------
3582 // Workdays and holidays support
3583 // ----------------------------------------------------------------------------
3585 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
3587 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
3590 // ============================================================================
3592 // ============================================================================
3594 // this enum is only used in wxTimeSpan::Format() below but we can't declare
3595 // it locally to the method as it provokes an internal compiler error in egcs
3596 // 2.91.60 when building with -O2
3607 // not all strftime(3) format specifiers make sense here because, for example,
3608 // a time span doesn't have a year nor a timezone
3610 // Here are the ones which are supported (all of them are supported by strftime
3612 // %H hour in 24 hour format
3613 // %M minute (00 - 59)
3614 // %S second (00 - 59)
3617 // Also, for MFC CTimeSpan compatibility, we support
3618 // %D number of days
3620 // And, to be better than MFC :-), we also have
3621 // %E number of wEeks
3622 // %l milliseconds (000 - 999)
3623 wxString
wxTimeSpan::Format(const wxChar
*format
) const
3625 wxCHECK_MSG( format
, _T(""), _T("NULL format in wxTimeSpan::Format") );
3628 str
.Alloc(wxStrlen(format
));
3630 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
3632 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
3633 // question is what should ts.Format("%S") do? The code here returns "3273"
3634 // in this case (i.e. the total number of seconds, not just seconds % 60)
3635 // because, for me, this call means "give me entire time interval in
3636 // seconds" and not "give me the seconds part of the time interval"
3638 // If we agree that it should behave like this, it is clear that the
3639 // interpretation of each format specifier depends on the presence of the
3640 // other format specs in the string: if there was "%H" before "%M", we
3641 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
3643 // we remember the most important unit found so far
3644 TimeSpanPart partBiggest
= Part_MSec
;
3646 for ( const wxChar
*pch
= format
; *pch
; pch
++ )
3650 if ( ch
== _T('%') )
3652 // the start of the format specification of the printf() below
3653 wxString fmtPrefix
= _T('%');
3658 ch
= *++pch
; // get the format spec char
3662 wxFAIL_MSG( _T("invalid format character") );
3668 // skip the part below switch
3673 if ( partBiggest
< Part_Day
)
3679 partBiggest
= Part_Day
;
3684 partBiggest
= Part_Week
;
3690 if ( partBiggest
< Part_Hour
)
3696 partBiggest
= Part_Hour
;
3699 fmtPrefix
+= _T("02");
3703 n
= GetMilliseconds().ToLong();
3704 if ( partBiggest
< Part_MSec
)
3708 //else: no need to reset partBiggest to Part_MSec, it is
3709 // the least significant one anyhow
3711 fmtPrefix
+= _T("03");
3716 if ( partBiggest
< Part_Min
)
3722 partBiggest
= Part_Min
;
3725 fmtPrefix
+= _T("02");
3729 n
= GetSeconds().ToLong();
3730 if ( partBiggest
< Part_Sec
)
3736 partBiggest
= Part_Sec
;
3739 fmtPrefix
+= _T("02");
3743 str
+= wxString::Format(fmtPrefix
+ _T("ld"), n
);
3747 // normal character, just copy
3755 // ============================================================================
3756 // wxDateTimeHolidayAuthority and related classes
3757 // ============================================================================
3759 #include "wx/arrimpl.cpp"
3761 WX_DEFINE_OBJARRAY(wxDateTimeArray
);
3763 static int wxCMPFUNC_CONV
3764 wxDateTimeCompareFunc(wxDateTime
**first
, wxDateTime
**second
)
3766 wxDateTime dt1
= **first
,
3769 return dt1
== dt2
? 0 : dt1
< dt2
? -1 : +1;
3772 // ----------------------------------------------------------------------------
3773 // wxDateTimeHolidayAuthority
3774 // ----------------------------------------------------------------------------
3776 wxHolidayAuthoritiesArray
wxDateTimeHolidayAuthority::ms_authorities
;
3779 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime
& dt
)
3781 size_t count
= ms_authorities
.GetCount();
3782 for ( size_t n
= 0; n
< count
; n
++ )
3784 if ( ms_authorities
[n
]->DoIsHoliday(dt
) )
3795 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime
& dtStart
,
3796 const wxDateTime
& dtEnd
,
3797 wxDateTimeArray
& holidays
)
3799 wxDateTimeArray hol
;
3803 size_t count
= ms_authorities
.GetCount();
3804 for ( size_t nAuth
= 0; nAuth
< count
; nAuth
++ )
3806 ms_authorities
[nAuth
]->DoGetHolidaysInRange(dtStart
, dtEnd
, hol
);
3808 WX_APPEND_ARRAY(holidays
, hol
);
3811 holidays
.Sort(wxDateTimeCompareFunc
);
3813 return holidays
.GetCount();
3817 void wxDateTimeHolidayAuthority::ClearAllAuthorities()
3819 WX_CLEAR_ARRAY(ms_authorities
);
3823 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority
*auth
)
3825 ms_authorities
.Add(auth
);
3828 // ----------------------------------------------------------------------------
3829 // wxDateTimeWorkDays
3830 // ----------------------------------------------------------------------------
3832 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime
& dt
) const
3834 wxDateTime::WeekDay wd
= dt
.GetWeekDay();
3836 return (wd
== wxDateTime::Sun
) || (wd
== wxDateTime::Sat
);
3839 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime
& dtStart
,
3840 const wxDateTime
& dtEnd
,
3841 wxDateTimeArray
& holidays
) const
3843 if ( dtStart
> dtEnd
)
3845 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
3852 // instead of checking all days, start with the first Sat after dtStart and
3853 // end with the last Sun before dtEnd
3854 wxDateTime dtSatFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sat
),
3855 dtSatLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sat
),
3856 dtSunFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sun
),
3857 dtSunLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sun
),
3860 for ( dt
= dtSatFirst
; dt
<= dtSatLast
; dt
+= wxDateSpan::Week() )
3865 for ( dt
= dtSunFirst
; dt
<= dtSunLast
; dt
+= wxDateSpan::Week() )
3870 return holidays
.GetCount();
3873 #endif // wxUSE_DATETIME