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"));
384 wxStrncpy(buf
, fmt
+ " (" + dt
->GetValue().ToString() + " ticks)",
391 // get the number of days in the given month of the given year
393 wxDateTime::wxDateTime_t
GetNumOfDaysInMonth(int year
, wxDateTime::Month month
)
395 // the number of days in month in Julian/Gregorian calendar: the first line
396 // is for normal years, the second one is for the leap ones
397 static wxDateTime::wxDateTime_t daysInMonth
[2][MONTHS_IN_YEAR
] =
399 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
400 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
403 return daysInMonth
[wxDateTime::IsLeapYear(year
)][month
];
406 // returns the time zone in the C sense, i.e. the difference UTC - local
408 static int GetTimeZone()
410 // set to true when the timezone is set
411 static bool s_timezoneSet
= false;
412 static long gmtoffset
= LONG_MAX
; // invalid timezone
414 // ensure that the timezone variable is set by calling wxLocaltime_r
415 if ( !s_timezoneSet
)
417 // just call wxLocaltime_r() instead of figuring out whether this
418 // system supports tzset(), _tzset() or something else
422 wxLocaltime_r(&t
, &tm
);
423 s_timezoneSet
= true;
425 #ifdef WX_GMTOFF_IN_TM
426 // note that GMT offset is the opposite of time zone and so to return
427 // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM
428 // cases we have to negate it
429 gmtoffset
= -tm
.tm_gmtoff
;
430 #else // !WX_GMTOFF_IN_TM
431 gmtoffset
= WX_TIMEZONE
;
432 #endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
435 return (int)gmtoffset
;
438 // return the integral part of the JDN for the midnight of the given date (to
439 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
440 // noon of the previous day)
441 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day
,
442 wxDateTime::Month mon
,
445 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
447 // check the date validity
449 (year
> JDN_0_YEAR
) ||
450 ((year
== JDN_0_YEAR
) && (mon
> JDN_0_MONTH
)) ||
451 ((year
== JDN_0_YEAR
) && (mon
== JDN_0_MONTH
) && (day
>= JDN_0_DAY
)),
452 _T("date out of range - can't convert to JDN")
455 // make the year positive to avoid problems with negative numbers division
458 // months are counted from March here
460 if ( mon
>= wxDateTime::Mar
)
470 // now we can simply add all the contributions together
471 return ((year
/ 100) * DAYS_PER_400_YEARS
) / 4
472 + ((year
% 100) * DAYS_PER_4_YEARS
) / 4
473 + (month
* DAYS_PER_5_MONTHS
+ 2) / 5
480 // this function is a wrapper around strftime(3) adding error checking
481 static wxString
CallStrftime(const wxString
& format
, const tm
* tm
)
484 // Create temp wxString here to work around mingw/cygwin bug 1046059
485 // http://sourceforge.net/tracker/?func=detail&atid=102435&aid=1046059&group_id=2435
488 if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) )
490 // buffer is too small?
491 wxFAIL_MSG(_T("strftime() failed"));
498 #endif // HAVE_STRFTIME
502 #if wxUSE_UNIX && !defined(HAVE_STRPTIME_DECL)
503 // configure detected that we had strptime() but not its declaration,
504 // provide it ourselves
505 extern "C" char *strptime(const char *, const char *, struct tm
*);
508 // Unicode-friendly strptime() wrapper
509 static const wxStringCharType
*
510 CallStrptime(const wxStringCharType
*input
, const char *fmt
, tm
*tm
)
512 // the problem here is that strptime() returns pointer into the string we
513 // passed to it while we're really interested in the pointer into the
514 // original, Unicode, string so we try to transform the pointer back
515 #if wxUSE_UNICODE_WCHAR
516 wxCharBuffer
inputMB(wxConvertWX2MB(input
));
518 const char * const inputMB
= input
;
519 #endif // Unicode/Ascii
521 const char *result
= strptime(inputMB
, fmt
, tm
);
525 #if wxUSE_UNICODE_WCHAR
526 // FIXME: this is wrong in presence of surrogates &c
527 return input
+ (result
- inputMB
.data());
530 #endif // Unicode/Ascii
533 #endif // HAVE_STRPTIME
535 // if year and/or month have invalid values, replace them with the current ones
536 static void ReplaceDefaultYearMonthWithCurrent(int *year
,
537 wxDateTime::Month
*month
)
539 struct tm
*tmNow
= NULL
;
542 if ( *year
== wxDateTime::Inv_Year
)
544 tmNow
= wxDateTime::GetTmNow(&tmstruct
);
546 *year
= 1900 + tmNow
->tm_year
;
549 if ( *month
== wxDateTime::Inv_Month
)
552 tmNow
= wxDateTime::GetTmNow(&tmstruct
);
554 *month
= (wxDateTime::Month
)tmNow
->tm_mon
;
558 // fll the struct tm with default values
559 static void InitTm(struct tm
& tm
)
561 // struct tm may have etxra fields (undocumented and with unportable
562 // names) which, nevertheless, must be set to 0
563 memset(&tm
, 0, sizeof(struct tm
));
565 tm
.tm_mday
= 1; // mday 0 is invalid
566 tm
.tm_year
= 76; // any valid year
567 tm
.tm_isdst
= -1; // auto determine
573 // return the month if the string is a month name or Inv_Month otherwise
574 static wxDateTime::Month
GetMonthFromName(const wxString
& name
, int flags
)
576 wxDateTime::Month mon
;
577 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
579 // case-insensitive comparison either one of or with both abbreviated
581 if ( flags
& wxDateTime::Name_Full
)
583 if ( name
.CmpNoCase(wxDateTime::
584 GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 )
590 if ( flags
& wxDateTime::Name_Abbr
)
592 if ( name
.CmpNoCase(wxDateTime::
593 GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 )
603 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
604 static wxDateTime::WeekDay
GetWeekDayFromName(const wxString
& name
, int flags
)
606 wxDateTime::WeekDay wd
;
607 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
609 // case-insensitive comparison either one of or with both abbreviated
611 if ( flags
& wxDateTime::Name_Full
)
613 if ( name
.CmpNoCase(wxDateTime::
614 GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 )
620 if ( flags
& wxDateTime::Name_Abbr
)
622 if ( name
.CmpNoCase(wxDateTime::
623 GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 )
634 struct tm
*wxDateTime::GetTmNow(struct tm
*tmstruct
)
636 time_t t
= GetTimeNow();
637 return wxLocaltime_r(&t
, tmstruct
);
640 // scans all digits (but no more than len) and returns the resulting number
641 static bool GetNumericToken(size_t len
,
642 const wxStringCharType
*& p
,
643 unsigned long *number
)
647 while ( wxIsdigit(*p
) )
651 if ( len
&& ++n
> len
)
655 return !s
.empty() && s
.ToULong(number
);
658 // scans all alphabetic characters and returns the resulting string
659 static wxString
GetAlphaToken(const wxStringCharType
*& p
)
662 while ( wxIsalpha(*p
) )
670 // ============================================================================
671 // implementation of wxDateTime
672 // ============================================================================
674 // ----------------------------------------------------------------------------
676 // ----------------------------------------------------------------------------
680 year
= (wxDateTime_t
)wxDateTime::Inv_Year
;
681 mon
= wxDateTime::Inv_Month
;
683 hour
= min
= sec
= msec
= 0;
684 wday
= wxDateTime::Inv_WeekDay
;
687 wxDateTime::Tm::Tm(const struct tm
& tm
, const TimeZone
& tz
)
691 sec
= (wxDateTime::wxDateTime_t
)tm
.tm_sec
;
692 min
= (wxDateTime::wxDateTime_t
)tm
.tm_min
;
693 hour
= (wxDateTime::wxDateTime_t
)tm
.tm_hour
;
694 mday
= (wxDateTime::wxDateTime_t
)tm
.tm_mday
;
695 mon
= (wxDateTime::Month
)tm
.tm_mon
;
696 year
= 1900 + tm
.tm_year
;
697 wday
= (wxDateTime::wxDateTime_t
)tm
.tm_wday
;
698 yday
= (wxDateTime::wxDateTime_t
)tm
.tm_yday
;
701 bool wxDateTime::Tm::IsValid() const
703 // we allow for the leap seconds, although we don't use them (yet)
704 return (year
!= wxDateTime::Inv_Year
) && (mon
!= wxDateTime::Inv_Month
) &&
705 (mday
<= GetNumOfDaysInMonth(year
, mon
)) &&
706 (hour
< 24) && (min
< 60) && (sec
< 62) && (msec
< 1000);
709 void wxDateTime::Tm::ComputeWeekDay()
711 // compute the week day from day/month/year: we use the dumbest algorithm
712 // possible: just compute our JDN and then use the (simple to derive)
713 // formula: weekday = (JDN + 1.5) % 7
714 wday
= (wxDateTime::wxDateTime_t
)((GetTruncatedJDN(mday
, mon
, year
) + 2) % 7);
717 void wxDateTime::Tm::AddMonths(int monDiff
)
719 // normalize the months field
720 while ( monDiff
< -mon
)
724 monDiff
+= MONTHS_IN_YEAR
;
727 while ( monDiff
+ mon
>= MONTHS_IN_YEAR
)
731 monDiff
-= MONTHS_IN_YEAR
;
734 mon
= (wxDateTime::Month
)(mon
+ monDiff
);
736 wxASSERT_MSG( mon
>= 0 && mon
< MONTHS_IN_YEAR
, _T("logic error") );
738 // NB: we don't check here that the resulting date is valid, this function
739 // is private and the caller must check it if needed
742 void wxDateTime::Tm::AddDays(int dayDiff
)
744 // normalize the days field
745 while ( dayDiff
+ mday
< 1 )
749 dayDiff
+= GetNumOfDaysInMonth(year
, mon
);
752 mday
= (wxDateTime::wxDateTime_t
)( mday
+ dayDiff
);
753 while ( mday
> GetNumOfDaysInMonth(year
, mon
) )
755 mday
-= GetNumOfDaysInMonth(year
, mon
);
760 wxASSERT_MSG( mday
> 0 && mday
<= GetNumOfDaysInMonth(year
, mon
),
764 // ----------------------------------------------------------------------------
766 // ----------------------------------------------------------------------------
768 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz
)
772 case wxDateTime::Local
:
773 // get the offset from C RTL: it returns the difference GMT-local
774 // while we want to have the offset _from_ GMT, hence the '-'
775 m_offset
= -GetTimeZone();
778 case wxDateTime::GMT_12
:
779 case wxDateTime::GMT_11
:
780 case wxDateTime::GMT_10
:
781 case wxDateTime::GMT_9
:
782 case wxDateTime::GMT_8
:
783 case wxDateTime::GMT_7
:
784 case wxDateTime::GMT_6
:
785 case wxDateTime::GMT_5
:
786 case wxDateTime::GMT_4
:
787 case wxDateTime::GMT_3
:
788 case wxDateTime::GMT_2
:
789 case wxDateTime::GMT_1
:
790 m_offset
= -3600*(wxDateTime::GMT0
- tz
);
793 case wxDateTime::GMT0
:
794 case wxDateTime::GMT1
:
795 case wxDateTime::GMT2
:
796 case wxDateTime::GMT3
:
797 case wxDateTime::GMT4
:
798 case wxDateTime::GMT5
:
799 case wxDateTime::GMT6
:
800 case wxDateTime::GMT7
:
801 case wxDateTime::GMT8
:
802 case wxDateTime::GMT9
:
803 case wxDateTime::GMT10
:
804 case wxDateTime::GMT11
:
805 case wxDateTime::GMT12
:
806 case wxDateTime::GMT13
:
807 m_offset
= 3600*(tz
- wxDateTime::GMT0
);
810 case wxDateTime::A_CST
:
811 // Central Standard Time in use in Australia = UTC + 9.5
812 m_offset
= 60l*(9*MIN_PER_HOUR
+ MIN_PER_HOUR
/2);
816 wxFAIL_MSG( _T("unknown time zone") );
820 // ----------------------------------------------------------------------------
822 // ----------------------------------------------------------------------------
825 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
)
827 if ( year
== Inv_Year
)
828 year
= GetCurrentYear();
830 if ( cal
== Gregorian
)
832 // in Gregorian calendar leap years are those divisible by 4 except
833 // those divisible by 100 unless they're also divisible by 400
834 // (in some countries, like Russia and Greece, additional corrections
835 // exist, but they won't manifest themselves until 2700)
836 return (year
% 4 == 0) && ((year
% 100 != 0) || (year
% 400 == 0));
838 else if ( cal
== Julian
)
840 // in Julian calendar the rule is simpler
841 return year
% 4 == 0;
845 wxFAIL_MSG(_T("unknown calendar"));
852 int wxDateTime::GetCentury(int year
)
854 return year
> 0 ? year
/ 100 : year
/ 100 - 1;
858 int wxDateTime::ConvertYearToBC(int year
)
861 return year
> 0 ? year
: year
- 1;
865 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
)
870 return Now().GetYear();
873 wxFAIL_MSG(_T("TODO"));
877 wxFAIL_MSG(_T("unsupported calendar"));
885 wxDateTime::Month
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
)
890 return Now().GetMonth();
893 wxFAIL_MSG(_T("TODO"));
897 wxFAIL_MSG(_T("unsupported calendar"));
905 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(int year
, Calendar cal
)
907 if ( year
== Inv_Year
)
909 // take the current year if none given
910 year
= GetCurrentYear();
917 return IsLeapYear(year
) ? 366 : 365;
920 wxFAIL_MSG(_T("unsupported calendar"));
928 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(wxDateTime::Month month
,
930 wxDateTime::Calendar cal
)
932 wxCHECK_MSG( month
< MONTHS_IN_YEAR
, 0, _T("invalid month") );
934 if ( cal
== Gregorian
|| cal
== Julian
)
936 if ( year
== Inv_Year
)
938 // take the current year if none given
939 year
= GetCurrentYear();
942 return GetNumOfDaysInMonth(year
, month
);
946 wxFAIL_MSG(_T("unsupported calendar"));
953 wxString
wxDateTime::GetMonthName(wxDateTime::Month month
,
954 wxDateTime::NameFlags flags
)
956 wxCHECK_MSG( month
!= Inv_Month
, wxEmptyString
, _T("invalid month") );
958 // notice that we must set all the fields to avoid confusing libc (GNU one
959 // gets confused to a crash if we don't do this)
964 return CallStrftime(flags
== Name_Abbr
? _T("%b") : _T("%B"), &tm
);
965 #else // !HAVE_STRFTIME
970 ret
= (flags
== Name_Abbr
? wxT("Jan"): wxT("January"));
973 ret
= (flags
== Name_Abbr
? wxT("Feb"): wxT("Febuary"));
976 ret
= (flags
== Name_Abbr
? wxT("Mar"): wxT("March"));
979 ret
= (flags
== Name_Abbr
? wxT("Apr"): wxT("April"));
982 ret
= (flags
== Name_Abbr
? wxT("May"): wxT("May"));
985 ret
= (flags
== Name_Abbr
? wxT("Jun"): wxT("June"));
988 ret
= (flags
== Name_Abbr
? wxT("Jul"): wxT("July"));
991 ret
= (flags
== Name_Abbr
? wxT("Aug"): wxT("August"));
994 ret
= (flags
== Name_Abbr
? wxT("Sep"): wxT("September"));
997 ret
= (flags
== Name_Abbr
? wxT("Oct"): wxT("October"));
1000 ret
= (flags
== Name_Abbr
? wxT("Nov"): wxT("November"));
1003 ret
= (flags
== Name_Abbr
? wxT("Dec"): wxT("December"));
1007 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
1011 wxString
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
,
1012 wxDateTime::NameFlags flags
)
1014 wxCHECK_MSG( wday
!= Inv_WeekDay
, wxEmptyString
, _T("invalid weekday") );
1015 #ifdef HAVE_STRFTIME
1016 // take some arbitrary Sunday (but notice that the day should be such that
1017 // after adding wday to it below we still have a valid date, e.g. don't
1025 // and offset it by the number of days needed to get the correct wday
1028 // call mktime() to normalize it...
1031 // ... and call strftime()
1032 return CallStrftime(flags
== Name_Abbr
? _T("%a") : _T("%A"), &tm
);
1033 #else // !HAVE_STRFTIME
1038 ret
= (flags
== Name_Abbr
? wxT("Sun") : wxT("Sunday"));
1041 ret
= (flags
== Name_Abbr
? wxT("Mon") : wxT("Monday"));
1044 ret
= (flags
== Name_Abbr
? wxT("Tue") : wxT("Tuesday"));
1047 ret
= (flags
== Name_Abbr
? wxT("Wed") : wxT("Wednesday"));
1050 ret
= (flags
== Name_Abbr
? wxT("Thu") : wxT("Thursday"));
1053 ret
= (flags
== Name_Abbr
? wxT("Fri") : wxT("Friday"));
1056 ret
= (flags
== Name_Abbr
? wxT("Sat") : wxT("Saturday"));
1060 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
1064 void wxDateTime::GetAmPmStrings(wxString
*am
, wxString
*pm
)
1069 // @Note: Do not call 'CallStrftime' here! CallStrftime checks the return code
1070 // and causes an assertion failed if the buffer is to small (which is good) - OR -
1071 // if strftime does not return anything because the format string is invalid - OR -
1072 // if there are no 'am' / 'pm' tokens defined for the current locale (which is not good).
1073 // wxDateTime::ParseTime will try several different formats to parse the time.
1074 // As a result, GetAmPmStrings might get called, even if the current locale
1075 // does not define any 'am' / 'pm' tokens. In this case, wxStrftime would
1076 // assert, even though it is a perfectly legal use.
1079 if (wxStrftime(buffer
, sizeof(buffer
)/sizeof(wxChar
), _T("%p"), &tm
) > 0)
1080 *am
= wxString(buffer
);
1087 if (wxStrftime(buffer
, sizeof(buffer
)/sizeof(wxChar
), _T("%p"), &tm
) > 0)
1088 *pm
= wxString(buffer
);
1094 // ----------------------------------------------------------------------------
1095 // Country stuff: date calculations depend on the country (DST, work days,
1096 // ...), so we need to know which rules to follow.
1097 // ----------------------------------------------------------------------------
1100 wxDateTime::Country
wxDateTime::GetCountry()
1102 // TODO use LOCALE_ICOUNTRY setting under Win32
1104 if ( ms_country
== Country_Unknown
)
1106 // try to guess from the time zone name
1107 time_t t
= time(NULL
);
1109 struct tm
*tm
= wxLocaltime_r(&t
, &tmstruct
);
1111 wxString tz
= CallStrftime(_T("%Z"), tm
);
1112 if ( tz
== _T("WET") || tz
== _T("WEST") )
1116 else if ( tz
== _T("CET") || tz
== _T("CEST") )
1118 ms_country
= Country_EEC
;
1120 else if ( tz
== _T("MSK") || tz
== _T("MSD") )
1122 ms_country
= Russia
;
1124 else if ( tz
== _T("AST") || tz
== _T("ADT") ||
1125 tz
== _T("EST") || tz
== _T("EDT") ||
1126 tz
== _T("CST") || tz
== _T("CDT") ||
1127 tz
== _T("MST") || tz
== _T("MDT") ||
1128 tz
== _T("PST") || tz
== _T("PDT") )
1134 // well, choose a default one
1138 #else // __WXWINCE__
1140 #endif // !__WXWINCE__/__WXWINCE__
1146 void wxDateTime::SetCountry(wxDateTime::Country country
)
1148 ms_country
= country
;
1152 bool wxDateTime::IsWestEuropeanCountry(Country country
)
1154 if ( country
== Country_Default
)
1156 country
= GetCountry();
1159 return (Country_WesternEurope_Start
<= country
) &&
1160 (country
<= Country_WesternEurope_End
);
1163 // ----------------------------------------------------------------------------
1164 // DST calculations: we use 3 different rules for the West European countries,
1165 // USA and for the rest of the world. This is undoubtedly false for many
1166 // countries, but I lack the necessary info (and the time to gather it),
1167 // please add the other rules here!
1168 // ----------------------------------------------------------------------------
1171 bool wxDateTime::IsDSTApplicable(int year
, Country country
)
1173 if ( year
== Inv_Year
)
1175 // take the current year if none given
1176 year
= GetCurrentYear();
1179 if ( country
== Country_Default
)
1181 country
= GetCountry();
1188 // DST was first observed in the US and UK during WWI, reused
1189 // during WWII and used again since 1966
1190 return year
>= 1966 ||
1191 (year
>= 1942 && year
<= 1945) ||
1192 (year
== 1918 || year
== 1919);
1195 // assume that it started after WWII
1201 wxDateTime
wxDateTime::GetBeginDST(int year
, Country country
)
1203 if ( year
== Inv_Year
)
1205 // take the current year if none given
1206 year
= GetCurrentYear();
1209 if ( country
== Country_Default
)
1211 country
= GetCountry();
1214 if ( !IsDSTApplicable(year
, country
) )
1216 return wxInvalidDateTime
;
1221 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1223 // DST begins at 1 a.m. GMT on the last Sunday of March
1224 if ( !dt
.SetToLastWeekDay(Sun
, Mar
, year
) )
1227 wxFAIL_MSG( _T("no last Sunday in March?") );
1230 dt
+= wxTimeSpan::Hours(1);
1232 else switch ( country
)
1239 // don't know for sure - assume it was in effect all year
1244 dt
.Set(1, Jan
, year
);
1248 // DST was installed Feb 2, 1942 by the Congress
1249 dt
.Set(2, Feb
, year
);
1252 // Oil embargo changed the DST period in the US
1254 dt
.Set(6, Jan
, 1974);
1258 dt
.Set(23, Feb
, 1975);
1262 // before 1986, DST begun on the last Sunday of April, but
1263 // in 1986 Reagan changed it to begin at 2 a.m. of the
1264 // first Sunday in April
1267 if ( !dt
.SetToLastWeekDay(Sun
, Apr
, year
) )
1270 wxFAIL_MSG( _T("no first Sunday in April?") );
1275 if ( !dt
.SetToWeekDay(Sun
, 1, Apr
, year
) )
1278 wxFAIL_MSG( _T("no first Sunday in April?") );
1282 dt
+= wxTimeSpan::Hours(2);
1284 // TODO what about timezone??
1290 // assume Mar 30 as the start of the DST for the rest of the world
1291 // - totally bogus, of course
1292 dt
.Set(30, Mar
, year
);
1299 wxDateTime
wxDateTime::GetEndDST(int year
, Country country
)
1301 if ( year
== Inv_Year
)
1303 // take the current year if none given
1304 year
= GetCurrentYear();
1307 if ( country
== Country_Default
)
1309 country
= GetCountry();
1312 if ( !IsDSTApplicable(year
, country
) )
1314 return wxInvalidDateTime
;
1319 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1321 // DST ends at 1 a.m. GMT on the last Sunday of October
1322 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1324 // weirder and weirder...
1325 wxFAIL_MSG( _T("no last Sunday in October?") );
1328 dt
+= wxTimeSpan::Hours(1);
1330 else switch ( country
)
1337 // don't know for sure - assume it was in effect all year
1341 dt
.Set(31, Dec
, year
);
1345 // the time was reset after the end of the WWII
1346 dt
.Set(30, Sep
, year
);
1350 // DST ends at 2 a.m. on the last Sunday of October
1351 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1353 // weirder and weirder...
1354 wxFAIL_MSG( _T("no last Sunday in October?") );
1357 dt
+= wxTimeSpan::Hours(2);
1359 // TODO what about timezone??
1364 // assume October 26th as the end of the DST - totally bogus too
1365 dt
.Set(26, Oct
, year
);
1371 // ----------------------------------------------------------------------------
1372 // constructors and assignment operators
1373 // ----------------------------------------------------------------------------
1375 // return the current time with ms precision
1376 /* static */ wxDateTime
wxDateTime::UNow()
1378 return wxDateTime(wxGetLocalTimeMillis());
1381 // the values in the tm structure contain the local time
1382 wxDateTime
& wxDateTime::Set(const struct tm
& tm
)
1385 time_t timet
= mktime(&tm2
);
1387 if ( timet
== (time_t)-1 )
1389 // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
1390 // less than timezone - try to make it work for this case
1391 if ( tm2
.tm_year
== 70 && tm2
.tm_mon
== 0 && tm2
.tm_mday
== 1 )
1393 return Set((time_t)(
1395 tm2
.tm_hour
* MIN_PER_HOUR
* SEC_PER_MIN
+
1396 tm2
.tm_min
* SEC_PER_MIN
+
1400 wxFAIL_MSG( _T("mktime() failed") );
1402 *this = wxInvalidDateTime
;
1412 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
,
1413 wxDateTime_t minute
,
1414 wxDateTime_t second
,
1415 wxDateTime_t millisec
)
1417 // we allow seconds to be 61 to account for the leap seconds, even if we
1418 // don't use them really
1419 wxDATETIME_CHECK( hour
< 24 &&
1423 _T("Invalid time in wxDateTime::Set()") );
1425 // get the current date from system
1427 struct tm
*tm
= GetTmNow(&tmstruct
);
1429 wxDATETIME_CHECK( tm
, _T("wxLocaltime_r() failed") );
1431 // make a copy so it isn't clobbered by the call to mktime() below
1436 tm1
.tm_min
= minute
;
1437 tm1
.tm_sec
= second
;
1439 // and the DST in case it changes on this date
1442 if ( tm2
.tm_isdst
!= tm1
.tm_isdst
)
1443 tm1
.tm_isdst
= tm2
.tm_isdst
;
1447 // and finally adjust milliseconds
1448 return SetMillisecond(millisec
);
1451 wxDateTime
& wxDateTime::Set(wxDateTime_t day
,
1455 wxDateTime_t minute
,
1456 wxDateTime_t second
,
1457 wxDateTime_t millisec
)
1459 wxDATETIME_CHECK( hour
< 24 &&
1463 _T("Invalid time in wxDateTime::Set()") );
1465 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1467 wxDATETIME_CHECK( (0 < day
) && (day
<= GetNumberOfDays(month
, year
)),
1468 _T("Invalid date in wxDateTime::Set()") );
1470 // the range of time_t type (inclusive)
1471 static const int yearMinInRange
= 1970;
1472 static const int yearMaxInRange
= 2037;
1474 // test only the year instead of testing for the exact end of the Unix
1475 // time_t range - it doesn't bring anything to do more precise checks
1476 if ( year
>= yearMinInRange
&& year
<= yearMaxInRange
)
1478 // use the standard library version if the date is in range - this is
1479 // probably more efficient than our code
1481 tm
.tm_year
= year
- 1900;
1487 tm
.tm_isdst
= -1; // mktime() will guess it
1491 // and finally adjust milliseconds
1493 SetMillisecond(millisec
);
1499 // do time calculations ourselves: we want to calculate the number of
1500 // milliseconds between the given date and the epoch
1502 // get the JDN for the midnight of this day
1503 m_time
= GetTruncatedJDN(day
, month
, year
);
1504 m_time
-= EPOCH_JDN
;
1505 m_time
*= SECONDS_PER_DAY
* TIME_T_FACTOR
;
1507 // JDN corresponds to GMT, we take localtime
1508 Add(wxTimeSpan(hour
, minute
, second
+ GetTimeZone(), millisec
));
1514 wxDateTime
& wxDateTime::Set(double jdn
)
1516 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1518 jdn
-= EPOCH_JDN
+ 0.5;
1520 m_time
.Assign(jdn
*MILLISECONDS_PER_DAY
);
1522 // JDNs always are in UTC, so we don't need any adjustments for time zone
1527 wxDateTime
& wxDateTime::ResetTime()
1531 if ( tm
.hour
|| tm
.min
|| tm
.sec
|| tm
.msec
)
1544 wxDateTime
wxDateTime::GetDateOnly() const
1551 return wxDateTime(tm
);
1554 // ----------------------------------------------------------------------------
1555 // DOS Date and Time Format functions
1556 // ----------------------------------------------------------------------------
1557 // the dos date and time value is an unsigned 32 bit value in the format:
1558 // YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
1560 // Y = year offset from 1980 (0-127)
1562 // D = day of month (1-31)
1564 // m = minute (0-59)
1565 // s = bisecond (0-29) each bisecond indicates two seconds
1566 // ----------------------------------------------------------------------------
1568 wxDateTime
& wxDateTime::SetFromDOS(unsigned long ddt
)
1573 long year
= ddt
& 0xFE000000;
1578 long month
= ddt
& 0x1E00000;
1583 long day
= ddt
& 0x1F0000;
1587 long hour
= ddt
& 0xF800;
1591 long minute
= ddt
& 0x7E0;
1595 long second
= ddt
& 0x1F;
1596 tm
.tm_sec
= second
* 2;
1598 return Set(mktime(&tm
));
1601 unsigned long wxDateTime::GetAsDOS() const
1604 time_t ticks
= GetTicks();
1606 struct tm
*tm
= wxLocaltime_r(&ticks
, &tmstruct
);
1607 wxCHECK_MSG( tm
, ULONG_MAX
, _T("time can't be represented in DOS format") );
1609 long year
= tm
->tm_year
;
1613 long month
= tm
->tm_mon
;
1617 long day
= tm
->tm_mday
;
1620 long hour
= tm
->tm_hour
;
1623 long minute
= tm
->tm_min
;
1626 long second
= tm
->tm_sec
;
1629 ddt
= year
| month
| day
| hour
| minute
| second
;
1633 // ----------------------------------------------------------------------------
1634 // time_t <-> broken down time conversions
1635 // ----------------------------------------------------------------------------
1637 wxDateTime::Tm
wxDateTime::GetTm(const TimeZone
& tz
) const
1639 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1641 time_t time
= GetTicks();
1642 if ( time
!= (time_t)-1 )
1644 // use C RTL functions
1647 if ( tz
.GetOffset() == -GetTimeZone() )
1649 // we are working with local time
1650 tm
= wxLocaltime_r(&time
, &tmstruct
);
1652 // should never happen
1653 wxCHECK_MSG( tm
, Tm(), _T("wxLocaltime_r() failed") );
1657 time
+= (time_t)tz
.GetOffset();
1658 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
1659 int time2
= (int) time
;
1665 tm
= wxGmtime_r(&time
, &tmstruct
);
1667 // should never happen
1668 wxCHECK_MSG( tm
, Tm(), _T("wxGmtime_r() failed") );
1672 tm
= (struct tm
*)NULL
;
1678 // adjust the milliseconds
1680 long timeOnly
= (m_time
% MILLISECONDS_PER_DAY
).ToLong();
1681 tm2
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1684 //else: use generic code below
1687 // remember the time and do the calculations with the date only - this
1688 // eliminates rounding errors of the floating point arithmetics
1690 wxLongLong timeMidnight
= m_time
+ tz
.GetOffset() * 1000;
1692 long timeOnly
= (timeMidnight
% MILLISECONDS_PER_DAY
).ToLong();
1694 // we want to always have positive time and timeMidnight to be really
1695 // the midnight before it
1698 timeOnly
= MILLISECONDS_PER_DAY
+ timeOnly
;
1701 timeMidnight
-= timeOnly
;
1703 // calculate the Gregorian date from JDN for the midnight of our date:
1704 // this will yield day, month (in 1..12 range) and year
1706 // actually, this is the JDN for the noon of the previous day
1707 long jdn
= (timeMidnight
/ MILLISECONDS_PER_DAY
).ToLong() + EPOCH_JDN
;
1709 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1711 wxASSERT_MSG( jdn
> -2, _T("JDN out of range") );
1713 // calculate the century
1714 long temp
= (jdn
+ JDN_OFFSET
) * 4 - 1;
1715 long century
= temp
/ DAYS_PER_400_YEARS
;
1717 // then the year and day of year (1 <= dayOfYear <= 366)
1718 temp
= ((temp
% DAYS_PER_400_YEARS
) / 4) * 4 + 3;
1719 long year
= (century
* 100) + (temp
/ DAYS_PER_4_YEARS
);
1720 long dayOfYear
= (temp
% DAYS_PER_4_YEARS
) / 4 + 1;
1722 // and finally the month and day of the month
1723 temp
= dayOfYear
* 5 - 3;
1724 long month
= temp
/ DAYS_PER_5_MONTHS
;
1725 long day
= (temp
% DAYS_PER_5_MONTHS
) / 5 + 1;
1727 // month is counted from March - convert to normal
1738 // year is offset by 4800
1741 // check that the algorithm gave us something reasonable
1742 wxASSERT_MSG( (0 < month
) && (month
<= 12), _T("invalid month") );
1743 wxASSERT_MSG( (1 <= day
) && (day
< 32), _T("invalid day") );
1745 // construct Tm from these values
1747 tm
.year
= (int)year
;
1748 tm
.mon
= (Month
)(month
- 1); // algorithm yields 1 for January, not 0
1749 tm
.mday
= (wxDateTime_t
)day
;
1750 tm
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1751 timeOnly
-= tm
.msec
;
1752 timeOnly
/= 1000; // now we have time in seconds
1754 tm
.sec
= (wxDateTime_t
)(timeOnly
% SEC_PER_MIN
);
1756 timeOnly
/= SEC_PER_MIN
; // now we have time in minutes
1758 tm
.min
= (wxDateTime_t
)(timeOnly
% MIN_PER_HOUR
);
1761 tm
.hour
= (wxDateTime_t
)(timeOnly
/ MIN_PER_HOUR
);
1766 wxDateTime
& wxDateTime::SetYear(int year
)
1768 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1777 wxDateTime
& wxDateTime::SetMonth(Month month
)
1779 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1788 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
)
1790 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1799 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
)
1801 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1810 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
)
1812 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1821 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
)
1823 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1832 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
)
1834 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1836 // we don't need to use GetTm() for this one
1837 m_time
-= m_time
% 1000l;
1838 m_time
+= millisecond
;
1843 // ----------------------------------------------------------------------------
1844 // wxDateTime arithmetics
1845 // ----------------------------------------------------------------------------
1847 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
)
1851 tm
.year
+= diff
.GetYears();
1852 tm
.AddMonths(diff
.GetMonths());
1854 // check that the resulting date is valid
1855 if ( tm
.mday
> GetNumOfDaysInMonth(tm
.year
, tm
.mon
) )
1857 // We suppose that when adding one month to Jan 31 we want to get Feb
1858 // 28 (or 29), i.e. adding a month to the last day of the month should
1859 // give the last day of the next month which is quite logical.
1861 // Unfortunately, there is no logic way to understand what should
1862 // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1863 // We make it Feb 28 (last day too), but it is highly questionable.
1864 tm
.mday
= GetNumOfDaysInMonth(tm
.year
, tm
.mon
);
1867 tm
.AddDays(diff
.GetTotalDays());
1871 wxASSERT_MSG( IsSameTime(tm
),
1872 _T("Add(wxDateSpan) shouldn't modify time") );
1877 // ----------------------------------------------------------------------------
1878 // Weekday and monthday stuff
1879 // ----------------------------------------------------------------------------
1881 // convert Sun, Mon, ..., Sat into 6, 0, ..., 5
1882 static inline int ConvertWeekDayToMondayBase(int wd
)
1884 return wd
== wxDateTime::Sun
? 6 : wd
- 1;
1889 wxDateTime::SetToWeekOfYear(int year
, wxDateTime_t numWeek
, WeekDay wd
)
1891 wxASSERT_MSG( numWeek
> 0,
1892 _T("invalid week number: weeks are counted from 1") );
1894 // Jan 4 always lies in the 1st week of the year
1895 wxDateTime
dt(4, Jan
, year
);
1896 dt
.SetToWeekDayInSameWeek(wd
);
1897 dt
+= wxDateSpan::Weeks(numWeek
- 1);
1902 #if WXWIN_COMPATIBILITY_2_6
1903 // use a separate function to avoid warnings about using deprecated
1904 // SetToTheWeek in GetWeek below
1906 SetToTheWeek(int year
,
1907 wxDateTime::wxDateTime_t numWeek
,
1908 wxDateTime::WeekDay weekday
,
1909 wxDateTime::WeekFlags flags
)
1911 // Jan 4 always lies in the 1st week of the year
1912 wxDateTime
dt(4, wxDateTime::Jan
, year
);
1913 dt
.SetToWeekDayInSameWeek(weekday
, flags
);
1914 dt
+= wxDateSpan::Weeks(numWeek
- 1);
1919 bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek
,
1923 int year
= GetYear();
1924 *this = ::SetToTheWeek(year
, numWeek
, weekday
, flags
);
1925 if ( GetYear() != year
)
1927 // oops... numWeek was too big
1934 wxDateTime
wxDateTime::GetWeek(wxDateTime_t numWeek
,
1936 WeekFlags flags
) const
1938 return ::SetToTheWeek(GetYear(), numWeek
, weekday
, flags
);
1940 #endif // WXWIN_COMPATIBILITY_2_6
1942 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
,
1945 // take the current month/year if none specified
1946 if ( year
== Inv_Year
)
1948 if ( month
== Inv_Month
)
1951 return Set(GetNumOfDaysInMonth(year
, month
), month
, year
);
1954 wxDateTime
& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday
, WeekFlags flags
)
1956 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1958 int wdayDst
= weekday
,
1959 wdayThis
= GetWeekDay();
1960 if ( wdayDst
== wdayThis
)
1966 if ( flags
== Default_First
)
1968 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
1971 // the logic below based on comparing weekday and wdayThis works if Sun (0)
1972 // is the first day in the week, but breaks down for Monday_First case so
1973 // we adjust the week days in this case
1974 if ( flags
== Monday_First
)
1976 if ( wdayThis
== Sun
)
1978 if ( wdayDst
== Sun
)
1981 //else: Sunday_First, nothing to do
1983 // go forward or back in time to the day we want
1984 if ( wdayDst
< wdayThis
)
1986 return Subtract(wxDateSpan::Days(wdayThis
- wdayDst
));
1988 else // weekday > wdayThis
1990 return Add(wxDateSpan::Days(wdayDst
- wdayThis
));
1994 wxDateTime
& wxDateTime::SetToNextWeekDay(WeekDay weekday
)
1996 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1999 WeekDay wdayThis
= GetWeekDay();
2000 if ( weekday
== wdayThis
)
2005 else if ( weekday
< wdayThis
)
2007 // need to advance a week
2008 diff
= 7 - (wdayThis
- weekday
);
2010 else // weekday > wdayThis
2012 diff
= weekday
- wdayThis
;
2015 return Add(wxDateSpan::Days(diff
));
2018 wxDateTime
& wxDateTime::SetToPrevWeekDay(WeekDay weekday
)
2020 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
2023 WeekDay wdayThis
= GetWeekDay();
2024 if ( weekday
== wdayThis
)
2029 else if ( weekday
> wdayThis
)
2031 // need to go to previous week
2032 diff
= 7 - (weekday
- wdayThis
);
2034 else // weekday < wdayThis
2036 diff
= wdayThis
- weekday
;
2039 return Subtract(wxDateSpan::Days(diff
));
2042 bool wxDateTime::SetToWeekDay(WeekDay weekday
,
2047 wxCHECK_MSG( weekday
!= Inv_WeekDay
, false, _T("invalid weekday") );
2049 // we don't check explicitly that -5 <= n <= 5 because we will return false
2050 // anyhow in such case - but may be should still give an assert for it?
2052 // take the current month/year if none specified
2053 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
2057 // TODO this probably could be optimised somehow...
2061 // get the first day of the month
2062 dt
.Set(1, month
, year
);
2065 WeekDay wdayFirst
= dt
.GetWeekDay();
2067 // go to the first weekday of the month
2068 int diff
= weekday
- wdayFirst
;
2072 // add advance n-1 weeks more
2075 dt
+= wxDateSpan::Days(diff
);
2077 else // count from the end of the month
2079 // get the last day of the month
2080 dt
.SetToLastMonthDay(month
, year
);
2083 WeekDay wdayLast
= dt
.GetWeekDay();
2085 // go to the last weekday of the month
2086 int diff
= wdayLast
- weekday
;
2090 // and rewind n-1 weeks from there
2093 dt
-= wxDateSpan::Days(diff
);
2096 // check that it is still in the same month
2097 if ( dt
.GetMonth() == month
)
2105 // no such day in this month
2111 wxDateTime::wxDateTime_t
GetDayOfYearFromTm(const wxDateTime::Tm
& tm
)
2113 return (wxDateTime::wxDateTime_t
)(gs_cumulatedDays
[wxDateTime::IsLeapYear(tm
.year
)][tm
.mon
] + tm
.mday
);
2116 wxDateTime::wxDateTime_t
wxDateTime::GetDayOfYear(const TimeZone
& tz
) const
2118 return GetDayOfYearFromTm(GetTm(tz
));
2121 wxDateTime::wxDateTime_t
2122 wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags
, const TimeZone
& tz
) const
2124 if ( flags
== Default_First
)
2126 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
2130 wxDateTime_t nDayInYear
= GetDayOfYearFromTm(tm
);
2132 int wdTarget
= GetWeekDay(tz
);
2133 int wdYearStart
= wxDateTime(1, Jan
, GetYear()).GetWeekDay();
2135 if ( flags
== Sunday_First
)
2137 // FIXME: First week is not calculated correctly.
2138 week
= (nDayInYear
- wdTarget
+ 7) / 7;
2139 if ( wdYearStart
== Wed
|| wdYearStart
== Thu
)
2142 else // week starts with monday
2144 // adjust the weekdays to non-US style.
2145 wdYearStart
= ConvertWeekDayToMondayBase(wdYearStart
);
2146 wdTarget
= ConvertWeekDayToMondayBase(wdTarget
);
2148 // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html:
2150 // Week 01 of a year is per definition the first week that has the
2151 // Thursday in this year, which is equivalent to the week that
2152 // contains the fourth day of January. In other words, the first
2153 // week of a new year is the week that has the majority of its
2154 // days in the new year. Week 01 might also contain days from the
2155 // previous year and the week before week 01 of a year is the last
2156 // week (52 or 53) of the previous year even if it contains days
2157 // from the new year. A week starts with Monday (day 1) and ends
2158 // with Sunday (day 7).
2161 // if Jan 1 is Thursday or less, it is in the first week of this year
2162 if ( wdYearStart
< 4 )
2164 // count the number of entire weeks between Jan 1 and this date
2165 week
= (nDayInYear
+ wdYearStart
+ 6 - wdTarget
)/7;
2167 // be careful to check for overflow in the next year
2168 if ( week
== 53 && tm
.mday
- wdTarget
> 28 )
2171 else // Jan 1 is in the last week of the previous year
2173 // check if we happen to be at the last week of previous year:
2174 if ( tm
.mon
== Jan
&& tm
.mday
< 8 - wdYearStart
)
2175 week
= wxDateTime(31, Dec
, GetYear()-1).GetWeekOfYear();
2177 week
= (nDayInYear
+ wdYearStart
- 1 - wdTarget
)/7;
2181 return (wxDateTime::wxDateTime_t
)week
;
2184 wxDateTime::wxDateTime_t
wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags
,
2185 const TimeZone
& tz
) const
2188 wxDateTime dtMonthStart
= wxDateTime(1, tm
.mon
, tm
.year
);
2189 int nWeek
= GetWeekOfYear(flags
) - dtMonthStart
.GetWeekOfYear(flags
) + 1;
2192 // this may happen for January when Jan, 1 is the last week of the
2194 nWeek
+= IsLeapYear(tm
.year
- 1) ? 53 : 52;
2197 return (wxDateTime::wxDateTime_t
)nWeek
;
2200 wxDateTime
& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday
)
2202 int year
= GetYear();
2203 wxDATETIME_CHECK( (0 < yday
) && (yday
<= GetNumberOfDays(year
)),
2204 _T("invalid year day") );
2206 bool isLeap
= IsLeapYear(year
);
2207 for ( Month mon
= Jan
; mon
< Inv_Month
; wxNextMonth(mon
) )
2209 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
2210 // don't need it neither - because of the CHECK above we know that
2211 // yday lies in December then
2212 if ( (mon
== Dec
) || (yday
<= gs_cumulatedDays
[isLeap
][mon
+ 1]) )
2214 Set((wxDateTime::wxDateTime_t
)(yday
- gs_cumulatedDays
[isLeap
][mon
]), mon
, year
);
2223 // ----------------------------------------------------------------------------
2224 // Julian day number conversion and related stuff
2225 // ----------------------------------------------------------------------------
2227 double wxDateTime::GetJulianDayNumber() const
2229 return m_time
.ToDouble() / MILLISECONDS_PER_DAY
+ EPOCH_JDN
+ 0.5;
2232 double wxDateTime::GetRataDie() const
2234 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
2235 return GetJulianDayNumber() - 1721119.5 - 306;
2238 // ----------------------------------------------------------------------------
2239 // timezone and DST stuff
2240 // ----------------------------------------------------------------------------
2242 int wxDateTime::IsDST(wxDateTime::Country country
) const
2244 wxCHECK_MSG( country
== Country_Default
, -1,
2245 _T("country support not implemented") );
2247 // use the C RTL for the dates in the standard range
2248 time_t timet
= GetTicks();
2249 if ( timet
!= (time_t)-1 )
2252 tm
*tm
= wxLocaltime_r(&timet
, &tmstruct
);
2254 wxCHECK_MSG( tm
, -1, _T("wxLocaltime_r() failed") );
2256 return tm
->tm_isdst
;
2260 int year
= GetYear();
2262 if ( !IsDSTApplicable(year
, country
) )
2264 // no DST time in this year in this country
2268 return IsBetween(GetBeginDST(year
, country
), GetEndDST(year
, country
));
2272 wxDateTime
& wxDateTime::MakeTimezone(const TimeZone
& tz
, bool noDST
)
2274 long secDiff
= GetTimeZone() + tz
.GetOffset();
2276 // we need to know whether DST is or not in effect for this date unless
2277 // the test disabled by the caller
2278 if ( !noDST
&& (IsDST() == 1) )
2280 // FIXME we assume that the DST is always shifted by 1 hour
2284 return Add(wxTimeSpan::Seconds(secDiff
));
2287 wxDateTime
& wxDateTime::MakeFromTimezone(const TimeZone
& tz
, bool noDST
)
2289 long secDiff
= GetTimeZone() + tz
.GetOffset();
2291 // we need to know whether DST is or not in effect for this date unless
2292 // the test disabled by the caller
2293 if ( !noDST
&& (IsDST() == 1) )
2295 // FIXME we assume that the DST is always shifted by 1 hour
2299 return Subtract(wxTimeSpan::Seconds(secDiff
));
2302 // ----------------------------------------------------------------------------
2303 // wxDateTime to/from text representations
2304 // ----------------------------------------------------------------------------
2306 wxString
wxDateTime::Format(const wxString
& format
, const TimeZone
& tz
) const
2308 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
2309 _T("NULL format in wxDateTime::Format") );
2311 // we have to use our own implementation if the date is out of range of
2312 // strftime() or if we use non standard specificators
2313 #ifdef HAVE_STRFTIME
2314 time_t time
= GetTicks();
2316 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
2321 if ( tz
.GetOffset() == -GetTimeZone() )
2323 // we are working with local time
2324 tm
= wxLocaltime_r(&time
, &tmstruct
);
2326 // should never happen
2327 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxLocaltime_r() failed") );
2331 time
+= (int)tz
.GetOffset();
2333 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
2334 int time2
= (int) time
;
2340 tm
= wxGmtime_r(&time
, &tmstruct
);
2342 // should never happen
2343 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxGmtime_r() failed") );
2347 tm
= (struct tm
*)NULL
;
2353 return CallStrftime(format
, tm
);
2356 //else: use generic code below
2357 #endif // HAVE_STRFTIME
2359 // we only parse ANSI C format specifications here, no POSIX 2
2360 // complications, no GNU extensions but we do add support for a "%l" format
2361 // specifier allowing to get the number of milliseconds
2364 // used for calls to strftime() when we only deal with time
2365 struct tm tmTimeOnly
;
2366 tmTimeOnly
.tm_hour
= tm
.hour
;
2367 tmTimeOnly
.tm_min
= tm
.min
;
2368 tmTimeOnly
.tm_sec
= tm
.sec
;
2369 tmTimeOnly
.tm_wday
= 0;
2370 tmTimeOnly
.tm_yday
= 0;
2371 tmTimeOnly
.tm_mday
= 1; // any date will do
2372 tmTimeOnly
.tm_mon
= 0;
2373 tmTimeOnly
.tm_year
= 76;
2374 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
2376 wxString tmp
, res
, fmt
;
2377 for ( wxString::const_iterator p
= format
.begin(); p
!= format
.end(); ++p
)
2379 if ( *p
!= _T('%') )
2387 // set the default format
2388 switch ( (*++p
).GetValue() )
2390 case _T('Y'): // year has 4 digits
2394 case _T('j'): // day of year has 3 digits
2395 case _T('l'): // milliseconds have 3 digits
2399 case _T('w'): // week day as number has only one
2404 // it's either another valid format specifier in which case
2405 // the format is "%02d" (for all the rest) or we have the
2406 // field width preceding the format in which case it will
2407 // override the default format anyhow
2411 bool restart
= true;
2416 // start of the format specification
2417 switch ( (*p
).GetValue() )
2419 case _T('a'): // a weekday name
2421 // second parameter should be true for abbreviated names
2422 res
+= GetWeekDayName(tm
.GetWeekDay(),
2423 *p
== _T('a') ? Name_Abbr
: Name_Full
);
2426 case _T('b'): // a month name
2428 res
+= GetMonthName(tm
.mon
,
2429 *p
== _T('b') ? Name_Abbr
: Name_Full
);
2432 case _T('c'): // locale default date and time representation
2433 case _T('x'): // locale default date representation
2434 #ifdef HAVE_STRFTIME
2436 // the problem: there is no way to know what do these format
2437 // specifications correspond to for the current locale.
2439 // the solution: use a hack and still use strftime(): first
2440 // find the YEAR which is a year in the strftime() range (1970
2441 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
2442 // of the real year. Then make a copy of the format and
2443 // replace all occurrences of YEAR in it with some unique
2444 // string not appearing anywhere else in it, then use
2445 // strftime() to format the date in year YEAR and then replace
2446 // YEAR back by the real year and the unique replacement
2447 // string back with YEAR. Notice that "all occurrences of YEAR"
2448 // means all occurrences of 4 digit as well as 2 digit form!
2450 // the bugs: we assume that neither of %c nor %x contains any
2451 // fields which may change between the YEAR and real year. For
2452 // example, the week number (%U, %W) and the day number (%j)
2453 // will change if one of these years is leap and the other one
2456 // find the YEAR: normally, for any year X, Jan 1 of the
2457 // year X + 28 is the same weekday as Jan 1 of X (because
2458 // the weekday advances by 1 for each normal X and by 2
2459 // for each leap X, hence by 5 every 4 years or by 35
2460 // which is 0 mod 7 every 28 years) but this rule breaks
2461 // down if there are years between X and Y which are
2462 // divisible by 4 but not leap (i.e. divisible by 100 but
2463 // not 400), hence the correction.
2465 int yearReal
= GetYear(tz
);
2466 int mod28
= yearReal
% 28;
2468 // be careful to not go too far - we risk to leave the
2473 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
2477 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
2480 int nCentury
= year
/ 100,
2481 nCenturyReal
= yearReal
/ 100;
2483 // need to adjust for the years divisble by 400 which are
2484 // not leap but are counted like leap ones if we just take
2485 // the number of centuries in between for nLostWeekDays
2486 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
2487 (nCentury
/ 4 - nCenturyReal
/ 4);
2489 // we have to gain back the "lost" weekdays: note that the
2490 // effect of this loop is to not do anything to
2491 // nLostWeekDays (which we won't use any more), but to
2492 // (indirectly) set the year correctly
2493 while ( (nLostWeekDays
% 7) != 0 )
2495 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
2498 // finally move the year below 2000 so that the 2-digit
2499 // year number can never match the month or day of the
2500 // month when we do the replacements below
2504 wxASSERT_MSG( year
>= 1970 && year
< 2000,
2505 _T("logic error in wxDateTime::Format") );
2508 // use strftime() to format the same date but in supported
2511 // NB: we assume that strftime() doesn't check for the
2512 // date validity and will happily format the date
2513 // corresponding to Feb 29 of a non leap year (which
2514 // may happen if yearReal was leap and year is not)
2515 struct tm tmAdjusted
;
2517 tmAdjusted
.tm_hour
= tm
.hour
;
2518 tmAdjusted
.tm_min
= tm
.min
;
2519 tmAdjusted
.tm_sec
= tm
.sec
;
2520 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
2521 tmAdjusted
.tm_yday
= GetDayOfYear();
2522 tmAdjusted
.tm_mday
= tm
.mday
;
2523 tmAdjusted
.tm_mon
= tm
.mon
;
2524 tmAdjusted
.tm_year
= year
- 1900;
2525 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
2526 wxString str
= CallStrftime(*p
== _T('c') ? _T("%c")
2530 // now replace the replacement year with the real year:
2531 // notice that we have to replace the 4 digit year with
2532 // a unique string not appearing in strftime() output
2533 // first to prevent the 2 digit year from matching any
2534 // substring of the 4 digit year (but any day, month,
2535 // hours or minutes components should be safe because
2536 // they are never in 70-99 range)
2537 wxString
replacement("|");
2538 while ( str
.find(replacement
) != wxString::npos
)
2541 str
.Replace(wxString::Format("%d", year
),
2543 str
.Replace(wxString::Format("%d", year
% 100),
2544 wxString::Format("%d", yearReal
% 100));
2545 str
.Replace(replacement
,
2546 wxString::Format("%d", yearReal
));
2550 #else // !HAVE_STRFTIME
2551 // Use "%m/%d/%y %H:%M:%S" format instead
2552 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
2553 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
2554 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
2557 case _T('d'): // day of a month (01-31)
2558 res
+= wxString::Format(fmt
, tm
.mday
);
2561 case _T('H'): // hour in 24h format (00-23)
2562 res
+= wxString::Format(fmt
, tm
.hour
);
2565 case _T('I'): // hour in 12h format (01-12)
2567 // 24h -> 12h, 0h -> 12h too
2568 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
2569 : tm
.hour
? tm
.hour
: 12;
2570 res
+= wxString::Format(fmt
, hour12
);
2574 case _T('j'): // day of the year
2575 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
2578 case _T('l'): // milliseconds (NOT STANDARD)
2579 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
2582 case _T('m'): // month as a number (01-12)
2583 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
2586 case _T('M'): // minute as a decimal number (00-59)
2587 res
+= wxString::Format(fmt
, tm
.min
);
2590 case _T('p'): // AM or PM string
2591 #ifdef HAVE_STRFTIME
2592 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
2593 #else // !HAVE_STRFTIME
2594 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
2595 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
2598 case _T('S'): // second as a decimal number (00-61)
2599 res
+= wxString::Format(fmt
, tm
.sec
);
2602 case _T('U'): // week number in the year (Sunday 1st week day)
2603 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
2606 case _T('W'): // week number in the year (Monday 1st week day)
2607 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
2610 case _T('w'): // weekday as a number (0-6), Sunday = 0
2611 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
2614 // case _T('x'): -- handled with "%c"
2616 case _T('X'): // locale default time representation
2617 // just use strftime() to format the time for us
2618 #ifdef HAVE_STRFTIME
2619 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
2620 #else // !HAVE_STRFTIME
2621 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
2622 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
2625 case _T('y'): // year without century (00-99)
2626 res
+= wxString::Format(fmt
, tm
.year
% 100);
2629 case _T('Y'): // year with century
2630 res
+= wxString::Format(fmt
, tm
.year
);
2633 case _T('Z'): // timezone name
2634 #ifdef HAVE_STRFTIME
2635 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
2640 // is it the format width?
2642 while ( *p
== _T('-') || *p
== _T('+') ||
2643 *p
== _T(' ') || wxIsdigit(*p
) )
2650 // we've only got the flags and width so far in fmt
2651 fmt
.Prepend(_T('%'));
2652 fmt
.Append(_T('d'));
2659 // no, it wasn't the width
2660 wxFAIL_MSG(_T("unknown format specificator"));
2662 // fall through and just copy it nevertheless
2664 case _T('%'): // a percent sign
2668 case 0: // the end of string
2669 wxFAIL_MSG(_T("missing format at the end of string"));
2671 // just put the '%' which was the last char in format
2681 // this function parses a string in (strict) RFC 822 format: see the section 5
2682 // of the RFC for the detailed description, but briefly it's something of the
2683 // form "Sat, 18 Dec 1999 00:48:30 +0100"
2685 // this function is "strict" by design - it must reject anything except true
2686 // RFC822 time specs.
2688 // TODO a great candidate for using reg exps
2690 wxDateTime::ParseRfc822Date(const wxString
& date
, wxString::const_iterator
*end
)
2692 // TODO: rewrite using iterators instead of wxChar pointers
2693 const wxStringCharType
*p
= date
.wx_str();
2694 const wxStringCharType
*comma
= wxStrchr(p
, wxS(','));
2697 // the part before comma is the weekday
2699 // skip it for now - we don't use but might check that it really
2700 // corresponds to the specfied date
2703 if ( *p
!= _T(' ') )
2705 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
2713 // the following 1 or 2 digits are the day number
2714 if ( !wxIsdigit(*p
) )
2716 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
2721 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - _T('0'));
2722 if ( wxIsdigit(*p
) )
2725 day
= (wxDateTime_t
)(day
+ (*p
++ - _T('0')));
2728 if ( *p
++ != _T(' ') )
2733 // the following 3 letters specify the month
2734 wxString
monName(p
, 3);
2736 if ( monName
== _T("Jan") )
2738 else if ( monName
== _T("Feb") )
2740 else if ( monName
== _T("Mar") )
2742 else if ( monName
== _T("Apr") )
2744 else if ( monName
== _T("May") )
2746 else if ( monName
== _T("Jun") )
2748 else if ( monName
== _T("Jul") )
2750 else if ( monName
== _T("Aug") )
2752 else if ( monName
== _T("Sep") )
2754 else if ( monName
== _T("Oct") )
2756 else if ( monName
== _T("Nov") )
2758 else if ( monName
== _T("Dec") )
2762 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str());
2769 if ( *p
++ != _T(' ') )
2775 if ( !wxIsdigit(*p
) )
2781 int year
= *p
++ - _T('0');
2783 if ( !wxIsdigit(*p
) )
2785 // should have at least 2 digits in the year
2790 year
+= *p
++ - _T('0');
2792 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
2793 if ( wxIsdigit(*p
) )
2796 year
+= *p
++ - _T('0');
2798 if ( !wxIsdigit(*p
) )
2800 // no 3 digit years please
2805 year
+= *p
++ - _T('0');
2808 if ( *p
++ != _T(' ') )
2813 // time is in the format hh:mm:ss and seconds are optional
2814 if ( !wxIsdigit(*p
) )
2819 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - _T('0'));
2821 if ( !wxIsdigit(*p
) )
2827 hour
= (wxDateTime_t
)(hour
+ (*p
++ - _T('0')));
2829 if ( *p
++ != _T(':') )
2834 if ( !wxIsdigit(*p
) )
2839 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - _T('0'));
2841 if ( !wxIsdigit(*p
) )
2847 min
= (wxDateTime_t
)(min
+ *p
++ - _T('0'));
2849 wxDateTime_t sec
= 0;
2850 if ( *p
== _T(':') )
2853 if ( !wxIsdigit(*p
) )
2858 sec
= (wxDateTime_t
)(*p
++ - _T('0'));
2860 if ( !wxIsdigit(*p
) )
2866 sec
= (wxDateTime_t
)(sec
+ *p
++ - _T('0'));
2869 if ( *p
++ != _T(' ') )
2874 // and now the interesting part: the timezone
2875 int offset
wxDUMMY_INITIALIZE(0);
2876 if ( *p
== _T('-') || *p
== _T('+') )
2878 // the explicit offset given: it has the form of hhmm
2879 bool plus
= *p
++ == _T('+');
2881 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2887 offset
= MIN_PER_HOUR
*(10*(*p
- _T('0')) + (*(p
+ 1) - _T('0')));
2891 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2897 offset
+= 10*(*p
- _T('0')) + (*(p
+ 1) - _T('0'));
2908 // the symbolic timezone given: may be either military timezone or one
2909 // of standard abbreviations
2912 // military: Z = UTC, J unused, A = -1, ..., Y = +12
2913 static const int offsets
[26] =
2915 //A B C D E F G H I J K L M
2916 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
2917 //N O P R Q S T U V W Z Y Z
2918 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
2921 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
2923 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
);
2928 offset
= offsets
[*p
++ - _T('A')];
2934 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
2936 else if ( tz
== _T("AST") )
2937 offset
= AST
- GMT0
;
2938 else if ( tz
== _T("ADT") )
2939 offset
= ADT
- GMT0
;
2940 else if ( tz
== _T("EST") )
2941 offset
= EST
- GMT0
;
2942 else if ( tz
== _T("EDT") )
2943 offset
= EDT
- GMT0
;
2944 else if ( tz
== _T("CST") )
2945 offset
= CST
- GMT0
;
2946 else if ( tz
== _T("CDT") )
2947 offset
= CDT
- GMT0
;
2948 else if ( tz
== _T("MST") )
2949 offset
= MST
- GMT0
;
2950 else if ( tz
== _T("MDT") )
2951 offset
= MDT
- GMT0
;
2952 else if ( tz
== _T("PST") )
2953 offset
= PST
- GMT0
;
2954 else if ( tz
== _T("PDT") )
2955 offset
= PDT
- GMT0
;
2958 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
);
2967 offset
*= MIN_PER_HOUR
;
2970 // the spec was correct, construct the date from the values we found
2971 Set(day
, mon
, year
, hour
, min
, sec
);
2972 MakeFromTimezone(TimeZone::Make(offset
*SEC_PER_MIN
));
2974 const size_t endpos
= p
- date
.wx_str();
2976 *end
= date
.begin() + endpos
;
2978 return date
.c_str() + endpos
;
2983 // returns the string containing strftime() format used for short dates in the
2984 // current locale or an empty string
2985 static wxString
GetLocaleDateFormat()
2989 // there is no setlocale() under Windows CE, so just always query the
2992 if ( strcmp(setlocale(LC_ALL
, NULL
), "C") != 0 )
2995 // The locale was programatically set to non-C. We assume that this was
2996 // done using wxLocale, in which case thread's current locale is also
2997 // set to correct LCID value and we can use GetLocaleInfo to determine
2998 // the correct formatting string:
3000 LCID lcid
= LOCALE_USER_DEFAULT
;
3002 LCID lcid
= GetThreadLocale();
3004 // according to MSDN 80 chars is max allowed for short date format
3006 if ( ::GetLocaleInfo(lcid
, LOCALE_SSHORTDATE
, fmt
, WXSIZEOF(fmt
)) )
3008 wxChar chLast
= _T('\0');
3009 size_t lastCount
= 0;
3010 for ( const wxChar
*p
= fmt
; /* NUL handled inside */; p
++ )
3020 // these characters come in groups, start counting them
3030 // first deal with any special characters we have had
3036 switch ( lastCount
)
3040 // these two are the same as we
3041 // don't distinguish between 1 and
3042 // 2 digits for days
3055 wxFAIL_MSG( _T("too many 'd's") );
3060 switch ( lastCount
)
3064 // as for 'd' and 'dd' above
3077 wxFAIL_MSG( _T("too many 'M's") );
3082 switch ( lastCount
)
3094 wxFAIL_MSG( _T("wrong number of 'y's") );
3099 // strftime() doesn't have era string,
3100 // ignore this format
3101 wxASSERT_MSG( lastCount
<= 2,
3102 _T("too many 'g's") );
3106 wxFAIL_MSG( _T("unreachable") );
3113 // not a special character so must be just a separator,
3115 if ( *p
!= _T('\0') )
3117 if ( *p
== _T('%') )
3119 // this one needs to be escaped
3127 if ( *p
== _T('\0') )
3131 //else: GetLocaleInfo() failed, leave fmtDate value unchanged and
3132 // try our luck with the default formats
3134 //else: default C locale, default formats should work
3139 #endif // __WINDOWS__
3142 wxDateTime::ParseFormat(const wxString
& date
,
3143 const wxString
& format
,
3144 const wxDateTime
& dateDef
,
3145 wxString::const_iterator
*end
)
3147 wxCHECK_MSG( !format
.empty(), NULL
, "format can't be empty" );
3152 // what fields have we found?
3153 bool haveWDay
= false,
3162 bool hourIsIn12hFormat
= false, // or in 24h one?
3163 isPM
= false; // AM by default
3165 // and the value of the items we have (init them to get rid of warnings)
3166 wxDateTime_t sec
= 0,
3169 WeekDay wday
= Inv_WeekDay
;
3170 wxDateTime_t yday
= 0,
3172 wxDateTime::Month mon
= Inv_Month
;
3175 const wxStringCharType
*input
= date
.wx_str();
3176 for ( wxString::const_iterator fmt
= format
.begin(); fmt
!= format
.end(); ++fmt
)
3178 if ( *fmt
!= _T('%') )
3180 if ( wxIsspace(*fmt
) )
3182 // a white space in the format string matches 0 or more white
3183 // spaces in the input
3184 while ( wxIsspace(*input
) )
3191 // any other character (not whitespace, not '%') must be
3192 // matched by itself in the input
3193 if ( *input
++ != *fmt
)
3200 // done with this format char
3204 // start of a format specification
3206 // parse the optional width
3208 while ( wxIsdigit(*++fmt
) )
3211 width
+= *fmt
- _T('0');
3214 // the default widths for the various fields
3217 switch ( (*fmt
).GetValue() )
3219 case _T('Y'): // year has 4 digits
3223 case _T('j'): // day of year has 3 digits
3224 case _T('l'): // milliseconds have 3 digits
3228 case _T('w'): // week day as number has only one
3233 // default for all other fields
3238 // then the format itself
3239 switch ( (*fmt
).GetValue() )
3241 case _T('a'): // a weekday name
3244 int flag
= *fmt
== _T('a') ? Name_Abbr
: Name_Full
;
3245 wday
= GetWeekDayFromName(GetAlphaToken(input
), flag
);
3246 if ( wday
== Inv_WeekDay
)
3255 case _T('b'): // a month name
3258 int flag
= *fmt
== _T('b') ? Name_Abbr
: Name_Full
;
3259 mon
= GetMonthFromName(GetAlphaToken(input
), flag
);
3260 if ( mon
== Inv_Month
)
3269 case _T('c'): // locale default date and time representation
3273 const wxString
inc(input
);
3275 // try the format which corresponds to ctime() output first
3276 wxString::const_iterator endc
;
3277 if ( !dt
.ParseFormat(inc
, wxS("%a %b %d %H:%M:%S %Y"), &endc
) &&
3278 !dt
.ParseFormat(inc
, wxS("%x %X"), &endc
) &&
3279 !dt
.ParseFormat(inc
, wxS("%X %x"), &endc
) )
3281 // we've tried everything and still no match
3287 haveDay
= haveMon
= haveYear
=
3288 haveHour
= haveMin
= haveSec
= true;
3298 input
+= endc
- inc
.begin();
3302 case _T('d'): // day of a month (01-31)
3303 if ( !GetNumericToken(width
, input
, &num
) ||
3304 (num
> 31) || (num
< 1) )
3310 // we can't check whether the day range is correct yet, will
3311 // do it later - assume ok for now
3313 mday
= (wxDateTime_t
)num
;
3316 case _T('H'): // hour in 24h format (00-23)
3317 if ( !GetNumericToken(width
, input
, &num
) || (num
> 23) )
3324 hour
= (wxDateTime_t
)num
;
3327 case _T('I'): // hour in 12h format (01-12)
3328 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3335 hourIsIn12hFormat
= true;
3336 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
3339 case _T('j'): // day of the year
3340 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 366) )
3347 yday
= (wxDateTime_t
)num
;
3350 case _T('m'): // month as a number (01-12)
3351 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3358 mon
= (Month
)(num
- 1);
3361 case _T('M'): // minute as a decimal number (00-59)
3362 if ( !GetNumericToken(width
, input
, &num
) || (num
> 59) )
3369 min
= (wxDateTime_t
)num
;
3372 case _T('p'): // AM or PM string
3374 wxString am
, pm
, token
= GetAlphaToken(input
);
3376 GetAmPmStrings(&am
, &pm
);
3377 if (am
.empty() && pm
.empty())
3378 return NULL
; // no am/pm strings defined
3379 if ( token
.CmpNoCase(pm
) == 0 )
3383 else if ( token
.CmpNoCase(am
) != 0 )
3391 case _T('r'): // time as %I:%M:%S %p
3394 input
= dt
.ParseFormat(input
, wxS("%I:%M:%S %p"));
3401 haveHour
= haveMin
= haveSec
= true;
3410 case _T('R'): // time as %H:%M
3413 input
= dt
.ParseFormat(input
, wxS("%H:%M"));
3420 haveHour
= haveMin
= true;
3428 case _T('S'): // second as a decimal number (00-61)
3429 if ( !GetNumericToken(width
, input
, &num
) || (num
> 61) )
3436 sec
= (wxDateTime_t
)num
;
3439 case _T('T'): // time as %H:%M:%S
3442 input
= dt
.ParseFormat(input
, _T("%H:%M:%S"));
3449 haveHour
= haveMin
= haveSec
= true;
3458 case _T('w'): // weekday as a number (0-6), Sunday = 0
3459 if ( !GetNumericToken(width
, input
, &num
) || (wday
> 6) )
3466 wday
= (WeekDay
)num
;
3469 case _T('x'): // locale default date representation
3470 #ifdef HAVE_STRPTIME
3471 // try using strptime() -- it may fail even if the input is
3472 // correct but the date is out of range, so we will fall back
3473 // to our generic code anyhow
3477 const wxStringCharType
*
3478 result
= CallStrptime(input
, "%x", &tm
);
3483 haveDay
= haveMon
= haveYear
= true;
3485 year
= 1900 + tm
.tm_year
;
3486 mon
= (Month
)tm
.tm_mon
;
3492 #endif // HAVE_STRPTIME
3500 // The above doesn't work for all locales, try to query
3501 // Windows for the right way of formatting the date:
3502 fmtDate
= GetLocaleDateFormat();
3503 if ( fmtDate
.empty() )
3504 #endif // __WINDOWS__
3506 if ( IsWestEuropeanCountry(GetCountry()) ||
3507 GetCountry() == Russia
)
3509 fmtDate
= _T("%d/%m/%y");
3510 fmtDateAlt
= _T("%m/%d/%y");
3514 fmtDate
= _T("%m/%d/%y");
3515 fmtDateAlt
= _T("%d/%m/%y");
3519 const wxString
indate(input
);
3520 wxString::const_iterator endDate
;
3521 if ( !dt
.ParseFormat(indate
, fmtDate
, &endDate
) )
3523 // try another one if we have it
3524 if ( fmtDateAlt
.empty() ||
3525 !dt
.ParseFormat(indate
, fmtDateAlt
, &endDate
) )
3541 input
+= endDate
- indate
.begin();
3546 case _T('X'): // locale default time representation
3547 #ifdef HAVE_STRPTIME
3549 // use strptime() to do it for us (FIXME !Unicode friendly)
3551 input
= CallStrptime(input
, "%X", &tm
);
3557 haveHour
= haveMin
= haveSec
= true;
3563 #else // !HAVE_STRPTIME
3564 // TODO under Win32 we can query the LOCALE_ITIME system
3565 // setting which says whether the default time format is
3568 // try to parse what follows as "%H:%M:%S" and, if this
3569 // fails, as "%I:%M:%S %p" - this should catch the most
3573 const wxStringCharType
*
3574 result
= dt
.ParseFormat(input
, wxS("%T"));
3577 result
= dt
.ParseFormat(input
, wxS("%r"));
3597 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
3600 case _T('y'): // year without century (00-99)
3601 if ( !GetNumericToken(width
, input
, &num
) || (num
> 99) )
3609 // TODO should have an option for roll over date instead of
3610 // hard coding it here
3611 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
3614 case _T('Y'): // year with century
3615 if ( !GetNumericToken(width
, input
, &num
) )
3622 year
= (wxDateTime_t
)num
;
3625 case _T('Z'): // timezone name
3626 wxFAIL_MSG(_T("TODO"));
3629 case _T('%'): // a percent sign
3630 if ( *input
++ != _T('%') )
3637 case 0: // the end of string
3638 wxFAIL_MSG(_T("unexpected format end"));
3642 default: // not a known format spec
3647 // format matched, try to construct a date from what we have now
3649 if ( dateDef
.IsValid() )
3651 // take this date as default
3652 tmDef
= dateDef
.GetTm();
3654 else if ( IsValid() )
3656 // if this date is valid, don't change it
3661 // no default and this date is invalid - fall back to Today()
3662 tmDef
= Today().GetTm();
3673 // TODO we don't check here that the values are consistent, if both year
3674 // day and month/day were found, we just ignore the year day and we
3675 // also always ignore the week day
3676 if ( haveMon
&& haveDay
)
3678 if ( mday
> GetNumOfDaysInMonth(tm
.year
, mon
) )
3680 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
3688 else if ( haveYDay
)
3690 if ( yday
> GetNumberOfDays(tm
.year
) )
3692 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
3697 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
3704 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
3706 // translate to 24hour format
3709 //else: either already in 24h format or no translation needed
3729 // finally check that the week day is consistent -- if we had it
3730 if ( haveWDay
&& GetWeekDay() != wday
)
3732 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
3737 const size_t endpos
= input
- date
.wx_str();
3739 *end
= date
.begin() + endpos
;
3741 return date
.c_str() + endpos
;
3745 wxDateTime::ParseDateTime(const wxString
& date
, wxString::const_iterator
*end
)
3747 // Set to current day and hour, so strings like '14:00' becomes today at
3748 // 14, not some other random date
3749 wxDateTime dtDate
= wxDateTime::Today();
3750 wxDateTime dtTime
= wxDateTime::Today();
3752 wxString::const_iterator
3757 // If we got a date in the beginning, see if there is a time specified
3759 if ( dtDate
.ParseDate(date
, &endDate
) )
3761 // Skip spaces, as the ParseTime() function fails on spaces
3762 while ( endDate
!= date
.end() && wxIsspace(*endDate
) )
3765 const wxString
timestr(endDate
, date
.end());
3766 if ( !dtTime
.ParseTime(timestr
, &endTime
) )
3769 endBoth
= endDate
+ (endTime
- timestr
.begin());
3771 else // no date in the beginning
3773 // check if we have a time followed by a date
3774 if ( !dtTime
.ParseTime(date
, &endTime
) )
3777 while ( endTime
!= date
.end() && wxIsspace(*endTime
) )
3780 const wxString
datestr(endTime
, date
.end());
3781 if ( !dtDate
.ParseDate(datestr
, &endDate
) )
3784 endBoth
= endTime
+ (endDate
- datestr
.begin());
3787 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
3788 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
3789 dtTime
.GetMillisecond());
3791 // Return endpoint of scan
3795 return date
.c_str() + (endBoth
- date
.begin());
3799 wxDateTime::ParseDate(const wxString
& date
, wxString::const_iterator
*end
)
3801 // this is a simplified version of ParseDateTime() which understands only
3802 // "today" (for wxDate compatibility) and digits only otherwise (and not
3803 // all esoteric constructions ParseDateTime() knows about)
3805 const wxStringCharType
*p
= date
.wx_str();
3806 while ( wxIsspace(*p
) )
3809 // some special cases
3813 int dayDiffFromToday
;
3816 { wxTRANSLATE("today"), 0 },
3817 { wxTRANSLATE("yesterday"), -1 },
3818 { wxTRANSLATE("tomorrow"), 1 },
3821 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
3823 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
3824 size_t len
= dateStr
.length();
3825 if ( wxStrlen(p
) >= len
)
3827 wxString
str(p
, len
);
3828 if ( str
.CmpNoCase(dateStr
) == 0 )
3830 // nothing can follow this, so stop here
3833 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
3835 if ( dayDiffFromToday
)
3837 *this += wxDateSpan::Days(dayDiffFromToday
);
3840 const size_t endpos
= p
- date
.wx_str();
3843 *end
= date
.begin() + endpos
;
3844 return date
.c_str() + endpos
;
3849 // We try to guess what we have here: for each new (numeric) token, we
3850 // determine if it can be a month, day or a year. Of course, there is an
3851 // ambiguity as some numbers may be days as well as months, so we also
3852 // have the ability to back track.
3855 bool haveDay
= false, // the months day?
3856 haveWDay
= false, // the day of week?
3857 haveMon
= false, // the month?
3858 haveYear
= false; // the year?
3860 // and the value of the items we have (init them to get rid of warnings)
3861 WeekDay wday
= Inv_WeekDay
;
3862 wxDateTime_t day
= 0;
3863 wxDateTime::Month mon
= Inv_Month
;
3866 // tokenize the string
3868 static const wxStringCharType
*dateDelimiters
= wxS(".,/-\t\r\n ");
3869 wxStringTokenizer
tok(p
, dateDelimiters
);
3870 while ( tok
.HasMoreTokens() )
3872 wxString token
= tok
.GetNextToken();
3878 if ( token
.ToULong(&val
) )
3880 // guess what this number is
3886 if ( !haveMon
&& val
> 0 && val
<= 12 )
3888 // assume it is month
3891 else // not the month
3895 // this can only be the year
3898 else // may be either day or year
3900 // use a leap year if we don't have the year yet to allow
3901 // dates like 2/29/1976 which would be rejected otherwise
3902 wxDateTime_t max_days
= (wxDateTime_t
)(
3904 ? GetNumOfDaysInMonth(haveYear
? year
: 1976, mon
)
3909 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
3914 else // yes, suppose it's the day
3928 year
= (wxDateTime_t
)val
;
3937 day
= (wxDateTime_t
)val
;
3943 mon
= (Month
)(val
- 1);
3946 else // not a number
3948 // be careful not to overwrite the current mon value
3949 Month mon2
= GetMonthFromName(token
, Name_Full
| Name_Abbr
);
3950 if ( mon2
!= Inv_Month
)
3955 // but we already have a month - maybe we guessed wrong?
3958 // no need to check in month range as always < 12, but
3959 // the days are counted from 1 unlike the months
3960 day
= (wxDateTime_t
)(mon
+ 1);
3965 // could possible be the year (doesn't the year come
3966 // before the month in the japanese format?) (FIXME)
3975 else // not a valid month name
3977 WeekDay wday2
= GetWeekDayFromName(token
, Name_Full
| Name_Abbr
);
3978 if ( wday2
!= Inv_WeekDay
)
3990 else // not a valid weekday name
3993 static const char *ordinals
[] =
3995 wxTRANSLATE("first"),
3996 wxTRANSLATE("second"),
3997 wxTRANSLATE("third"),
3998 wxTRANSLATE("fourth"),
3999 wxTRANSLATE("fifth"),
4000 wxTRANSLATE("sixth"),
4001 wxTRANSLATE("seventh"),
4002 wxTRANSLATE("eighth"),
4003 wxTRANSLATE("ninth"),
4004 wxTRANSLATE("tenth"),
4005 wxTRANSLATE("eleventh"),
4006 wxTRANSLATE("twelfth"),
4007 wxTRANSLATE("thirteenth"),
4008 wxTRANSLATE("fourteenth"),
4009 wxTRANSLATE("fifteenth"),
4010 wxTRANSLATE("sixteenth"),
4011 wxTRANSLATE("seventeenth"),
4012 wxTRANSLATE("eighteenth"),
4013 wxTRANSLATE("nineteenth"),
4014 wxTRANSLATE("twentieth"),
4015 // that's enough - otherwise we'd have problems with
4016 // composite (or not) ordinals
4020 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
4022 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
4028 if ( n
== WXSIZEOF(ordinals
) )
4030 // stop here - something unknown
4037 // don't try anything here (as in case of numeric day
4038 // above) - the symbolic day spec should always
4039 // precede the month/year
4045 day
= (wxDateTime_t
)(n
+ 1);
4050 nPosCur
= tok
.GetPosition();
4053 // either no more tokens or the scan was stopped by something we couldn't
4054 // parse - in any case, see if we can construct a date from what we have
4055 if ( !haveDay
&& !haveWDay
)
4057 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
4062 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
4063 !(haveDay
&& haveMon
&& haveYear
) )
4065 // without adjectives (which we don't support here) the week day only
4066 // makes sense completely separately or with the full date
4067 // specification (what would "Wed 1999" mean?)
4071 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
4073 // may be we have month and day instead of day and year?
4074 if ( haveDay
&& !haveMon
)
4078 // exchange day and month
4079 mon
= (wxDateTime::Month
)(day
- 1);
4081 // we're in the current year then
4082 if ( (year
> 0) && (year
<= (int)GetNumOfDaysInMonth(Inv_Year
, mon
)) )
4084 day
= (wxDateTime_t
)year
;
4089 //else: no, can't exchange, leave haveMon == false
4095 // if we give the year, month and day must be given too
4096 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
4104 mon
= GetCurrentMonth();
4109 year
= GetCurrentYear();
4114 // normally we check the day above but the check is optimistic in case
4115 // we find the day before its month/year so we have to redo it now
4116 if ( day
> GetNumOfDaysInMonth(year
, mon
) )
4119 Set(day
, mon
, year
);
4123 // check that it is really the same
4124 if ( GetWeekDay() != wday
)
4126 // inconsistency detected
4127 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
4137 SetToWeekDayInSameWeek(wday
);
4140 // return the pointer to the first unparsed char
4142 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
4144 // if we couldn't parse the token after the delimiter, put back the
4145 // delimiter as well
4149 const size_t endpos
= p
- date
.wx_str();
4151 *end
= date
.begin() + endpos
;
4153 return date
.c_str() + endpos
;
4157 wxDateTime::ParseTime(const wxString
& time
, wxString::const_iterator
*end
)
4159 // first try some extra things
4166 { wxTRANSLATE("noon"), 12 },
4167 { wxTRANSLATE("midnight"), 00 },
4171 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
4173 wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
4174 size_t len
= timeString
.length();
4175 if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 )
4177 // casts required by DigitalMars
4178 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
4181 *end
= time
.begin() + len
;
4183 return time
.c_str() + len
;
4187 // try all time formats we may think about in the order from longest to
4189 static const char *timeFormats
[] =
4191 "%I:%M:%S %p", // 12hour with AM/PM
4192 "%H:%M:%S", // could be the same or 24 hour one so try it too
4193 "%I:%M %p", // 12hour with AM/PM but without seconds
4194 "%H:%M:%S", // and a possibly 24 hour version without seconds
4195 "%X", // possibly something from above or maybe something
4196 // completely different -- try it last
4198 // TODO: parse timezones
4201 for ( size_t nFmt
= 0; nFmt
< WXSIZEOF(timeFormats
); nFmt
++ )
4203 const char *result
= ParseFormat(time
, timeFormats
[nFmt
], end
);
4211 // ----------------------------------------------------------------------------
4212 // Workdays and holidays support
4213 // ----------------------------------------------------------------------------
4215 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
4217 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
4220 // ============================================================================
4222 // ============================================================================
4224 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
4227 return ds1
.Multiply(n
);
4230 // ============================================================================
4232 // ============================================================================
4234 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
4236 return wxTimeSpan(ts
).Multiply(n
);
4239 // this enum is only used in wxTimeSpan::Format() below but we can't declare
4240 // it locally to the method as it provokes an internal compiler error in egcs
4241 // 2.91.60 when building with -O2
4252 // not all strftime(3) format specifiers make sense here because, for example,
4253 // a time span doesn't have a year nor a timezone
4255 // Here are the ones which are supported (all of them are supported by strftime
4257 // %H hour in 24 hour format
4258 // %M minute (00 - 59)
4259 // %S second (00 - 59)
4262 // Also, for MFC CTimeSpan compatibility, we support
4263 // %D number of days
4265 // And, to be better than MFC :-), we also have
4266 // %E number of wEeks
4267 // %l milliseconds (000 - 999)
4268 wxString
wxTimeSpan::Format(const wxString
& format
) const
4270 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
4271 _T("NULL format in wxTimeSpan::Format") );
4274 str
.Alloc(format
.length());
4276 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
4278 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
4279 // question is what should ts.Format("%S") do? The code here returns "3273"
4280 // in this case (i.e. the total number of seconds, not just seconds % 60)
4281 // because, for me, this call means "give me entire time interval in
4282 // seconds" and not "give me the seconds part of the time interval"
4284 // If we agree that it should behave like this, it is clear that the
4285 // interpretation of each format specifier depends on the presence of the
4286 // other format specs in the string: if there was "%H" before "%M", we
4287 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
4289 // we remember the most important unit found so far
4290 TimeSpanPart partBiggest
= Part_MSec
;
4292 for ( wxString::const_iterator pch
= format
.begin(); pch
!= format
.end(); ++pch
)
4296 if ( ch
== _T('%') )
4298 // the start of the format specification of the printf() below
4299 wxString
fmtPrefix(_T('%'));
4304 // the number of digits for the format string, 0 if unused
4305 unsigned digits
= 0;
4307 ch
= *++pch
; // get the format spec char
4311 wxFAIL_MSG( _T("invalid format character") );
4317 // skip the part below switch
4322 if ( partBiggest
< Part_Day
)
4328 partBiggest
= Part_Day
;
4333 partBiggest
= Part_Week
;
4339 if ( partBiggest
< Part_Hour
)
4343 // the sign has already been taken into account
4344 // when outputting the biggest part
4352 partBiggest
= Part_Hour
;
4359 n
= GetMilliseconds().ToLong();
4360 if ( partBiggest
< Part_MSec
)
4367 //else: no need to reset partBiggest to Part_MSec, it is
4368 // the least significant one anyhow
4375 if ( partBiggest
< Part_Min
)
4384 partBiggest
= Part_Min
;
4391 n
= GetSeconds().ToLong();
4392 if ( partBiggest
< Part_Sec
)
4401 partBiggest
= Part_Sec
;
4410 // negative numbers need one extra position for '-' display
4414 fmtPrefix
<< _T("0") << digits
;
4417 str
+= wxString::Format(fmtPrefix
+ _T("ld"), n
);
4421 // normal character, just copy
4429 // ============================================================================
4430 // wxDateTimeHolidayAuthority and related classes
4431 // ============================================================================
4433 #include "wx/arrimpl.cpp"
4435 WX_DEFINE_OBJARRAY(wxDateTimeArray
)
4437 static int wxCMPFUNC_CONV
4438 wxDateTimeCompareFunc(wxDateTime
**first
, wxDateTime
**second
)
4440 wxDateTime dt1
= **first
,
4443 return dt1
== dt2
? 0 : dt1
< dt2
? -1 : +1;
4446 // ----------------------------------------------------------------------------
4447 // wxDateTimeHolidayAuthority
4448 // ----------------------------------------------------------------------------
4450 wxHolidayAuthoritiesArray
wxDateTimeHolidayAuthority::ms_authorities
;
4453 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime
& dt
)
4455 size_t count
= ms_authorities
.size();
4456 for ( size_t n
= 0; n
< count
; n
++ )
4458 if ( ms_authorities
[n
]->DoIsHoliday(dt
) )
4469 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime
& dtStart
,
4470 const wxDateTime
& dtEnd
,
4471 wxDateTimeArray
& holidays
)
4473 wxDateTimeArray hol
;
4477 const size_t countAuth
= ms_authorities
.size();
4478 for ( size_t nAuth
= 0; nAuth
< countAuth
; nAuth
++ )
4480 ms_authorities
[nAuth
]->DoGetHolidaysInRange(dtStart
, dtEnd
, hol
);
4482 WX_APPEND_ARRAY(holidays
, hol
);
4485 holidays
.Sort(wxDateTimeCompareFunc
);
4487 return holidays
.size();
4491 void wxDateTimeHolidayAuthority::ClearAllAuthorities()
4493 WX_CLEAR_ARRAY(ms_authorities
);
4497 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority
*auth
)
4499 ms_authorities
.push_back(auth
);
4502 wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
4504 // required here for Darwin
4507 // ----------------------------------------------------------------------------
4508 // wxDateTimeWorkDays
4509 // ----------------------------------------------------------------------------
4511 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime
& dt
) const
4513 wxDateTime::WeekDay wd
= dt
.GetWeekDay();
4515 return (wd
== wxDateTime::Sun
) || (wd
== wxDateTime::Sat
);
4518 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime
& dtStart
,
4519 const wxDateTime
& dtEnd
,
4520 wxDateTimeArray
& holidays
) const
4522 if ( dtStart
> dtEnd
)
4524 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
4531 // instead of checking all days, start with the first Sat after dtStart and
4532 // end with the last Sun before dtEnd
4533 wxDateTime dtSatFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sat
),
4534 dtSatLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sat
),
4535 dtSunFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sun
),
4536 dtSunLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sun
),
4539 for ( dt
= dtSatFirst
; dt
<= dtSatLast
; dt
+= wxDateSpan::Week() )
4544 for ( dt
= dtSunFirst
; dt
<= dtSunLast
; dt
+= wxDateSpan::Week() )
4549 return holidays
.GetCount();
4552 // ============================================================================
4553 // other helper functions
4554 // ============================================================================
4556 // ----------------------------------------------------------------------------
4557 // iteration helpers: can be used to write a for loop over enum variable like
4559 // for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
4560 // ----------------------------------------------------------------------------
4562 WXDLLIMPEXP_BASE
void wxNextMonth(wxDateTime::Month
& m
)
4564 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4566 // no wrapping or the for loop above would never end!
4567 m
= (wxDateTime::Month
)(m
+ 1);
4570 WXDLLIMPEXP_BASE
void wxPrevMonth(wxDateTime::Month
& m
)
4572 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4574 m
= m
== wxDateTime::Jan
? wxDateTime::Inv_Month
4575 : (wxDateTime::Month
)(m
- 1);
4578 WXDLLIMPEXP_BASE
void wxNextWDay(wxDateTime::WeekDay
& wd
)
4580 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4582 // no wrapping or the for loop above would never end!
4583 wd
= (wxDateTime::WeekDay
)(wd
+ 1);
4586 WXDLLIMPEXP_BASE
void wxPrevWDay(wxDateTime::WeekDay
& wd
)
4588 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4590 wd
= wd
== wxDateTime::Sun
? wxDateTime::Inv_WeekDay
4591 : (wxDateTime::WeekDay
)(wd
- 1);
4596 wxDateTime
& wxDateTime::SetFromMSWSysTime(const SYSTEMTIME
& st
)
4599 wx_static_cast(wxDateTime::Month
, wxDateTime::Jan
+ st
.wMonth
- 1),
4604 void wxDateTime::GetAsMSWSysTime(SYSTEMTIME
* st
) const
4606 const wxDateTime::Tm
tm(GetTm());
4608 st
->wYear
= (WXWORD
)tm
.year
;
4609 st
->wMonth
= (WXWORD
)(tm
.mon
- wxDateTime::Jan
+ 1);
4616 st
->wMilliseconds
= 0;
4620 #endif // wxUSE_DATETIME