1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/datetime.cpp
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 ///////////////////////////////////////////////////////////////////////////////
19 // TODO: for $DEITY sake, someone please fix the #ifdef __WXWINCE__ everywhere,
20 // the proper way to do it is to implement (subset of) wxStrftime() for
21 // CE instead of this horror!!
24 * Implementation notes:
26 * 1. the time is stored as a 64bit integer containing the signed number of
27 * milliseconds since Jan 1. 1970 (the Unix Epoch) - so it is always
30 * 2. the range is thus something about 580 million years, but due to current
31 * algorithms limitations, only dates from Nov 24, 4714BC are handled
33 * 3. standard ANSI C functions are used to do time calculations whenever
34 * possible, i.e. when the date is in the range Jan 1, 1970 to 2038
36 * 4. otherwise, the calculations are done by converting the date to/from JDN
37 * first (the range limitation mentioned above comes from here: the
38 * algorithm used by Scott E. Lee's code only works for positive JDNs, more
41 * 5. the object constructed for the given DD-MM-YYYY HH:MM:SS corresponds to
42 * this moment in local time and may be converted to the object
43 * corresponding to the same date/time in another time zone by using
46 * 6. the conversions to the current (or any other) timezone are done when the
47 * internal time representation is converted to the broken-down one in
51 // ============================================================================
53 // ============================================================================
55 // ----------------------------------------------------------------------------
57 // ----------------------------------------------------------------------------
59 // For compilers that support precompilation, includes "wx.h".
60 #include "wx/wxprec.h"
66 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
70 #include "wx/msw/wrapwin.h"
72 #include "wx/string.h"
75 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
76 #include "wx/module.h"
79 #include "wx/thread.h"
80 #include "wx/tokenzr.h"
91 #include "wx/datetime.h"
93 const long wxDateTime::TIME_T_FACTOR
= 1000l;
95 #if wxUSE_EXTENDED_RTTI
97 template<> void wxStringReadValue(const wxString
&s
, wxDateTime
&data
)
99 data
.ParseFormat(s
,wxT("%Y-%m-%d %H:%M:%S")) ;
102 template<> void wxStringWriteValue(wxString
&s
, const wxDateTime
&data
)
104 s
= data
.Format(wxT("%Y-%m-%d %H:%M:%S")) ;
107 wxCUSTOM_TYPE_INFO(wxDateTime
, wxToStringConverter
<wxDateTime
> , wxFromStringConverter
<wxDateTime
>)
112 // ----------------------------------------------------------------------------
113 // conditional compilation
114 // ----------------------------------------------------------------------------
116 #if defined(HAVE_STRPTIME) && defined(__GLIBC__) && \
117 ((__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0))
118 // glibc 2.0.7 strptime() is broken - the following snippet causes it to
119 // crash (instead of just failing):
121 // strncpy(buf, "Tue Dec 21 20:25:40 1999", 128);
122 // strptime(buf, "%x", &tm);
126 #endif // broken strptime()
128 #if defined(HAVE_STRPTIME) && defined(__DARWIN__) && defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
129 // configure detects strptime as linkable because it's in the OS X
130 // System library but MSL headers don't declare it.
132 // char *strptime(const char *, const char *, struct tm *);
133 // However, we DON'T want to just provide it here because we would
134 // crash and/or overwrite data when strptime from OS X tries
135 // to fill in MW's struct tm which is two fields shorter (no TZ stuff)
136 // So for now let's just say we don't have strptime
140 #if defined(__MWERKS__) && wxUSE_UNICODE
144 // define a special symbol for VC8 instead of writing tests for 1400 repeatedly
146 #if __VISUALC__ >= 1400
151 #if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM)
152 #if defined(__WXPALMOS__)
153 #define WX_GMTOFF_IN_TM
154 #elif defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
155 #define WX_TIMEZONE _timezone
156 #elif defined(__MWERKS__)
157 long wxmw_timezone
= 28800;
158 #define WX_TIMEZONE wxmw_timezone
159 #elif defined(__DJGPP__) || defined(__WINE__)
160 #include <sys/timeb.h>
162 static long wxGetTimeZone()
164 static long timezone
= MAXLONG
; // invalid timezone
165 if (timezone
== MAXLONG
)
169 timezone
= tb
.timezone
;
173 #define WX_TIMEZONE wxGetTimeZone()
174 #elif defined(__DARWIN__)
175 #define WX_GMTOFF_IN_TM
176 #elif defined(__WXWINCE__) && defined(__VISUALC8__)
177 #define WX_TIMEZONE _timezone
178 #else // unknown platform - try timezone
179 #define WX_TIMEZONE timezone
181 #endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
183 // NB: VC8 safe time functions could/should be used for wxMSW as well probably
184 #if defined(__WXWINCE__) && defined(__VISUALC8__)
186 struct tm
*wxLocaltime_r(const time_t *t
, struct tm
* tm
)
189 return _localtime64_s(tm
, &t64
) == 0 ? tm
: NULL
;
192 struct tm
*wxGmtime_r(const time_t* t
, struct tm
* tm
)
195 return _gmtime64_s(tm
, &t64
) == 0 ? tm
: NULL
;
198 #else // !wxWinCE with VC8
200 #if (!defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)) && wxUSE_THREADS && !defined(__WINDOWS__)
201 static wxMutex timeLock
;
204 #ifndef HAVE_LOCALTIME_R
205 struct tm
*wxLocaltime_r(const time_t* ticks
, struct tm
* temp
)
207 #if wxUSE_THREADS && !defined(__WINDOWS__)
208 // No need to waste time with a mutex on windows since it's using
209 // thread local storage for localtime anyway.
210 wxMutexLocker
locker(timeLock
);
212 memcpy(temp
, localtime(ticks
), sizeof(struct tm
));
215 #endif // !HAVE_LOCALTIME_R
217 #ifndef HAVE_GMTIME_R
218 struct tm
*wxGmtime_r(const time_t* ticks
, struct tm
* temp
)
220 #if wxUSE_THREADS && !defined(__WINDOWS__)
221 // No need to waste time with a mutex on windows since it's
222 // using thread local storage for gmtime anyway.
223 wxMutexLocker
locker(timeLock
);
225 memcpy(temp
, gmtime(ticks
), sizeof(struct tm
));
228 #endif // !HAVE_GMTIME_R
230 #endif // wxWinCE with VC8/other platforms
232 // ----------------------------------------------------------------------------
234 // ----------------------------------------------------------------------------
236 // debugging helper: just a convenient replacement of wxCHECK()
237 #define wxDATETIME_CHECK(expr, msg) \
238 wxCHECK2_MSG(expr, *this = wxInvalidDateTime; return *this, msg)
240 // ----------------------------------------------------------------------------
242 // ----------------------------------------------------------------------------
244 class wxDateTimeHolidaysModule
: public wxModule
247 virtual bool OnInit()
249 wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays
);
254 virtual void OnExit()
256 wxDateTimeHolidayAuthority::ClearAllAuthorities();
257 wxDateTimeHolidayAuthority::ms_authorities
.clear();
261 DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule
)
264 IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule
, wxModule
)
266 // ----------------------------------------------------------------------------
268 // ----------------------------------------------------------------------------
271 static const int MONTHS_IN_YEAR
= 12;
273 static const int SEC_PER_MIN
= 60;
275 static const int MIN_PER_HOUR
= 60;
277 static const int HOURS_PER_DAY
= 24;
279 static const long SECONDS_PER_DAY
= 86400l;
281 static const int DAYS_PER_WEEK
= 7;
283 static const long MILLISECONDS_PER_DAY
= 86400000l;
285 // this is the integral part of JDN of the midnight of Jan 1, 1970
286 // (i.e. JDN(Jan 1, 1970) = 2440587.5)
287 static const long EPOCH_JDN
= 2440587l;
289 // used only in asserts
291 // the date of JDN -0.5 (as we don't work with fractional parts, this is the
292 // reference date for us) is Nov 24, 4714BC
293 static const int JDN_0_YEAR
= -4713;
294 static const int JDN_0_MONTH
= wxDateTime::Nov
;
295 static const int JDN_0_DAY
= 24;
296 #endif // __WXDEBUG__
298 // the constants used for JDN calculations
299 static const long JDN_OFFSET
= 32046l;
300 static const long DAYS_PER_5_MONTHS
= 153l;
301 static const long DAYS_PER_4_YEARS
= 1461l;
302 static const long DAYS_PER_400_YEARS
= 146097l;
304 // this array contains the cumulated number of days in all previous months for
305 // normal and leap years
306 static const wxDateTime::wxDateTime_t gs_cumulatedDays
[2][MONTHS_IN_YEAR
] =
308 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
309 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
312 // ----------------------------------------------------------------------------
314 // ----------------------------------------------------------------------------
316 const wxChar
* wxDefaultDateTimeFormat
= wxT("%c");
317 const wxChar
* wxDefaultTimeSpanFormat
= wxT("%H:%M:%S");
319 // in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
320 // indicate an invalid wxDateTime object
321 const wxDateTime wxDefaultDateTime
;
323 wxDateTime::Country
wxDateTime::ms_country
= wxDateTime::Country_Unknown
;
325 // ----------------------------------------------------------------------------
327 // ----------------------------------------------------------------------------
329 // debugger helper: shows what the date really is
331 extern const wxChar
*wxDumpDate(const wxDateTime
* dt
)
333 static wxChar buf
[128];
335 wxStrcpy(buf
, dt
->Format(_T("%Y-%m-%d (%a) %H:%M:%S")));
341 // get the number of days in the given month of the given year
343 wxDateTime::wxDateTime_t
GetNumOfDaysInMonth(int year
, wxDateTime::Month month
)
345 // the number of days in month in Julian/Gregorian calendar: the first line
346 // is for normal years, the second one is for the leap ones
347 static wxDateTime::wxDateTime_t daysInMonth
[2][MONTHS_IN_YEAR
] =
349 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
350 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
353 return daysInMonth
[wxDateTime::IsLeapYear(year
)][month
];
356 // returns the time zone in the C sense, i.e. the difference UTC - local
358 static int GetTimeZone()
360 // set to true when the timezone is set
361 static bool s_timezoneSet
= false;
362 static long gmtoffset
= LONG_MAX
; // invalid timezone
364 // ensure that the timezone variable is set by calling wxLocaltime_r
365 if ( !s_timezoneSet
)
367 // just call wxLocaltime_r() instead of figuring out whether this
368 // system supports tzset(), _tzset() or something else
372 wxLocaltime_r(&t
, &tm
);
373 s_timezoneSet
= true;
375 #ifdef WX_GMTOFF_IN_TM
376 // note that GMT offset is the opposite of time zone and so to return
377 // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM
378 // cases we have to negate it
379 gmtoffset
= -tm
.tm_gmtoff
;
380 #else // !WX_GMTOFF_IN_TM
381 gmtoffset
= WX_TIMEZONE
;
382 #endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
385 return (int)gmtoffset
;
388 // return the integral part of the JDN for the midnight of the given date (to
389 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
390 // noon of the previous day)
391 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day
,
392 wxDateTime::Month mon
,
395 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
397 // check the date validity
399 (year
> JDN_0_YEAR
) ||
400 ((year
== JDN_0_YEAR
) && (mon
> JDN_0_MONTH
)) ||
401 ((year
== JDN_0_YEAR
) && (mon
== JDN_0_MONTH
) && (day
>= JDN_0_DAY
)),
402 _T("date out of range - can't convert to JDN")
405 // make the year positive to avoid problems with negative numbers division
408 // months are counted from March here
410 if ( mon
>= wxDateTime::Mar
)
420 // now we can simply add all the contributions together
421 return ((year
/ 100) * DAYS_PER_400_YEARS
) / 4
422 + ((year
% 100) * DAYS_PER_4_YEARS
) / 4
423 + (month
* DAYS_PER_5_MONTHS
+ 2) / 5
429 // this function is a wrapper around strftime(3) adding error checking
430 static wxString
CallStrftime(const wxChar
*format
, const tm
* tm
)
433 // Create temp wxString here to work around mingw/cygwin bug 1046059
434 // http://sourceforge.net/tracker/?func=detail&atid=102435&aid=1046059&group_id=2435
437 if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) )
439 // buffer is too small?
440 wxFAIL_MSG(_T("strftime() failed"));
450 #if wxUSE_UNIX && !defined(HAVE_STRPTIME_DECL)
451 // configure detected that we had strptime() but not its declaration,
452 // provide it ourselves
453 extern "C" char *strptime(const char *, const char *, struct tm
*);
456 // Unicode-friendly strptime() wrapper
457 static const wxChar
*
458 CallStrptime(const wxChar
*input
, const char *fmt
, tm
*tm
)
460 // the problem here is that strptime() returns pointer into the string we
461 // passed to it while we're really interested in the pointer into the
462 // original, Unicode, string so we try to transform the pointer back
464 wxCharBuffer
inputMB(wxConvertWX2MB(input
));
466 const char * const inputMB
= input
;
467 #endif // Unicode/Ascii
469 const char *result
= strptime(inputMB
, fmt
, tm
);
474 // FIXME: this is wrong in presence of surrogates &c
475 return input
+ (result
- inputMB
.data());
478 #endif // Unicode/Ascii
481 #endif // HAVE_STRPTIME
483 // if year and/or month have invalid values, replace them with the current ones
484 static void ReplaceDefaultYearMonthWithCurrent(int *year
,
485 wxDateTime::Month
*month
)
487 struct tm
*tmNow
= NULL
;
490 if ( *year
== wxDateTime::Inv_Year
)
492 tmNow
= wxDateTime::GetTmNow(&tmstruct
);
494 *year
= 1900 + tmNow
->tm_year
;
497 if ( *month
== wxDateTime::Inv_Month
)
500 tmNow
= wxDateTime::GetTmNow(&tmstruct
);
502 *month
= (wxDateTime::Month
)tmNow
->tm_mon
;
506 // fll the struct tm with default values
507 static void InitTm(struct tm
& tm
)
509 // struct tm may have etxra fields (undocumented and with unportable
510 // names) which, nevertheless, must be set to 0
511 memset(&tm
, 0, sizeof(struct tm
));
513 tm
.tm_mday
= 1; // mday 0 is invalid
514 tm
.tm_year
= 76; // any valid year
515 tm
.tm_isdst
= -1; // auto determine
521 // return the month if the string is a month name or Inv_Month otherwise
522 static wxDateTime::Month
GetMonthFromName(const wxString
& name
, int flags
)
524 wxDateTime::Month mon
;
525 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
527 // case-insensitive comparison either one of or with both abbreviated
529 if ( flags
& wxDateTime::Name_Full
)
531 if ( name
.CmpNoCase(wxDateTime::
532 GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 )
538 if ( flags
& wxDateTime::Name_Abbr
)
540 if ( name
.CmpNoCase(wxDateTime::
541 GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 )
551 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
552 static wxDateTime::WeekDay
GetWeekDayFromName(const wxString
& name
, int flags
)
554 wxDateTime::WeekDay wd
;
555 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
557 // case-insensitive comparison either one of or with both abbreviated
559 if ( flags
& wxDateTime::Name_Full
)
561 if ( name
.CmpNoCase(wxDateTime::
562 GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 )
568 if ( flags
& wxDateTime::Name_Abbr
)
570 if ( name
.CmpNoCase(wxDateTime::
571 GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 )
582 struct tm
*wxDateTime::GetTmNow(struct tm
*tmstruct
)
584 time_t t
= GetTimeNow();
585 return wxLocaltime_r(&t
, tmstruct
);
588 // scans all digits (but no more than len) and returns the resulting number
589 static bool GetNumericToken(size_t len
, const wxChar
*& p
, unsigned long *number
)
593 while ( wxIsdigit(*p
) )
597 if ( len
&& ++n
> len
)
601 return !s
.empty() && s
.ToULong(number
);
604 // scans all alphabetic characters and returns the resulting string
605 static wxString
GetAlphaToken(const wxChar
*& p
)
608 while ( wxIsalpha(*p
) )
616 // ============================================================================
617 // implementation of wxDateTime
618 // ============================================================================
620 // ----------------------------------------------------------------------------
622 // ----------------------------------------------------------------------------
626 year
= (wxDateTime_t
)wxDateTime::Inv_Year
;
627 mon
= wxDateTime::Inv_Month
;
629 hour
= min
= sec
= msec
= 0;
630 wday
= wxDateTime::Inv_WeekDay
;
633 wxDateTime::Tm::Tm(const struct tm
& tm
, const TimeZone
& tz
)
637 sec
= (wxDateTime::wxDateTime_t
)tm
.tm_sec
;
638 min
= (wxDateTime::wxDateTime_t
)tm
.tm_min
;
639 hour
= (wxDateTime::wxDateTime_t
)tm
.tm_hour
;
640 mday
= (wxDateTime::wxDateTime_t
)tm
.tm_mday
;
641 mon
= (wxDateTime::Month
)tm
.tm_mon
;
642 year
= 1900 + tm
.tm_year
;
643 wday
= (wxDateTime::wxDateTime_t
)tm
.tm_wday
;
644 yday
= (wxDateTime::wxDateTime_t
)tm
.tm_yday
;
647 bool wxDateTime::Tm::IsValid() const
649 // we allow for the leap seconds, although we don't use them (yet)
650 return (year
!= wxDateTime::Inv_Year
) && (mon
!= wxDateTime::Inv_Month
) &&
651 (mday
<= GetNumOfDaysInMonth(year
, mon
)) &&
652 (hour
< 24) && (min
< 60) && (sec
< 62) && (msec
< 1000);
655 void wxDateTime::Tm::ComputeWeekDay()
657 // compute the week day from day/month/year: we use the dumbest algorithm
658 // possible: just compute our JDN and then use the (simple to derive)
659 // formula: weekday = (JDN + 1.5) % 7
660 wday
= (wxDateTime::wxDateTime_t
)((wxDateTime::WeekDay
)(GetTruncatedJDN(mday
, mon
, year
) + 2) % 7);
663 void wxDateTime::Tm::AddMonths(int monDiff
)
665 // normalize the months field
666 while ( monDiff
< -mon
)
670 monDiff
+= MONTHS_IN_YEAR
;
673 while ( monDiff
+ mon
>= MONTHS_IN_YEAR
)
677 monDiff
-= MONTHS_IN_YEAR
;
680 mon
= (wxDateTime::Month
)(mon
+ monDiff
);
682 wxASSERT_MSG( mon
>= 0 && mon
< MONTHS_IN_YEAR
, _T("logic error") );
684 // NB: we don't check here that the resulting date is valid, this function
685 // is private and the caller must check it if needed
688 void wxDateTime::Tm::AddDays(int dayDiff
)
690 // normalize the days field
691 while ( dayDiff
+ mday
< 1 )
695 dayDiff
+= GetNumOfDaysInMonth(year
, mon
);
698 mday
= (wxDateTime::wxDateTime_t
)( mday
+ dayDiff
);
699 while ( mday
> GetNumOfDaysInMonth(year
, mon
) )
701 mday
-= GetNumOfDaysInMonth(year
, mon
);
706 wxASSERT_MSG( mday
> 0 && mday
<= GetNumOfDaysInMonth(year
, mon
),
710 // ----------------------------------------------------------------------------
712 // ----------------------------------------------------------------------------
714 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz
)
718 case wxDateTime::Local
:
719 // get the offset from C RTL: it returns the difference GMT-local
720 // while we want to have the offset _from_ GMT, hence the '-'
721 m_offset
= -GetTimeZone();
724 case wxDateTime::GMT_12
:
725 case wxDateTime::GMT_11
:
726 case wxDateTime::GMT_10
:
727 case wxDateTime::GMT_9
:
728 case wxDateTime::GMT_8
:
729 case wxDateTime::GMT_7
:
730 case wxDateTime::GMT_6
:
731 case wxDateTime::GMT_5
:
732 case wxDateTime::GMT_4
:
733 case wxDateTime::GMT_3
:
734 case wxDateTime::GMT_2
:
735 case wxDateTime::GMT_1
:
736 m_offset
= -3600*(wxDateTime::GMT0
- tz
);
739 case wxDateTime::GMT0
:
740 case wxDateTime::GMT1
:
741 case wxDateTime::GMT2
:
742 case wxDateTime::GMT3
:
743 case wxDateTime::GMT4
:
744 case wxDateTime::GMT5
:
745 case wxDateTime::GMT6
:
746 case wxDateTime::GMT7
:
747 case wxDateTime::GMT8
:
748 case wxDateTime::GMT9
:
749 case wxDateTime::GMT10
:
750 case wxDateTime::GMT11
:
751 case wxDateTime::GMT12
:
752 case wxDateTime::GMT13
:
753 m_offset
= 3600*(tz
- wxDateTime::GMT0
);
756 case wxDateTime::A_CST
:
757 // Central Standard Time in use in Australia = UTC + 9.5
758 m_offset
= 60l*(9*MIN_PER_HOUR
+ MIN_PER_HOUR
/2);
762 wxFAIL_MSG( _T("unknown time zone") );
766 // ----------------------------------------------------------------------------
768 // ----------------------------------------------------------------------------
771 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
)
773 if ( year
== Inv_Year
)
774 year
= GetCurrentYear();
776 if ( cal
== Gregorian
)
778 // in Gregorian calendar leap years are those divisible by 4 except
779 // those divisible by 100 unless they're also divisible by 400
780 // (in some countries, like Russia and Greece, additional corrections
781 // exist, but they won't manifest themselves until 2700)
782 return (year
% 4 == 0) && ((year
% 100 != 0) || (year
% 400 == 0));
784 else if ( cal
== Julian
)
786 // in Julian calendar the rule is simpler
787 return year
% 4 == 0;
791 wxFAIL_MSG(_T("unknown calendar"));
798 int wxDateTime::GetCentury(int year
)
800 return year
> 0 ? year
/ 100 : year
/ 100 - 1;
804 int wxDateTime::ConvertYearToBC(int year
)
807 return year
> 0 ? year
: year
- 1;
811 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
)
816 return Now().GetYear();
819 wxFAIL_MSG(_T("TODO"));
823 wxFAIL_MSG(_T("unsupported calendar"));
831 wxDateTime::Month
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
)
836 return Now().GetMonth();
839 wxFAIL_MSG(_T("TODO"));
843 wxFAIL_MSG(_T("unsupported calendar"));
851 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(int year
, Calendar cal
)
853 if ( year
== Inv_Year
)
855 // take the current year if none given
856 year
= GetCurrentYear();
863 return IsLeapYear(year
) ? 366 : 365;
866 wxFAIL_MSG(_T("unsupported calendar"));
874 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(wxDateTime::Month month
,
876 wxDateTime::Calendar cal
)
878 wxCHECK_MSG( month
< MONTHS_IN_YEAR
, 0, _T("invalid month") );
880 if ( cal
== Gregorian
|| cal
== Julian
)
882 if ( year
== Inv_Year
)
884 // take the current year if none given
885 year
= GetCurrentYear();
888 return GetNumOfDaysInMonth(year
, month
);
892 wxFAIL_MSG(_T("unsupported calendar"));
899 wxString
wxDateTime::GetMonthName(wxDateTime::Month month
,
900 wxDateTime::NameFlags flags
)
902 wxCHECK_MSG( month
!= Inv_Month
, wxEmptyString
, _T("invalid month") );
904 // notice that we must set all the fields to avoid confusing libc (GNU one
905 // gets confused to a crash if we don't do this)
910 return CallStrftime(flags
== Name_Abbr
? _T("%b") : _T("%B"), &tm
);
916 ret
= (flags
== Name_Abbr
? wxT("Jan"): wxT("January"));
919 ret
= (flags
== Name_Abbr
? wxT("Feb"): wxT("Febuary"));
922 ret
= (flags
== Name_Abbr
? wxT("Mar"): wxT("March"));
925 ret
= (flags
== Name_Abbr
? wxT("Apr"): wxT("April"));
928 ret
= (flags
== Name_Abbr
? wxT("May"): wxT("May"));
931 ret
= (flags
== Name_Abbr
? wxT("Jun"): wxT("June"));
934 ret
= (flags
== Name_Abbr
? wxT("Jul"): wxT("July"));
937 ret
= (flags
== Name_Abbr
? wxT("Aug"): wxT("August"));
940 ret
= (flags
== Name_Abbr
? wxT("Sep"): wxT("September"));
943 ret
= (flags
== Name_Abbr
? wxT("Oct"): wxT("October"));
946 ret
= (flags
== Name_Abbr
? wxT("Nov"): wxT("November"));
949 ret
= (flags
== Name_Abbr
? wxT("Dec"): wxT("December"));
957 wxString
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
,
958 wxDateTime::NameFlags flags
)
960 wxCHECK_MSG( wday
!= Inv_WeekDay
, wxEmptyString
, _T("invalid weekday") );
962 // take some arbitrary Sunday (but notice that the day should be such that
963 // after adding wday to it below we still have a valid date, e.g. don't
971 // and offset it by the number of days needed to get the correct wday
974 // call mktime() to normalize it...
977 // ... and call strftime()
978 return CallStrftime(flags
== Name_Abbr
? _T("%a") : _T("%A"), &tm
);
984 ret
= (flags
== Name_Abbr
? wxT("Sun") : wxT("Sunday"));
987 ret
= (flags
== Name_Abbr
? wxT("Mon") : wxT("Monday"));
990 ret
= (flags
== Name_Abbr
? wxT("Tue") : wxT("Tuesday"));
993 ret
= (flags
== Name_Abbr
? wxT("Wed") : wxT("Wednesday"));
996 ret
= (flags
== Name_Abbr
? wxT("Thu") : wxT("Thursday"));
999 ret
= (flags
== Name_Abbr
? wxT("Fri") : wxT("Friday"));
1002 ret
= (flags
== Name_Abbr
? wxT("Sat") : wxT("Saturday"));
1011 void wxDateTime::GetAmPmStrings(wxString
*am
, wxString
*pm
)
1016 // @Note: Do not call 'CallStrftime' here! CallStrftime checks the return code
1017 // and causes an assertion failed if the buffer is to small (which is good) - OR -
1018 // if strftime does not return anything because the format string is invalid - OR -
1019 // if there are no 'am' / 'pm' tokens defined for the current locale (which is not good).
1020 // wxDateTime::ParseTime will try several different formats to parse the time.
1021 // As a result, GetAmPmStrings might get called, even if the current locale
1022 // does not define any 'am' / 'pm' tokens. In this case, wxStrftime would
1023 // assert, even though it is a perfectly legal use.
1026 if (wxStrftime(buffer
, sizeof(buffer
)/sizeof(wxChar
), _T("%p"), &tm
) > 0)
1027 *am
= wxString(buffer
);
1034 if (wxStrftime(buffer
, sizeof(buffer
)/sizeof(wxChar
), _T("%p"), &tm
) > 0)
1035 *pm
= wxString(buffer
);
1041 // ----------------------------------------------------------------------------
1042 // Country stuff: date calculations depend on the country (DST, work days,
1043 // ...), so we need to know which rules to follow.
1044 // ----------------------------------------------------------------------------
1047 wxDateTime::Country
wxDateTime::GetCountry()
1049 // TODO use LOCALE_ICOUNTRY setting under Win32
1051 if ( ms_country
== Country_Unknown
)
1053 // try to guess from the time zone name
1054 time_t t
= time(NULL
);
1056 struct tm
*tm
= wxLocaltime_r(&t
, &tmstruct
);
1058 wxString tz
= CallStrftime(_T("%Z"), tm
);
1059 if ( tz
== _T("WET") || tz
== _T("WEST") )
1063 else if ( tz
== _T("CET") || tz
== _T("CEST") )
1065 ms_country
= Country_EEC
;
1067 else if ( tz
== _T("MSK") || tz
== _T("MSD") )
1069 ms_country
= Russia
;
1071 else if ( tz
== _T("AST") || tz
== _T("ADT") ||
1072 tz
== _T("EST") || tz
== _T("EDT") ||
1073 tz
== _T("CST") || tz
== _T("CDT") ||
1074 tz
== _T("MST") || tz
== _T("MDT") ||
1075 tz
== _T("PST") || tz
== _T("PDT") )
1081 // well, choose a default one
1093 void wxDateTime::SetCountry(wxDateTime::Country country
)
1095 ms_country
= country
;
1099 bool wxDateTime::IsWestEuropeanCountry(Country country
)
1101 if ( country
== Country_Default
)
1103 country
= GetCountry();
1106 return (Country_WesternEurope_Start
<= country
) &&
1107 (country
<= Country_WesternEurope_End
);
1110 // ----------------------------------------------------------------------------
1111 // DST calculations: we use 3 different rules for the West European countries,
1112 // USA and for the rest of the world. This is undoubtedly false for many
1113 // countries, but I lack the necessary info (and the time to gather it),
1114 // please add the other rules here!
1115 // ----------------------------------------------------------------------------
1118 bool wxDateTime::IsDSTApplicable(int year
, Country country
)
1120 if ( year
== Inv_Year
)
1122 // take the current year if none given
1123 year
= GetCurrentYear();
1126 if ( country
== Country_Default
)
1128 country
= GetCountry();
1135 // DST was first observed in the US and UK during WWI, reused
1136 // during WWII and used again since 1966
1137 return year
>= 1966 ||
1138 (year
>= 1942 && year
<= 1945) ||
1139 (year
== 1918 || year
== 1919);
1142 // assume that it started after WWII
1148 wxDateTime
wxDateTime::GetBeginDST(int year
, Country country
)
1150 if ( year
== Inv_Year
)
1152 // take the current year if none given
1153 year
= GetCurrentYear();
1156 if ( country
== Country_Default
)
1158 country
= GetCountry();
1161 if ( !IsDSTApplicable(year
, country
) )
1163 return wxInvalidDateTime
;
1168 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1170 // DST begins at 1 a.m. GMT on the last Sunday of March
1171 if ( !dt
.SetToLastWeekDay(Sun
, Mar
, year
) )
1174 wxFAIL_MSG( _T("no last Sunday in March?") );
1177 dt
+= wxTimeSpan::Hours(1);
1179 // disable DST tests because it could result in an infinite recursion!
1182 else switch ( country
)
1189 // don't know for sure - assume it was in effect all year
1194 dt
.Set(1, Jan
, year
);
1198 // DST was installed Feb 2, 1942 by the Congress
1199 dt
.Set(2, Feb
, year
);
1202 // Oil embargo changed the DST period in the US
1204 dt
.Set(6, Jan
, 1974);
1208 dt
.Set(23, Feb
, 1975);
1212 // before 1986, DST begun on the last Sunday of April, but
1213 // in 1986 Reagan changed it to begin at 2 a.m. of the
1214 // first Sunday in April
1217 if ( !dt
.SetToLastWeekDay(Sun
, Apr
, year
) )
1220 wxFAIL_MSG( _T("no first Sunday in April?") );
1225 if ( !dt
.SetToWeekDay(Sun
, 1, Apr
, year
) )
1228 wxFAIL_MSG( _T("no first Sunday in April?") );
1232 dt
+= wxTimeSpan::Hours(2);
1234 // TODO what about timezone??
1240 // assume Mar 30 as the start of the DST for the rest of the world
1241 // - totally bogus, of course
1242 dt
.Set(30, Mar
, year
);
1249 wxDateTime
wxDateTime::GetEndDST(int year
, Country country
)
1251 if ( year
== Inv_Year
)
1253 // take the current year if none given
1254 year
= GetCurrentYear();
1257 if ( country
== Country_Default
)
1259 country
= GetCountry();
1262 if ( !IsDSTApplicable(year
, country
) )
1264 return wxInvalidDateTime
;
1269 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1271 // DST ends at 1 a.m. GMT on the last Sunday of October
1272 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1274 // weirder and weirder...
1275 wxFAIL_MSG( _T("no last Sunday in October?") );
1278 dt
+= wxTimeSpan::Hours(1);
1280 // disable DST tests because it could result in an infinite recursion!
1283 else switch ( country
)
1290 // don't know for sure - assume it was in effect all year
1294 dt
.Set(31, Dec
, year
);
1298 // the time was reset after the end of the WWII
1299 dt
.Set(30, Sep
, year
);
1303 // DST ends at 2 a.m. on the last Sunday of October
1304 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1306 // weirder and weirder...
1307 wxFAIL_MSG( _T("no last Sunday in October?") );
1310 dt
+= wxTimeSpan::Hours(2);
1312 // TODO what about timezone??
1317 // assume October 26th as the end of the DST - totally bogus too
1318 dt
.Set(26, Oct
, year
);
1324 // ----------------------------------------------------------------------------
1325 // constructors and assignment operators
1326 // ----------------------------------------------------------------------------
1328 // return the current time with ms precision
1329 /* static */ wxDateTime
wxDateTime::UNow()
1331 return wxDateTime(wxGetLocalTimeMillis());
1334 // the values in the tm structure contain the local time
1335 wxDateTime
& wxDateTime::Set(const struct tm
& tm
)
1338 time_t timet
= mktime(&tm2
);
1340 if ( timet
== (time_t)-1 )
1342 // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
1343 // less than timezone - try to make it work for this case
1344 if ( tm2
.tm_year
== 70 && tm2
.tm_mon
== 0 && tm2
.tm_mday
== 1 )
1346 return Set((time_t)(
1348 tm2
.tm_hour
* MIN_PER_HOUR
* SEC_PER_MIN
+
1349 tm2
.tm_min
* SEC_PER_MIN
+
1353 wxFAIL_MSG( _T("mktime() failed") );
1355 *this = wxInvalidDateTime
;
1365 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
,
1366 wxDateTime_t minute
,
1367 wxDateTime_t second
,
1368 wxDateTime_t millisec
)
1370 // we allow seconds to be 61 to account for the leap seconds, even if we
1371 // don't use them really
1372 wxDATETIME_CHECK( hour
< 24 &&
1376 _T("Invalid time in wxDateTime::Set()") );
1378 // get the current date from system
1380 struct tm
*tm
= GetTmNow(&tmstruct
);
1382 wxDATETIME_CHECK( tm
, _T("wxLocaltime_r() failed") );
1384 // make a copy so it isn't clobbered by the call to mktime() below
1389 tm1
.tm_min
= minute
;
1390 tm1
.tm_sec
= second
;
1392 // and the DST in case it changes on this date
1395 if ( tm2
.tm_isdst
!= tm1
.tm_isdst
)
1396 tm1
.tm_isdst
= tm2
.tm_isdst
;
1400 // and finally adjust milliseconds
1401 return SetMillisecond(millisec
);
1404 wxDateTime
& wxDateTime::Set(wxDateTime_t day
,
1408 wxDateTime_t minute
,
1409 wxDateTime_t second
,
1410 wxDateTime_t millisec
)
1412 wxDATETIME_CHECK( hour
< 24 &&
1416 _T("Invalid time in wxDateTime::Set()") );
1418 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1420 wxDATETIME_CHECK( (0 < day
) && (day
<= GetNumberOfDays(month
, year
)),
1421 _T("Invalid date in wxDateTime::Set()") );
1423 // the range of time_t type (inclusive)
1424 static const int yearMinInRange
= 1970;
1425 static const int yearMaxInRange
= 2037;
1427 // test only the year instead of testing for the exact end of the Unix
1428 // time_t range - it doesn't bring anything to do more precise checks
1429 if ( year
>= yearMinInRange
&& year
<= yearMaxInRange
)
1431 // use the standard library version if the date is in range - this is
1432 // probably more efficient than our code
1434 tm
.tm_year
= year
- 1900;
1440 tm
.tm_isdst
= -1; // mktime() will guess it
1444 // and finally adjust milliseconds
1446 SetMillisecond(millisec
);
1452 // do time calculations ourselves: we want to calculate the number of
1453 // milliseconds between the given date and the epoch
1455 // get the JDN for the midnight of this day
1456 m_time
= GetTruncatedJDN(day
, month
, year
);
1457 m_time
-= EPOCH_JDN
;
1458 m_time
*= SECONDS_PER_DAY
* TIME_T_FACTOR
;
1460 // JDN corresponds to GMT, we take localtime
1461 Add(wxTimeSpan(hour
, minute
, second
+ GetTimeZone(), millisec
));
1467 wxDateTime
& wxDateTime::Set(double jdn
)
1469 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1471 jdn
-= EPOCH_JDN
+ 0.5;
1473 m_time
.Assign(jdn
*MILLISECONDS_PER_DAY
);
1475 // JDNs always are in UTC, so we don't need any adjustments for time zone
1480 wxDateTime
& wxDateTime::ResetTime()
1484 if ( tm
.hour
|| tm
.min
|| tm
.sec
|| tm
.msec
)
1497 // ----------------------------------------------------------------------------
1498 // DOS Date and Time Format functions
1499 // ----------------------------------------------------------------------------
1500 // the dos date and time value is an unsigned 32 bit value in the format:
1501 // YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
1503 // Y = year offset from 1980 (0-127)
1505 // D = day of month (1-31)
1507 // m = minute (0-59)
1508 // s = bisecond (0-29) each bisecond indicates two seconds
1509 // ----------------------------------------------------------------------------
1511 wxDateTime
& wxDateTime::SetFromDOS(unsigned long ddt
)
1516 long year
= ddt
& 0xFE000000;
1521 long month
= ddt
& 0x1E00000;
1526 long day
= ddt
& 0x1F0000;
1530 long hour
= ddt
& 0xF800;
1534 long minute
= ddt
& 0x7E0;
1538 long second
= ddt
& 0x1F;
1539 tm
.tm_sec
= second
* 2;
1541 return Set(mktime(&tm
));
1544 unsigned long wxDateTime::GetAsDOS() const
1547 time_t ticks
= GetTicks();
1549 struct tm
*tm
= wxLocaltime_r(&ticks
, &tmstruct
);
1551 long year
= tm
->tm_year
;
1555 long month
= tm
->tm_mon
;
1559 long day
= tm
->tm_mday
;
1562 long hour
= tm
->tm_hour
;
1565 long minute
= tm
->tm_min
;
1568 long second
= tm
->tm_sec
;
1571 ddt
= year
| month
| day
| hour
| minute
| second
;
1575 // ----------------------------------------------------------------------------
1576 // time_t <-> broken down time conversions
1577 // ----------------------------------------------------------------------------
1579 wxDateTime::Tm
wxDateTime::GetTm(const TimeZone
& tz
) const
1581 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1583 time_t time
= GetTicks();
1584 if ( time
!= (time_t)-1 )
1586 // use C RTL functions
1589 if ( tz
.GetOffset() == -GetTimeZone() )
1591 // we are working with local time
1592 tm
= wxLocaltime_r(&time
, &tmstruct
);
1594 // should never happen
1595 wxCHECK_MSG( tm
, Tm(), _T("wxLocaltime_r() failed") );
1599 time
+= (time_t)tz
.GetOffset();
1600 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
1601 int time2
= (int) time
;
1607 tm
= wxGmtime_r(&time
, &tmstruct
);
1609 // should never happen
1610 wxCHECK_MSG( tm
, Tm(), _T("wxGmtime_r() failed") );
1614 tm
= (struct tm
*)NULL
;
1620 // adjust the milliseconds
1622 long timeOnly
= (m_time
% MILLISECONDS_PER_DAY
).ToLong();
1623 tm2
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1626 //else: use generic code below
1629 // remember the time and do the calculations with the date only - this
1630 // eliminates rounding errors of the floating point arithmetics
1632 wxLongLong timeMidnight
= m_time
+ tz
.GetOffset() * 1000;
1634 long timeOnly
= (timeMidnight
% MILLISECONDS_PER_DAY
).ToLong();
1636 // we want to always have positive time and timeMidnight to be really
1637 // the midnight before it
1640 timeOnly
= MILLISECONDS_PER_DAY
+ timeOnly
;
1643 timeMidnight
-= timeOnly
;
1645 // calculate the Gregorian date from JDN for the midnight of our date:
1646 // this will yield day, month (in 1..12 range) and year
1648 // actually, this is the JDN for the noon of the previous day
1649 long jdn
= (timeMidnight
/ MILLISECONDS_PER_DAY
).ToLong() + EPOCH_JDN
;
1651 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1653 wxASSERT_MSG( jdn
> -2, _T("JDN out of range") );
1655 // calculate the century
1656 long temp
= (jdn
+ JDN_OFFSET
) * 4 - 1;
1657 long century
= temp
/ DAYS_PER_400_YEARS
;
1659 // then the year and day of year (1 <= dayOfYear <= 366)
1660 temp
= ((temp
% DAYS_PER_400_YEARS
) / 4) * 4 + 3;
1661 long year
= (century
* 100) + (temp
/ DAYS_PER_4_YEARS
);
1662 long dayOfYear
= (temp
% DAYS_PER_4_YEARS
) / 4 + 1;
1664 // and finally the month and day of the month
1665 temp
= dayOfYear
* 5 - 3;
1666 long month
= temp
/ DAYS_PER_5_MONTHS
;
1667 long day
= (temp
% DAYS_PER_5_MONTHS
) / 5 + 1;
1669 // month is counted from March - convert to normal
1680 // year is offset by 4800
1683 // check that the algorithm gave us something reasonable
1684 wxASSERT_MSG( (0 < month
) && (month
<= 12), _T("invalid month") );
1685 wxASSERT_MSG( (1 <= day
) && (day
< 32), _T("invalid day") );
1687 // construct Tm from these values
1689 tm
.year
= (int)year
;
1690 tm
.mon
= (Month
)(month
- 1); // algorithm yields 1 for January, not 0
1691 tm
.mday
= (wxDateTime_t
)day
;
1692 tm
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1693 timeOnly
-= tm
.msec
;
1694 timeOnly
/= 1000; // now we have time in seconds
1696 tm
.sec
= (wxDateTime_t
)(timeOnly
% SEC_PER_MIN
);
1698 timeOnly
/= SEC_PER_MIN
; // now we have time in minutes
1700 tm
.min
= (wxDateTime_t
)(timeOnly
% MIN_PER_HOUR
);
1703 tm
.hour
= (wxDateTime_t
)(timeOnly
/ MIN_PER_HOUR
);
1708 wxDateTime
& wxDateTime::SetYear(int year
)
1710 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1719 wxDateTime
& wxDateTime::SetMonth(Month month
)
1721 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1730 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
)
1732 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1741 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
)
1743 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1752 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
)
1754 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1763 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
)
1765 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1774 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
)
1776 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1778 // we don't need to use GetTm() for this one
1779 m_time
-= m_time
% 1000l;
1780 m_time
+= millisecond
;
1785 // ----------------------------------------------------------------------------
1786 // wxDateTime arithmetics
1787 // ----------------------------------------------------------------------------
1789 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
)
1793 tm
.year
+= diff
.GetYears();
1794 tm
.AddMonths(diff
.GetMonths());
1796 // check that the resulting date is valid
1797 if ( tm
.mday
> GetNumOfDaysInMonth(tm
.year
, tm
.mon
) )
1799 // We suppose that when adding one month to Jan 31 we want to get Feb
1800 // 28 (or 29), i.e. adding a month to the last day of the month should
1801 // give the last day of the next month which is quite logical.
1803 // Unfortunately, there is no logic way to understand what should
1804 // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1805 // We make it Feb 28 (last day too), but it is highly questionable.
1806 tm
.mday
= GetNumOfDaysInMonth(tm
.year
, tm
.mon
);
1809 tm
.AddDays(diff
.GetTotalDays());
1813 wxASSERT_MSG( IsSameTime(tm
),
1814 _T("Add(wxDateSpan) shouldn't modify time") );
1819 // ----------------------------------------------------------------------------
1820 // Weekday and monthday stuff
1821 // ----------------------------------------------------------------------------
1823 // convert Sun, Mon, ..., Sat into 6, 0, ..., 5
1824 static inline int ConvertWeekDayToMondayBase(int wd
)
1826 return wd
== wxDateTime::Sun
? 6 : wd
- 1;
1831 wxDateTime::SetToWeekOfYear(int year
, wxDateTime_t numWeek
, WeekDay wd
)
1833 wxASSERT_MSG( numWeek
> 0,
1834 _T("invalid week number: weeks are counted from 1") );
1836 // Jan 4 always lies in the 1st week of the year
1837 wxDateTime
dt(4, Jan
, year
);
1838 dt
.SetToWeekDayInSameWeek(wd
);
1839 dt
+= wxDateSpan::Weeks(numWeek
- 1);
1844 #if WXWIN_COMPATIBILITY_2_6
1845 // use a separate function to avoid warnings about using deprecated
1846 // SetToTheWeek in GetWeek below
1848 SetToTheWeek(int year
,
1849 wxDateTime::wxDateTime_t numWeek
,
1850 wxDateTime::WeekDay weekday
,
1851 wxDateTime::WeekFlags flags
)
1853 // Jan 4 always lies in the 1st week of the year
1854 wxDateTime
dt(4, wxDateTime::Jan
, year
);
1855 dt
.SetToWeekDayInSameWeek(weekday
, flags
);
1856 dt
+= wxDateSpan::Weeks(numWeek
- 1);
1861 bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek
,
1865 int year
= GetYear();
1866 *this = ::SetToTheWeek(year
, numWeek
, weekday
, flags
);
1867 if ( GetYear() != year
)
1869 // oops... numWeek was too big
1876 wxDateTime
wxDateTime::GetWeek(wxDateTime_t numWeek
,
1878 WeekFlags flags
) const
1880 return ::SetToTheWeek(GetYear(), numWeek
, weekday
, flags
);
1882 #endif // WXWIN_COMPATIBILITY_2_6
1884 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
,
1887 // take the current month/year if none specified
1888 if ( year
== Inv_Year
)
1890 if ( month
== Inv_Month
)
1893 return Set(GetNumOfDaysInMonth(year
, month
), month
, year
);
1896 wxDateTime
& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday
, WeekFlags flags
)
1898 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1900 int wdayDst
= weekday
,
1901 wdayThis
= GetWeekDay();
1902 if ( wdayDst
== wdayThis
)
1908 if ( flags
== Default_First
)
1910 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
1913 // the logic below based on comparing weekday and wdayThis works if Sun (0)
1914 // is the first day in the week, but breaks down for Monday_First case so
1915 // we adjust the week days in this case
1916 if ( flags
== Monday_First
)
1918 if ( wdayThis
== Sun
)
1920 if ( wdayDst
== Sun
)
1923 //else: Sunday_First, nothing to do
1925 // go forward or back in time to the day we want
1926 if ( wdayDst
< wdayThis
)
1928 return Subtract(wxDateSpan::Days(wdayThis
- wdayDst
));
1930 else // weekday > wdayThis
1932 return Add(wxDateSpan::Days(wdayDst
- wdayThis
));
1936 wxDateTime
& wxDateTime::SetToNextWeekDay(WeekDay weekday
)
1938 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1941 WeekDay wdayThis
= GetWeekDay();
1942 if ( weekday
== wdayThis
)
1947 else if ( weekday
< wdayThis
)
1949 // need to advance a week
1950 diff
= 7 - (wdayThis
- weekday
);
1952 else // weekday > wdayThis
1954 diff
= weekday
- wdayThis
;
1957 return Add(wxDateSpan::Days(diff
));
1960 wxDateTime
& wxDateTime::SetToPrevWeekDay(WeekDay weekday
)
1962 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1965 WeekDay wdayThis
= GetWeekDay();
1966 if ( weekday
== wdayThis
)
1971 else if ( weekday
> wdayThis
)
1973 // need to go to previous week
1974 diff
= 7 - (weekday
- wdayThis
);
1976 else // weekday < wdayThis
1978 diff
= wdayThis
- weekday
;
1981 return Subtract(wxDateSpan::Days(diff
));
1984 bool wxDateTime::SetToWeekDay(WeekDay weekday
,
1989 wxCHECK_MSG( weekday
!= Inv_WeekDay
, false, _T("invalid weekday") );
1991 // we don't check explicitly that -5 <= n <= 5 because we will return false
1992 // anyhow in such case - but may be should still give an assert for it?
1994 // take the current month/year if none specified
1995 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1999 // TODO this probably could be optimised somehow...
2003 // get the first day of the month
2004 dt
.Set(1, month
, year
);
2007 WeekDay wdayFirst
= dt
.GetWeekDay();
2009 // go to the first weekday of the month
2010 int diff
= weekday
- wdayFirst
;
2014 // add advance n-1 weeks more
2017 dt
+= wxDateSpan::Days(diff
);
2019 else // count from the end of the month
2021 // get the last day of the month
2022 dt
.SetToLastMonthDay(month
, year
);
2025 WeekDay wdayLast
= dt
.GetWeekDay();
2027 // go to the last weekday of the month
2028 int diff
= wdayLast
- weekday
;
2032 // and rewind n-1 weeks from there
2035 dt
-= wxDateSpan::Days(diff
);
2038 // check that it is still in the same month
2039 if ( dt
.GetMonth() == month
)
2047 // no such day in this month
2053 wxDateTime::wxDateTime_t
GetDayOfYearFromTm(const wxDateTime::Tm
& tm
)
2055 return (wxDateTime::wxDateTime_t
)(gs_cumulatedDays
[wxDateTime::IsLeapYear(tm
.year
)][tm
.mon
] + tm
.mday
);
2058 wxDateTime::wxDateTime_t
wxDateTime::GetDayOfYear(const TimeZone
& tz
) const
2060 return GetDayOfYearFromTm(GetTm(tz
));
2063 wxDateTime::wxDateTime_t
2064 wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags
, const TimeZone
& tz
) const
2066 if ( flags
== Default_First
)
2068 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
2072 wxDateTime_t nDayInYear
= GetDayOfYearFromTm(tm
);
2074 int wdTarget
= GetWeekDay(tz
);
2075 int wdYearStart
= wxDateTime(1, Jan
, GetYear()).GetWeekDay();
2077 if ( flags
== Sunday_First
)
2079 // FIXME: First week is not calculated correctly.
2080 week
= (nDayInYear
- wdTarget
+ 7) / 7;
2081 if ( wdYearStart
== Wed
|| wdYearStart
== Thu
)
2084 else // week starts with monday
2086 // adjust the weekdays to non-US style.
2087 wdYearStart
= ConvertWeekDayToMondayBase(wdYearStart
);
2088 wdTarget
= ConvertWeekDayToMondayBase(wdTarget
);
2090 // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html:
2092 // Week 01 of a year is per definition the first week that has the
2093 // Thursday in this year, which is equivalent to the week that
2094 // contains the fourth day of January. In other words, the first
2095 // week of a new year is the week that has the majority of its
2096 // days in the new year. Week 01 might also contain days from the
2097 // previous year and the week before week 01 of a year is the last
2098 // week (52 or 53) of the previous year even if it contains days
2099 // from the new year. A week starts with Monday (day 1) and ends
2100 // with Sunday (day 7).
2103 // if Jan 1 is Thursday or less, it is in the first week of this year
2104 if ( wdYearStart
< 4 )
2106 // count the number of entire weeks between Jan 1 and this date
2107 week
= (nDayInYear
+ wdYearStart
+ 6 - wdTarget
)/7;
2109 // be careful to check for overflow in the next year
2110 if ( week
== 53 && tm
.mday
- wdTarget
> 28 )
2113 else // Jan 1 is in the last week of the previous year
2115 // check if we happen to be at the last week of previous year:
2116 if ( tm
.mon
== Jan
&& tm
.mday
< 8 - wdYearStart
)
2117 week
= wxDateTime(31, Dec
, GetYear()-1).GetWeekOfYear();
2119 week
= (nDayInYear
+ wdYearStart
- 1 - wdTarget
)/7;
2123 return (wxDateTime::wxDateTime_t
)week
;
2126 wxDateTime::wxDateTime_t
wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags
,
2127 const TimeZone
& tz
) const
2130 wxDateTime dtMonthStart
= wxDateTime(1, tm
.mon
, tm
.year
);
2131 int nWeek
= GetWeekOfYear(flags
) - dtMonthStart
.GetWeekOfYear(flags
) + 1;
2134 // this may happen for January when Jan, 1 is the last week of the
2136 nWeek
+= IsLeapYear(tm
.year
- 1) ? 53 : 52;
2139 return (wxDateTime::wxDateTime_t
)nWeek
;
2142 wxDateTime
& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday
)
2144 int year
= GetYear();
2145 wxDATETIME_CHECK( (0 < yday
) && (yday
<= GetNumberOfDays(year
)),
2146 _T("invalid year day") );
2148 bool isLeap
= IsLeapYear(year
);
2149 for ( Month mon
= Jan
; mon
< Inv_Month
; wxNextMonth(mon
) )
2151 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
2152 // don't need it neither - because of the CHECK above we know that
2153 // yday lies in December then
2154 if ( (mon
== Dec
) || (yday
<= gs_cumulatedDays
[isLeap
][mon
+ 1]) )
2156 Set((wxDateTime::wxDateTime_t
)(yday
- gs_cumulatedDays
[isLeap
][mon
]), mon
, year
);
2165 // ----------------------------------------------------------------------------
2166 // Julian day number conversion and related stuff
2167 // ----------------------------------------------------------------------------
2169 double wxDateTime::GetJulianDayNumber() const
2171 return m_time
.ToDouble() / MILLISECONDS_PER_DAY
+ EPOCH_JDN
+ 0.5;
2174 double wxDateTime::GetRataDie() const
2176 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
2177 return GetJulianDayNumber() - 1721119.5 - 306;
2180 // ----------------------------------------------------------------------------
2181 // timezone and DST stuff
2182 // ----------------------------------------------------------------------------
2184 int wxDateTime::IsDST(wxDateTime::Country country
) const
2186 wxCHECK_MSG( country
== Country_Default
, -1,
2187 _T("country support not implemented") );
2189 // use the C RTL for the dates in the standard range
2190 time_t timet
= GetTicks();
2191 if ( timet
!= (time_t)-1 )
2194 tm
*tm
= wxLocaltime_r(&timet
, &tmstruct
);
2196 wxCHECK_MSG( tm
, -1, _T("wxLocaltime_r() failed") );
2198 return tm
->tm_isdst
;
2202 int year
= GetYear();
2204 if ( !IsDSTApplicable(year
, country
) )
2206 // no DST time in this year in this country
2210 return IsBetween(GetBeginDST(year
, country
), GetEndDST(year
, country
));
2214 wxDateTime
& wxDateTime::MakeTimezone(const TimeZone
& tz
, bool noDST
)
2216 long secDiff
= GetTimeZone() + tz
.GetOffset();
2218 // we need to know whether DST is or not in effect for this date unless
2219 // the test disabled by the caller
2220 if ( !noDST
&& (IsDST() == 1) )
2222 // FIXME we assume that the DST is always shifted by 1 hour
2226 return Add(wxTimeSpan::Seconds(secDiff
));
2229 wxDateTime
& wxDateTime::MakeFromTimezone(const TimeZone
& tz
, bool noDST
)
2231 long secDiff
= GetTimeZone() + tz
.GetOffset();
2233 // we need to know whether DST is or not in effect for this date unless
2234 // the test disabled by the caller
2235 if ( !noDST
&& (IsDST() == 1) )
2237 // FIXME we assume that the DST is always shifted by 1 hour
2241 return Subtract(wxTimeSpan::Seconds(secDiff
));
2244 // ----------------------------------------------------------------------------
2245 // wxDateTime to/from text representations
2246 // ----------------------------------------------------------------------------
2248 wxString
wxDateTime::Format(const wxChar
*format
, const TimeZone
& tz
) const
2250 wxCHECK_MSG( format
, wxEmptyString
, _T("NULL format in wxDateTime::Format") );
2252 // we have to use our own implementation if the date is out of range of
2253 // strftime() or if we use non standard specificators
2254 time_t time
= GetTicks();
2255 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
2260 if ( tz
.GetOffset() == -GetTimeZone() )
2262 // we are working with local time
2263 tm
= wxLocaltime_r(&time
, &tmstruct
);
2265 // should never happen
2266 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxLocaltime_r() failed") );
2270 time
+= (int)tz
.GetOffset();
2272 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
2273 int time2
= (int) time
;
2279 tm
= wxGmtime_r(&time
, &tmstruct
);
2281 // should never happen
2282 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxGmtime_r() failed") );
2286 tm
= (struct tm
*)NULL
;
2290 //Windows CE doesn't support strftime or wcsftime, so we use the generic implementation
2293 return CallStrftime(format
, tm
);
2296 //else: use generic code below
2299 // we only parse ANSI C format specifications here, no POSIX 2
2300 // complications, no GNU extensions but we do add support for a "%l" format
2301 // specifier allowing to get the number of milliseconds
2304 // used for calls to strftime() when we only deal with time
2305 struct tm tmTimeOnly
;
2306 tmTimeOnly
.tm_hour
= tm
.hour
;
2307 tmTimeOnly
.tm_min
= tm
.min
;
2308 tmTimeOnly
.tm_sec
= tm
.sec
;
2309 tmTimeOnly
.tm_wday
= 0;
2310 tmTimeOnly
.tm_yday
= 0;
2311 tmTimeOnly
.tm_mday
= 1; // any date will do
2312 tmTimeOnly
.tm_mon
= 0;
2313 tmTimeOnly
.tm_year
= 76;
2314 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
2316 wxString tmp
, res
, fmt
;
2317 for ( const wxChar
*p
= format
; *p
; p
++ )
2319 if ( *p
!= _T('%') )
2327 // set the default format
2330 case _T('Y'): // year has 4 digits
2334 case _T('j'): // day of year has 3 digits
2335 case _T('l'): // milliseconds have 3 digits
2339 case _T('w'): // week day as number has only one
2344 // it's either another valid format specifier in which case
2345 // the format is "%02d" (for all the rest) or we have the
2346 // field width preceding the format in which case it will
2347 // override the default format anyhow
2351 bool restart
= true;
2356 // start of the format specification
2359 case _T('a'): // a weekday name
2361 // second parameter should be true for abbreviated names
2362 res
+= GetWeekDayName(tm
.GetWeekDay(),
2363 *p
== _T('a') ? Name_Abbr
: Name_Full
);
2366 case _T('b'): // a month name
2368 res
+= GetMonthName(tm
.mon
,
2369 *p
== _T('b') ? Name_Abbr
: Name_Full
);
2372 case _T('c'): // locale default date and time representation
2373 case _T('x'): // locale default date representation
2376 // the problem: there is no way to know what do these format
2377 // specifications correspond to for the current locale.
2379 // the solution: use a hack and still use strftime(): first
2380 // find the YEAR which is a year in the strftime() range (1970
2381 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
2382 // of the real year. Then make a copy of the format and
2383 // replace all occurrences of YEAR in it with some unique
2384 // string not appearing anywhere else in it, then use
2385 // strftime() to format the date in year YEAR and then replace
2386 // YEAR back by the real year and the unique replacement
2387 // string back with YEAR. Notice that "all occurrences of YEAR"
2388 // means all occurrences of 4 digit as well as 2 digit form!
2390 // the bugs: we assume that neither of %c nor %x contains any
2391 // fields which may change between the YEAR and real year. For
2392 // example, the week number (%U, %W) and the day number (%j)
2393 // will change if one of these years is leap and the other one
2396 // find the YEAR: normally, for any year X, Jan 1 or the
2397 // year X + 28 is the same weekday as Jan 1 of X (because
2398 // the weekday advances by 1 for each normal X and by 2
2399 // for each leap X, hence by 5 every 4 years or by 35
2400 // which is 0 mod 7 every 28 years) but this rule breaks
2401 // down if there are years between X and Y which are
2402 // divisible by 4 but not leap (i.e. divisible by 100 but
2403 // not 400), hence the correction.
2405 int yearReal
= GetYear(tz
);
2406 int mod28
= yearReal
% 28;
2408 // be careful to not go too far - we risk to leave the
2413 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
2417 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
2420 int nCentury
= year
/ 100,
2421 nCenturyReal
= yearReal
/ 100;
2423 // need to adjust for the years divisble by 400 which are
2424 // not leap but are counted like leap ones if we just take
2425 // the number of centuries in between for nLostWeekDays
2426 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
2427 (nCentury
/ 4 - nCenturyReal
/ 4);
2429 // we have to gain back the "lost" weekdays: note that the
2430 // effect of this loop is to not do anything to
2431 // nLostWeekDays (which we won't use any more), but to
2432 // (indirectly) set the year correctly
2433 while ( (nLostWeekDays
% 7) != 0 )
2435 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
2438 // Keep year below 2000 so the 2digit year number
2439 // can never match the month or day of the month
2440 if (year
>=2000) year
-=28;
2441 // at any rate, we couldn't go further than 1988 + 9 + 28!
2442 wxASSERT_MSG( year
< 2030,
2443 _T("logic error in wxDateTime::Format") );
2445 wxString strYear
, strYear2
;
2446 strYear
.Printf(_T("%d"), year
);
2447 strYear2
.Printf(_T("%d"), year
% 100);
2449 // find four strings not occurring in format (this is surely
2450 // not the optimal way of doing it... improvements welcome!)
2451 wxString fmt2
= format
;
2452 wxString replacement
,replacement2
,replacement3
,replacement4
;
2453 for (int rnr
=1; rnr
<5 ; rnr
++)
2455 wxString r
= (wxChar
)-rnr
;
2456 while ( fmt2
.Find(r
) != wxNOT_FOUND
)
2463 case 1: replacement
=r
; break;
2464 case 2: replacement2
=r
; break;
2465 case 3: replacement3
=r
; break;
2466 case 4: replacement4
=r
; break;
2469 // replace all occurrences of year with it
2470 bool wasReplaced
= fmt2
.Replace(strYear
, replacement
) > 0;
2471 // evaluation order ensures we always attempt the replacement.
2472 wasReplaced
= (fmt2
.Replace(strYear2
, replacement2
) > 0) | wasReplaced
;
2474 // use strftime() to format the same date but in supported
2477 // NB: we assume that strftime() doesn't check for the
2478 // date validity and will happily format the date
2479 // corresponding to Feb 29 of a non leap year (which
2480 // may happen if yearReal was leap and year is not)
2481 struct tm tmAdjusted
;
2483 tmAdjusted
.tm_hour
= tm
.hour
;
2484 tmAdjusted
.tm_min
= tm
.min
;
2485 tmAdjusted
.tm_sec
= tm
.sec
;
2486 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
2487 tmAdjusted
.tm_yday
= GetDayOfYear();
2488 tmAdjusted
.tm_mday
= tm
.mday
;
2489 tmAdjusted
.tm_mon
= tm
.mon
;
2490 tmAdjusted
.tm_year
= year
- 1900;
2491 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
2492 wxString str
= CallStrftime(*p
== _T('c') ? _T("%c")
2496 // now replace the occurrence of 1999 with the real year
2497 // we do this in two stages to stop the 2 digit year
2498 // matching any substring of the 4 digit year.
2499 // Any day,month hours and minutes components should be safe due
2500 // to ensuring the range of the years.
2501 wxString strYearReal
, strYearReal2
;
2502 strYearReal
.Printf(_T("%04d"), yearReal
);
2503 strYearReal2
.Printf(_T("%02d"), yearReal
% 100);
2504 str
.Replace(strYear
, replacement3
);
2505 str
.Replace(strYear2
,replacement4
);
2506 str
.Replace(replacement3
, strYearReal
);
2507 str
.Replace(replacement4
, strYearReal2
);
2509 // and replace back all occurrences of replacement string
2512 str
.Replace(replacement2
, strYear2
);
2513 str
.Replace(replacement
, strYear
);
2519 //Use "%m/%d/%y %H:%M:%S" format instead
2520 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
2521 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
2525 case _T('d'): // day of a month (01-31)
2526 res
+= wxString::Format(fmt
, tm
.mday
);
2529 case _T('H'): // hour in 24h format (00-23)
2530 res
+= wxString::Format(fmt
, tm
.hour
);
2533 case _T('I'): // hour in 12h format (01-12)
2535 // 24h -> 12h, 0h -> 12h too
2536 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
2537 : tm
.hour
? tm
.hour
: 12;
2538 res
+= wxString::Format(fmt
, hour12
);
2542 case _T('j'): // day of the year
2543 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
2546 case _T('l'): // milliseconds (NOT STANDARD)
2547 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
2550 case _T('m'): // month as a number (01-12)
2551 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
2554 case _T('M'): // minute as a decimal number (00-59)
2555 res
+= wxString::Format(fmt
, tm
.min
);
2558 case _T('p'): // AM or PM string
2560 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
2562 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
2566 case _T('S'): // second as a decimal number (00-61)
2567 res
+= wxString::Format(fmt
, tm
.sec
);
2570 case _T('U'): // week number in the year (Sunday 1st week day)
2571 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
2574 case _T('W'): // week number in the year (Monday 1st week day)
2575 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
2578 case _T('w'): // weekday as a number (0-6), Sunday = 0
2579 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
2582 // case _T('x'): -- handled with "%c"
2584 case _T('X'): // locale default time representation
2585 // just use strftime() to format the time for us
2587 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
2589 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
2593 case _T('y'): // year without century (00-99)
2594 res
+= wxString::Format(fmt
, tm
.year
% 100);
2597 case _T('Y'): // year with century
2598 res
+= wxString::Format(fmt
, tm
.year
);
2601 case _T('Z'): // timezone name
2603 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
2608 // is it the format width?
2610 while ( *p
== _T('-') || *p
== _T('+') ||
2611 *p
== _T(' ') || wxIsdigit(*p
) )
2618 // we've only got the flags and width so far in fmt
2619 fmt
.Prepend(_T('%'));
2620 fmt
.Append(_T('d'));
2627 // no, it wasn't the width
2628 wxFAIL_MSG(_T("unknown format specificator"));
2630 // fall through and just copy it nevertheless
2632 case _T('%'): // a percent sign
2636 case 0: // the end of string
2637 wxFAIL_MSG(_T("missing format at the end of string"));
2639 // just put the '%' which was the last char in format
2649 // this function parses a string in (strict) RFC 822 format: see the section 5
2650 // of the RFC for the detailed description, but briefly it's something of the
2651 // form "Sat, 18 Dec 1999 00:48:30 +0100"
2653 // this function is "strict" by design - it must reject anything except true
2654 // RFC822 time specs.
2656 // TODO a great candidate for using reg exps
2657 const wxChar
*wxDateTime::ParseRfc822Date(const wxChar
* date
)
2659 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
2661 const wxChar
*p
= date
;
2662 const wxChar
*comma
= wxStrchr(p
, _T(','));
2665 // the part before comma is the weekday
2667 // skip it for now - we don't use but might check that it really
2668 // corresponds to the specfied date
2671 if ( *p
!= _T(' ') )
2673 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
2675 return (wxChar
*)NULL
;
2681 // the following 1 or 2 digits are the day number
2682 if ( !wxIsdigit(*p
) )
2684 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
2686 return (wxChar
*)NULL
;
2689 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - _T('0'));
2690 if ( wxIsdigit(*p
) )
2693 day
= (wxDateTime_t
)(day
+ (*p
++ - _T('0')));
2696 if ( *p
++ != _T(' ') )
2698 return (wxChar
*)NULL
;
2701 // the following 3 letters specify the month
2702 wxString
monName(p
, 3);
2704 if ( monName
== _T("Jan") )
2706 else if ( monName
== _T("Feb") )
2708 else if ( monName
== _T("Mar") )
2710 else if ( monName
== _T("Apr") )
2712 else if ( monName
== _T("May") )
2714 else if ( monName
== _T("Jun") )
2716 else if ( monName
== _T("Jul") )
2718 else if ( monName
== _T("Aug") )
2720 else if ( monName
== _T("Sep") )
2722 else if ( monName
== _T("Oct") )
2724 else if ( monName
== _T("Nov") )
2726 else if ( monName
== _T("Dec") )
2730 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str());
2732 return (wxChar
*)NULL
;
2737 if ( *p
++ != _T(' ') )
2739 return (wxChar
*)NULL
;
2743 if ( !wxIsdigit(*p
) )
2746 return (wxChar
*)NULL
;
2749 int year
= *p
++ - _T('0');
2751 if ( !wxIsdigit(*p
) )
2753 // should have at least 2 digits in the year
2754 return (wxChar
*)NULL
;
2758 year
+= *p
++ - _T('0');
2760 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
2761 if ( wxIsdigit(*p
) )
2764 year
+= *p
++ - _T('0');
2766 if ( !wxIsdigit(*p
) )
2768 // no 3 digit years please
2769 return (wxChar
*)NULL
;
2773 year
+= *p
++ - _T('0');
2776 if ( *p
++ != _T(' ') )
2778 return (wxChar
*)NULL
;
2781 // time is in the format hh:mm:ss and seconds are optional
2782 if ( !wxIsdigit(*p
) )
2784 return (wxChar
*)NULL
;
2787 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - _T('0'));
2789 if ( !wxIsdigit(*p
) )
2791 return (wxChar
*)NULL
;
2795 hour
= (wxDateTime_t
)(hour
+ (*p
++ - _T('0')));
2797 if ( *p
++ != _T(':') )
2799 return (wxChar
*)NULL
;
2802 if ( !wxIsdigit(*p
) )
2804 return (wxChar
*)NULL
;
2807 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - _T('0'));
2809 if ( !wxIsdigit(*p
) )
2811 return (wxChar
*)NULL
;
2815 min
= (wxDateTime_t
)(min
+ *p
++ - _T('0'));
2817 wxDateTime_t sec
= 0;
2818 if ( *p
++ == _T(':') )
2820 if ( !wxIsdigit(*p
) )
2822 return (wxChar
*)NULL
;
2825 sec
= (wxDateTime_t
)(*p
++ - _T('0'));
2827 if ( !wxIsdigit(*p
) )
2829 return (wxChar
*)NULL
;
2833 sec
= (wxDateTime_t
)(sec
+ *p
++ - _T('0'));
2836 if ( *p
++ != _T(' ') )
2838 return (wxChar
*)NULL
;
2841 // and now the interesting part: the timezone
2842 int offset
wxDUMMY_INITIALIZE(0);
2843 if ( *p
== _T('-') || *p
== _T('+') )
2845 // the explicit offset given: it has the form of hhmm
2846 bool plus
= *p
++ == _T('+');
2848 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2850 return (wxChar
*)NULL
;
2854 offset
= MIN_PER_HOUR
*(10*(*p
- _T('0')) + (*(p
+ 1) - _T('0')));
2858 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2860 return (wxChar
*)NULL
;
2864 offset
+= 10*(*p
- _T('0')) + (*(p
+ 1) - _T('0'));
2875 // the symbolic timezone given: may be either military timezone or one
2876 // of standard abbreviations
2879 // military: Z = UTC, J unused, A = -1, ..., Y = +12
2880 static const int offsets
[26] =
2882 //A B C D E F G H I J K L M
2883 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
2884 //N O P R Q S T U V W Z Y Z
2885 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
2888 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
2890 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
);
2892 return (wxChar
*)NULL
;
2895 offset
= offsets
[*p
++ - _T('A')];
2901 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
2903 else if ( tz
== _T("AST") )
2904 offset
= AST
- GMT0
;
2905 else if ( tz
== _T("ADT") )
2906 offset
= ADT
- GMT0
;
2907 else if ( tz
== _T("EST") )
2908 offset
= EST
- GMT0
;
2909 else if ( tz
== _T("EDT") )
2910 offset
= EDT
- GMT0
;
2911 else if ( tz
== _T("CST") )
2912 offset
= CST
- GMT0
;
2913 else if ( tz
== _T("CDT") )
2914 offset
= CDT
- GMT0
;
2915 else if ( tz
== _T("MST") )
2916 offset
= MST
- GMT0
;
2917 else if ( tz
== _T("MDT") )
2918 offset
= MDT
- GMT0
;
2919 else if ( tz
== _T("PST") )
2920 offset
= PST
- GMT0
;
2921 else if ( tz
== _T("PDT") )
2922 offset
= PDT
- GMT0
;
2925 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
);
2927 return (wxChar
*)NULL
;
2934 offset
*= MIN_PER_HOUR
;
2937 // the spec was correct, construct the date from the values we found
2938 Set(day
, mon
, year
, hour
, min
, sec
);
2939 MakeFromTimezone(TimeZone((wxDateTime_t
)(offset
*SEC_PER_MIN
)));
2946 // returns the string containing strftime() format used for short dates in the
2947 // current locale or an empty string
2948 static wxString
GetLocaleDateFormat()
2952 // there is no setlocale() under Windows CE, so just always query the
2955 if ( strcmp(setlocale(LC_ALL
, NULL
), "C") != 0 )
2958 // The locale was programatically set to non-C. We assume that this was
2959 // done using wxLocale, in which case thread's current locale is also
2960 // set to correct LCID value and we can use GetLocaleInfo to determine
2961 // the correct formatting string:
2963 LCID lcid
= LOCALE_USER_DEFAULT
;
2965 LCID lcid
= GetThreadLocale();
2967 // according to MSDN 80 chars is max allowed for short date format
2969 if ( ::GetLocaleInfo(lcid
, LOCALE_SSHORTDATE
, fmt
, WXSIZEOF(fmt
)) )
2971 wxChar chLast
= _T('\0');
2972 size_t lastCount
= 0;
2973 for ( const wxChar
*p
= fmt
; /* NUL handled inside */; p
++ )
2983 // these characters come in groups, start counting them
2993 // first deal with any special characters we have had
2999 switch ( lastCount
)
3003 // these two are the same as we
3004 // don't distinguish between 1 and
3005 // 2 digits for days
3018 wxFAIL_MSG( _T("too many 'd's") );
3023 switch ( lastCount
)
3027 // as for 'd' and 'dd' above
3040 wxFAIL_MSG( _T("too many 'M's") );
3045 switch ( lastCount
)
3057 wxFAIL_MSG( _T("wrong number of 'y's") );
3062 // strftime() doesn't have era string,
3063 // ignore this format
3064 wxASSERT_MSG( lastCount
<= 2,
3065 _T("too many 'g's") );
3069 wxFAIL_MSG( _T("unreachable") );
3076 // not a special character so must be just a separator,
3078 if ( *p
!= _T('\0') )
3080 if ( *p
== _T('%') )
3082 // this one needs to be escaped
3090 if ( *p
== _T('\0') )
3094 //else: GetLocaleInfo() failed, leave fmtDate value unchanged and
3095 // try our luck with the default formats
3097 //else: default C locale, default formats should work
3102 #endif // __WINDOWS__
3104 const wxChar
*wxDateTime::ParseFormat(const wxChar
*date
,
3105 const wxChar
*format
,
3106 const wxDateTime
& dateDef
)
3108 wxCHECK_MSG( date
&& format
, (wxChar
*)NULL
,
3109 _T("NULL pointer in wxDateTime::ParseFormat()") );
3114 // what fields have we found?
3115 bool haveWDay
= false,
3124 bool hourIsIn12hFormat
= false, // or in 24h one?
3125 isPM
= false; // AM by default
3127 // and the value of the items we have (init them to get rid of warnings)
3128 wxDateTime_t sec
= 0,
3131 WeekDay wday
= Inv_WeekDay
;
3132 wxDateTime_t yday
= 0,
3134 wxDateTime::Month mon
= Inv_Month
;
3137 const wxChar
*input
= date
;
3138 for ( const wxChar
*fmt
= format
; *fmt
; fmt
++ )
3140 if ( *fmt
!= _T('%') )
3142 if ( wxIsspace(*fmt
) )
3144 // a white space in the format string matches 0 or more white
3145 // spaces in the input
3146 while ( wxIsspace(*input
) )
3153 // any other character (not whitespace, not '%') must be
3154 // matched by itself in the input
3155 if ( *input
++ != *fmt
)
3158 return (wxChar
*)NULL
;
3162 // done with this format char
3166 // start of a format specification
3168 // parse the optional width
3170 while ( wxIsdigit(*++fmt
) )
3173 width
+= *fmt
- _T('0');
3176 // the default widths for the various fields
3181 case _T('Y'): // year has 4 digits
3185 case _T('j'): // day of year has 3 digits
3186 case _T('l'): // milliseconds have 3 digits
3190 case _T('w'): // week day as number has only one
3195 // default for all other fields
3200 // then the format itself
3203 case _T('a'): // a weekday name
3206 int flag
= *fmt
== _T('a') ? Name_Abbr
: Name_Full
;
3207 wday
= GetWeekDayFromName(GetAlphaToken(input
), flag
);
3208 if ( wday
== Inv_WeekDay
)
3211 return (wxChar
*)NULL
;
3217 case _T('b'): // a month name
3220 int flag
= *fmt
== _T('b') ? Name_Abbr
: Name_Full
;
3221 mon
= GetMonthFromName(GetAlphaToken(input
), flag
);
3222 if ( mon
== Inv_Month
)
3225 return (wxChar
*)NULL
;
3231 case _T('c'): // locale default date and time representation
3235 // this is the format which corresponds to ctime() output
3236 // and strptime("%c") should parse it, so try it first
3237 static const wxChar
*fmtCtime
= _T("%a %b %d %H:%M:%S %Y");
3239 const wxChar
*result
= dt
.ParseFormat(input
, fmtCtime
);
3242 result
= dt
.ParseFormat(input
, _T("%x %X"));
3247 result
= dt
.ParseFormat(input
, _T("%X %x"));
3252 // we've tried everything and still no match
3253 return (wxChar
*)NULL
;
3258 haveDay
= haveMon
= haveYear
=
3259 haveHour
= haveMin
= haveSec
= true;
3273 case _T('d'): // day of a month (01-31)
3274 if ( !GetNumericToken(width
, input
, &num
) ||
3275 (num
> 31) || (num
< 1) )
3278 return (wxChar
*)NULL
;
3281 // we can't check whether the day range is correct yet, will
3282 // do it later - assume ok for now
3284 mday
= (wxDateTime_t
)num
;
3287 case _T('H'): // hour in 24h format (00-23)
3288 if ( !GetNumericToken(width
, input
, &num
) || (num
> 23) )
3291 return (wxChar
*)NULL
;
3295 hour
= (wxDateTime_t
)num
;
3298 case _T('I'): // hour in 12h format (01-12)
3299 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3302 return (wxChar
*)NULL
;
3306 hourIsIn12hFormat
= true;
3307 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
3310 case _T('j'): // day of the year
3311 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 366) )
3314 return (wxChar
*)NULL
;
3318 yday
= (wxDateTime_t
)num
;
3321 case _T('m'): // month as a number (01-12)
3322 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3325 return (wxChar
*)NULL
;
3329 mon
= (Month
)(num
- 1);
3332 case _T('M'): // minute as a decimal number (00-59)
3333 if ( !GetNumericToken(width
, input
, &num
) || (num
> 59) )
3336 return (wxChar
*)NULL
;
3340 min
= (wxDateTime_t
)num
;
3343 case _T('p'): // AM or PM string
3345 wxString am
, pm
, token
= GetAlphaToken(input
);
3347 GetAmPmStrings(&am
, &pm
);
3348 if (am
.empty() && pm
.empty())
3349 return (wxChar
*)NULL
; // no am/pm strings defined
3350 if ( token
.CmpNoCase(pm
) == 0 )
3354 else if ( token
.CmpNoCase(am
) != 0 )
3357 return (wxChar
*)NULL
;
3362 case _T('r'): // time as %I:%M:%S %p
3365 input
= dt
.ParseFormat(input
, _T("%I:%M:%S %p"));
3369 return (wxChar
*)NULL
;
3372 haveHour
= haveMin
= haveSec
= true;
3381 case _T('R'): // time as %H:%M
3384 input
= dt
.ParseFormat(input
, _T("%H:%M"));
3388 return (wxChar
*)NULL
;
3391 haveHour
= haveMin
= true;
3399 case _T('S'): // second as a decimal number (00-61)
3400 if ( !GetNumericToken(width
, input
, &num
) || (num
> 61) )
3403 return (wxChar
*)NULL
;
3407 sec
= (wxDateTime_t
)num
;
3410 case _T('T'): // time as %H:%M:%S
3413 input
= dt
.ParseFormat(input
, _T("%H:%M:%S"));
3417 return (wxChar
*)NULL
;
3420 haveHour
= haveMin
= haveSec
= true;
3429 case _T('w'): // weekday as a number (0-6), Sunday = 0
3430 if ( !GetNumericToken(width
, input
, &num
) || (wday
> 6) )
3433 return (wxChar
*)NULL
;
3437 wday
= (WeekDay
)num
;
3440 case _T('x'): // locale default date representation
3441 #ifdef HAVE_STRPTIME
3442 // try using strptime() -- it may fail even if the input is
3443 // correct but the date is out of range, so we will fall back
3444 // to our generic code anyhow
3448 const wxChar
*result
= CallStrptime(input
, "%x", &tm
);
3453 haveDay
= haveMon
= haveYear
= true;
3455 year
= 1900 + tm
.tm_year
;
3456 mon
= (Month
)tm
.tm_mon
;
3462 #endif // HAVE_STRPTIME
3470 // The above doesn't work for all locales, try to query
3471 // Windows for the right way of formatting the date:
3472 fmtDate
= GetLocaleDateFormat();
3473 if ( fmtDate
.empty() )
3476 if ( IsWestEuropeanCountry(GetCountry()) ||
3477 GetCountry() == Russia
)
3479 fmtDate
= _T("%d/%m/%y");
3480 fmtDateAlt
= _T("%m/%d/%y");
3484 fmtDate
= _T("%m/%d/%y");
3485 fmtDateAlt
= _T("%d/%m/%y");
3489 const wxChar
*result
= dt
.ParseFormat(input
, fmtDate
);
3491 if ( !result
&& !fmtDateAlt
.empty() )
3493 // ok, be nice and try another one
3494 result
= dt
.ParseFormat(input
, fmtDateAlt
);
3500 return (wxChar
*)NULL
;
3505 haveDay
= haveMon
= haveYear
= true;
3516 case _T('X'): // locale default time representation
3517 #ifdef HAVE_STRPTIME
3519 // use strptime() to do it for us (FIXME !Unicode friendly)
3521 input
= CallStrptime(input
, "%X", &tm
);
3524 return (wxChar
*)NULL
;
3527 haveHour
= haveMin
= haveSec
= true;
3533 #else // !HAVE_STRPTIME
3534 // TODO under Win32 we can query the LOCALE_ITIME system
3535 // setting which says whether the default time format is
3538 // try to parse what follows as "%H:%M:%S" and, if this
3539 // fails, as "%I:%M:%S %p" - this should catch the most
3543 const wxChar
*result
= dt
.ParseFormat(input
, _T("%T"));
3546 result
= dt
.ParseFormat(input
, _T("%r"));
3552 return (wxChar
*)NULL
;
3555 haveHour
= haveMin
= haveSec
= true;
3564 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
3567 case _T('y'): // year without century (00-99)
3568 if ( !GetNumericToken(width
, input
, &num
) || (num
> 99) )
3571 return (wxChar
*)NULL
;
3576 // TODO should have an option for roll over date instead of
3577 // hard coding it here
3578 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
3581 case _T('Y'): // year with century
3582 if ( !GetNumericToken(width
, input
, &num
) )
3585 return (wxChar
*)NULL
;
3589 year
= (wxDateTime_t
)num
;
3592 case _T('Z'): // timezone name
3593 wxFAIL_MSG(_T("TODO"));
3596 case _T('%'): // a percent sign
3597 if ( *input
++ != _T('%') )
3600 return (wxChar
*)NULL
;
3604 case 0: // the end of string
3605 wxFAIL_MSG(_T("unexpected format end"));
3609 default: // not a known format spec
3610 return (wxChar
*)NULL
;
3614 // format matched, try to construct a date from what we have now
3616 if ( dateDef
.IsValid() )
3618 // take this date as default
3619 tmDef
= dateDef
.GetTm();
3621 else if ( IsValid() )
3623 // if this date is valid, don't change it
3628 // no default and this date is invalid - fall back to Today()
3629 tmDef
= Today().GetTm();
3640 // TODO we don't check here that the values are consistent, if both year
3641 // day and month/day were found, we just ignore the year day and we
3642 // also always ignore the week day
3643 if ( haveMon
&& haveDay
)
3645 if ( mday
> GetNumOfDaysInMonth(tm
.year
, mon
) )
3647 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
3649 return (wxChar
*)NULL
;
3655 else if ( haveYDay
)
3657 if ( yday
> GetNumberOfDays(tm
.year
) )
3659 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
3661 return (wxChar
*)NULL
;
3664 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
3671 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
3673 // translate to 24hour format
3676 //else: either already in 24h format or no translation needed
3696 // finally check that the week day is consistent -- if we had it
3697 if ( haveWDay
&& GetWeekDay() != wday
)
3699 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
3707 const wxChar
*wxDateTime::ParseDateTime(const wxChar
*date
)
3709 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3711 // Set to current day and hour, so strings like '14:00' becomes today at
3712 // 14, not some other random date
3713 wxDateTime dtDate
= wxDateTime::Today();
3714 wxDateTime dtTime
= wxDateTime::Today();
3716 const wxChar
* pchTime
;
3718 // Try to parse the beginning of the string as a date
3719 const wxChar
* pchDate
= dtDate
.ParseDate(date
);
3721 // We got a date in the beginning, see if there is a time specified after the date
3724 // Skip spaces, as the ParseTime() function fails on spaces
3725 while ( wxIsspace(*pchDate
) )
3728 pchTime
= dtTime
.ParseTime(pchDate
);
3730 else // no date in the beginning
3732 // check and see if we have a time followed by a date
3733 pchTime
= dtTime
.ParseTime(date
);
3736 while ( wxIsspace(*pchTime
) )
3739 pchDate
= dtDate
.ParseDate(pchTime
);
3743 // If we have a date specified, set our own data to the same date
3744 if ( !pchDate
|| !pchTime
)
3747 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
3748 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
3749 dtTime
.GetMillisecond());
3751 // Return endpoint of scan
3752 return pchDate
> pchTime
? pchDate
: pchTime
;
3755 const wxChar
*wxDateTime::ParseDate(const wxChar
*date
)
3757 // this is a simplified version of ParseDateTime() which understands only
3758 // "today" (for wxDate compatibility) and digits only otherwise (and not
3759 // all esoteric constructions ParseDateTime() knows about)
3761 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3763 const wxChar
*p
= date
;
3764 while ( wxIsspace(*p
) )
3767 // some special cases
3771 int dayDiffFromToday
;
3774 { wxTRANSLATE("today"), 0 },
3775 { wxTRANSLATE("yesterday"), -1 },
3776 { wxTRANSLATE("tomorrow"), 1 },
3779 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
3781 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
3782 size_t len
= dateStr
.length();
3783 if ( wxStrlen(p
) >= len
)
3785 wxString
str(p
, len
);
3786 if ( str
.CmpNoCase(dateStr
) == 0 )
3788 // nothing can follow this, so stop here
3791 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
3793 if ( dayDiffFromToday
)
3795 *this += wxDateSpan::Days(dayDiffFromToday
);
3803 // We try to guess what we have here: for each new (numeric) token, we
3804 // determine if it can be a month, day or a year. Of course, there is an
3805 // ambiguity as some numbers may be days as well as months, so we also
3806 // have the ability to back track.
3809 bool haveDay
= false, // the months day?
3810 haveWDay
= false, // the day of week?
3811 haveMon
= false, // the month?
3812 haveYear
= false; // the year?
3814 // and the value of the items we have (init them to get rid of warnings)
3815 WeekDay wday
= Inv_WeekDay
;
3816 wxDateTime_t day
= 0;
3817 wxDateTime::Month mon
= Inv_Month
;
3820 // tokenize the string
3822 static const wxChar
*dateDelimiters
= _T(".,/-\t\r\n ");
3823 wxStringTokenizer
tok(p
, dateDelimiters
);
3824 while ( tok
.HasMoreTokens() )
3826 wxString token
= tok
.GetNextToken();
3832 if ( token
.ToULong(&val
) )
3834 // guess what this number is
3840 if ( !haveMon
&& val
> 0 && val
<= 12 )
3842 // assume it is month
3845 else // not the month
3849 // this can only be the year
3852 else // may be either day or year
3854 // use a leap year if we don't have the year yet to allow
3855 // dates like 2/29/1976 which would be rejected otherwise
3856 wxDateTime_t max_days
= (wxDateTime_t
)(
3858 ? GetNumOfDaysInMonth(haveYear
? year
: 1976, mon
)
3863 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
3868 else // yes, suppose it's the day
3882 year
= (wxDateTime_t
)val
;
3891 day
= (wxDateTime_t
)val
;
3897 mon
= (Month
)(val
- 1);
3900 else // not a number
3902 // be careful not to overwrite the current mon value
3903 Month mon2
= GetMonthFromName(token
, Name_Full
| Name_Abbr
);
3904 if ( mon2
!= Inv_Month
)
3909 // but we already have a month - maybe we guessed wrong?
3912 // no need to check in month range as always < 12, but
3913 // the days are counted from 1 unlike the months
3914 day
= (wxDateTime_t
)(mon
+ 1);
3919 // could possible be the year (doesn't the year come
3920 // before the month in the japanese format?) (FIXME)
3929 else // not a valid month name
3931 wday
= GetWeekDayFromName(token
, Name_Full
| Name_Abbr
);
3932 if ( wday
!= Inv_WeekDay
)
3942 else // not a valid weekday name
3945 static const wxChar
*ordinals
[] =
3947 wxTRANSLATE("first"),
3948 wxTRANSLATE("second"),
3949 wxTRANSLATE("third"),
3950 wxTRANSLATE("fourth"),
3951 wxTRANSLATE("fifth"),
3952 wxTRANSLATE("sixth"),
3953 wxTRANSLATE("seventh"),
3954 wxTRANSLATE("eighth"),
3955 wxTRANSLATE("ninth"),
3956 wxTRANSLATE("tenth"),
3957 wxTRANSLATE("eleventh"),
3958 wxTRANSLATE("twelfth"),
3959 wxTRANSLATE("thirteenth"),
3960 wxTRANSLATE("fourteenth"),
3961 wxTRANSLATE("fifteenth"),
3962 wxTRANSLATE("sixteenth"),
3963 wxTRANSLATE("seventeenth"),
3964 wxTRANSLATE("eighteenth"),
3965 wxTRANSLATE("nineteenth"),
3966 wxTRANSLATE("twentieth"),
3967 // that's enough - otherwise we'd have problems with
3968 // composite (or not) ordinals
3972 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
3974 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
3980 if ( n
== WXSIZEOF(ordinals
) )
3982 // stop here - something unknown
3989 // don't try anything here (as in case of numeric day
3990 // above) - the symbolic day spec should always
3991 // precede the month/year
3997 day
= (wxDateTime_t
)(n
+ 1);
4002 nPosCur
= tok
.GetPosition();
4005 // either no more tokens or the scan was stopped by something we couldn't
4006 // parse - in any case, see if we can construct a date from what we have
4007 if ( !haveDay
&& !haveWDay
)
4009 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
4014 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
4015 !(haveDay
&& haveMon
&& haveYear
) )
4017 // without adjectives (which we don't support here) the week day only
4018 // makes sense completely separately or with the full date
4019 // specification (what would "Wed 1999" mean?)
4023 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
4025 // may be we have month and day instead of day and year?
4026 if ( haveDay
&& !haveMon
)
4030 // exchange day and month
4031 mon
= (wxDateTime::Month
)(day
- 1);
4033 // we're in the current year then
4034 if ( (year
> 0) && (year
<= (int)GetNumOfDaysInMonth(Inv_Year
, mon
)) )
4036 day
= (wxDateTime_t
)year
;
4041 //else: no, can't exchange, leave haveMon == false
4047 // if we give the year, month and day must be given too
4048 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
4056 mon
= GetCurrentMonth();
4061 year
= GetCurrentYear();
4066 // normally we check the day above but the check is optimistic in case
4067 // we find the day before its month/year so we have to redo it now
4068 if ( day
> GetNumOfDaysInMonth(year
, mon
) )
4071 Set(day
, mon
, year
);
4075 // check that it is really the same
4076 if ( GetWeekDay() != wday
)
4078 // inconsistency detected
4079 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
4081 return (wxChar
*)NULL
;
4089 SetToWeekDayInSameWeek(wday
);
4092 // return the pointer to the first unparsed char
4094 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
4096 // if we couldn't parse the token after the delimiter, put back the
4097 // delimiter as well
4104 const wxChar
*wxDateTime::ParseTime(const wxChar
*time
)
4106 wxCHECK_MSG( time
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
4108 // first try some extra things
4115 { wxTRANSLATE("noon"), 12 },
4116 { wxTRANSLATE("midnight"), 00 },
4120 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
4122 wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
4123 size_t len
= timeString
.length();
4124 if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 )
4126 // casts required by DigitalMars
4127 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
4133 // try all time formats we may think about in the order from longest to
4136 // 12hour with AM/PM?
4137 const wxChar
*result
= ParseFormat(time
, _T("%I:%M:%S %p"));
4141 // normally, it's the same, but why not try it?
4142 result
= ParseFormat(time
, _T("%H:%M:%S"));
4147 // 12hour with AM/PM but without seconds?
4148 result
= ParseFormat(time
, _T("%I:%M %p"));
4154 result
= ParseFormat(time
, _T("%H:%M"));
4159 // just the hour and AM/PM?
4160 result
= ParseFormat(time
, _T("%I %p"));
4166 result
= ParseFormat(time
, _T("%H"));
4171 // parse the standard format: normally it is one of the formats above
4172 // but it may be set to something completely different by the user
4173 result
= ParseFormat(time
, _T("%X"));
4176 // TODO: parse timezones
4181 // ----------------------------------------------------------------------------
4182 // Workdays and holidays support
4183 // ----------------------------------------------------------------------------
4185 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
4187 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
4190 // ============================================================================
4192 // ============================================================================
4194 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
4197 return ds1
.Multiply(n
);
4200 // ============================================================================
4202 // ============================================================================
4204 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
4206 return wxTimeSpan(ts
).Multiply(n
);
4209 // this enum is only used in wxTimeSpan::Format() below but we can't declare
4210 // it locally to the method as it provokes an internal compiler error in egcs
4211 // 2.91.60 when building with -O2
4222 // not all strftime(3) format specifiers make sense here because, for example,
4223 // a time span doesn't have a year nor a timezone
4225 // Here are the ones which are supported (all of them are supported by strftime
4227 // %H hour in 24 hour format
4228 // %M minute (00 - 59)
4229 // %S second (00 - 59)
4232 // Also, for MFC CTimeSpan compatibility, we support
4233 // %D number of days
4235 // And, to be better than MFC :-), we also have
4236 // %E number of wEeks
4237 // %l milliseconds (000 - 999)
4238 wxString
wxTimeSpan::Format(const wxChar
*format
) const
4240 wxCHECK_MSG( format
, wxEmptyString
, _T("NULL format in wxTimeSpan::Format") );
4243 str
.Alloc(wxStrlen(format
));
4245 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
4247 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
4248 // question is what should ts.Format("%S") do? The code here returns "3273"
4249 // in this case (i.e. the total number of seconds, not just seconds % 60)
4250 // because, for me, this call means "give me entire time interval in
4251 // seconds" and not "give me the seconds part of the time interval"
4253 // If we agree that it should behave like this, it is clear that the
4254 // interpretation of each format specifier depends on the presence of the
4255 // other format specs in the string: if there was "%H" before "%M", we
4256 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
4258 // we remember the most important unit found so far
4259 TimeSpanPart partBiggest
= Part_MSec
;
4261 for ( const wxChar
*pch
= format
; *pch
; pch
++ )
4265 if ( ch
== _T('%') )
4267 // the start of the format specification of the printf() below
4268 wxString
fmtPrefix(_T('%'));
4273 // the number of digits for the format string, 0 if unused
4274 unsigned digits
= 0;
4276 ch
= *++pch
; // get the format spec char
4280 wxFAIL_MSG( _T("invalid format character") );
4286 // skip the part below switch
4291 if ( partBiggest
< Part_Day
)
4297 partBiggest
= Part_Day
;
4302 partBiggest
= Part_Week
;
4308 if ( partBiggest
< Part_Hour
)
4312 // the sign has already been taken into account
4313 // when outputting the biggest part
4321 partBiggest
= Part_Hour
;
4328 n
= GetMilliseconds().ToLong();
4329 if ( partBiggest
< Part_MSec
)
4336 //else: no need to reset partBiggest to Part_MSec, it is
4337 // the least significant one anyhow
4344 if ( partBiggest
< Part_Min
)
4353 partBiggest
= Part_Min
;
4360 n
= GetSeconds().ToLong();
4361 if ( partBiggest
< Part_Sec
)
4370 partBiggest
= Part_Sec
;
4379 // negative numbers need one extra position for '-' display
4383 fmtPrefix
<< _T("0") << digits
;
4386 str
+= wxString::Format(fmtPrefix
+ _T("ld"), n
);
4390 // normal character, just copy
4398 // ============================================================================
4399 // wxDateTimeHolidayAuthority and related classes
4400 // ============================================================================
4402 #include "wx/arrimpl.cpp"
4404 WX_DEFINE_OBJARRAY(wxDateTimeArray
)
4406 static int wxCMPFUNC_CONV
4407 wxDateTimeCompareFunc(wxDateTime
**first
, wxDateTime
**second
)
4409 wxDateTime dt1
= **first
,
4412 return dt1
== dt2
? 0 : dt1
< dt2
? -1 : +1;
4415 // ----------------------------------------------------------------------------
4416 // wxDateTimeHolidayAuthority
4417 // ----------------------------------------------------------------------------
4419 wxHolidayAuthoritiesArray
wxDateTimeHolidayAuthority::ms_authorities
;
4422 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime
& dt
)
4424 size_t count
= ms_authorities
.size();
4425 for ( size_t n
= 0; n
< count
; n
++ )
4427 if ( ms_authorities
[n
]->DoIsHoliday(dt
) )
4438 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime
& dtStart
,
4439 const wxDateTime
& dtEnd
,
4440 wxDateTimeArray
& holidays
)
4442 wxDateTimeArray hol
;
4446 const size_t countAuth
= ms_authorities
.size();
4447 for ( size_t nAuth
= 0; nAuth
< countAuth
; nAuth
++ )
4449 ms_authorities
[nAuth
]->DoGetHolidaysInRange(dtStart
, dtEnd
, hol
);
4451 WX_APPEND_ARRAY(holidays
, hol
);
4454 holidays
.Sort(wxDateTimeCompareFunc
);
4456 return holidays
.size();
4460 void wxDateTimeHolidayAuthority::ClearAllAuthorities()
4462 WX_CLEAR_ARRAY(ms_authorities
);
4466 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority
*auth
)
4468 ms_authorities
.push_back(auth
);
4471 wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
4473 // required here for Darwin
4476 // ----------------------------------------------------------------------------
4477 // wxDateTimeWorkDays
4478 // ----------------------------------------------------------------------------
4480 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime
& dt
) const
4482 wxDateTime::WeekDay wd
= dt
.GetWeekDay();
4484 return (wd
== wxDateTime::Sun
) || (wd
== wxDateTime::Sat
);
4487 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime
& dtStart
,
4488 const wxDateTime
& dtEnd
,
4489 wxDateTimeArray
& holidays
) const
4491 if ( dtStart
> dtEnd
)
4493 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
4500 // instead of checking all days, start with the first Sat after dtStart and
4501 // end with the last Sun before dtEnd
4502 wxDateTime dtSatFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sat
),
4503 dtSatLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sat
),
4504 dtSunFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sun
),
4505 dtSunLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sun
),
4508 for ( dt
= dtSatFirst
; dt
<= dtSatLast
; dt
+= wxDateSpan::Week() )
4513 for ( dt
= dtSunFirst
; dt
<= dtSunLast
; dt
+= wxDateSpan::Week() )
4518 return holidays
.GetCount();
4521 // ============================================================================
4522 // other helper functions
4523 // ============================================================================
4525 // ----------------------------------------------------------------------------
4526 // iteration helpers: can be used to write a for loop over enum variable like
4528 // for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
4529 // ----------------------------------------------------------------------------
4531 WXDLLIMPEXP_BASE
void wxNextMonth(wxDateTime::Month
& m
)
4533 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4535 // no wrapping or the for loop above would never end!
4536 m
= (wxDateTime::Month
)(m
+ 1);
4539 WXDLLIMPEXP_BASE
void wxPrevMonth(wxDateTime::Month
& m
)
4541 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4543 m
= m
== wxDateTime::Jan
? wxDateTime::Inv_Month
4544 : (wxDateTime::Month
)(m
- 1);
4547 WXDLLIMPEXP_BASE
void wxNextWDay(wxDateTime::WeekDay
& wd
)
4549 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4551 // no wrapping or the for loop above would never end!
4552 wd
= (wxDateTime::WeekDay
)(wd
+ 1);
4555 WXDLLIMPEXP_BASE
void wxPrevWDay(wxDateTime::WeekDay
& wd
)
4557 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4559 wd
= wd
== wxDateTime::Sun
? wxDateTime::Inv_WeekDay
4560 : (wxDateTime::WeekDay
)(wd
- 1);
4563 #endif // wxUSE_DATETIME