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 ///////////////////////////////////////////////////////////////////////////////
20 * Implementation notes:
22 * 1. the time is stored as a 64bit integer containing the signed number of
23 * milliseconds since Jan 1. 1970 (the Unix Epoch) - so it is always
26 * 2. the range is thus something about 580 million years, but due to current
27 * algorithms limitations, only dates from Nov 24, 4714BC are handled
29 * 3. standard ANSI C functions are used to do time calculations whenever
30 * possible, i.e. when the date is in the range Jan 1, 1970 to 2038
32 * 4. otherwise, the calculations are done by converting the date to/from JDN
33 * first (the range limitation mentioned above comes from here: the
34 * algorithm used by Scott E. Lee's code only works for positive JDNs, more
37 * 5. the object constructed for the given DD-MM-YYYY HH:MM:SS corresponds to
38 * this moment in local time and may be converted to the object
39 * corresponding to the same date/time in another time zone by using
42 * 6. the conversions to the current (or any other) timezone are done when the
43 * internal time representation is converted to the broken-down one in
47 // ============================================================================
49 // ============================================================================
51 // ----------------------------------------------------------------------------
53 // ----------------------------------------------------------------------------
55 // For compilers that support precompilation, includes "wx.h".
56 #include "wx/wxprec.h"
62 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
66 #include "wx/msw/wrapwin.h"
68 #include "wx/string.h"
71 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
72 #include "wx/module.h"
76 #include "wx/thread.h"
77 #include "wx/tokenzr.h"
88 #include "wx/datetime.h"
90 const long wxDateTime::TIME_T_FACTOR
= 1000l;
92 #if wxUSE_EXTENDED_RTTI
94 template<> void wxStringReadValue(const wxString
&s
, wxDateTime
&data
)
96 data
.ParseFormat(s
,"%Y-%m-%d %H:%M:%S", NULL
);
99 template<> void wxStringWriteValue(wxString
&s
, const wxDateTime
&data
)
101 s
= data
.Format("%Y-%m-%d %H:%M:%S");
104 wxCUSTOM_TYPE_INFO(wxDateTime
, wxToStringConverter
<wxDateTime
> , wxFromStringConverter
<wxDateTime
>)
106 #endif // wxUSE_EXTENDED_RTTI
109 // ----------------------------------------------------------------------------
110 // conditional compilation
111 // ----------------------------------------------------------------------------
113 #if defined(HAVE_STRPTIME) && defined(__GLIBC__) && \
114 ((__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0))
115 // glibc 2.0.7 strptime() is broken - the following snippet causes it to
116 // crash (instead of just failing):
118 // strncpy(buf, "Tue Dec 21 20:25:40 1999", 128);
119 // strptime(buf, "%x", &tm);
123 #endif // broken strptime()
125 #if defined(HAVE_STRPTIME) && defined(__DARWIN__) && defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
126 // configure detects strptime as linkable because it's in the OS X
127 // System library but MSL headers don't declare it.
129 // char *strptime(const char *, const char *, struct tm *);
130 // However, we DON'T want to just provide it here because we would
131 // crash and/or overwrite data when strptime from OS X tries
132 // to fill in MW's struct tm which is two fields shorter (no TZ stuff)
133 // So for now let's just say we don't have strptime
137 #if defined(__MWERKS__) && wxUSE_UNICODE
141 #if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM)
142 #if defined(__WXPALMOS__)
143 #define WX_GMTOFF_IN_TM
144 #elif defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
145 #define WX_TIMEZONE _timezone
146 #elif defined(__MWERKS__)
147 long wxmw_timezone
= 28800;
148 #define WX_TIMEZONE wxmw_timezone
149 #elif defined(__DJGPP__) || defined(__WINE__)
150 #include <sys/timeb.h>
152 static long wxGetTimeZone()
154 static long timezone
= MAXLONG
; // invalid timezone
155 if (timezone
== MAXLONG
)
159 timezone
= tb
.timezone
;
163 #define WX_TIMEZONE wxGetTimeZone()
164 #elif defined(__DARWIN__)
165 #define WX_GMTOFF_IN_TM
166 #elif defined(__WXWINCE__) && defined(__VISUALC8__)
167 // _timezone is not present in dynamic run-time library
169 // Solution (1): use the function equivalent of _timezone
170 static long wxGetTimeZone()
172 static long s_Timezone
= MAXLONG
; // invalid timezone
173 if (s_Timezone
== MAXLONG
)
177 s_Timezone
= (long) t
;
181 #define WX_TIMEZONE wxGetTimeZone()
183 // Solution (2): using GetTimeZoneInformation
184 static long wxGetTimeZone()
186 static long timezone
= MAXLONG
; // invalid timezone
187 if (timezone
== MAXLONG
)
189 TIME_ZONE_INFORMATION tzi
;
190 ::GetTimeZoneInformation(&tzi
);
195 #define WX_TIMEZONE wxGetTimeZone()
197 // Old method using _timezone: this symbol doesn't exist in the dynamic run-time library (i.e. using /MD)
198 #define WX_TIMEZONE _timezone
200 #else // unknown platform - try timezone
201 #define WX_TIMEZONE timezone
203 #endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
205 // everyone has strftime except Win CE unless VC8 is used
206 #if !defined(__WXWINCE__) || defined(__VISUALC8__)
207 #define HAVE_STRFTIME
210 // NB: VC8 safe time functions could/should be used for wxMSW as well probably
211 #if defined(__WXWINCE__) && defined(__VISUALC8__)
213 struct tm
*wxLocaltime_r(const time_t *t
, struct tm
* tm
)
216 return _localtime64_s(tm
, &t64
) == 0 ? tm
: NULL
;
219 struct tm
*wxGmtime_r(const time_t* t
, struct tm
* tm
)
222 return _gmtime64_s(tm
, &t64
) == 0 ? tm
: NULL
;
225 #else // !wxWinCE with VC8
227 #if (!defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)) && wxUSE_THREADS && !defined(__WINDOWS__)
228 static wxMutex timeLock
;
231 #ifndef HAVE_LOCALTIME_R
232 struct tm
*wxLocaltime_r(const time_t* ticks
, struct tm
* temp
)
234 #if wxUSE_THREADS && !defined(__WINDOWS__)
235 // No need to waste time with a mutex on windows since it's using
236 // thread local storage for localtime anyway.
237 wxMutexLocker
locker(timeLock
);
240 // Borland CRT crashes when passed 0 ticks for some reason, see SF bug 1704438
246 const tm
* const t
= localtime(ticks
);
250 memcpy(temp
, t
, sizeof(struct tm
));
253 #endif // !HAVE_LOCALTIME_R
255 #ifndef HAVE_GMTIME_R
256 struct tm
*wxGmtime_r(const time_t* ticks
, struct tm
* temp
)
258 #if wxUSE_THREADS && !defined(__WINDOWS__)
259 // No need to waste time with a mutex on windows since it's
260 // using thread local storage for gmtime anyway.
261 wxMutexLocker
locker(timeLock
);
269 const tm
* const t
= gmtime(ticks
);
273 memcpy(temp
, gmtime(ticks
), sizeof(struct tm
));
276 #endif // !HAVE_GMTIME_R
278 #endif // wxWinCE with VC8/other platforms
280 // ----------------------------------------------------------------------------
282 // ----------------------------------------------------------------------------
284 // debugging helper: just a convenient replacement of wxCHECK()
285 #define wxDATETIME_CHECK(expr, msg) \
286 wxCHECK2_MSG(expr, *this = wxInvalidDateTime; return *this, msg)
288 // ----------------------------------------------------------------------------
290 // ----------------------------------------------------------------------------
292 class wxDateTimeHolidaysModule
: public wxModule
295 virtual bool OnInit()
297 wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays
);
302 virtual void OnExit()
304 wxDateTimeHolidayAuthority::ClearAllAuthorities();
305 wxDateTimeHolidayAuthority::ms_authorities
.clear();
309 DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule
)
312 IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule
, wxModule
)
314 // ----------------------------------------------------------------------------
316 // ----------------------------------------------------------------------------
319 static const int MONTHS_IN_YEAR
= 12;
321 static const int SEC_PER_MIN
= 60;
323 static const int MIN_PER_HOUR
= 60;
325 static const int HOURS_PER_DAY
= 24;
327 static const long SECONDS_PER_DAY
= 86400l;
329 static const int DAYS_PER_WEEK
= 7;
331 static const long MILLISECONDS_PER_DAY
= 86400000l;
333 // this is the integral part of JDN of the midnight of Jan 1, 1970
334 // (i.e. JDN(Jan 1, 1970) = 2440587.5)
335 static const long EPOCH_JDN
= 2440587l;
337 // used only in asserts
339 // the date of JDN -0.5 (as we don't work with fractional parts, this is the
340 // reference date for us) is Nov 24, 4714BC
341 static const int JDN_0_YEAR
= -4713;
342 static const int JDN_0_MONTH
= wxDateTime::Nov
;
343 static const int JDN_0_DAY
= 24;
344 #endif // __WXDEBUG__
346 // the constants used for JDN calculations
347 static const long JDN_OFFSET
= 32046l;
348 static const long DAYS_PER_5_MONTHS
= 153l;
349 static const long DAYS_PER_4_YEARS
= 1461l;
350 static const long DAYS_PER_400_YEARS
= 146097l;
352 // this array contains the cumulated number of days in all previous months for
353 // normal and leap years
354 static const wxDateTime::wxDateTime_t gs_cumulatedDays
[2][MONTHS_IN_YEAR
] =
356 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
357 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
360 // ----------------------------------------------------------------------------
362 // ----------------------------------------------------------------------------
364 const char *wxDefaultDateTimeFormat
= "%c";
365 const char *wxDefaultTimeSpanFormat
= "%H:%M:%S";
367 // in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
368 // indicate an invalid wxDateTime object
369 const wxDateTime wxDefaultDateTime
;
371 wxDateTime::Country
wxDateTime::ms_country
= wxDateTime::Country_Unknown
;
373 // ----------------------------------------------------------------------------
375 // ----------------------------------------------------------------------------
377 // debugger helper: shows what the date really is
379 extern const char *wxDumpDate(const wxDateTime
* dt
)
381 static char buf
[128];
383 wxString
fmt(dt
->Format("%Y-%m-%d (%a) %H:%M:%S"));
385 (fmt
+ " (" + dt
->GetValue().ToString() + " ticks)").ToAscii(),
392 // get the number of days in the given month of the given year
394 wxDateTime::wxDateTime_t
GetNumOfDaysInMonth(int year
, wxDateTime::Month month
)
396 // the number of days in month in Julian/Gregorian calendar: the first line
397 // is for normal years, the second one is for the leap ones
398 static wxDateTime::wxDateTime_t daysInMonth
[2][MONTHS_IN_YEAR
] =
400 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
401 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
404 return daysInMonth
[wxDateTime::IsLeapYear(year
)][month
];
407 // returns the time zone in the C sense, i.e. the difference UTC - local
409 static int GetTimeZone()
411 // set to true when the timezone is set
412 static bool s_timezoneSet
= false;
413 static long gmtoffset
= LONG_MAX
; // invalid timezone
415 // ensure that the timezone variable is set by calling wxLocaltime_r
416 if ( !s_timezoneSet
)
418 // just call wxLocaltime_r() instead of figuring out whether this
419 // system supports tzset(), _tzset() or something else
423 wxLocaltime_r(&t
, &tm
);
424 s_timezoneSet
= true;
426 #ifdef WX_GMTOFF_IN_TM
427 // note that GMT offset is the opposite of time zone and so to return
428 // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM
429 // cases we have to negate it
430 gmtoffset
= -tm
.tm_gmtoff
;
431 #else // !WX_GMTOFF_IN_TM
432 gmtoffset
= WX_TIMEZONE
;
433 #endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
436 return (int)gmtoffset
;
439 // return the integral part of the JDN for the midnight of the given date (to
440 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
441 // noon of the previous day)
442 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day
,
443 wxDateTime::Month mon
,
446 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
448 // check the date validity
450 (year
> JDN_0_YEAR
) ||
451 ((year
== JDN_0_YEAR
) && (mon
> JDN_0_MONTH
)) ||
452 ((year
== JDN_0_YEAR
) && (mon
== JDN_0_MONTH
) && (day
>= JDN_0_DAY
)),
453 _T("date out of range - can't convert to JDN")
456 // make the year positive to avoid problems with negative numbers division
459 // months are counted from March here
461 if ( mon
>= wxDateTime::Mar
)
471 // now we can simply add all the contributions together
472 return ((year
/ 100) * DAYS_PER_400_YEARS
) / 4
473 + ((year
% 100) * DAYS_PER_4_YEARS
) / 4
474 + (month
* DAYS_PER_5_MONTHS
+ 2) / 5
481 // this function is a wrapper around strftime(3) adding error checking
482 static wxString
CallStrftime(const wxString
& format
, const tm
* tm
)
485 // Create temp wxString here to work around mingw/cygwin bug 1046059
486 // http://sourceforge.net/tracker/?func=detail&atid=102435&aid=1046059&group_id=2435
489 if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) )
491 // if the format is valid, buffer must be too small?
492 wxFAIL_MSG(_T("strftime() failed"));
501 #endif // HAVE_STRFTIME
505 #if wxUSE_UNIX && !defined(HAVE_STRPTIME_DECL)
506 // configure detected that we had strptime() but not its declaration,
507 // provide it ourselves
508 extern "C" char *strptime(const char *, const char *, struct tm
*);
511 // Unicode-friendly strptime() wrapper
512 static const wxStringCharType
*
513 CallStrptime(const wxStringCharType
*input
, const char *fmt
, tm
*tm
)
515 // the problem here is that strptime() returns pointer into the string we
516 // passed to it while we're really interested in the pointer into the
517 // original, Unicode, string so we try to transform the pointer back
518 #if wxUSE_UNICODE_WCHAR
519 wxCharBuffer
inputMB(wxConvertWX2MB(input
));
521 const char * const inputMB
= input
;
522 #endif // Unicode/Ascii
524 const char *result
= strptime(inputMB
, fmt
, tm
);
528 #if wxUSE_UNICODE_WCHAR
529 // FIXME: this is wrong in presence of surrogates &c
530 return input
+ (result
- inputMB
.data());
533 #endif // Unicode/Ascii
536 #endif // HAVE_STRPTIME
538 // if year and/or month have invalid values, replace them with the current ones
539 static void ReplaceDefaultYearMonthWithCurrent(int *year
,
540 wxDateTime::Month
*month
)
542 struct tm
*tmNow
= NULL
;
545 if ( *year
== wxDateTime::Inv_Year
)
547 tmNow
= wxDateTime::GetTmNow(&tmstruct
);
549 *year
= 1900 + tmNow
->tm_year
;
552 if ( *month
== wxDateTime::Inv_Month
)
555 tmNow
= wxDateTime::GetTmNow(&tmstruct
);
557 *month
= (wxDateTime::Month
)tmNow
->tm_mon
;
561 // fll the struct tm with default values
562 static void InitTm(struct tm
& tm
)
564 // struct tm may have etxra fields (undocumented and with unportable
565 // names) which, nevertheless, must be set to 0
566 memset(&tm
, 0, sizeof(struct tm
));
568 tm
.tm_mday
= 1; // mday 0 is invalid
569 tm
.tm_year
= 76; // any valid year
570 tm
.tm_isdst
= -1; // auto determine
576 // return the month if the string is a month name or Inv_Month otherwise
577 static wxDateTime::Month
GetMonthFromName(const wxString
& name
, int flags
)
579 wxDateTime::Month mon
;
580 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
582 // case-insensitive comparison either one of or with both abbreviated
584 if ( flags
& wxDateTime::Name_Full
)
586 if ( name
.CmpNoCase(wxDateTime::
587 GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 )
593 if ( flags
& wxDateTime::Name_Abbr
)
595 if ( name
.CmpNoCase(wxDateTime::
596 GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 )
606 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
607 static wxDateTime::WeekDay
GetWeekDayFromName(const wxString
& name
, int flags
)
609 wxDateTime::WeekDay wd
;
610 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
612 // case-insensitive comparison either one of or with both abbreviated
614 if ( flags
& wxDateTime::Name_Full
)
616 if ( name
.CmpNoCase(wxDateTime::
617 GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 )
623 if ( flags
& wxDateTime::Name_Abbr
)
625 if ( name
.CmpNoCase(wxDateTime::
626 GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 )
637 struct tm
*wxDateTime::GetTmNow(struct tm
*tmstruct
)
639 time_t t
= GetTimeNow();
640 return wxLocaltime_r(&t
, tmstruct
);
643 // scans all digits (but no more than len) and returns the resulting number
644 static bool GetNumericToken(size_t len
,
645 const wxStringCharType
*& p
,
646 unsigned long *number
)
650 while ( wxIsdigit(*p
) )
654 if ( len
&& ++n
> len
)
658 return !s
.empty() && s
.ToULong(number
);
661 // scans all alphabetic characters and returns the resulting string
662 static wxString
GetAlphaToken(const wxStringCharType
*& p
)
665 while ( wxIsalpha(*p
) )
673 // ============================================================================
674 // implementation of wxDateTime
675 // ============================================================================
677 // ----------------------------------------------------------------------------
679 // ----------------------------------------------------------------------------
683 year
= (wxDateTime_t
)wxDateTime::Inv_Year
;
684 mon
= wxDateTime::Inv_Month
;
686 hour
= min
= sec
= msec
= 0;
687 wday
= wxDateTime::Inv_WeekDay
;
690 wxDateTime::Tm::Tm(const struct tm
& tm
, const TimeZone
& tz
)
694 sec
= (wxDateTime::wxDateTime_t
)tm
.tm_sec
;
695 min
= (wxDateTime::wxDateTime_t
)tm
.tm_min
;
696 hour
= (wxDateTime::wxDateTime_t
)tm
.tm_hour
;
697 mday
= (wxDateTime::wxDateTime_t
)tm
.tm_mday
;
698 mon
= (wxDateTime::Month
)tm
.tm_mon
;
699 year
= 1900 + tm
.tm_year
;
700 wday
= (wxDateTime::wxDateTime_t
)tm
.tm_wday
;
701 yday
= (wxDateTime::wxDateTime_t
)tm
.tm_yday
;
704 bool wxDateTime::Tm::IsValid() const
706 // we allow for the leap seconds, although we don't use them (yet)
707 return (year
!= wxDateTime::Inv_Year
) && (mon
!= wxDateTime::Inv_Month
) &&
708 (mday
<= GetNumOfDaysInMonth(year
, mon
)) &&
709 (hour
< 24) && (min
< 60) && (sec
< 62) && (msec
< 1000);
712 void wxDateTime::Tm::ComputeWeekDay()
714 // compute the week day from day/month/year: we use the dumbest algorithm
715 // possible: just compute our JDN and then use the (simple to derive)
716 // formula: weekday = (JDN + 1.5) % 7
717 wday
= (wxDateTime::wxDateTime_t
)((GetTruncatedJDN(mday
, mon
, year
) + 2) % 7);
720 void wxDateTime::Tm::AddMonths(int monDiff
)
722 // normalize the months field
723 while ( monDiff
< -mon
)
727 monDiff
+= MONTHS_IN_YEAR
;
730 while ( monDiff
+ mon
>= MONTHS_IN_YEAR
)
734 monDiff
-= MONTHS_IN_YEAR
;
737 mon
= (wxDateTime::Month
)(mon
+ monDiff
);
739 wxASSERT_MSG( mon
>= 0 && mon
< MONTHS_IN_YEAR
, _T("logic error") );
741 // NB: we don't check here that the resulting date is valid, this function
742 // is private and the caller must check it if needed
745 void wxDateTime::Tm::AddDays(int dayDiff
)
747 // normalize the days field
748 while ( dayDiff
+ mday
< 1 )
752 dayDiff
+= GetNumOfDaysInMonth(year
, mon
);
755 mday
= (wxDateTime::wxDateTime_t
)( mday
+ dayDiff
);
756 while ( mday
> GetNumOfDaysInMonth(year
, mon
) )
758 mday
-= GetNumOfDaysInMonth(year
, mon
);
763 wxASSERT_MSG( mday
> 0 && mday
<= GetNumOfDaysInMonth(year
, mon
),
767 // ----------------------------------------------------------------------------
769 // ----------------------------------------------------------------------------
771 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz
)
775 case wxDateTime::Local
:
776 // get the offset from C RTL: it returns the difference GMT-local
777 // while we want to have the offset _from_ GMT, hence the '-'
778 m_offset
= -GetTimeZone();
781 case wxDateTime::GMT_12
:
782 case wxDateTime::GMT_11
:
783 case wxDateTime::GMT_10
:
784 case wxDateTime::GMT_9
:
785 case wxDateTime::GMT_8
:
786 case wxDateTime::GMT_7
:
787 case wxDateTime::GMT_6
:
788 case wxDateTime::GMT_5
:
789 case wxDateTime::GMT_4
:
790 case wxDateTime::GMT_3
:
791 case wxDateTime::GMT_2
:
792 case wxDateTime::GMT_1
:
793 m_offset
= -3600*(wxDateTime::GMT0
- tz
);
796 case wxDateTime::GMT0
:
797 case wxDateTime::GMT1
:
798 case wxDateTime::GMT2
:
799 case wxDateTime::GMT3
:
800 case wxDateTime::GMT4
:
801 case wxDateTime::GMT5
:
802 case wxDateTime::GMT6
:
803 case wxDateTime::GMT7
:
804 case wxDateTime::GMT8
:
805 case wxDateTime::GMT9
:
806 case wxDateTime::GMT10
:
807 case wxDateTime::GMT11
:
808 case wxDateTime::GMT12
:
809 case wxDateTime::GMT13
:
810 m_offset
= 3600*(tz
- wxDateTime::GMT0
);
813 case wxDateTime::A_CST
:
814 // Central Standard Time in use in Australia = UTC + 9.5
815 m_offset
= 60l*(9*MIN_PER_HOUR
+ MIN_PER_HOUR
/2);
819 wxFAIL_MSG( _T("unknown time zone") );
823 // ----------------------------------------------------------------------------
825 // ----------------------------------------------------------------------------
828 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
)
830 if ( year
== Inv_Year
)
831 year
= GetCurrentYear();
833 if ( cal
== Gregorian
)
835 // in Gregorian calendar leap years are those divisible by 4 except
836 // those divisible by 100 unless they're also divisible by 400
837 // (in some countries, like Russia and Greece, additional corrections
838 // exist, but they won't manifest themselves until 2700)
839 return (year
% 4 == 0) && ((year
% 100 != 0) || (year
% 400 == 0));
841 else if ( cal
== Julian
)
843 // in Julian calendar the rule is simpler
844 return year
% 4 == 0;
848 wxFAIL_MSG(_T("unknown calendar"));
855 int wxDateTime::GetCentury(int year
)
857 return year
> 0 ? year
/ 100 : year
/ 100 - 1;
861 int wxDateTime::ConvertYearToBC(int year
)
864 return year
> 0 ? year
: year
- 1;
868 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
)
873 return Now().GetYear();
876 wxFAIL_MSG(_T("TODO"));
880 wxFAIL_MSG(_T("unsupported calendar"));
888 wxDateTime::Month
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
)
893 return Now().GetMonth();
896 wxFAIL_MSG(_T("TODO"));
900 wxFAIL_MSG(_T("unsupported calendar"));
908 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(int year
, Calendar cal
)
910 if ( year
== Inv_Year
)
912 // take the current year if none given
913 year
= GetCurrentYear();
920 return IsLeapYear(year
) ? 366 : 365;
923 wxFAIL_MSG(_T("unsupported calendar"));
931 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(wxDateTime::Month month
,
933 wxDateTime::Calendar cal
)
935 wxCHECK_MSG( month
< MONTHS_IN_YEAR
, 0, _T("invalid month") );
937 if ( cal
== Gregorian
|| cal
== Julian
)
939 if ( year
== Inv_Year
)
941 // take the current year if none given
942 year
= GetCurrentYear();
945 return GetNumOfDaysInMonth(year
, month
);
949 wxFAIL_MSG(_T("unsupported calendar"));
956 wxString
wxDateTime::GetMonthName(wxDateTime::Month month
,
957 wxDateTime::NameFlags flags
)
959 wxCHECK_MSG( month
!= Inv_Month
, wxEmptyString
, _T("invalid month") );
961 // notice that we must set all the fields to avoid confusing libc (GNU one
962 // gets confused to a crash if we don't do this)
967 return CallStrftime(flags
== Name_Abbr
? _T("%b") : _T("%B"), &tm
);
968 #else // !HAVE_STRFTIME
973 ret
= (flags
== Name_Abbr
? wxT("Jan"): wxT("January"));
976 ret
= (flags
== Name_Abbr
? wxT("Feb"): wxT("Febuary"));
979 ret
= (flags
== Name_Abbr
? wxT("Mar"): wxT("March"));
982 ret
= (flags
== Name_Abbr
? wxT("Apr"): wxT("April"));
985 ret
= (flags
== Name_Abbr
? wxT("May"): wxT("May"));
988 ret
= (flags
== Name_Abbr
? wxT("Jun"): wxT("June"));
991 ret
= (flags
== Name_Abbr
? wxT("Jul"): wxT("July"));
994 ret
= (flags
== Name_Abbr
? wxT("Aug"): wxT("August"));
997 ret
= (flags
== Name_Abbr
? wxT("Sep"): wxT("September"));
1000 ret
= (flags
== Name_Abbr
? wxT("Oct"): wxT("October"));
1003 ret
= (flags
== Name_Abbr
? wxT("Nov"): wxT("November"));
1006 ret
= (flags
== Name_Abbr
? wxT("Dec"): wxT("December"));
1010 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
1014 wxString
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
,
1015 wxDateTime::NameFlags flags
)
1017 wxCHECK_MSG( wday
!= Inv_WeekDay
, wxEmptyString
, _T("invalid weekday") );
1018 #ifdef HAVE_STRFTIME
1019 // take some arbitrary Sunday (but notice that the day should be such that
1020 // after adding wday to it below we still have a valid date, e.g. don't
1028 // and offset it by the number of days needed to get the correct wday
1031 // call mktime() to normalize it...
1034 // ... and call strftime()
1035 return CallStrftime(flags
== Name_Abbr
? _T("%a") : _T("%A"), &tm
);
1036 #else // !HAVE_STRFTIME
1041 ret
= (flags
== Name_Abbr
? wxT("Sun") : wxT("Sunday"));
1044 ret
= (flags
== Name_Abbr
? wxT("Mon") : wxT("Monday"));
1047 ret
= (flags
== Name_Abbr
? wxT("Tue") : wxT("Tuesday"));
1050 ret
= (flags
== Name_Abbr
? wxT("Wed") : wxT("Wednesday"));
1053 ret
= (flags
== Name_Abbr
? wxT("Thu") : wxT("Thursday"));
1056 ret
= (flags
== Name_Abbr
? wxT("Fri") : wxT("Friday"));
1059 ret
= (flags
== Name_Abbr
? wxT("Sat") : wxT("Saturday"));
1063 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
1067 void wxDateTime::GetAmPmStrings(wxString
*am
, wxString
*pm
)
1072 // @Note: Do not call 'CallStrftime' here! CallStrftime checks the return code
1073 // and causes an assertion failed if the buffer is to small (which is good) - OR -
1074 // if strftime does not return anything because the format string is invalid - OR -
1075 // if there are no 'am' / 'pm' tokens defined for the current locale (which is not good).
1076 // wxDateTime::ParseTime will try several different formats to parse the time.
1077 // As a result, GetAmPmStrings might get called, even if the current locale
1078 // does not define any 'am' / 'pm' tokens. In this case, wxStrftime would
1079 // assert, even though it is a perfectly legal use.
1082 if (wxStrftime(buffer
, sizeof(buffer
)/sizeof(wxChar
), _T("%p"), &tm
) > 0)
1083 *am
= wxString(buffer
);
1090 if (wxStrftime(buffer
, sizeof(buffer
)/sizeof(wxChar
), _T("%p"), &tm
) > 0)
1091 *pm
= wxString(buffer
);
1097 // ----------------------------------------------------------------------------
1098 // Country stuff: date calculations depend on the country (DST, work days,
1099 // ...), so we need to know which rules to follow.
1100 // ----------------------------------------------------------------------------
1103 wxDateTime::Country
wxDateTime::GetCountry()
1105 // TODO use LOCALE_ICOUNTRY setting under Win32
1107 if ( ms_country
== Country_Unknown
)
1109 // try to guess from the time zone name
1110 time_t t
= time(NULL
);
1112 struct tm
*tm
= wxLocaltime_r(&t
, &tmstruct
);
1114 wxString tz
= CallStrftime(_T("%Z"), tm
);
1115 if ( tz
== _T("WET") || tz
== _T("WEST") )
1119 else if ( tz
== _T("CET") || tz
== _T("CEST") )
1121 ms_country
= Country_EEC
;
1123 else if ( tz
== _T("MSK") || tz
== _T("MSD") )
1125 ms_country
= Russia
;
1127 else if ( tz
== _T("AST") || tz
== _T("ADT") ||
1128 tz
== _T("EST") || tz
== _T("EDT") ||
1129 tz
== _T("CST") || tz
== _T("CDT") ||
1130 tz
== _T("MST") || tz
== _T("MDT") ||
1131 tz
== _T("PST") || tz
== _T("PDT") )
1137 // well, choose a default one
1141 #else // __WXWINCE__
1143 #endif // !__WXWINCE__/__WXWINCE__
1149 void wxDateTime::SetCountry(wxDateTime::Country country
)
1151 ms_country
= country
;
1155 bool wxDateTime::IsWestEuropeanCountry(Country country
)
1157 if ( country
== Country_Default
)
1159 country
= GetCountry();
1162 return (Country_WesternEurope_Start
<= country
) &&
1163 (country
<= Country_WesternEurope_End
);
1166 // ----------------------------------------------------------------------------
1167 // DST calculations: we use 3 different rules for the West European countries,
1168 // USA and for the rest of the world. This is undoubtedly false for many
1169 // countries, but I lack the necessary info (and the time to gather it),
1170 // please add the other rules here!
1171 // ----------------------------------------------------------------------------
1174 bool wxDateTime::IsDSTApplicable(int year
, Country country
)
1176 if ( year
== Inv_Year
)
1178 // take the current year if none given
1179 year
= GetCurrentYear();
1182 if ( country
== Country_Default
)
1184 country
= GetCountry();
1191 // DST was first observed in the US and UK during WWI, reused
1192 // during WWII and used again since 1966
1193 return year
>= 1966 ||
1194 (year
>= 1942 && year
<= 1945) ||
1195 (year
== 1918 || year
== 1919);
1198 // assume that it started after WWII
1204 wxDateTime
wxDateTime::GetBeginDST(int year
, Country country
)
1206 if ( year
== Inv_Year
)
1208 // take the current year if none given
1209 year
= GetCurrentYear();
1212 if ( country
== Country_Default
)
1214 country
= GetCountry();
1217 if ( !IsDSTApplicable(year
, country
) )
1219 return wxInvalidDateTime
;
1224 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1226 // DST begins at 1 a.m. GMT on the last Sunday of March
1227 if ( !dt
.SetToLastWeekDay(Sun
, Mar
, year
) )
1230 wxFAIL_MSG( _T("no last Sunday in March?") );
1233 dt
+= wxTimeSpan::Hours(1);
1235 else switch ( country
)
1242 // don't know for sure - assume it was in effect all year
1247 dt
.Set(1, Jan
, year
);
1251 // DST was installed Feb 2, 1942 by the Congress
1252 dt
.Set(2, Feb
, year
);
1255 // Oil embargo changed the DST period in the US
1257 dt
.Set(6, Jan
, 1974);
1261 dt
.Set(23, Feb
, 1975);
1265 // before 1986, DST begun on the last Sunday of April, but
1266 // in 1986 Reagan changed it to begin at 2 a.m. of the
1267 // first Sunday in April
1270 if ( !dt
.SetToLastWeekDay(Sun
, Apr
, year
) )
1273 wxFAIL_MSG( _T("no first Sunday in April?") );
1276 else if ( year
> 2006 )
1277 // Energy Policy Act of 2005, Pub. L. no. 109-58, 119 Stat 594 (2005).
1278 // Starting in 2007, daylight time begins in the United States on the
1279 // second Sunday in March and ends on the first Sunday in November
1281 if ( !dt
.SetToWeekDay(Sun
, 2, Mar
, year
) )
1284 wxFAIL_MSG( _T("no second Sunday in March?") );
1289 if ( !dt
.SetToWeekDay(Sun
, 1, Apr
, year
) )
1292 wxFAIL_MSG( _T("no first Sunday in April?") );
1296 dt
+= wxTimeSpan::Hours(2);
1298 // TODO what about timezone??
1304 // assume Mar 30 as the start of the DST for the rest of the world
1305 // - totally bogus, of course
1306 dt
.Set(30, Mar
, year
);
1313 wxDateTime
wxDateTime::GetEndDST(int year
, Country country
)
1315 if ( year
== Inv_Year
)
1317 // take the current year if none given
1318 year
= GetCurrentYear();
1321 if ( country
== Country_Default
)
1323 country
= GetCountry();
1326 if ( !IsDSTApplicable(year
, country
) )
1328 return wxInvalidDateTime
;
1333 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1335 // DST ends at 1 a.m. GMT on the last Sunday of October
1336 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1338 // weirder and weirder...
1339 wxFAIL_MSG( _T("no last Sunday in October?") );
1342 dt
+= wxTimeSpan::Hours(1);
1344 else switch ( country
)
1351 // don't know for sure - assume it was in effect all year
1355 dt
.Set(31, Dec
, year
);
1359 // the time was reset after the end of the WWII
1360 dt
.Set(30, Sep
, year
);
1363 default: // default for switch (year)
1365 // Energy Policy Act of 2005, Pub. L. no. 109-58, 119 Stat 594 (2005).
1366 // Starting in 2007, daylight time begins in the United States on the
1367 // second Sunday in March and ends on the first Sunday in November
1369 if ( !dt
.SetToWeekDay(Sun
, 1, Nov
, year
) )
1372 wxFAIL_MSG( _T("no first Sunday in November?") );
1377 // DST ends at 2 a.m. on the last Sunday of October
1379 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1381 // weirder and weirder...
1382 wxFAIL_MSG( _T("no last Sunday in October?") );
1386 dt
+= wxTimeSpan::Hours(2);
1388 // TODO: what about timezone??
1392 default: // default for switch (country)
1393 // assume October 26th as the end of the DST - totally bogus too
1394 dt
.Set(26, Oct
, year
);
1400 // ----------------------------------------------------------------------------
1401 // constructors and assignment operators
1402 // ----------------------------------------------------------------------------
1404 // return the current time with ms precision
1405 /* static */ wxDateTime
wxDateTime::UNow()
1407 return wxDateTime(wxGetLocalTimeMillis());
1410 // the values in the tm structure contain the local time
1411 wxDateTime
& wxDateTime::Set(const struct tm
& tm
)
1414 time_t timet
= mktime(&tm2
);
1416 if ( timet
== (time_t)-1 )
1418 // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
1419 // less than timezone - try to make it work for this case
1420 if ( tm2
.tm_year
== 70 && tm2
.tm_mon
== 0 && tm2
.tm_mday
== 1 )
1422 return Set((time_t)(
1424 tm2
.tm_hour
* MIN_PER_HOUR
* SEC_PER_MIN
+
1425 tm2
.tm_min
* SEC_PER_MIN
+
1429 wxFAIL_MSG( _T("mktime() failed") );
1431 *this = wxInvalidDateTime
;
1441 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
,
1442 wxDateTime_t minute
,
1443 wxDateTime_t second
,
1444 wxDateTime_t millisec
)
1446 // we allow seconds to be 61 to account for the leap seconds, even if we
1447 // don't use them really
1448 wxDATETIME_CHECK( hour
< 24 &&
1452 _T("Invalid time in wxDateTime::Set()") );
1454 // get the current date from system
1456 struct tm
*tm
= GetTmNow(&tmstruct
);
1458 wxDATETIME_CHECK( tm
, _T("wxLocaltime_r() failed") );
1460 // make a copy so it isn't clobbered by the call to mktime() below
1465 tm1
.tm_min
= minute
;
1466 tm1
.tm_sec
= second
;
1468 // and the DST in case it changes on this date
1471 if ( tm2
.tm_isdst
!= tm1
.tm_isdst
)
1472 tm1
.tm_isdst
= tm2
.tm_isdst
;
1476 // and finally adjust milliseconds
1477 return SetMillisecond(millisec
);
1480 wxDateTime
& wxDateTime::Set(wxDateTime_t day
,
1484 wxDateTime_t minute
,
1485 wxDateTime_t second
,
1486 wxDateTime_t millisec
)
1488 wxDATETIME_CHECK( hour
< 24 &&
1492 _T("Invalid time in wxDateTime::Set()") );
1494 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1496 wxDATETIME_CHECK( (0 < day
) && (day
<= GetNumberOfDays(month
, year
)),
1497 _T("Invalid date in wxDateTime::Set()") );
1499 // the range of time_t type (inclusive)
1500 static const int yearMinInRange
= 1970;
1501 static const int yearMaxInRange
= 2037;
1503 // test only the year instead of testing for the exact end of the Unix
1504 // time_t range - it doesn't bring anything to do more precise checks
1505 if ( year
>= yearMinInRange
&& year
<= yearMaxInRange
)
1507 // use the standard library version if the date is in range - this is
1508 // probably more efficient than our code
1510 tm
.tm_year
= year
- 1900;
1516 tm
.tm_isdst
= -1; // mktime() will guess it
1520 // and finally adjust milliseconds
1522 SetMillisecond(millisec
);
1528 // do time calculations ourselves: we want to calculate the number of
1529 // milliseconds between the given date and the epoch
1531 // get the JDN for the midnight of this day
1532 m_time
= GetTruncatedJDN(day
, month
, year
);
1533 m_time
-= EPOCH_JDN
;
1534 m_time
*= SECONDS_PER_DAY
* TIME_T_FACTOR
;
1536 // JDN corresponds to GMT, we take localtime
1537 Add(wxTimeSpan(hour
, minute
, second
+ GetTimeZone(), millisec
));
1543 wxDateTime
& wxDateTime::Set(double jdn
)
1545 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1547 jdn
-= EPOCH_JDN
+ 0.5;
1549 m_time
.Assign(jdn
*MILLISECONDS_PER_DAY
);
1551 // JDNs always are in UTC, so we don't need any adjustments for time zone
1556 wxDateTime
& wxDateTime::ResetTime()
1560 if ( tm
.hour
|| tm
.min
|| tm
.sec
|| tm
.msec
)
1573 wxDateTime
wxDateTime::GetDateOnly() const
1580 return wxDateTime(tm
);
1583 // ----------------------------------------------------------------------------
1584 // DOS Date and Time Format functions
1585 // ----------------------------------------------------------------------------
1586 // the dos date and time value is an unsigned 32 bit value in the format:
1587 // YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
1589 // Y = year offset from 1980 (0-127)
1591 // D = day of month (1-31)
1593 // m = minute (0-59)
1594 // s = bisecond (0-29) each bisecond indicates two seconds
1595 // ----------------------------------------------------------------------------
1597 wxDateTime
& wxDateTime::SetFromDOS(unsigned long ddt
)
1602 long year
= ddt
& 0xFE000000;
1607 long month
= ddt
& 0x1E00000;
1612 long day
= ddt
& 0x1F0000;
1616 long hour
= ddt
& 0xF800;
1620 long minute
= ddt
& 0x7E0;
1624 long second
= ddt
& 0x1F;
1625 tm
.tm_sec
= second
* 2;
1627 return Set(mktime(&tm
));
1630 unsigned long wxDateTime::GetAsDOS() const
1633 time_t ticks
= GetTicks();
1635 struct tm
*tm
= wxLocaltime_r(&ticks
, &tmstruct
);
1636 wxCHECK_MSG( tm
, ULONG_MAX
, _T("time can't be represented in DOS format") );
1638 long year
= tm
->tm_year
;
1642 long month
= tm
->tm_mon
;
1646 long day
= tm
->tm_mday
;
1649 long hour
= tm
->tm_hour
;
1652 long minute
= tm
->tm_min
;
1655 long second
= tm
->tm_sec
;
1658 ddt
= year
| month
| day
| hour
| minute
| second
;
1662 // ----------------------------------------------------------------------------
1663 // time_t <-> broken down time conversions
1664 // ----------------------------------------------------------------------------
1666 wxDateTime::Tm
wxDateTime::GetTm(const TimeZone
& tz
) const
1668 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1670 time_t time
= GetTicks();
1671 if ( time
!= (time_t)-1 )
1673 // use C RTL functions
1676 if ( tz
.GetOffset() == -GetTimeZone() )
1678 // we are working with local time
1679 tm
= wxLocaltime_r(&time
, &tmstruct
);
1681 // should never happen
1682 wxCHECK_MSG( tm
, Tm(), _T("wxLocaltime_r() failed") );
1686 time
+= (time_t)tz
.GetOffset();
1687 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
1688 int time2
= (int) time
;
1694 tm
= wxGmtime_r(&time
, &tmstruct
);
1696 // should never happen
1697 wxCHECK_MSG( tm
, Tm(), _T("wxGmtime_r() failed") );
1701 tm
= (struct tm
*)NULL
;
1707 // adjust the milliseconds
1709 long timeOnly
= (m_time
% MILLISECONDS_PER_DAY
).ToLong();
1710 tm2
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1713 //else: use generic code below
1716 // remember the time and do the calculations with the date only - this
1717 // eliminates rounding errors of the floating point arithmetics
1719 wxLongLong timeMidnight
= m_time
+ tz
.GetOffset() * 1000;
1721 long timeOnly
= (timeMidnight
% MILLISECONDS_PER_DAY
).ToLong();
1723 // we want to always have positive time and timeMidnight to be really
1724 // the midnight before it
1727 timeOnly
= MILLISECONDS_PER_DAY
+ timeOnly
;
1730 timeMidnight
-= timeOnly
;
1732 // calculate the Gregorian date from JDN for the midnight of our date:
1733 // this will yield day, month (in 1..12 range) and year
1735 // actually, this is the JDN for the noon of the previous day
1736 long jdn
= (timeMidnight
/ MILLISECONDS_PER_DAY
).ToLong() + EPOCH_JDN
;
1738 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1740 wxASSERT_MSG( jdn
> -2, _T("JDN out of range") );
1742 // calculate the century
1743 long temp
= (jdn
+ JDN_OFFSET
) * 4 - 1;
1744 long century
= temp
/ DAYS_PER_400_YEARS
;
1746 // then the year and day of year (1 <= dayOfYear <= 366)
1747 temp
= ((temp
% DAYS_PER_400_YEARS
) / 4) * 4 + 3;
1748 long year
= (century
* 100) + (temp
/ DAYS_PER_4_YEARS
);
1749 long dayOfYear
= (temp
% DAYS_PER_4_YEARS
) / 4 + 1;
1751 // and finally the month and day of the month
1752 temp
= dayOfYear
* 5 - 3;
1753 long month
= temp
/ DAYS_PER_5_MONTHS
;
1754 long day
= (temp
% DAYS_PER_5_MONTHS
) / 5 + 1;
1756 // month is counted from March - convert to normal
1767 // year is offset by 4800
1770 // check that the algorithm gave us something reasonable
1771 wxASSERT_MSG( (0 < month
) && (month
<= 12), _T("invalid month") );
1772 wxASSERT_MSG( (1 <= day
) && (day
< 32), _T("invalid day") );
1774 // construct Tm from these values
1776 tm
.year
= (int)year
;
1777 tm
.mon
= (Month
)(month
- 1); // algorithm yields 1 for January, not 0
1778 tm
.mday
= (wxDateTime_t
)day
;
1779 tm
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1780 timeOnly
-= tm
.msec
;
1781 timeOnly
/= 1000; // now we have time in seconds
1783 tm
.sec
= (wxDateTime_t
)(timeOnly
% SEC_PER_MIN
);
1785 timeOnly
/= SEC_PER_MIN
; // now we have time in minutes
1787 tm
.min
= (wxDateTime_t
)(timeOnly
% MIN_PER_HOUR
);
1790 tm
.hour
= (wxDateTime_t
)(timeOnly
/ MIN_PER_HOUR
);
1795 wxDateTime
& wxDateTime::SetYear(int year
)
1797 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1806 wxDateTime
& wxDateTime::SetMonth(Month month
)
1808 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1817 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
)
1819 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1828 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
)
1830 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1839 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
)
1841 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1850 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
)
1852 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1861 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
)
1863 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1865 // we don't need to use GetTm() for this one
1866 m_time
-= m_time
% 1000l;
1867 m_time
+= millisecond
;
1872 // ----------------------------------------------------------------------------
1873 // wxDateTime arithmetics
1874 // ----------------------------------------------------------------------------
1876 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
)
1880 tm
.year
+= diff
.GetYears();
1881 tm
.AddMonths(diff
.GetMonths());
1883 // check that the resulting date is valid
1884 if ( tm
.mday
> GetNumOfDaysInMonth(tm
.year
, tm
.mon
) )
1886 // We suppose that when adding one month to Jan 31 we want to get Feb
1887 // 28 (or 29), i.e. adding a month to the last day of the month should
1888 // give the last day of the next month which is quite logical.
1890 // Unfortunately, there is no logic way to understand what should
1891 // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1892 // We make it Feb 28 (last day too), but it is highly questionable.
1893 tm
.mday
= GetNumOfDaysInMonth(tm
.year
, tm
.mon
);
1896 tm
.AddDays(diff
.GetTotalDays());
1900 wxASSERT_MSG( IsSameTime(tm
),
1901 _T("Add(wxDateSpan) shouldn't modify time") );
1906 // ----------------------------------------------------------------------------
1907 // Weekday and monthday stuff
1908 // ----------------------------------------------------------------------------
1910 // convert Sun, Mon, ..., Sat into 6, 0, ..., 5
1911 static inline int ConvertWeekDayToMondayBase(int wd
)
1913 return wd
== wxDateTime::Sun
? 6 : wd
- 1;
1918 wxDateTime::SetToWeekOfYear(int year
, wxDateTime_t numWeek
, WeekDay wd
)
1920 wxASSERT_MSG( numWeek
> 0,
1921 _T("invalid week number: weeks are counted from 1") );
1923 // Jan 4 always lies in the 1st week of the year
1924 wxDateTime
dt(4, Jan
, year
);
1925 dt
.SetToWeekDayInSameWeek(wd
);
1926 dt
+= wxDateSpan::Weeks(numWeek
- 1);
1931 #if WXWIN_COMPATIBILITY_2_6
1932 // use a separate function to avoid warnings about using deprecated
1933 // SetToTheWeek in GetWeek below
1935 SetToTheWeek(int year
,
1936 wxDateTime::wxDateTime_t numWeek
,
1937 wxDateTime::WeekDay weekday
,
1938 wxDateTime::WeekFlags flags
)
1940 // Jan 4 always lies in the 1st week of the year
1941 wxDateTime
dt(4, wxDateTime::Jan
, year
);
1942 dt
.SetToWeekDayInSameWeek(weekday
, flags
);
1943 dt
+= wxDateSpan::Weeks(numWeek
- 1);
1948 bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek
,
1952 int year
= GetYear();
1953 *this = ::SetToTheWeek(year
, numWeek
, weekday
, flags
);
1954 if ( GetYear() != year
)
1956 // oops... numWeek was too big
1963 wxDateTime
wxDateTime::GetWeek(wxDateTime_t numWeek
,
1965 WeekFlags flags
) const
1967 return ::SetToTheWeek(GetYear(), numWeek
, weekday
, flags
);
1969 #endif // WXWIN_COMPATIBILITY_2_6
1971 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
,
1974 // take the current month/year if none specified
1975 if ( year
== Inv_Year
)
1977 if ( month
== Inv_Month
)
1980 return Set(GetNumOfDaysInMonth(year
, month
), month
, year
);
1983 wxDateTime
& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday
, WeekFlags flags
)
1985 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1987 int wdayDst
= weekday
,
1988 wdayThis
= GetWeekDay();
1989 if ( wdayDst
== wdayThis
)
1995 if ( flags
== Default_First
)
1997 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
2000 // the logic below based on comparing weekday and wdayThis works if Sun (0)
2001 // is the first day in the week, but breaks down for Monday_First case so
2002 // we adjust the week days in this case
2003 if ( flags
== Monday_First
)
2005 if ( wdayThis
== Sun
)
2007 if ( wdayDst
== Sun
)
2010 //else: Sunday_First, nothing to do
2012 // go forward or back in time to the day we want
2013 if ( wdayDst
< wdayThis
)
2015 return Subtract(wxDateSpan::Days(wdayThis
- wdayDst
));
2017 else // weekday > wdayThis
2019 return Add(wxDateSpan::Days(wdayDst
- wdayThis
));
2023 wxDateTime
& wxDateTime::SetToNextWeekDay(WeekDay weekday
)
2025 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
2028 WeekDay wdayThis
= GetWeekDay();
2029 if ( weekday
== wdayThis
)
2034 else if ( weekday
< wdayThis
)
2036 // need to advance a week
2037 diff
= 7 - (wdayThis
- weekday
);
2039 else // weekday > wdayThis
2041 diff
= weekday
- wdayThis
;
2044 return Add(wxDateSpan::Days(diff
));
2047 wxDateTime
& wxDateTime::SetToPrevWeekDay(WeekDay weekday
)
2049 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
2052 WeekDay wdayThis
= GetWeekDay();
2053 if ( weekday
== wdayThis
)
2058 else if ( weekday
> wdayThis
)
2060 // need to go to previous week
2061 diff
= 7 - (weekday
- wdayThis
);
2063 else // weekday < wdayThis
2065 diff
= wdayThis
- weekday
;
2068 return Subtract(wxDateSpan::Days(diff
));
2071 bool wxDateTime::SetToWeekDay(WeekDay weekday
,
2076 wxCHECK_MSG( weekday
!= Inv_WeekDay
, false, _T("invalid weekday") );
2078 // we don't check explicitly that -5 <= n <= 5 because we will return false
2079 // anyhow in such case - but may be should still give an assert for it?
2081 // take the current month/year if none specified
2082 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
2086 // TODO this probably could be optimised somehow...
2090 // get the first day of the month
2091 dt
.Set(1, month
, year
);
2094 WeekDay wdayFirst
= dt
.GetWeekDay();
2096 // go to the first weekday of the month
2097 int diff
= weekday
- wdayFirst
;
2101 // add advance n-1 weeks more
2104 dt
+= wxDateSpan::Days(diff
);
2106 else // count from the end of the month
2108 // get the last day of the month
2109 dt
.SetToLastMonthDay(month
, year
);
2112 WeekDay wdayLast
= dt
.GetWeekDay();
2114 // go to the last weekday of the month
2115 int diff
= wdayLast
- weekday
;
2119 // and rewind n-1 weeks from there
2122 dt
-= wxDateSpan::Days(diff
);
2125 // check that it is still in the same month
2126 if ( dt
.GetMonth() == month
)
2134 // no such day in this month
2140 wxDateTime::wxDateTime_t
GetDayOfYearFromTm(const wxDateTime::Tm
& tm
)
2142 return (wxDateTime::wxDateTime_t
)(gs_cumulatedDays
[wxDateTime::IsLeapYear(tm
.year
)][tm
.mon
] + tm
.mday
);
2145 wxDateTime::wxDateTime_t
wxDateTime::GetDayOfYear(const TimeZone
& tz
) const
2147 return GetDayOfYearFromTm(GetTm(tz
));
2150 wxDateTime::wxDateTime_t
2151 wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags
, const TimeZone
& tz
) const
2153 if ( flags
== Default_First
)
2155 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
2159 wxDateTime_t nDayInYear
= GetDayOfYearFromTm(tm
);
2161 int wdTarget
= GetWeekDay(tz
);
2162 int wdYearStart
= wxDateTime(1, Jan
, GetYear()).GetWeekDay();
2164 if ( flags
== Sunday_First
)
2166 // FIXME: First week is not calculated correctly.
2167 week
= (nDayInYear
- wdTarget
+ 7) / 7;
2168 if ( wdYearStart
== Wed
|| wdYearStart
== Thu
)
2171 else // week starts with monday
2173 // adjust the weekdays to non-US style.
2174 wdYearStart
= ConvertWeekDayToMondayBase(wdYearStart
);
2175 wdTarget
= ConvertWeekDayToMondayBase(wdTarget
);
2177 // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html:
2179 // Week 01 of a year is per definition the first week that has the
2180 // Thursday in this year, which is equivalent to the week that
2181 // contains the fourth day of January. In other words, the first
2182 // week of a new year is the week that has the majority of its
2183 // days in the new year. Week 01 might also contain days from the
2184 // previous year and the week before week 01 of a year is the last
2185 // week (52 or 53) of the previous year even if it contains days
2186 // from the new year. A week starts with Monday (day 1) and ends
2187 // with Sunday (day 7).
2190 // if Jan 1 is Thursday or less, it is in the first week of this year
2191 if ( wdYearStart
< 4 )
2193 // count the number of entire weeks between Jan 1 and this date
2194 week
= (nDayInYear
+ wdYearStart
+ 6 - wdTarget
)/7;
2196 // be careful to check for overflow in the next year
2197 if ( week
== 53 && tm
.mday
- wdTarget
> 28 )
2200 else // Jan 1 is in the last week of the previous year
2202 // check if we happen to be at the last week of previous year:
2203 if ( tm
.mon
== Jan
&& tm
.mday
< 8 - wdYearStart
)
2204 week
= wxDateTime(31, Dec
, GetYear()-1).GetWeekOfYear();
2206 week
= (nDayInYear
+ wdYearStart
- 1 - wdTarget
)/7;
2210 return (wxDateTime::wxDateTime_t
)week
;
2213 wxDateTime::wxDateTime_t
wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags
,
2214 const TimeZone
& tz
) const
2217 wxDateTime dtMonthStart
= wxDateTime(1, tm
.mon
, tm
.year
);
2218 int nWeek
= GetWeekOfYear(flags
) - dtMonthStart
.GetWeekOfYear(flags
) + 1;
2221 // this may happen for January when Jan, 1 is the last week of the
2223 nWeek
+= IsLeapYear(tm
.year
- 1) ? 53 : 52;
2226 return (wxDateTime::wxDateTime_t
)nWeek
;
2229 wxDateTime
& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday
)
2231 int year
= GetYear();
2232 wxDATETIME_CHECK( (0 < yday
) && (yday
<= GetNumberOfDays(year
)),
2233 _T("invalid year day") );
2235 bool isLeap
= IsLeapYear(year
);
2236 for ( Month mon
= Jan
; mon
< Inv_Month
; wxNextMonth(mon
) )
2238 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
2239 // don't need it neither - because of the CHECK above we know that
2240 // yday lies in December then
2241 if ( (mon
== Dec
) || (yday
<= gs_cumulatedDays
[isLeap
][mon
+ 1]) )
2243 Set((wxDateTime::wxDateTime_t
)(yday
- gs_cumulatedDays
[isLeap
][mon
]), mon
, year
);
2252 // ----------------------------------------------------------------------------
2253 // Julian day number conversion and related stuff
2254 // ----------------------------------------------------------------------------
2256 double wxDateTime::GetJulianDayNumber() const
2258 return m_time
.ToDouble() / MILLISECONDS_PER_DAY
+ EPOCH_JDN
+ 0.5;
2261 double wxDateTime::GetRataDie() const
2263 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
2264 return GetJulianDayNumber() - 1721119.5 - 306;
2267 // ----------------------------------------------------------------------------
2268 // timezone and DST stuff
2269 // ----------------------------------------------------------------------------
2271 int wxDateTime::IsDST(wxDateTime::Country country
) const
2273 wxCHECK_MSG( country
== Country_Default
, -1,
2274 _T("country support not implemented") );
2276 // use the C RTL for the dates in the standard range
2277 time_t timet
= GetTicks();
2278 if ( timet
!= (time_t)-1 )
2281 tm
*tm
= wxLocaltime_r(&timet
, &tmstruct
);
2283 wxCHECK_MSG( tm
, -1, _T("wxLocaltime_r() failed") );
2285 return tm
->tm_isdst
;
2289 int year
= GetYear();
2291 if ( !IsDSTApplicable(year
, country
) )
2293 // no DST time in this year in this country
2297 return IsBetween(GetBeginDST(year
, country
), GetEndDST(year
, country
));
2301 wxDateTime
& wxDateTime::MakeTimezone(const TimeZone
& tz
, bool noDST
)
2303 long secDiff
= GetTimeZone() + tz
.GetOffset();
2305 // we need to know whether DST is or not in effect for this date unless
2306 // the test disabled by the caller
2307 if ( !noDST
&& (IsDST() == 1) )
2309 // FIXME we assume that the DST is always shifted by 1 hour
2313 return Add(wxTimeSpan::Seconds(secDiff
));
2316 wxDateTime
& wxDateTime::MakeFromTimezone(const TimeZone
& tz
, bool noDST
)
2318 long secDiff
= GetTimeZone() + tz
.GetOffset();
2320 // we need to know whether DST is or not in effect for this date unless
2321 // the test disabled by the caller
2322 if ( !noDST
&& (IsDST() == 1) )
2324 // FIXME we assume that the DST is always shifted by 1 hour
2328 return Subtract(wxTimeSpan::Seconds(secDiff
));
2331 // ----------------------------------------------------------------------------
2332 // wxDateTime to/from text representations
2333 // ----------------------------------------------------------------------------
2335 wxString
wxDateTime::Format(const wxString
& format
, const TimeZone
& tz
) const
2337 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
2338 _T("NULL format in wxDateTime::Format") );
2340 // we have to use our own implementation if the date is out of range of
2341 // strftime() or if we use non standard specificators
2342 #ifdef HAVE_STRFTIME
2343 time_t time
= GetTicks();
2345 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
2350 if ( tz
.GetOffset() == -GetTimeZone() )
2352 // we are working with local time
2353 tm
= wxLocaltime_r(&time
, &tmstruct
);
2355 // should never happen
2356 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxLocaltime_r() failed") );
2360 time
+= (int)tz
.GetOffset();
2362 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
2363 int time2
= (int) time
;
2369 tm
= wxGmtime_r(&time
, &tmstruct
);
2371 // should never happen
2372 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxGmtime_r() failed") );
2376 tm
= (struct tm
*)NULL
;
2382 return CallStrftime(format
, tm
);
2385 //else: use generic code below
2386 #endif // HAVE_STRFTIME
2388 // we only parse ANSI C format specifications here, no POSIX 2
2389 // complications, no GNU extensions but we do add support for a "%l" format
2390 // specifier allowing to get the number of milliseconds
2393 // used for calls to strftime() when we only deal with time
2394 struct tm tmTimeOnly
;
2395 tmTimeOnly
.tm_hour
= tm
.hour
;
2396 tmTimeOnly
.tm_min
= tm
.min
;
2397 tmTimeOnly
.tm_sec
= tm
.sec
;
2398 tmTimeOnly
.tm_wday
= 0;
2399 tmTimeOnly
.tm_yday
= 0;
2400 tmTimeOnly
.tm_mday
= 1; // any date will do
2401 tmTimeOnly
.tm_mon
= 0;
2402 tmTimeOnly
.tm_year
= 76;
2403 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
2405 wxString tmp
, res
, fmt
;
2406 for ( wxString::const_iterator p
= format
.begin(); p
!= format
.end(); ++p
)
2408 if ( *p
!= _T('%') )
2416 // set the default format
2417 switch ( (*++p
).GetValue() )
2419 case _T('Y'): // year has 4 digits
2423 case _T('j'): // day of year has 3 digits
2424 case _T('l'): // milliseconds have 3 digits
2428 case _T('w'): // week day as number has only one
2433 // it's either another valid format specifier in which case
2434 // the format is "%02d" (for all the rest) or we have the
2435 // field width preceding the format in which case it will
2436 // override the default format anyhow
2440 bool restart
= true;
2445 // start of the format specification
2446 switch ( (*p
).GetValue() )
2448 case _T('a'): // a weekday name
2450 // second parameter should be true for abbreviated names
2451 res
+= GetWeekDayName(tm
.GetWeekDay(),
2452 *p
== _T('a') ? Name_Abbr
: Name_Full
);
2455 case _T('b'): // a month name
2457 res
+= GetMonthName(tm
.mon
,
2458 *p
== _T('b') ? Name_Abbr
: Name_Full
);
2461 case _T('c'): // locale default date and time representation
2462 case _T('x'): // locale default date representation
2463 #ifdef HAVE_STRFTIME
2465 // the problem: there is no way to know what do these format
2466 // specifications correspond to for the current locale.
2468 // the solution: use a hack and still use strftime(): first
2469 // find the YEAR which is a year in the strftime() range (1970
2470 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
2471 // of the real year. Then make a copy of the format and
2472 // replace all occurrences of YEAR in it with some unique
2473 // string not appearing anywhere else in it, then use
2474 // strftime() to format the date in year YEAR and then replace
2475 // YEAR back by the real year and the unique replacement
2476 // string back with YEAR. Notice that "all occurrences of YEAR"
2477 // means all occurrences of 4 digit as well as 2 digit form!
2479 // the bugs: we assume that neither of %c nor %x contains any
2480 // fields which may change between the YEAR and real year. For
2481 // example, the week number (%U, %W) and the day number (%j)
2482 // will change if one of these years is leap and the other one
2485 // find the YEAR: normally, for any year X, Jan 1 of the
2486 // year X + 28 is the same weekday as Jan 1 of X (because
2487 // the weekday advances by 1 for each normal X and by 2
2488 // for each leap X, hence by 5 every 4 years or by 35
2489 // which is 0 mod 7 every 28 years) but this rule breaks
2490 // down if there are years between X and Y which are
2491 // divisible by 4 but not leap (i.e. divisible by 100 but
2492 // not 400), hence the correction.
2494 int yearReal
= GetYear(tz
);
2495 int mod28
= yearReal
% 28;
2497 // be careful to not go too far - we risk to leave the
2502 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
2506 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
2509 int nCentury
= year
/ 100,
2510 nCenturyReal
= yearReal
/ 100;
2512 // need to adjust for the years divisble by 400 which are
2513 // not leap but are counted like leap ones if we just take
2514 // the number of centuries in between for nLostWeekDays
2515 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
2516 (nCentury
/ 4 - nCenturyReal
/ 4);
2518 // we have to gain back the "lost" weekdays: note that the
2519 // effect of this loop is to not do anything to
2520 // nLostWeekDays (which we won't use any more), but to
2521 // (indirectly) set the year correctly
2522 while ( (nLostWeekDays
% 7) != 0 )
2524 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
2527 // finally move the year below 2000 so that the 2-digit
2528 // year number can never match the month or day of the
2529 // month when we do the replacements below
2533 wxASSERT_MSG( year
>= 1970 && year
< 2000,
2534 _T("logic error in wxDateTime::Format") );
2537 // use strftime() to format the same date but in supported
2540 // NB: we assume that strftime() doesn't check for the
2541 // date validity and will happily format the date
2542 // corresponding to Feb 29 of a non leap year (which
2543 // may happen if yearReal was leap and year is not)
2544 struct tm tmAdjusted
;
2546 tmAdjusted
.tm_hour
= tm
.hour
;
2547 tmAdjusted
.tm_min
= tm
.min
;
2548 tmAdjusted
.tm_sec
= tm
.sec
;
2549 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
2550 tmAdjusted
.tm_yday
= GetDayOfYear();
2551 tmAdjusted
.tm_mday
= tm
.mday
;
2552 tmAdjusted
.tm_mon
= tm
.mon
;
2553 tmAdjusted
.tm_year
= year
- 1900;
2554 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
2555 wxString str
= CallStrftime(*p
== _T('c') ? _T("%c")
2559 // now replace the replacement year with the real year:
2560 // notice that we have to replace the 4 digit year with
2561 // a unique string not appearing in strftime() output
2562 // first to prevent the 2 digit year from matching any
2563 // substring of the 4 digit year (but any day, month,
2564 // hours or minutes components should be safe because
2565 // they are never in 70-99 range)
2566 wxString
replacement("|");
2567 while ( str
.find(replacement
) != wxString::npos
)
2570 str
.Replace(wxString::Format("%d", year
),
2572 str
.Replace(wxString::Format("%d", year
% 100),
2573 wxString::Format("%d", yearReal
% 100));
2574 str
.Replace(replacement
,
2575 wxString::Format("%d", yearReal
));
2579 #else // !HAVE_STRFTIME
2580 // Use "%m/%d/%y %H:%M:%S" format instead
2581 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
2582 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
2583 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
2586 case _T('d'): // day of a month (01-31)
2587 res
+= wxString::Format(fmt
, tm
.mday
);
2590 case _T('H'): // hour in 24h format (00-23)
2591 res
+= wxString::Format(fmt
, tm
.hour
);
2594 case _T('I'): // hour in 12h format (01-12)
2596 // 24h -> 12h, 0h -> 12h too
2597 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
2598 : tm
.hour
? tm
.hour
: 12;
2599 res
+= wxString::Format(fmt
, hour12
);
2603 case _T('j'): // day of the year
2604 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
2607 case _T('l'): // milliseconds (NOT STANDARD)
2608 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
2611 case _T('m'): // month as a number (01-12)
2612 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
2615 case _T('M'): // minute as a decimal number (00-59)
2616 res
+= wxString::Format(fmt
, tm
.min
);
2619 case _T('p'): // AM or PM string
2620 #ifdef HAVE_STRFTIME
2621 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
2622 #else // !HAVE_STRFTIME
2623 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
2624 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
2627 case _T('S'): // second as a decimal number (00-61)
2628 res
+= wxString::Format(fmt
, tm
.sec
);
2631 case _T('U'): // week number in the year (Sunday 1st week day)
2632 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
2635 case _T('W'): // week number in the year (Monday 1st week day)
2636 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
2639 case _T('w'): // weekday as a number (0-6), Sunday = 0
2640 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
2643 // case _T('x'): -- handled with "%c"
2645 case _T('X'): // locale default time representation
2646 // just use strftime() to format the time for us
2647 #ifdef HAVE_STRFTIME
2648 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
2649 #else // !HAVE_STRFTIME
2650 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
2651 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
2654 case _T('y'): // year without century (00-99)
2655 res
+= wxString::Format(fmt
, tm
.year
% 100);
2658 case _T('Y'): // year with century
2659 res
+= wxString::Format(fmt
, tm
.year
);
2662 case _T('Z'): // timezone name
2663 #ifdef HAVE_STRFTIME
2664 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
2669 // is it the format width?
2671 while ( *p
== _T('-') || *p
== _T('+') ||
2672 *p
== _T(' ') || wxIsdigit(*p
) )
2679 // we've only got the flags and width so far in fmt
2680 fmt
.Prepend(_T('%'));
2681 fmt
.Append(_T('d'));
2688 // no, it wasn't the width
2689 wxFAIL_MSG(_T("unknown format specificator"));
2691 // fall through and just copy it nevertheless
2693 case _T('%'): // a percent sign
2697 case 0: // the end of string
2698 wxFAIL_MSG(_T("missing format at the end of string"));
2700 // just put the '%' which was the last char in format
2710 // this function parses a string in (strict) RFC 822 format: see the section 5
2711 // of the RFC for the detailed description, but briefly it's something of the
2712 // form "Sat, 18 Dec 1999 00:48:30 +0100"
2714 // this function is "strict" by design - it must reject anything except true
2715 // RFC822 time specs.
2717 // TODO a great candidate for using reg exps
2719 wxDateTime::ParseRfc822Date(const wxString
& date
, wxString::const_iterator
*end
)
2721 // TODO: rewrite using iterators instead of wxChar pointers
2722 const wxStringCharType
*p
= date
.wx_str();
2723 const wxStringCharType
*comma
= wxStrchr(p
, wxS(','));
2726 // the part before comma is the weekday
2728 // skip it for now - we don't use but might check that it really
2729 // corresponds to the specfied date
2732 if ( *p
!= _T(' ') )
2734 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
2742 // the following 1 or 2 digits are the day number
2743 if ( !wxIsdigit(*p
) )
2745 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
2750 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - _T('0'));
2751 if ( wxIsdigit(*p
) )
2754 day
= (wxDateTime_t
)(day
+ (*p
++ - _T('0')));
2757 if ( *p
++ != _T(' ') )
2762 // the following 3 letters specify the month
2763 wxString
monName(p
, 3);
2765 if ( monName
== _T("Jan") )
2767 else if ( monName
== _T("Feb") )
2769 else if ( monName
== _T("Mar") )
2771 else if ( monName
== _T("Apr") )
2773 else if ( monName
== _T("May") )
2775 else if ( monName
== _T("Jun") )
2777 else if ( monName
== _T("Jul") )
2779 else if ( monName
== _T("Aug") )
2781 else if ( monName
== _T("Sep") )
2783 else if ( monName
== _T("Oct") )
2785 else if ( monName
== _T("Nov") )
2787 else if ( monName
== _T("Dec") )
2791 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str());
2798 if ( *p
++ != _T(' ') )
2804 if ( !wxIsdigit(*p
) )
2810 int year
= *p
++ - _T('0');
2812 if ( !wxIsdigit(*p
) )
2814 // should have at least 2 digits in the year
2819 year
+= *p
++ - _T('0');
2821 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
2822 if ( wxIsdigit(*p
) )
2825 year
+= *p
++ - _T('0');
2827 if ( !wxIsdigit(*p
) )
2829 // no 3 digit years please
2834 year
+= *p
++ - _T('0');
2837 if ( *p
++ != _T(' ') )
2842 // time is in the format hh:mm:ss and seconds are optional
2843 if ( !wxIsdigit(*p
) )
2848 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - _T('0'));
2850 if ( !wxIsdigit(*p
) )
2856 hour
= (wxDateTime_t
)(hour
+ (*p
++ - _T('0')));
2858 if ( *p
++ != _T(':') )
2863 if ( !wxIsdigit(*p
) )
2868 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - _T('0'));
2870 if ( !wxIsdigit(*p
) )
2876 min
= (wxDateTime_t
)(min
+ *p
++ - _T('0'));
2878 wxDateTime_t sec
= 0;
2879 if ( *p
== _T(':') )
2882 if ( !wxIsdigit(*p
) )
2887 sec
= (wxDateTime_t
)(*p
++ - _T('0'));
2889 if ( !wxIsdigit(*p
) )
2895 sec
= (wxDateTime_t
)(sec
+ *p
++ - _T('0'));
2898 if ( *p
++ != _T(' ') )
2903 // and now the interesting part: the timezone
2904 int offset
wxDUMMY_INITIALIZE(0);
2905 if ( *p
== _T('-') || *p
== _T('+') )
2907 // the explicit offset given: it has the form of hhmm
2908 bool plus
= *p
++ == _T('+');
2910 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2916 offset
= MIN_PER_HOUR
*(10*(*p
- _T('0')) + (*(p
+ 1) - _T('0')));
2920 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2926 offset
+= 10*(*p
- _T('0')) + (*(p
+ 1) - _T('0'));
2937 // the symbolic timezone given: may be either military timezone or one
2938 // of standard abbreviations
2941 // military: Z = UTC, J unused, A = -1, ..., Y = +12
2942 static const int offsets
[26] =
2944 //A B C D E F G H I J K L M
2945 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
2946 //N O P R Q S T U V W Z Y Z
2947 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
2950 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
2952 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
);
2957 offset
= offsets
[*p
++ - _T('A')];
2963 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
2965 else if ( tz
== _T("AST") )
2966 offset
= AST
- GMT0
;
2967 else if ( tz
== _T("ADT") )
2968 offset
= ADT
- GMT0
;
2969 else if ( tz
== _T("EST") )
2970 offset
= EST
- GMT0
;
2971 else if ( tz
== _T("EDT") )
2972 offset
= EDT
- GMT0
;
2973 else if ( tz
== _T("CST") )
2974 offset
= CST
- GMT0
;
2975 else if ( tz
== _T("CDT") )
2976 offset
= CDT
- GMT0
;
2977 else if ( tz
== _T("MST") )
2978 offset
= MST
- GMT0
;
2979 else if ( tz
== _T("MDT") )
2980 offset
= MDT
- GMT0
;
2981 else if ( tz
== _T("PST") )
2982 offset
= PST
- GMT0
;
2983 else if ( tz
== _T("PDT") )
2984 offset
= PDT
- GMT0
;
2987 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
);
2996 offset
*= MIN_PER_HOUR
;
2999 // the spec was correct, construct the date from the values we found
3000 Set(day
, mon
, year
, hour
, min
, sec
);
3001 MakeFromTimezone(TimeZone::Make(offset
*SEC_PER_MIN
));
3003 const size_t endpos
= p
- date
.wx_str();
3005 *end
= date
.begin() + endpos
;
3007 return date
.c_str() + endpos
;
3012 // returns the string containing strftime() format used for short dates in the
3013 // current locale or an empty string
3014 static wxString
GetLocaleDateFormat()
3018 // there is no setlocale() under Windows CE, so just always query the
3021 if ( strcmp(setlocale(LC_ALL
, NULL
), "C") != 0 )
3024 // The locale was programatically set to non-C. We assume that this was
3025 // done using wxLocale, in which case thread's current locale is also
3026 // set to correct LCID value and we can use GetLocaleInfo to determine
3027 // the correct formatting string:
3029 LCID lcid
= LOCALE_USER_DEFAULT
;
3031 LCID lcid
= GetThreadLocale();
3033 // according to MSDN 80 chars is max allowed for short date format
3035 if ( ::GetLocaleInfo(lcid
, LOCALE_SSHORTDATE
, fmt
, WXSIZEOF(fmt
)) )
3037 wxChar chLast
= _T('\0');
3038 size_t lastCount
= 0;
3039 for ( const wxChar
*p
= fmt
; /* NUL handled inside */; p
++ )
3049 // these characters come in groups, start counting them
3059 // first deal with any special characters we have had
3065 switch ( lastCount
)
3069 // these two are the same as we
3070 // don't distinguish between 1 and
3071 // 2 digits for days
3084 wxFAIL_MSG( _T("too many 'd's") );
3089 switch ( lastCount
)
3093 // as for 'd' and 'dd' above
3106 wxFAIL_MSG( _T("too many 'M's") );
3111 switch ( lastCount
)
3123 wxFAIL_MSG( _T("wrong number of 'y's") );
3128 // strftime() doesn't have era string,
3129 // ignore this format
3130 wxASSERT_MSG( lastCount
<= 2,
3131 _T("too many 'g's") );
3135 wxFAIL_MSG( _T("unreachable") );
3142 // not a special character so must be just a separator,
3144 if ( *p
!= _T('\0') )
3146 if ( *p
== _T('%') )
3148 // this one needs to be escaped
3156 if ( *p
== _T('\0') )
3160 //else: GetLocaleInfo() failed, leave fmtDate value unchanged and
3161 // try our luck with the default formats
3163 //else: default C locale, default formats should work
3168 #endif // __WINDOWS__
3171 wxDateTime::ParseFormat(const wxString
& date
,
3172 const wxString
& format
,
3173 const wxDateTime
& dateDef
,
3174 wxString::const_iterator
*end
)
3176 wxCHECK_MSG( !format
.empty(), NULL
, "format can't be empty" );
3181 // what fields have we found?
3182 bool haveWDay
= false,
3192 bool hourIsIn12hFormat
= false, // or in 24h one?
3193 isPM
= false; // AM by default
3195 // and the value of the items we have (init them to get rid of warnings)
3196 wxDateTime_t msec
= 0,
3200 WeekDay wday
= Inv_WeekDay
;
3201 wxDateTime_t yday
= 0,
3203 wxDateTime::Month mon
= Inv_Month
;
3206 const wxStringCharType
*input
= date
.wx_str();
3207 for ( wxString::const_iterator fmt
= format
.begin(); fmt
!= format
.end(); ++fmt
)
3209 if ( *fmt
!= _T('%') )
3211 if ( wxIsspace(*fmt
) )
3213 // a white space in the format string matches 0 or more white
3214 // spaces in the input
3215 while ( wxIsspace(*input
) )
3222 // any other character (not whitespace, not '%') must be
3223 // matched by itself in the input
3224 if ( *input
++ != *fmt
)
3231 // done with this format char
3235 // start of a format specification
3237 // parse the optional width
3239 while ( wxIsdigit(*++fmt
) )
3242 width
+= *fmt
- _T('0');
3245 // the default widths for the various fields
3248 switch ( (*fmt
).GetValue() )
3250 case _T('Y'): // year has 4 digits
3254 case _T('j'): // day of year has 3 digits
3255 case _T('l'): // milliseconds have 3 digits
3259 case _T('w'): // week day as number has only one
3264 // default for all other fields
3269 // then the format itself
3270 switch ( (*fmt
).GetValue() )
3272 case _T('a'): // a weekday name
3275 int flag
= *fmt
== _T('a') ? Name_Abbr
: Name_Full
;
3276 wday
= GetWeekDayFromName(GetAlphaToken(input
), flag
);
3277 if ( wday
== Inv_WeekDay
)
3286 case _T('b'): // a month name
3289 int flag
= *fmt
== _T('b') ? Name_Abbr
: Name_Full
;
3290 mon
= GetMonthFromName(GetAlphaToken(input
), flag
);
3291 if ( mon
== Inv_Month
)
3300 case _T('c'): // locale default date and time representation
3304 const wxString
inc(input
);
3306 // try the format which corresponds to ctime() output first
3307 wxString::const_iterator endc
;
3308 if ( !dt
.ParseFormat(inc
, wxS("%a %b %d %H:%M:%S %Y"), &endc
) &&
3309 !dt
.ParseFormat(inc
, wxS("%x %X"), &endc
) &&
3310 !dt
.ParseFormat(inc
, wxS("%X %x"), &endc
) )
3312 // we've tried everything and still no match
3318 haveDay
= haveMon
= haveYear
=
3319 haveHour
= haveMin
= haveSec
= true;
3329 input
+= endc
- inc
.begin();
3333 case _T('d'): // day of a month (01-31)
3334 if ( !GetNumericToken(width
, input
, &num
) ||
3335 (num
> 31) || (num
< 1) )
3341 // we can't check whether the day range is correct yet, will
3342 // do it later - assume ok for now
3344 mday
= (wxDateTime_t
)num
;
3347 case _T('H'): // hour in 24h format (00-23)
3348 if ( !GetNumericToken(width
, input
, &num
) || (num
> 23) )
3355 hour
= (wxDateTime_t
)num
;
3358 case _T('I'): // hour in 12h format (01-12)
3359 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3366 hourIsIn12hFormat
= true;
3367 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
3370 case _T('j'): // day of the year
3371 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 366) )
3378 yday
= (wxDateTime_t
)num
;
3381 case _T('l'): // milliseconds (0-999)
3382 if ( !GetNumericToken(width
, input
, &num
) )
3386 msec
= (wxDateTime_t
)num
;
3389 case _T('m'): // month as a number (01-12)
3390 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3397 mon
= (Month
)(num
- 1);
3400 case _T('M'): // minute as a decimal number (00-59)
3401 if ( !GetNumericToken(width
, input
, &num
) || (num
> 59) )
3408 min
= (wxDateTime_t
)num
;
3411 case _T('p'): // AM or PM string
3413 wxString am
, pm
, token
= GetAlphaToken(input
);
3415 GetAmPmStrings(&am
, &pm
);
3416 if (am
.empty() && pm
.empty())
3417 return NULL
; // no am/pm strings defined
3418 if ( token
.CmpNoCase(pm
) == 0 )
3422 else if ( token
.CmpNoCase(am
) != 0 )
3430 case _T('r'): // time as %I:%M:%S %p
3433 input
= dt
.ParseFormat(input
, wxS("%I:%M:%S %p"));
3440 haveHour
= haveMin
= haveSec
= true;
3449 case _T('R'): // time as %H:%M
3452 input
= dt
.ParseFormat(input
, wxS("%H:%M"));
3459 haveHour
= haveMin
= true;
3467 case _T('S'): // second as a decimal number (00-61)
3468 if ( !GetNumericToken(width
, input
, &num
) || (num
> 61) )
3475 sec
= (wxDateTime_t
)num
;
3478 case _T('T'): // time as %H:%M:%S
3481 input
= dt
.ParseFormat(input
, _T("%H:%M:%S"));
3488 haveHour
= haveMin
= haveSec
= true;
3497 case _T('w'): // weekday as a number (0-6), Sunday = 0
3498 if ( !GetNumericToken(width
, input
, &num
) || (wday
> 6) )
3505 wday
= (WeekDay
)num
;
3508 case _T('x'): // locale default date representation
3509 #ifdef HAVE_STRPTIME
3510 // try using strptime() -- it may fail even if the input is
3511 // correct but the date is out of range, so we will fall back
3512 // to our generic code anyhow
3516 const wxStringCharType
*
3517 result
= CallStrptime(input
, "%x", &tm
);
3522 haveDay
= haveMon
= haveYear
= true;
3524 year
= 1900 + tm
.tm_year
;
3525 mon
= (Month
)tm
.tm_mon
;
3531 #endif // HAVE_STRPTIME
3539 // The above doesn't work for all locales, try to query
3540 // Windows for the right way of formatting the date:
3541 fmtDate
= GetLocaleDateFormat();
3542 if ( fmtDate
.empty() )
3543 #endif // __WINDOWS__
3545 if ( IsWestEuropeanCountry(GetCountry()) ||
3546 GetCountry() == Russia
)
3548 fmtDate
= _T("%d/%m/%y");
3549 fmtDateAlt
= _T("%m/%d/%y");
3553 fmtDate
= _T("%m/%d/%y");
3554 fmtDateAlt
= _T("%d/%m/%y");
3558 const wxString
indate(input
);
3559 wxString::const_iterator endDate
;
3560 if ( !dt
.ParseFormat(indate
, fmtDate
, &endDate
) )
3562 // try another one if we have it
3563 if ( fmtDateAlt
.empty() ||
3564 !dt
.ParseFormat(indate
, fmtDateAlt
, &endDate
) )
3580 input
+= endDate
- indate
.begin();
3585 case _T('X'): // locale default time representation
3586 #ifdef HAVE_STRPTIME
3588 // use strptime() to do it for us (FIXME !Unicode friendly)
3590 input
= CallStrptime(input
, "%X", &tm
);
3596 haveHour
= haveMin
= haveSec
= true;
3602 #else // !HAVE_STRPTIME
3603 // TODO under Win32 we can query the LOCALE_ITIME system
3604 // setting which says whether the default time format is
3607 // try to parse what follows as "%H:%M:%S" and, if this
3608 // fails, as "%I:%M:%S %p" - this should catch the most
3612 const wxStringCharType
*
3613 result
= dt
.ParseFormat(input
, wxS("%T"));
3616 result
= dt
.ParseFormat(input
, wxS("%r"));
3636 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
3639 case _T('y'): // year without century (00-99)
3640 if ( !GetNumericToken(width
, input
, &num
) || (num
> 99) )
3648 // TODO should have an option for roll over date instead of
3649 // hard coding it here
3650 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
3653 case _T('Y'): // year with century
3654 if ( !GetNumericToken(width
, input
, &num
) )
3661 year
= (wxDateTime_t
)num
;
3664 case _T('Z'): // timezone name
3665 wxFAIL_MSG(_T("TODO"));
3668 case _T('%'): // a percent sign
3669 if ( *input
++ != _T('%') )
3676 case 0: // the end of string
3677 wxFAIL_MSG(_T("unexpected format end"));
3681 default: // not a known format spec
3686 // format matched, try to construct a date from what we have now
3688 if ( dateDef
.IsValid() )
3690 // take this date as default
3691 tmDef
= dateDef
.GetTm();
3693 else if ( IsValid() )
3695 // if this date is valid, don't change it
3700 // no default and this date is invalid - fall back to Today()
3701 tmDef
= Today().GetTm();
3717 // TODO we don't check here that the values are consistent, if both year
3718 // day and month/day were found, we just ignore the year day and we
3719 // also always ignore the week day
3722 if ( mday
> GetNumOfDaysInMonth(tm
.year
, tm
.mon
) )
3724 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
3731 else if ( haveYDay
)
3733 if ( yday
> GetNumberOfDays(tm
.year
) )
3735 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
3740 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
3747 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
3749 // translate to 24hour format
3752 //else: either already in 24h format or no translation needed
3775 // finally check that the week day is consistent -- if we had it
3776 if ( haveWDay
&& GetWeekDay() != wday
)
3778 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
3783 const size_t endpos
= input
- date
.wx_str();
3785 *end
= date
.begin() + endpos
;
3787 return date
.c_str() + endpos
;
3791 wxDateTime::ParseDateTime(const wxString
& date
, wxString::const_iterator
*end
)
3793 // Set to current day and hour, so strings like '14:00' becomes today at
3794 // 14, not some other random date
3795 wxDateTime dtDate
= wxDateTime::Today();
3796 wxDateTime dtTime
= wxDateTime::Today();
3798 wxString::const_iterator
3803 // If we got a date in the beginning, see if there is a time specified
3805 if ( dtDate
.ParseDate(date
, &endDate
) )
3807 // Skip spaces, as the ParseTime() function fails on spaces
3808 while ( endDate
!= date
.end() && wxIsspace(*endDate
) )
3811 const wxString
timestr(endDate
, date
.end());
3812 if ( !dtTime
.ParseTime(timestr
, &endTime
) )
3815 endBoth
= endDate
+ (endTime
- timestr
.begin());
3817 else // no date in the beginning
3819 // check if we have a time followed by a date
3820 if ( !dtTime
.ParseTime(date
, &endTime
) )
3823 while ( endTime
!= date
.end() && wxIsspace(*endTime
) )
3826 const wxString
datestr(endTime
, date
.end());
3827 if ( !dtDate
.ParseDate(datestr
, &endDate
) )
3830 endBoth
= endTime
+ (endDate
- datestr
.begin());
3833 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
3834 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
3835 dtTime
.GetMillisecond());
3837 // Return endpoint of scan
3841 return date
.c_str() + (endBoth
- date
.begin());
3845 wxDateTime::ParseDate(const wxString
& date
, wxString::const_iterator
*end
)
3847 // this is a simplified version of ParseDateTime() which understands only
3848 // "today" (for wxDate compatibility) and digits only otherwise (and not
3849 // all esoteric constructions ParseDateTime() knows about)
3851 const wxStringCharType
*p
= date
.wx_str();
3852 while ( wxIsspace(*p
) )
3855 // some special cases
3859 int dayDiffFromToday
;
3862 { wxTRANSLATE("today"), 0 },
3863 { wxTRANSLATE("yesterday"), -1 },
3864 { wxTRANSLATE("tomorrow"), 1 },
3867 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
3869 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
3870 size_t len
= dateStr
.length();
3871 if ( wxStrlen(p
) >= len
)
3873 wxString
str(p
, len
);
3874 if ( str
.CmpNoCase(dateStr
) == 0 )
3876 // nothing can follow this, so stop here
3879 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
3881 if ( dayDiffFromToday
)
3883 *this += wxDateSpan::Days(dayDiffFromToday
);
3886 const size_t endpos
= p
- date
.wx_str();
3889 *end
= date
.begin() + endpos
;
3890 return date
.c_str() + endpos
;
3895 // We try to guess what we have here: for each new (numeric) token, we
3896 // determine if it can be a month, day or a year. Of course, there is an
3897 // ambiguity as some numbers may be days as well as months, so we also
3898 // have the ability to back track.
3901 bool haveDay
= false, // the months day?
3902 haveWDay
= false, // the day of week?
3903 haveMon
= false, // the month?
3904 haveYear
= false; // the year?
3906 // and the value of the items we have (init them to get rid of warnings)
3907 WeekDay wday
= Inv_WeekDay
;
3908 wxDateTime_t day
= 0;
3909 wxDateTime::Month mon
= Inv_Month
;
3912 // tokenize the string
3914 static const wxStringCharType
*dateDelimiters
= wxS(".,/-\t\r\n ");
3915 wxStringTokenizer
tok(p
, dateDelimiters
);
3916 while ( tok
.HasMoreTokens() )
3918 wxString token
= tok
.GetNextToken();
3924 if ( token
.ToULong(&val
) )
3926 // guess what this number is
3932 if ( !haveMon
&& val
> 0 && val
<= 12 )
3934 // assume it is month
3937 else // not the month
3941 // this can only be the year
3944 else // may be either day or year
3946 // use a leap year if we don't have the year yet to allow
3947 // dates like 2/29/1976 which would be rejected otherwise
3948 wxDateTime_t max_days
= (wxDateTime_t
)(
3950 ? GetNumOfDaysInMonth(haveYear
? year
: 1976, mon
)
3955 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
3960 else // yes, suppose it's the day
3974 year
= (wxDateTime_t
)val
;
3983 day
= (wxDateTime_t
)val
;
3989 mon
= (Month
)(val
- 1);
3992 else // not a number
3994 // be careful not to overwrite the current mon value
3995 Month mon2
= GetMonthFromName(token
, Name_Full
| Name_Abbr
);
3996 if ( mon2
!= Inv_Month
)
4001 // but we already have a month - maybe we guessed wrong?
4004 // no need to check in month range as always < 12, but
4005 // the days are counted from 1 unlike the months
4006 day
= (wxDateTime_t
)(mon
+ 1);
4011 // could possible be the year (doesn't the year come
4012 // before the month in the japanese format?) (FIXME)
4021 else // not a valid month name
4023 WeekDay wday2
= GetWeekDayFromName(token
, Name_Full
| Name_Abbr
);
4024 if ( wday2
!= Inv_WeekDay
)
4036 else // not a valid weekday name
4039 static const char *ordinals
[] =
4041 wxTRANSLATE("first"),
4042 wxTRANSLATE("second"),
4043 wxTRANSLATE("third"),
4044 wxTRANSLATE("fourth"),
4045 wxTRANSLATE("fifth"),
4046 wxTRANSLATE("sixth"),
4047 wxTRANSLATE("seventh"),
4048 wxTRANSLATE("eighth"),
4049 wxTRANSLATE("ninth"),
4050 wxTRANSLATE("tenth"),
4051 wxTRANSLATE("eleventh"),
4052 wxTRANSLATE("twelfth"),
4053 wxTRANSLATE("thirteenth"),
4054 wxTRANSLATE("fourteenth"),
4055 wxTRANSLATE("fifteenth"),
4056 wxTRANSLATE("sixteenth"),
4057 wxTRANSLATE("seventeenth"),
4058 wxTRANSLATE("eighteenth"),
4059 wxTRANSLATE("nineteenth"),
4060 wxTRANSLATE("twentieth"),
4061 // that's enough - otherwise we'd have problems with
4062 // composite (or not) ordinals
4066 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
4068 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
4074 if ( n
== WXSIZEOF(ordinals
) )
4076 // stop here - something unknown
4083 // don't try anything here (as in case of numeric day
4084 // above) - the symbolic day spec should always
4085 // precede the month/year
4091 day
= (wxDateTime_t
)(n
+ 1);
4096 nPosCur
= tok
.GetPosition();
4099 // either no more tokens or the scan was stopped by something we couldn't
4100 // parse - in any case, see if we can construct a date from what we have
4101 if ( !haveDay
&& !haveWDay
)
4103 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
4108 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
4109 !(haveDay
&& haveMon
&& haveYear
) )
4111 // without adjectives (which we don't support here) the week day only
4112 // makes sense completely separately or with the full date
4113 // specification (what would "Wed 1999" mean?)
4117 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
4119 // may be we have month and day instead of day and year?
4120 if ( haveDay
&& !haveMon
)
4124 // exchange day and month
4125 mon
= (wxDateTime::Month
)(day
- 1);
4127 // we're in the current year then
4128 if ( (year
> 0) && (year
<= (int)GetNumOfDaysInMonth(Inv_Year
, mon
)) )
4130 day
= (wxDateTime_t
)year
;
4135 //else: no, can't exchange, leave haveMon == false
4141 // if we give the year, month and day must be given too
4142 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
4150 mon
= GetCurrentMonth();
4155 year
= GetCurrentYear();
4160 // normally we check the day above but the check is optimistic in case
4161 // we find the day before its month/year so we have to redo it now
4162 if ( day
> GetNumOfDaysInMonth(year
, mon
) )
4165 Set(day
, mon
, year
);
4169 // check that it is really the same
4170 if ( GetWeekDay() != wday
)
4172 // inconsistency detected
4173 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
4183 SetToWeekDayInSameWeek(wday
);
4186 // return the pointer to the first unparsed char
4188 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
4190 // if we couldn't parse the token after the delimiter, put back the
4191 // delimiter as well
4195 const size_t endpos
= p
- date
.wx_str();
4197 *end
= date
.begin() + endpos
;
4199 return date
.c_str() + endpos
;
4203 wxDateTime::ParseTime(const wxString
& time
, wxString::const_iterator
*end
)
4205 // first try some extra things
4212 { wxTRANSLATE("noon"), 12 },
4213 { wxTRANSLATE("midnight"), 00 },
4217 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
4219 wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
4220 size_t len
= timeString
.length();
4221 if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 )
4223 // casts required by DigitalMars
4224 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
4227 *end
= time
.begin() + len
;
4229 return time
.c_str() + len
;
4233 // try all time formats we may think about in the order from longest to
4235 static const char *timeFormats
[] =
4237 "%I:%M:%S %p", // 12hour with AM/PM
4238 "%H:%M:%S", // could be the same or 24 hour one so try it too
4239 "%I:%M %p", // 12hour with AM/PM but without seconds
4240 "%H:%M:%S", // and a possibly 24 hour version without seconds
4241 "%X", // possibly something from above or maybe something
4242 // completely different -- try it last
4244 // TODO: parse timezones
4247 for ( size_t nFmt
= 0; nFmt
< WXSIZEOF(timeFormats
); nFmt
++ )
4249 const char *result
= ParseFormat(time
, timeFormats
[nFmt
], end
);
4257 // ----------------------------------------------------------------------------
4258 // Workdays and holidays support
4259 // ----------------------------------------------------------------------------
4261 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
4263 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
4266 // ============================================================================
4268 // ============================================================================
4270 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
4273 return ds1
.Multiply(n
);
4276 // ============================================================================
4278 // ============================================================================
4280 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
4282 return wxTimeSpan(ts
).Multiply(n
);
4285 // this enum is only used in wxTimeSpan::Format() below but we can't declare
4286 // it locally to the method as it provokes an internal compiler error in egcs
4287 // 2.91.60 when building with -O2
4298 // not all strftime(3) format specifiers make sense here because, for example,
4299 // a time span doesn't have a year nor a timezone
4301 // Here are the ones which are supported (all of them are supported by strftime
4303 // %H hour in 24 hour format
4304 // %M minute (00 - 59)
4305 // %S second (00 - 59)
4308 // Also, for MFC CTimeSpan compatibility, we support
4309 // %D number of days
4311 // And, to be better than MFC :-), we also have
4312 // %E number of wEeks
4313 // %l milliseconds (000 - 999)
4314 wxString
wxTimeSpan::Format(const wxString
& format
) const
4316 // we deal with only positive time spans here and just add the sign in
4317 // front for the negative ones
4320 wxString
str(Negate().Format(format
));
4324 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
4325 _T("NULL format in wxTimeSpan::Format") );
4328 str
.Alloc(format
.length());
4330 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
4332 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
4333 // question is what should ts.Format("%S") do? The code here returns "3273"
4334 // in this case (i.e. the total number of seconds, not just seconds % 60)
4335 // because, for me, this call means "give me entire time interval in
4336 // seconds" and not "give me the seconds part of the time interval"
4338 // If we agree that it should behave like this, it is clear that the
4339 // interpretation of each format specifier depends on the presence of the
4340 // other format specs in the string: if there was "%H" before "%M", we
4341 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
4343 // we remember the most important unit found so far
4344 TimeSpanPart partBiggest
= Part_MSec
;
4346 for ( wxString::const_iterator pch
= format
.begin(); pch
!= format
.end(); ++pch
)
4350 if ( ch
== _T('%') )
4352 // the start of the format specification of the printf() below
4353 wxString
fmtPrefix(_T('%'));
4358 // the number of digits for the format string, 0 if unused
4359 unsigned digits
= 0;
4361 ch
= *++pch
; // get the format spec char
4365 wxFAIL_MSG( _T("invalid format character") );
4371 // skip the part below switch
4376 if ( partBiggest
< Part_Day
)
4382 partBiggest
= Part_Day
;
4387 partBiggest
= Part_Week
;
4393 if ( partBiggest
< Part_Hour
)
4399 partBiggest
= Part_Hour
;
4406 n
= GetMilliseconds().ToLong();
4407 if ( partBiggest
< Part_MSec
)
4411 //else: no need to reset partBiggest to Part_MSec, it is
4412 // the least significant one anyhow
4419 if ( partBiggest
< Part_Min
)
4425 partBiggest
= Part_Min
;
4432 n
= GetSeconds().ToLong();
4433 if ( partBiggest
< Part_Sec
)
4439 partBiggest
= Part_Sec
;
4448 fmtPrefix
<< _T("0") << digits
;
4451 str
+= wxString::Format(fmtPrefix
+ _T("ld"), n
);
4455 // normal character, just copy
4463 // ============================================================================
4464 // wxDateTimeHolidayAuthority and related classes
4465 // ============================================================================
4467 #include "wx/arrimpl.cpp"
4469 WX_DEFINE_OBJARRAY(wxDateTimeArray
)
4471 static int wxCMPFUNC_CONV
4472 wxDateTimeCompareFunc(wxDateTime
**first
, wxDateTime
**second
)
4474 wxDateTime dt1
= **first
,
4477 return dt1
== dt2
? 0 : dt1
< dt2
? -1 : +1;
4480 // ----------------------------------------------------------------------------
4481 // wxDateTimeHolidayAuthority
4482 // ----------------------------------------------------------------------------
4484 wxHolidayAuthoritiesArray
wxDateTimeHolidayAuthority::ms_authorities
;
4487 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime
& dt
)
4489 size_t count
= ms_authorities
.size();
4490 for ( size_t n
= 0; n
< count
; n
++ )
4492 if ( ms_authorities
[n
]->DoIsHoliday(dt
) )
4503 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime
& dtStart
,
4504 const wxDateTime
& dtEnd
,
4505 wxDateTimeArray
& holidays
)
4507 wxDateTimeArray hol
;
4511 const size_t countAuth
= ms_authorities
.size();
4512 for ( size_t nAuth
= 0; nAuth
< countAuth
; nAuth
++ )
4514 ms_authorities
[nAuth
]->DoGetHolidaysInRange(dtStart
, dtEnd
, hol
);
4516 WX_APPEND_ARRAY(holidays
, hol
);
4519 holidays
.Sort(wxDateTimeCompareFunc
);
4521 return holidays
.size();
4525 void wxDateTimeHolidayAuthority::ClearAllAuthorities()
4527 WX_CLEAR_ARRAY(ms_authorities
);
4531 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority
*auth
)
4533 ms_authorities
.push_back(auth
);
4536 wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
4538 // required here for Darwin
4541 // ----------------------------------------------------------------------------
4542 // wxDateTimeWorkDays
4543 // ----------------------------------------------------------------------------
4545 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime
& dt
) const
4547 wxDateTime::WeekDay wd
= dt
.GetWeekDay();
4549 return (wd
== wxDateTime::Sun
) || (wd
== wxDateTime::Sat
);
4552 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime
& dtStart
,
4553 const wxDateTime
& dtEnd
,
4554 wxDateTimeArray
& holidays
) const
4556 if ( dtStart
> dtEnd
)
4558 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
4565 // instead of checking all days, start with the first Sat after dtStart and
4566 // end with the last Sun before dtEnd
4567 wxDateTime dtSatFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sat
),
4568 dtSatLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sat
),
4569 dtSunFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sun
),
4570 dtSunLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sun
),
4573 for ( dt
= dtSatFirst
; dt
<= dtSatLast
; dt
+= wxDateSpan::Week() )
4578 for ( dt
= dtSunFirst
; dt
<= dtSunLast
; dt
+= wxDateSpan::Week() )
4583 return holidays
.GetCount();
4586 // ============================================================================
4587 // other helper functions
4588 // ============================================================================
4590 // ----------------------------------------------------------------------------
4591 // iteration helpers: can be used to write a for loop over enum variable like
4593 // for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
4594 // ----------------------------------------------------------------------------
4596 WXDLLIMPEXP_BASE
void wxNextMonth(wxDateTime::Month
& m
)
4598 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4600 // no wrapping or the for loop above would never end!
4601 m
= (wxDateTime::Month
)(m
+ 1);
4604 WXDLLIMPEXP_BASE
void wxPrevMonth(wxDateTime::Month
& m
)
4606 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4608 m
= m
== wxDateTime::Jan
? wxDateTime::Inv_Month
4609 : (wxDateTime::Month
)(m
- 1);
4612 WXDLLIMPEXP_BASE
void wxNextWDay(wxDateTime::WeekDay
& wd
)
4614 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4616 // no wrapping or the for loop above would never end!
4617 wd
= (wxDateTime::WeekDay
)(wd
+ 1);
4620 WXDLLIMPEXP_BASE
void wxPrevWDay(wxDateTime::WeekDay
& wd
)
4622 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4624 wd
= wd
== wxDateTime::Sun
? wxDateTime::Inv_WeekDay
4625 : (wxDateTime::WeekDay
)(wd
- 1);
4630 wxDateTime
& wxDateTime::SetFromMSWSysTime(const SYSTEMTIME
& st
)
4633 static_cast<wxDateTime::Month
>(wxDateTime::Jan
+ st
.wMonth
- 1),
4638 void wxDateTime::GetAsMSWSysTime(SYSTEMTIME
* st
) const
4640 const wxDateTime::Tm
tm(GetTm());
4642 st
->wYear
= (WXWORD
)tm
.year
;
4643 st
->wMonth
= (WXWORD
)(tm
.mon
- wxDateTime::Jan
+ 1);
4650 st
->wMilliseconds
= 0;
4654 #endif // wxUSE_DATETIME