1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/datetime.cpp
3 // Purpose: implementation of time/date related classes
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // parts of code taken from sndcal library by Scott E. Lee:
11 // Copyright 1993-1995, Scott E. Lee, all rights reserved.
12 // Permission granted to use, copy, modify, distribute and sell
13 // so long as the above copyright and this permission statement
14 // are retained in all copies.
16 // Licence: wxWindows licence
17 ///////////////////////////////////////////////////////////////////////////////
19 // TODO: for $DEITY sake, someone please fix the #ifdef __WXWINCE__ everywhere,
20 // the proper way to do it is to implement (subset of) wxStrftime() for
21 // CE instead of this horror!!
24 * Implementation notes:
26 * 1. the time is stored as a 64bit integer containing the signed number of
27 * milliseconds since Jan 1. 1970 (the Unix Epoch) - so it is always
30 * 2. the range is thus something about 580 million years, but due to current
31 * algorithms limitations, only dates from Nov 24, 4714BC are handled
33 * 3. standard ANSI C functions are used to do time calculations whenever
34 * possible, i.e. when the date is in the range Jan 1, 1970 to 2038
36 * 4. otherwise, the calculations are done by converting the date to/from JDN
37 * first (the range limitation mentioned above comes from here: the
38 * algorithm used by Scott E. Lee's code only works for positive JDNs, more
41 * 5. the object constructed for the given DD-MM-YYYY HH:MM:SS corresponds to
42 * this moment in local time and may be converted to the object
43 * corresponding to the same date/time in another time zone by using
46 * 6. the conversions to the current (or any other) timezone are done when the
47 * internal time representation is converted to the broken-down one in
51 // ============================================================================
53 // ============================================================================
55 // ----------------------------------------------------------------------------
57 // ----------------------------------------------------------------------------
59 // For compilers that support precompilation, includes "wx.h".
60 #include "wx/wxprec.h"
66 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
69 #include "wx/string.h"
74 #include "wx/thread.h"
75 #include "wx/tokenzr.h"
76 #include "wx/module.h"
81 #include "wx/msw/wrapwin.h"
88 #include "wx/datetime.h"
89 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
91 const long wxDateTime::TIME_T_FACTOR
= 1000l;
93 #if wxUSE_EXTENDED_RTTI
95 template<> void wxStringReadValue(const wxString
&s
, wxDateTime
&data
)
97 data
.ParseFormat(s
,wxT("%Y-%m-%d %H:%M:%S")) ;
100 template<> void wxStringWriteValue(wxString
&s
, const wxDateTime
&data
)
102 s
= data
.Format(wxT("%Y-%m-%d %H:%M:%S")) ;
105 wxCUSTOM_TYPE_INFO(wxDateTime
, wxToStringConverter
<wxDateTime
> , wxFromStringConverter
<wxDateTime
>)
110 // ----------------------------------------------------------------------------
111 // conditional compilation
112 // ----------------------------------------------------------------------------
114 #if defined(HAVE_STRPTIME) && defined(__GLIBC__) && \
115 ((__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0))
116 // glibc 2.0.7 strptime() is broken - the following snippet causes it to
117 // crash (instead of just failing):
119 // strncpy(buf, "Tue Dec 21 20:25:40 1999", 128);
120 // strptime(buf, "%x", &tm);
124 #endif // broken strptime()
126 #if defined(HAVE_STRPTIME) && defined(__DARWIN__) && defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
127 // configure detects strptime as linkable because it's in the OS X
128 // System library but MSL headers don't declare it.
130 // char *strptime(const char *, const char *, struct tm *);
131 // However, we DON'T want to just provide it here because we would
132 // crash and/or overwrite data when strptime from OS X tries
133 // to fill in MW's struct tm which is two fields shorter (no TZ stuff)
134 // So for now let's just say we don't have strptime
138 #if defined(__MWERKS__) && wxUSE_UNICODE
142 #if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM)
143 #if defined(__WXPALMOS__)
144 #define WX_GMTOFF_IN_TM
145 #elif defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
146 #define WX_TIMEZONE _timezone
147 #elif defined(__MWERKS__)
148 long wxmw_timezone
= 28800;
149 #define WX_TIMEZONE wxmw_timezone
150 #elif defined(__DJGPP__) || defined(__WINE__)
151 #include <sys/timeb.h>
153 static long wxGetTimeZone()
155 static long timezone
= MAXLONG
; // invalid timezone
156 if (timezone
== MAXLONG
)
160 timezone
= tb
.timezone
;
164 #define WX_TIMEZONE wxGetTimeZone()
165 #elif defined(__DARWIN__)
166 #define WX_GMTOFF_IN_TM
167 #else // unknown platform - try timezone
168 #define WX_TIMEZONE timezone
170 #endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
172 #if (!defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)) && wxUSE_THREADS && !defined(__WINDOWS__)
173 static wxMutex timeLock
;
176 #ifndef HAVE_LOCALTIME_R
177 struct tm
*wxLocaltime_r(const time_t* ticks
, struct tm
* temp
)
179 #if wxUSE_THREADS && !defined(__WINDOWS__)
180 // No need to waste time with a mutex on windows since it's using
181 // thread local storage for localtime anyway.
182 wxMutexLocker
locker(timeLock
);
184 memcpy(temp
, localtime(ticks
), sizeof(struct tm
));
189 #ifndef HAVE_GMTIME_R
190 struct tm
*wxGmtime_r(const time_t* ticks
, struct tm
* temp
)
192 #if wxUSE_THREADS && !defined(__WINDOWS__)
193 // No need to waste time with a mutex on windows since it's
194 // using thread local storage for gmtime anyway.
195 wxMutexLocker
locker(timeLock
);
197 memcpy(temp
, gmtime(ticks
), sizeof(struct tm
));
202 // ----------------------------------------------------------------------------
204 // ----------------------------------------------------------------------------
206 // debugging helper: just a convenient replacement of wxCHECK()
207 #define wxDATETIME_CHECK(expr, msg) \
208 wxCHECK2_MSG(expr, *this = wxInvalidDateTime; return *this, msg)
210 // ----------------------------------------------------------------------------
212 // ----------------------------------------------------------------------------
214 class wxDateTimeHolidaysModule
: public wxModule
217 virtual bool OnInit()
219 wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays
);
224 virtual void OnExit()
226 wxDateTimeHolidayAuthority::ClearAllAuthorities();
227 wxDateTimeHolidayAuthority::ms_authorities
.clear();
231 DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule
)
234 IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule
, wxModule
)
236 // ----------------------------------------------------------------------------
238 // ----------------------------------------------------------------------------
241 static const int MONTHS_IN_YEAR
= 12;
243 static const int SEC_PER_MIN
= 60;
245 static const int MIN_PER_HOUR
= 60;
247 static const int HOURS_PER_DAY
= 24;
249 static const long SECONDS_PER_DAY
= 86400l;
251 static const int DAYS_PER_WEEK
= 7;
253 static const long MILLISECONDS_PER_DAY
= 86400000l;
255 // this is the integral part of JDN of the midnight of Jan 1, 1970
256 // (i.e. JDN(Jan 1, 1970) = 2440587.5)
257 static const long EPOCH_JDN
= 2440587l;
259 // used only in asserts
261 // the date of JDN -0.5 (as we don't work with fractional parts, this is the
262 // reference date for us) is Nov 24, 4714BC
263 static const int JDN_0_YEAR
= -4713;
264 static const int JDN_0_MONTH
= wxDateTime::Nov
;
265 static const int JDN_0_DAY
= 24;
266 #endif // __WXDEBUG__
268 // the constants used for JDN calculations
269 static const long JDN_OFFSET
= 32046l;
270 static const long DAYS_PER_5_MONTHS
= 153l;
271 static const long DAYS_PER_4_YEARS
= 1461l;
272 static const long DAYS_PER_400_YEARS
= 146097l;
274 // this array contains the cumulated number of days in all previous months for
275 // normal and leap years
276 static const wxDateTime::wxDateTime_t gs_cumulatedDays
[2][MONTHS_IN_YEAR
] =
278 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
279 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
282 // ----------------------------------------------------------------------------
284 // ----------------------------------------------------------------------------
286 const wxChar
* wxDefaultDateTimeFormat
= wxT("%c");
287 const wxChar
* wxDefaultTimeSpanFormat
= wxT("%H:%M:%S");
289 // in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
290 // indicate an invalid wxDateTime object
291 const wxDateTime wxDefaultDateTime
;
293 wxDateTime::Country
wxDateTime::ms_country
= wxDateTime::Country_Unknown
;
295 // ----------------------------------------------------------------------------
297 // ----------------------------------------------------------------------------
299 // debugger helper: shows what the date really is
301 extern const wxChar
*wxDumpDate(const wxDateTime
* dt
)
303 static wxChar buf
[128];
305 wxStrcpy(buf
, dt
->Format(_T("%Y-%m-%d (%a) %H:%M:%S")));
311 // get the number of days in the given month of the given year
313 wxDateTime::wxDateTime_t
GetNumOfDaysInMonth(int year
, wxDateTime::Month month
)
315 // the number of days in month in Julian/Gregorian calendar: the first line
316 // is for normal years, the second one is for the leap ones
317 static wxDateTime::wxDateTime_t daysInMonth
[2][MONTHS_IN_YEAR
] =
319 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
320 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
323 return daysInMonth
[wxDateTime::IsLeapYear(year
)][month
];
326 // returns the time zone in the C sense, i.e. the difference UTC - local
328 static int GetTimeZone()
330 #ifdef WX_GMTOFF_IN_TM
331 // set to true when the timezone is set
332 static bool s_timezoneSet
= false;
333 static long gmtoffset
= LONG_MAX
; // invalid timezone
335 // ensure that the timezone variable is set by calling wxLocaltime_r
336 if ( !s_timezoneSet
)
338 // just call wxLocaltime_r() instead of figuring out whether this
339 // system supports tzset(), _tzset() or something else
344 tm
= wxLocaltime_r(&t
, &tmstruct
);
345 s_timezoneSet
= true;
347 // note that GMT offset is the opposite of time zone and so to return
348 // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM
349 // cases we have to negate it
350 gmtoffset
= -tm
->tm_gmtoff
;
353 return (int)gmtoffset
;
354 #else // !WX_GMTOFF_IN_TM
355 return (int)WX_TIMEZONE
;
356 #endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
359 // return the integral part of the JDN for the midnight of the given date (to
360 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
361 // noon of the previous day)
362 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day
,
363 wxDateTime::Month mon
,
366 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
368 // check the date validity
370 (year
> JDN_0_YEAR
) ||
371 ((year
== JDN_0_YEAR
) && (mon
> JDN_0_MONTH
)) ||
372 ((year
== JDN_0_YEAR
) && (mon
== JDN_0_MONTH
) && (day
>= JDN_0_DAY
)),
373 _T("date out of range - can't convert to JDN")
376 // make the year positive to avoid problems with negative numbers division
379 // months are counted from March here
381 if ( mon
>= wxDateTime::Mar
)
391 // now we can simply add all the contributions together
392 return ((year
/ 100) * DAYS_PER_400_YEARS
) / 4
393 + ((year
% 100) * DAYS_PER_4_YEARS
) / 4
394 + (month
* DAYS_PER_5_MONTHS
+ 2) / 5
400 // this function is a wrapper around strftime(3) adding error checking
401 static wxString
CallStrftime(const wxChar
*format
, const tm
* tm
)
404 // Create temp wxString here to work around mingw/cygwin bug 1046059
405 // http://sourceforge.net/tracker/?func=detail&atid=102435&aid=1046059&group_id=2435
408 if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) )
410 // buffer is too small?
411 wxFAIL_MSG(_T("strftime() failed"));
421 #if wxUSE_UNIX && !defined(HAVE_STRPTIME_DECL)
422 // configure detected that we had strptime() but not its declaration,
423 // provide it ourselves
424 extern "C" char *strptime(const char *, const char *, struct tm
*);
427 // Unicode-friendly strptime() wrapper
428 static const wxChar
*
429 CallStrptime(const wxChar
*input
, const char *fmt
, tm
*tm
)
431 // the problem here is that strptime() returns pointer into the string we
432 // passed to it while we're really interested in the pointer into the
433 // original, Unicode, string so we try to transform the pointer back
435 wxCharBuffer
inputMB(wxConvertWX2MB(input
));
437 const char * const inputMB
= input
;
438 #endif // Unicode/Ascii
440 const char *result
= strptime(inputMB
, fmt
, tm
);
445 // FIXME: this is wrong in presence of surrogates &c
446 return input
+ (result
- inputMB
.data());
449 #endif // Unicode/Ascii
452 #endif // HAVE_STRPTIME
454 // if year and/or month have invalid values, replace them with the current ones
455 static void ReplaceDefaultYearMonthWithCurrent(int *year
,
456 wxDateTime::Month
*month
)
458 struct tm
*tmNow
= NULL
;
461 if ( *year
== wxDateTime::Inv_Year
)
463 tmNow
= wxDateTime::GetTmNow(&tmstruct
);
465 *year
= 1900 + tmNow
->tm_year
;
468 if ( *month
== wxDateTime::Inv_Month
)
471 tmNow
= wxDateTime::GetTmNow(&tmstruct
);
473 *month
= (wxDateTime::Month
)tmNow
->tm_mon
;
477 // fll the struct tm with default values
478 static void InitTm(struct tm
& tm
)
480 // struct tm may have etxra fields (undocumented and with unportable
481 // names) which, nevertheless, must be set to 0
482 memset(&tm
, 0, sizeof(struct tm
));
484 tm
.tm_mday
= 1; // mday 0 is invalid
485 tm
.tm_year
= 76; // any valid year
486 tm
.tm_isdst
= -1; // auto determine
492 // return the month if the string is a month name or Inv_Month otherwise
493 static wxDateTime::Month
GetMonthFromName(const wxString
& name
, int flags
)
495 wxDateTime::Month mon
;
496 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
498 // case-insensitive comparison either one of or with both abbreviated
500 if ( flags
& wxDateTime::Name_Full
)
502 if ( name
.CmpNoCase(wxDateTime::
503 GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 )
509 if ( flags
& wxDateTime::Name_Abbr
)
511 if ( name
.CmpNoCase(wxDateTime::
512 GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 )
522 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
523 static wxDateTime::WeekDay
GetWeekDayFromName(const wxString
& name
, int flags
)
525 wxDateTime::WeekDay wd
;
526 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
528 // case-insensitive comparison either one of or with both abbreviated
530 if ( flags
& wxDateTime::Name_Full
)
532 if ( name
.CmpNoCase(wxDateTime::
533 GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 )
539 if ( flags
& wxDateTime::Name_Abbr
)
541 if ( name
.CmpNoCase(wxDateTime::
542 GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 )
553 struct tm
*wxDateTime::GetTmNow(struct tm
*tmstruct
)
555 time_t t
= GetTimeNow();
556 return wxLocaltime_r(&t
, tmstruct
);
559 // scans all digits (but no more than len) and returns the resulting number
560 static bool GetNumericToken(size_t len
, const wxChar
*& p
, unsigned long *number
)
564 while ( wxIsdigit(*p
) )
568 if ( len
&& ++n
> len
)
572 return !s
.empty() && s
.ToULong(number
);
575 // scans all alphabetic characters and returns the resulting string
576 static wxString
GetAlphaToken(const wxChar
*& p
)
579 while ( wxIsalpha(*p
) )
587 // ============================================================================
588 // implementation of wxDateTime
589 // ============================================================================
591 // ----------------------------------------------------------------------------
593 // ----------------------------------------------------------------------------
597 year
= (wxDateTime_t
)wxDateTime::Inv_Year
;
598 mon
= wxDateTime::Inv_Month
;
600 hour
= min
= sec
= msec
= 0;
601 wday
= wxDateTime::Inv_WeekDay
;
604 wxDateTime::Tm::Tm(const struct tm
& tm
, const TimeZone
& tz
)
608 sec
= (wxDateTime::wxDateTime_t
)tm
.tm_sec
;
609 min
= (wxDateTime::wxDateTime_t
)tm
.tm_min
;
610 hour
= (wxDateTime::wxDateTime_t
)tm
.tm_hour
;
611 mday
= (wxDateTime::wxDateTime_t
)tm
.tm_mday
;
612 mon
= (wxDateTime::Month
)tm
.tm_mon
;
613 year
= 1900 + tm
.tm_year
;
614 wday
= (wxDateTime::wxDateTime_t
)tm
.tm_wday
;
615 yday
= (wxDateTime::wxDateTime_t
)tm
.tm_yday
;
618 bool wxDateTime::Tm::IsValid() const
620 // we allow for the leap seconds, although we don't use them (yet)
621 return (year
!= wxDateTime::Inv_Year
) && (mon
!= wxDateTime::Inv_Month
) &&
622 (mday
<= GetNumOfDaysInMonth(year
, mon
)) &&
623 (hour
< 24) && (min
< 60) && (sec
< 62) && (msec
< 1000);
626 void wxDateTime::Tm::ComputeWeekDay()
628 // compute the week day from day/month/year: we use the dumbest algorithm
629 // possible: just compute our JDN and then use the (simple to derive)
630 // formula: weekday = (JDN + 1.5) % 7
631 wday
= (wxDateTime::wxDateTime_t
)((wxDateTime::WeekDay
)(GetTruncatedJDN(mday
, mon
, year
) + 2) % 7);
634 void wxDateTime::Tm::AddMonths(int monDiff
)
636 // normalize the months field
637 while ( monDiff
< -mon
)
641 monDiff
+= MONTHS_IN_YEAR
;
644 while ( monDiff
+ mon
>= MONTHS_IN_YEAR
)
648 monDiff
-= MONTHS_IN_YEAR
;
651 mon
= (wxDateTime::Month
)(mon
+ monDiff
);
653 wxASSERT_MSG( mon
>= 0 && mon
< MONTHS_IN_YEAR
, _T("logic error") );
655 // NB: we don't check here that the resulting date is valid, this function
656 // is private and the caller must check it if needed
659 void wxDateTime::Tm::AddDays(int dayDiff
)
661 // normalize the days field
662 while ( dayDiff
+ mday
< 1 )
666 dayDiff
+= GetNumOfDaysInMonth(year
, mon
);
669 mday
= (wxDateTime::wxDateTime_t
)( mday
+ dayDiff
);
670 while ( mday
> GetNumOfDaysInMonth(year
, mon
) )
672 mday
-= GetNumOfDaysInMonth(year
, mon
);
677 wxASSERT_MSG( mday
> 0 && mday
<= GetNumOfDaysInMonth(year
, mon
),
681 // ----------------------------------------------------------------------------
683 // ----------------------------------------------------------------------------
685 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz
)
689 case wxDateTime::Local
:
690 // get the offset from C RTL: it returns the difference GMT-local
691 // while we want to have the offset _from_ GMT, hence the '-'
692 m_offset
= -GetTimeZone();
695 case wxDateTime::GMT_12
:
696 case wxDateTime::GMT_11
:
697 case wxDateTime::GMT_10
:
698 case wxDateTime::GMT_9
:
699 case wxDateTime::GMT_8
:
700 case wxDateTime::GMT_7
:
701 case wxDateTime::GMT_6
:
702 case wxDateTime::GMT_5
:
703 case wxDateTime::GMT_4
:
704 case wxDateTime::GMT_3
:
705 case wxDateTime::GMT_2
:
706 case wxDateTime::GMT_1
:
707 m_offset
= -3600*(wxDateTime::GMT0
- tz
);
710 case wxDateTime::GMT0
:
711 case wxDateTime::GMT1
:
712 case wxDateTime::GMT2
:
713 case wxDateTime::GMT3
:
714 case wxDateTime::GMT4
:
715 case wxDateTime::GMT5
:
716 case wxDateTime::GMT6
:
717 case wxDateTime::GMT7
:
718 case wxDateTime::GMT8
:
719 case wxDateTime::GMT9
:
720 case wxDateTime::GMT10
:
721 case wxDateTime::GMT11
:
722 case wxDateTime::GMT12
:
723 m_offset
= 3600*(tz
- wxDateTime::GMT0
);
726 case wxDateTime::A_CST
:
727 // Central Standard Time in use in Australia = UTC + 9.5
728 m_offset
= 60l*(9*MIN_PER_HOUR
+ MIN_PER_HOUR
/2);
732 wxFAIL_MSG( _T("unknown time zone") );
736 // ----------------------------------------------------------------------------
738 // ----------------------------------------------------------------------------
741 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
)
743 if ( year
== Inv_Year
)
744 year
= GetCurrentYear();
746 if ( cal
== Gregorian
)
748 // in Gregorian calendar leap years are those divisible by 4 except
749 // those divisible by 100 unless they're also divisible by 400
750 // (in some countries, like Russia and Greece, additional corrections
751 // exist, but they won't manifest themselves until 2700)
752 return (year
% 4 == 0) && ((year
% 100 != 0) || (year
% 400 == 0));
754 else if ( cal
== Julian
)
756 // in Julian calendar the rule is simpler
757 return year
% 4 == 0;
761 wxFAIL_MSG(_T("unknown calendar"));
768 int wxDateTime::GetCentury(int year
)
770 return year
> 0 ? year
/ 100 : year
/ 100 - 1;
774 int wxDateTime::ConvertYearToBC(int year
)
777 return year
> 0 ? year
: year
- 1;
781 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
)
786 return Now().GetYear();
789 wxFAIL_MSG(_T("TODO"));
793 wxFAIL_MSG(_T("unsupported calendar"));
801 wxDateTime::Month
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
)
806 return Now().GetMonth();
809 wxFAIL_MSG(_T("TODO"));
813 wxFAIL_MSG(_T("unsupported calendar"));
821 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(int year
, Calendar cal
)
823 if ( year
== Inv_Year
)
825 // take the current year if none given
826 year
= GetCurrentYear();
833 return IsLeapYear(year
) ? 366 : 365;
836 wxFAIL_MSG(_T("unsupported calendar"));
844 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(wxDateTime::Month month
,
846 wxDateTime::Calendar cal
)
848 wxCHECK_MSG( month
< MONTHS_IN_YEAR
, 0, _T("invalid month") );
850 if ( cal
== Gregorian
|| cal
== Julian
)
852 if ( year
== Inv_Year
)
854 // take the current year if none given
855 year
= GetCurrentYear();
858 return GetNumOfDaysInMonth(year
, month
);
862 wxFAIL_MSG(_T("unsupported calendar"));
869 wxString
wxDateTime::GetMonthName(wxDateTime::Month month
,
870 wxDateTime::NameFlags flags
)
872 wxCHECK_MSG( month
!= Inv_Month
, wxEmptyString
, _T("invalid month") );
874 // notice that we must set all the fields to avoid confusing libc (GNU one
875 // gets confused to a crash if we don't do this)
880 return CallStrftime(flags
== Name_Abbr
? _T("%b") : _T("%B"), &tm
);
886 ret
= (flags
== Name_Abbr
? wxT("Jan"): wxT("January"));
889 ret
= (flags
== Name_Abbr
? wxT("Feb"): wxT("Febuary"));
892 ret
= (flags
== Name_Abbr
? wxT("Mar"): wxT("March"));
895 ret
= (flags
== Name_Abbr
? wxT("Apr"): wxT("April"));
898 ret
= (flags
== Name_Abbr
? wxT("May"): wxT("May"));
901 ret
= (flags
== Name_Abbr
? wxT("Jun"): wxT("June"));
904 ret
= (flags
== Name_Abbr
? wxT("Jul"): wxT("July"));
907 ret
= (flags
== Name_Abbr
? wxT("Aug"): wxT("August"));
910 ret
= (flags
== Name_Abbr
? wxT("Sep"): wxT("September"));
913 ret
= (flags
== Name_Abbr
? wxT("Oct"): wxT("October"));
916 ret
= (flags
== Name_Abbr
? wxT("Nov"): wxT("November"));
919 ret
= (flags
== Name_Abbr
? wxT("Dec"): wxT("December"));
927 wxString
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
,
928 wxDateTime::NameFlags flags
)
930 wxCHECK_MSG( wday
!= Inv_WeekDay
, wxEmptyString
, _T("invalid weekday") );
932 // take some arbitrary Sunday (but notice that the day should be such that
933 // after adding wday to it below we still have a valid date, e.g. don't
941 // and offset it by the number of days needed to get the correct wday
944 // call mktime() to normalize it...
947 // ... and call strftime()
948 return CallStrftime(flags
== Name_Abbr
? _T("%a") : _T("%A"), &tm
);
954 ret
= (flags
== Name_Abbr
? wxT("Sun") : wxT("Sunday"));
957 ret
= (flags
== Name_Abbr
? wxT("Mon") : wxT("Monday"));
960 ret
= (flags
== Name_Abbr
? wxT("Tue") : wxT("Tuesday"));
963 ret
= (flags
== Name_Abbr
? wxT("Wed") : wxT("Wednesday"));
966 ret
= (flags
== Name_Abbr
? wxT("Thu") : wxT("Thursday"));
969 ret
= (flags
== Name_Abbr
? wxT("Fri") : wxT("Friday"));
972 ret
= (flags
== Name_Abbr
? wxT("Sat") : wxT("Saturday"));
981 void wxDateTime::GetAmPmStrings(wxString
*am
, wxString
*pm
)
986 // @Note: Do not call 'CallStrftime' here! CallStrftime checks the return code
987 // and causes an assertion failed if the buffer is to small (which is good) - OR -
988 // if strftime does not return anything because the format string is invalid - OR -
989 // if there are no 'am' / 'pm' tokens defined for the current locale (which is not good).
990 // wxDateTime::ParseTime will try several different formats to parse the time.
991 // As a result, GetAmPmStrings might get called, even if the current locale
992 // does not define any 'am' / 'pm' tokens. In this case, wxStrftime would
993 // assert, even though it is a perfectly legal use.
996 if (wxStrftime(buffer
, sizeof(buffer
)/sizeof(wxChar
), _T("%p"), &tm
) > 0)
997 *am
= wxString(buffer
);
1004 if (wxStrftime(buffer
, sizeof(buffer
)/sizeof(wxChar
), _T("%p"), &tm
) > 0)
1005 *pm
= wxString(buffer
);
1011 // ----------------------------------------------------------------------------
1012 // Country stuff: date calculations depend on the country (DST, work days,
1013 // ...), so we need to know which rules to follow.
1014 // ----------------------------------------------------------------------------
1017 wxDateTime::Country
wxDateTime::GetCountry()
1019 // TODO use LOCALE_ICOUNTRY setting under Win32
1021 if ( ms_country
== Country_Unknown
)
1023 // try to guess from the time zone name
1024 time_t t
= time(NULL
);
1026 struct tm
*tm
= wxLocaltime_r(&t
, &tmstruct
);
1028 wxString tz
= CallStrftime(_T("%Z"), tm
);
1029 if ( tz
== _T("WET") || tz
== _T("WEST") )
1033 else if ( tz
== _T("CET") || tz
== _T("CEST") )
1035 ms_country
= Country_EEC
;
1037 else if ( tz
== _T("MSK") || tz
== _T("MSD") )
1039 ms_country
= Russia
;
1041 else if ( tz
== _T("AST") || tz
== _T("ADT") ||
1042 tz
== _T("EST") || tz
== _T("EDT") ||
1043 tz
== _T("CST") || tz
== _T("CDT") ||
1044 tz
== _T("MST") || tz
== _T("MDT") ||
1045 tz
== _T("PST") || tz
== _T("PDT") )
1051 // well, choose a default one
1063 void wxDateTime::SetCountry(wxDateTime::Country country
)
1065 ms_country
= country
;
1069 bool wxDateTime::IsWestEuropeanCountry(Country country
)
1071 if ( country
== Country_Default
)
1073 country
= GetCountry();
1076 return (Country_WesternEurope_Start
<= country
) &&
1077 (country
<= Country_WesternEurope_End
);
1080 // ----------------------------------------------------------------------------
1081 // DST calculations: we use 3 different rules for the West European countries,
1082 // USA and for the rest of the world. This is undoubtedly false for many
1083 // countries, but I lack the necessary info (and the time to gather it),
1084 // please add the other rules here!
1085 // ----------------------------------------------------------------------------
1088 bool wxDateTime::IsDSTApplicable(int year
, Country country
)
1090 if ( year
== Inv_Year
)
1092 // take the current year if none given
1093 year
= GetCurrentYear();
1096 if ( country
== Country_Default
)
1098 country
= GetCountry();
1105 // DST was first observed in the US and UK during WWI, reused
1106 // during WWII and used again since 1966
1107 return year
>= 1966 ||
1108 (year
>= 1942 && year
<= 1945) ||
1109 (year
== 1918 || year
== 1919);
1112 // assume that it started after WWII
1118 wxDateTime
wxDateTime::GetBeginDST(int year
, Country country
)
1120 if ( year
== Inv_Year
)
1122 // take the current year if none given
1123 year
= GetCurrentYear();
1126 if ( country
== Country_Default
)
1128 country
= GetCountry();
1131 if ( !IsDSTApplicable(year
, country
) )
1133 return wxInvalidDateTime
;
1138 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1140 // DST begins at 1 a.m. GMT on the last Sunday of March
1141 if ( !dt
.SetToLastWeekDay(Sun
, Mar
, year
) )
1144 wxFAIL_MSG( _T("no last Sunday in March?") );
1147 dt
+= wxTimeSpan::Hours(1);
1149 // disable DST tests because it could result in an infinite recursion!
1152 else switch ( country
)
1159 // don't know for sure - assume it was in effect all year
1164 dt
.Set(1, Jan
, year
);
1168 // DST was installed Feb 2, 1942 by the Congress
1169 dt
.Set(2, Feb
, year
);
1172 // Oil embargo changed the DST period in the US
1174 dt
.Set(6, Jan
, 1974);
1178 dt
.Set(23, Feb
, 1975);
1182 // before 1986, DST begun on the last Sunday of April, but
1183 // in 1986 Reagan changed it to begin at 2 a.m. of the
1184 // first Sunday in April
1187 if ( !dt
.SetToLastWeekDay(Sun
, Apr
, year
) )
1190 wxFAIL_MSG( _T("no first Sunday in April?") );
1195 if ( !dt
.SetToWeekDay(Sun
, 1, Apr
, year
) )
1198 wxFAIL_MSG( _T("no first Sunday in April?") );
1202 dt
+= wxTimeSpan::Hours(2);
1204 // TODO what about timezone??
1210 // assume Mar 30 as the start of the DST for the rest of the world
1211 // - totally bogus, of course
1212 dt
.Set(30, Mar
, year
);
1219 wxDateTime
wxDateTime::GetEndDST(int year
, Country country
)
1221 if ( year
== Inv_Year
)
1223 // take the current year if none given
1224 year
= GetCurrentYear();
1227 if ( country
== Country_Default
)
1229 country
= GetCountry();
1232 if ( !IsDSTApplicable(year
, country
) )
1234 return wxInvalidDateTime
;
1239 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1241 // DST ends at 1 a.m. GMT on the last Sunday of October
1242 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1244 // weirder and weirder...
1245 wxFAIL_MSG( _T("no last Sunday in October?") );
1248 dt
+= wxTimeSpan::Hours(1);
1250 // disable DST tests because it could result in an infinite recursion!
1253 else switch ( country
)
1260 // don't know for sure - assume it was in effect all year
1264 dt
.Set(31, Dec
, year
);
1268 // the time was reset after the end of the WWII
1269 dt
.Set(30, Sep
, year
);
1273 // DST ends at 2 a.m. on the last Sunday of October
1274 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1276 // weirder and weirder...
1277 wxFAIL_MSG( _T("no last Sunday in October?") );
1280 dt
+= wxTimeSpan::Hours(2);
1282 // TODO what about timezone??
1287 // assume October 26th as the end of the DST - totally bogus too
1288 dt
.Set(26, Oct
, year
);
1294 // ----------------------------------------------------------------------------
1295 // constructors and assignment operators
1296 // ----------------------------------------------------------------------------
1298 // return the current time with ms precision
1299 /* static */ wxDateTime
wxDateTime::UNow()
1301 return wxDateTime(wxGetLocalTimeMillis());
1304 // the values in the tm structure contain the local time
1305 wxDateTime
& wxDateTime::Set(const struct tm
& tm
)
1308 time_t timet
= mktime(&tm2
);
1310 if ( timet
== (time_t)-1 )
1312 // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
1313 // less than timezone - try to make it work for this case
1314 if ( tm2
.tm_year
== 70 && tm2
.tm_mon
== 0 && tm2
.tm_mday
== 1 )
1316 return Set((time_t)(
1318 tm2
.tm_hour
* MIN_PER_HOUR
* SEC_PER_MIN
+
1319 tm2
.tm_min
* SEC_PER_MIN
+
1323 wxFAIL_MSG( _T("mktime() failed") );
1325 *this = wxInvalidDateTime
;
1335 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
,
1336 wxDateTime_t minute
,
1337 wxDateTime_t second
,
1338 wxDateTime_t millisec
)
1340 // we allow seconds to be 61 to account for the leap seconds, even if we
1341 // don't use them really
1342 wxDATETIME_CHECK( hour
< 24 &&
1346 _T("Invalid time in wxDateTime::Set()") );
1348 // get the current date from system
1350 struct tm
*tm
= GetTmNow(&tmstruct
);
1352 wxDATETIME_CHECK( tm
, _T("wxLocaltime_r() failed") );
1354 // make a copy so it isn't clobbered by the call to mktime() below
1359 tm1
.tm_min
= minute
;
1360 tm1
.tm_sec
= second
;
1362 // and the DST in case it changes on this date
1365 if ( tm2
.tm_isdst
!= tm1
.tm_isdst
)
1366 tm1
.tm_isdst
= tm2
.tm_isdst
;
1370 // and finally adjust milliseconds
1371 return SetMillisecond(millisec
);
1374 wxDateTime
& wxDateTime::Set(wxDateTime_t day
,
1378 wxDateTime_t minute
,
1379 wxDateTime_t second
,
1380 wxDateTime_t millisec
)
1382 wxDATETIME_CHECK( hour
< 24 &&
1386 _T("Invalid time in wxDateTime::Set()") );
1388 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1390 wxDATETIME_CHECK( (0 < day
) && (day
<= GetNumberOfDays(month
, year
)),
1391 _T("Invalid date in wxDateTime::Set()") );
1393 // the range of time_t type (inclusive)
1394 static const int yearMinInRange
= 1970;
1395 static const int yearMaxInRange
= 2037;
1397 // test only the year instead of testing for the exact end of the Unix
1398 // time_t range - it doesn't bring anything to do more precise checks
1399 if ( year
>= yearMinInRange
&& year
<= yearMaxInRange
)
1401 // use the standard library version if the date is in range - this is
1402 // probably more efficient than our code
1404 tm
.tm_year
= year
- 1900;
1410 tm
.tm_isdst
= -1; // mktime() will guess it
1414 // and finally adjust milliseconds
1416 SetMillisecond(millisec
);
1422 // do time calculations ourselves: we want to calculate the number of
1423 // milliseconds between the given date and the epoch
1425 // get the JDN for the midnight of this day
1426 m_time
= GetTruncatedJDN(day
, month
, year
);
1427 m_time
-= EPOCH_JDN
;
1428 m_time
*= SECONDS_PER_DAY
* TIME_T_FACTOR
;
1430 // JDN corresponds to GMT, we take localtime
1431 Add(wxTimeSpan(hour
, minute
, second
+ GetTimeZone(), millisec
));
1437 wxDateTime
& wxDateTime::Set(double jdn
)
1439 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1441 jdn
-= EPOCH_JDN
+ 0.5;
1443 m_time
.Assign(jdn
*MILLISECONDS_PER_DAY
);
1445 // JDNs always are in UTC, so we don't need any adjustments for time zone
1450 wxDateTime
& wxDateTime::ResetTime()
1454 if ( tm
.hour
|| tm
.min
|| tm
.sec
|| tm
.msec
)
1467 // ----------------------------------------------------------------------------
1468 // DOS Date and Time Format functions
1469 // ----------------------------------------------------------------------------
1470 // the dos date and time value is an unsigned 32 bit value in the format:
1471 // YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
1473 // Y = year offset from 1980 (0-127)
1475 // D = day of month (1-31)
1477 // m = minute (0-59)
1478 // s = bisecond (0-29) each bisecond indicates two seconds
1479 // ----------------------------------------------------------------------------
1481 wxDateTime
& wxDateTime::SetFromDOS(unsigned long ddt
)
1486 long year
= ddt
& 0xFE000000;
1491 long month
= ddt
& 0x1E00000;
1496 long day
= ddt
& 0x1F0000;
1500 long hour
= ddt
& 0xF800;
1504 long minute
= ddt
& 0x7E0;
1508 long second
= ddt
& 0x1F;
1509 tm
.tm_sec
= second
* 2;
1511 return Set(mktime(&tm
));
1514 unsigned long wxDateTime::GetAsDOS() const
1517 time_t ticks
= GetTicks();
1519 struct tm
*tm
= wxLocaltime_r(&ticks
, &tmstruct
);
1521 long year
= tm
->tm_year
;
1525 long month
= tm
->tm_mon
;
1529 long day
= tm
->tm_mday
;
1532 long hour
= tm
->tm_hour
;
1535 long minute
= tm
->tm_min
;
1538 long second
= tm
->tm_sec
;
1541 ddt
= year
| month
| day
| hour
| minute
| second
;
1545 // ----------------------------------------------------------------------------
1546 // time_t <-> broken down time conversions
1547 // ----------------------------------------------------------------------------
1549 wxDateTime::Tm
wxDateTime::GetTm(const TimeZone
& tz
) const
1551 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1553 time_t time
= GetTicks();
1554 if ( time
!= (time_t)-1 )
1556 // use C RTL functions
1559 if ( tz
.GetOffset() == -GetTimeZone() )
1561 // we are working with local time
1562 tm
= wxLocaltime_r(&time
, &tmstruct
);
1564 // should never happen
1565 wxCHECK_MSG( tm
, Tm(), _T("wxLocaltime_r() failed") );
1569 time
+= (time_t)tz
.GetOffset();
1570 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
1571 int time2
= (int) time
;
1577 tm
= wxGmtime_r(&time
, &tmstruct
);
1579 // should never happen
1580 wxCHECK_MSG( tm
, Tm(), _T("wxGmtime_r() failed") );
1584 tm
= (struct tm
*)NULL
;
1590 // adjust the milliseconds
1592 long timeOnly
= (m_time
% MILLISECONDS_PER_DAY
).ToLong();
1593 tm2
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1596 //else: use generic code below
1599 // remember the time and do the calculations with the date only - this
1600 // eliminates rounding errors of the floating point arithmetics
1602 wxLongLong timeMidnight
= m_time
+ tz
.GetOffset() * 1000;
1604 long timeOnly
= (timeMidnight
% MILLISECONDS_PER_DAY
).ToLong();
1606 // we want to always have positive time and timeMidnight to be really
1607 // the midnight before it
1610 timeOnly
= MILLISECONDS_PER_DAY
+ timeOnly
;
1613 timeMidnight
-= timeOnly
;
1615 // calculate the Gregorian date from JDN for the midnight of our date:
1616 // this will yield day, month (in 1..12 range) and year
1618 // actually, this is the JDN for the noon of the previous day
1619 long jdn
= (timeMidnight
/ MILLISECONDS_PER_DAY
).ToLong() + EPOCH_JDN
;
1621 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1623 wxASSERT_MSG( jdn
> -2, _T("JDN out of range") );
1625 // calculate the century
1626 long temp
= (jdn
+ JDN_OFFSET
) * 4 - 1;
1627 long century
= temp
/ DAYS_PER_400_YEARS
;
1629 // then the year and day of year (1 <= dayOfYear <= 366)
1630 temp
= ((temp
% DAYS_PER_400_YEARS
) / 4) * 4 + 3;
1631 long year
= (century
* 100) + (temp
/ DAYS_PER_4_YEARS
);
1632 long dayOfYear
= (temp
% DAYS_PER_4_YEARS
) / 4 + 1;
1634 // and finally the month and day of the month
1635 temp
= dayOfYear
* 5 - 3;
1636 long month
= temp
/ DAYS_PER_5_MONTHS
;
1637 long day
= (temp
% DAYS_PER_5_MONTHS
) / 5 + 1;
1639 // month is counted from March - convert to normal
1650 // year is offset by 4800
1653 // check that the algorithm gave us something reasonable
1654 wxASSERT_MSG( (0 < month
) && (month
<= 12), _T("invalid month") );
1655 wxASSERT_MSG( (1 <= day
) && (day
< 32), _T("invalid day") );
1657 // construct Tm from these values
1659 tm
.year
= (int)year
;
1660 tm
.mon
= (Month
)(month
- 1); // algorithm yields 1 for January, not 0
1661 tm
.mday
= (wxDateTime_t
)day
;
1662 tm
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1663 timeOnly
-= tm
.msec
;
1664 timeOnly
/= 1000; // now we have time in seconds
1666 tm
.sec
= (wxDateTime_t
)(timeOnly
% SEC_PER_MIN
);
1668 timeOnly
/= SEC_PER_MIN
; // now we have time in minutes
1670 tm
.min
= (wxDateTime_t
)(timeOnly
% MIN_PER_HOUR
);
1673 tm
.hour
= (wxDateTime_t
)(timeOnly
/ MIN_PER_HOUR
);
1678 wxDateTime
& wxDateTime::SetYear(int year
)
1680 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1689 wxDateTime
& wxDateTime::SetMonth(Month month
)
1691 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1700 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
)
1702 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1711 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
)
1713 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1722 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
)
1724 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1733 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
)
1735 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1744 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
)
1746 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1748 // we don't need to use GetTm() for this one
1749 m_time
-= m_time
% 1000l;
1750 m_time
+= millisecond
;
1755 // ----------------------------------------------------------------------------
1756 // wxDateTime arithmetics
1757 // ----------------------------------------------------------------------------
1759 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
)
1763 tm
.year
+= diff
.GetYears();
1764 tm
.AddMonths(diff
.GetMonths());
1766 // check that the resulting date is valid
1767 if ( tm
.mday
> GetNumOfDaysInMonth(tm
.year
, tm
.mon
) )
1769 // We suppose that when adding one month to Jan 31 we want to get Feb
1770 // 28 (or 29), i.e. adding a month to the last day of the month should
1771 // give the last day of the next month which is quite logical.
1773 // Unfortunately, there is no logic way to understand what should
1774 // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1775 // We make it Feb 28 (last day too), but it is highly questionable.
1776 tm
.mday
= GetNumOfDaysInMonth(tm
.year
, tm
.mon
);
1779 tm
.AddDays(diff
.GetTotalDays());
1783 wxASSERT_MSG( IsSameTime(tm
),
1784 _T("Add(wxDateSpan) shouldn't modify time") );
1789 // ----------------------------------------------------------------------------
1790 // Weekday and monthday stuff
1791 // ----------------------------------------------------------------------------
1793 // convert Sun, Mon, ..., Sat into 6, 0, ..., 5
1794 static inline int ConvertWeekDayToMondayBase(int wd
)
1796 return wd
== wxDateTime::Sun
? 6 : wd
- 1;
1801 wxDateTime::SetToWeekOfYear(int year
, wxDateTime_t numWeek
, WeekDay wd
)
1803 wxASSERT_MSG( numWeek
> 0,
1804 _T("invalid week number: weeks are counted from 1") );
1806 // Jan 4 always lies in the 1st week of the year
1807 wxDateTime
dt(4, Jan
, year
);
1808 dt
.SetToWeekDayInSameWeek(wd
);
1809 dt
+= wxDateSpan::Weeks(numWeek
- 1);
1814 // use a separate function to avoid warnings about using deprecated
1815 // SetToTheWeek in GetWeek below
1817 SetToTheWeek(int year
,
1818 wxDateTime::wxDateTime_t numWeek
,
1819 wxDateTime::WeekDay weekday
,
1820 wxDateTime::WeekFlags flags
)
1822 // Jan 4 always lies in the 1st week of the year
1823 wxDateTime
dt(4, wxDateTime::Jan
, year
);
1824 dt
.SetToWeekDayInSameWeek(weekday
, flags
);
1825 dt
+= wxDateSpan::Weeks(numWeek
- 1);
1830 bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek
,
1834 int year
= GetYear();
1835 *this = ::SetToTheWeek(year
, numWeek
, weekday
, flags
);
1836 if ( GetYear() != year
)
1838 // oops... numWeek was too big
1845 wxDateTime
wxDateTime::GetWeek(wxDateTime_t numWeek
,
1847 WeekFlags flags
) const
1849 return ::SetToTheWeek(GetYear(), numWeek
, weekday
, flags
);
1852 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
,
1855 // take the current month/year if none specified
1856 if ( year
== Inv_Year
)
1858 if ( month
== Inv_Month
)
1861 return Set(GetNumOfDaysInMonth(year
, month
), month
, year
);
1864 wxDateTime
& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday
, WeekFlags flags
)
1866 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1868 int wdayDst
= weekday
,
1869 wdayThis
= GetWeekDay();
1870 if ( wdayDst
== wdayThis
)
1876 if ( flags
== Default_First
)
1878 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
1881 // the logic below based on comparing weekday and wdayThis works if Sun (0)
1882 // is the first day in the week, but breaks down for Monday_First case so
1883 // we adjust the week days in this case
1884 if ( flags
== Monday_First
)
1886 if ( wdayThis
== Sun
)
1888 if ( wdayDst
== Sun
)
1891 //else: Sunday_First, nothing to do
1893 // go forward or back in time to the day we want
1894 if ( wdayDst
< wdayThis
)
1896 return Subtract(wxDateSpan::Days(wdayThis
- wdayDst
));
1898 else // weekday > wdayThis
1900 return Add(wxDateSpan::Days(wdayDst
- wdayThis
));
1904 wxDateTime
& wxDateTime::SetToNextWeekDay(WeekDay weekday
)
1906 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1909 WeekDay wdayThis
= GetWeekDay();
1910 if ( weekday
== wdayThis
)
1915 else if ( weekday
< wdayThis
)
1917 // need to advance a week
1918 diff
= 7 - (wdayThis
- weekday
);
1920 else // weekday > wdayThis
1922 diff
= weekday
- wdayThis
;
1925 return Add(wxDateSpan::Days(diff
));
1928 wxDateTime
& wxDateTime::SetToPrevWeekDay(WeekDay weekday
)
1930 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1933 WeekDay wdayThis
= GetWeekDay();
1934 if ( weekday
== wdayThis
)
1939 else if ( weekday
> wdayThis
)
1941 // need to go to previous week
1942 diff
= 7 - (weekday
- wdayThis
);
1944 else // weekday < wdayThis
1946 diff
= wdayThis
- weekday
;
1949 return Subtract(wxDateSpan::Days(diff
));
1952 bool wxDateTime::SetToWeekDay(WeekDay weekday
,
1957 wxCHECK_MSG( weekday
!= Inv_WeekDay
, false, _T("invalid weekday") );
1959 // we don't check explicitly that -5 <= n <= 5 because we will return false
1960 // anyhow in such case - but may be should still give an assert for it?
1962 // take the current month/year if none specified
1963 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1967 // TODO this probably could be optimised somehow...
1971 // get the first day of the month
1972 dt
.Set(1, month
, year
);
1975 WeekDay wdayFirst
= dt
.GetWeekDay();
1977 // go to the first weekday of the month
1978 int diff
= weekday
- wdayFirst
;
1982 // add advance n-1 weeks more
1985 dt
+= wxDateSpan::Days(diff
);
1987 else // count from the end of the month
1989 // get the last day of the month
1990 dt
.SetToLastMonthDay(month
, year
);
1993 WeekDay wdayLast
= dt
.GetWeekDay();
1995 // go to the last weekday of the month
1996 int diff
= wdayLast
- weekday
;
2000 // and rewind n-1 weeks from there
2003 dt
-= wxDateSpan::Days(diff
);
2006 // check that it is still in the same month
2007 if ( dt
.GetMonth() == month
)
2015 // no such day in this month
2021 wxDateTime::wxDateTime_t
GetDayOfYearFromTm(const wxDateTime::Tm
& tm
)
2023 return (wxDateTime::wxDateTime_t
)(gs_cumulatedDays
[wxDateTime::IsLeapYear(tm
.year
)][tm
.mon
] + tm
.mday
);
2026 wxDateTime::wxDateTime_t
wxDateTime::GetDayOfYear(const TimeZone
& tz
) const
2028 return GetDayOfYearFromTm(GetTm(tz
));
2031 wxDateTime::wxDateTime_t
2032 wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags
, const TimeZone
& tz
) const
2034 if ( flags
== Default_First
)
2036 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
2040 wxDateTime_t nDayInYear
= GetDayOfYearFromTm(tm
);
2042 int wdTarget
= GetWeekDay(tz
);
2043 int wdYearStart
= wxDateTime(1, Jan
, GetYear()).GetWeekDay();
2045 if ( flags
== Sunday_First
)
2047 // FIXME: First week is not calculated correctly.
2048 week
= (nDayInYear
- wdTarget
+ 7) / 7;
2049 if ( wdYearStart
== Wed
|| wdYearStart
== Thu
)
2052 else // week starts with monday
2054 // adjust the weekdays to non-US style.
2055 wdYearStart
= ConvertWeekDayToMondayBase(wdYearStart
);
2056 wdTarget
= ConvertWeekDayToMondayBase(wdTarget
);
2058 // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html:
2060 // Week 01 of a year is per definition the first week that has the
2061 // Thursday in this year, which is equivalent to the week that
2062 // contains the fourth day of January. In other words, the first
2063 // week of a new year is the week that has the majority of its
2064 // days in the new year. Week 01 might also contain days from the
2065 // previous year and the week before week 01 of a year is the last
2066 // week (52 or 53) of the previous year even if it contains days
2067 // from the new year. A week starts with Monday (day 1) and ends
2068 // with Sunday (day 7).
2071 // if Jan 1 is Thursday or less, it is in the first week of this year
2072 if ( wdYearStart
< 4 )
2074 // count the number of entire weeks between Jan 1 and this date
2075 week
= (nDayInYear
+ wdYearStart
+ 6 - wdTarget
)/7;
2077 // be careful to check for overflow in the next year
2078 if ( week
== 53 && tm
.mday
- wdTarget
> 28 )
2081 else // Jan 1 is in the last week of the previous year
2083 // check if we happen to be at the last week of previous year:
2084 if ( tm
.mon
== Jan
&& tm
.mday
< 8 - wdYearStart
)
2085 week
= wxDateTime(31, Dec
, GetYear()-1).GetWeekOfYear();
2087 week
= (nDayInYear
+ wdYearStart
- 1 - wdTarget
)/7;
2091 return (wxDateTime::wxDateTime_t
)week
;
2094 wxDateTime::wxDateTime_t
wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags
,
2095 const TimeZone
& tz
) const
2098 wxDateTime dtMonthStart
= wxDateTime(1, tm
.mon
, tm
.year
);
2099 int nWeek
= GetWeekOfYear(flags
) - dtMonthStart
.GetWeekOfYear(flags
) + 1;
2102 // this may happen for January when Jan, 1 is the last week of the
2104 nWeek
+= IsLeapYear(tm
.year
- 1) ? 53 : 52;
2107 return (wxDateTime::wxDateTime_t
)nWeek
;
2110 wxDateTime
& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday
)
2112 int year
= GetYear();
2113 wxDATETIME_CHECK( (0 < yday
) && (yday
<= GetNumberOfDays(year
)),
2114 _T("invalid year day") );
2116 bool isLeap
= IsLeapYear(year
);
2117 for ( Month mon
= Jan
; mon
< Inv_Month
; wxNextMonth(mon
) )
2119 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
2120 // don't need it neither - because of the CHECK above we know that
2121 // yday lies in December then
2122 if ( (mon
== Dec
) || (yday
<= gs_cumulatedDays
[isLeap
][mon
+ 1]) )
2124 Set((wxDateTime::wxDateTime_t
)(yday
- gs_cumulatedDays
[isLeap
][mon
]), mon
, year
);
2133 // ----------------------------------------------------------------------------
2134 // Julian day number conversion and related stuff
2135 // ----------------------------------------------------------------------------
2137 double wxDateTime::GetJulianDayNumber() const
2139 return m_time
.ToDouble() / MILLISECONDS_PER_DAY
+ EPOCH_JDN
+ 0.5;
2142 double wxDateTime::GetRataDie() const
2144 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
2145 return GetJulianDayNumber() - 1721119.5 - 306;
2148 // ----------------------------------------------------------------------------
2149 // timezone and DST stuff
2150 // ----------------------------------------------------------------------------
2152 int wxDateTime::IsDST(wxDateTime::Country country
) const
2154 wxCHECK_MSG( country
== Country_Default
, -1,
2155 _T("country support not implemented") );
2157 // use the C RTL for the dates in the standard range
2158 time_t timet
= GetTicks();
2159 if ( timet
!= (time_t)-1 )
2162 tm
*tm
= wxLocaltime_r(&timet
, &tmstruct
);
2164 wxCHECK_MSG( tm
, -1, _T("wxLocaltime_r() failed") );
2166 return tm
->tm_isdst
;
2170 int year
= GetYear();
2172 if ( !IsDSTApplicable(year
, country
) )
2174 // no DST time in this year in this country
2178 return IsBetween(GetBeginDST(year
, country
), GetEndDST(year
, country
));
2182 wxDateTime
& wxDateTime::MakeTimezone(const TimeZone
& tz
, bool noDST
)
2184 long secDiff
= GetTimeZone() + tz
.GetOffset();
2186 // we need to know whether DST is or not in effect for this date unless
2187 // the test disabled by the caller
2188 if ( !noDST
&& (IsDST() == 1) )
2190 // FIXME we assume that the DST is always shifted by 1 hour
2194 return Add(wxTimeSpan::Seconds(secDiff
));
2197 wxDateTime
& wxDateTime::MakeFromTimezone(const TimeZone
& tz
, bool noDST
)
2199 long secDiff
= GetTimeZone() + tz
.GetOffset();
2201 // we need to know whether DST is or not in effect for this date unless
2202 // the test disabled by the caller
2203 if ( !noDST
&& (IsDST() == 1) )
2205 // FIXME we assume that the DST is always shifted by 1 hour
2209 return Subtract(wxTimeSpan::Seconds(secDiff
));
2212 // ----------------------------------------------------------------------------
2213 // wxDateTime to/from text representations
2214 // ----------------------------------------------------------------------------
2216 wxString
wxDateTime::Format(const wxChar
*format
, const TimeZone
& tz
) const
2218 wxCHECK_MSG( format
, wxEmptyString
, _T("NULL format in wxDateTime::Format") );
2220 // we have to use our own implementation if the date is out of range of
2221 // strftime() or if we use non standard specificators
2222 time_t time
= GetTicks();
2223 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
2228 if ( tz
.GetOffset() == -GetTimeZone() )
2230 // we are working with local time
2231 tm
= wxLocaltime_r(&time
, &tmstruct
);
2233 // should never happen
2234 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxLocaltime_r() failed") );
2238 time
+= (int)tz
.GetOffset();
2240 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
2241 int time2
= (int) time
;
2247 tm
= wxGmtime_r(&time
, &tmstruct
);
2249 // should never happen
2250 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxGmtime_r() failed") );
2254 tm
= (struct tm
*)NULL
;
2258 //Windows CE doesn't support strftime or wcsftime, so we use the generic implementation
2261 return CallStrftime(format
, tm
);
2264 //else: use generic code below
2267 // we only parse ANSI C format specifications here, no POSIX 2
2268 // complications, no GNU extensions but we do add support for a "%l" format
2269 // specifier allowing to get the number of milliseconds
2272 // used for calls to strftime() when we only deal with time
2273 struct tm tmTimeOnly
;
2274 tmTimeOnly
.tm_hour
= tm
.hour
;
2275 tmTimeOnly
.tm_min
= tm
.min
;
2276 tmTimeOnly
.tm_sec
= tm
.sec
;
2277 tmTimeOnly
.tm_wday
= 0;
2278 tmTimeOnly
.tm_yday
= 0;
2279 tmTimeOnly
.tm_mday
= 1; // any date will do
2280 tmTimeOnly
.tm_mon
= 0;
2281 tmTimeOnly
.tm_year
= 76;
2282 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
2284 wxString tmp
, res
, fmt
;
2285 for ( const wxChar
*p
= format
; *p
; p
++ )
2287 if ( *p
!= _T('%') )
2295 // set the default format
2298 case _T('Y'): // year has 4 digits
2302 case _T('j'): // day of year has 3 digits
2303 case _T('l'): // milliseconds have 3 digits
2307 case _T('w'): // week day as number has only one
2312 // it's either another valid format specifier in which case
2313 // the format is "%02d" (for all the rest) or we have the
2314 // field width preceding the format in which case it will
2315 // override the default format anyhow
2319 bool restart
= true;
2324 // start of the format specification
2327 case _T('a'): // a weekday name
2329 // second parameter should be true for abbreviated names
2330 res
+= GetWeekDayName(tm
.GetWeekDay(),
2331 *p
== _T('a') ? Name_Abbr
: Name_Full
);
2334 case _T('b'): // a month name
2336 res
+= GetMonthName(tm
.mon
,
2337 *p
== _T('b') ? Name_Abbr
: Name_Full
);
2340 case _T('c'): // locale default date and time representation
2341 case _T('x'): // locale default date representation
2344 // the problem: there is no way to know what do these format
2345 // specifications correspond to for the current locale.
2347 // the solution: use a hack and still use strftime(): first
2348 // find the YEAR which is a year in the strftime() range (1970
2349 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
2350 // of the real year. Then make a copy of the format and
2351 // replace all occurrences of YEAR in it with some unique
2352 // string not appearing anywhere else in it, then use
2353 // strftime() to format the date in year YEAR and then replace
2354 // YEAR back by the real year and the unique replacement
2355 // string back with YEAR. Notice that "all occurrences of YEAR"
2356 // means all occurrences of 4 digit as well as 2 digit form!
2358 // the bugs: we assume that neither of %c nor %x contains any
2359 // fields which may change between the YEAR and real year. For
2360 // example, the week number (%U, %W) and the day number (%j)
2361 // will change if one of these years is leap and the other one
2364 // find the YEAR: normally, for any year X, Jan 1 or the
2365 // year X + 28 is the same weekday as Jan 1 of X (because
2366 // the weekday advances by 1 for each normal X and by 2
2367 // for each leap X, hence by 5 every 4 years or by 35
2368 // which is 0 mod 7 every 28 years) but this rule breaks
2369 // down if there are years between X and Y which are
2370 // divisible by 4 but not leap (i.e. divisible by 100 but
2371 // not 400), hence the correction.
2373 int yearReal
= GetYear(tz
);
2374 int mod28
= yearReal
% 28;
2376 // be careful to not go too far - we risk to leave the
2381 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
2385 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
2388 int nCentury
= year
/ 100,
2389 nCenturyReal
= yearReal
/ 100;
2391 // need to adjust for the years divisble by 400 which are
2392 // not leap but are counted like leap ones if we just take
2393 // the number of centuries in between for nLostWeekDays
2394 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
2395 (nCentury
/ 4 - nCenturyReal
/ 4);
2397 // we have to gain back the "lost" weekdays: note that the
2398 // effect of this loop is to not do anything to
2399 // nLostWeekDays (which we won't use any more), but to
2400 // (indirectly) set the year correctly
2401 while ( (nLostWeekDays
% 7) != 0 )
2403 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
2406 // Keep year below 2000 so the 2digit year number
2407 // can never match the month or day of the month
2408 if (year
>=2000) year
-=28;
2409 // at any rate, we couldn't go further than 1988 + 9 + 28!
2410 wxASSERT_MSG( year
< 2030,
2411 _T("logic error in wxDateTime::Format") );
2413 wxString strYear
, strYear2
;
2414 strYear
.Printf(_T("%d"), year
);
2415 strYear2
.Printf(_T("%d"), year
% 100);
2417 // find four strings not occurring in format (this is surely
2418 // not the optimal way of doing it... improvements welcome!)
2419 wxString fmt2
= format
;
2420 wxString replacement
,replacement2
,replacement3
,replacement4
;
2421 for (int rnr
=1; rnr
<5 ; rnr
++) {
2422 wxString r
= (wxChar
)-rnr
;
2423 while ( fmt2
.Find(r
) != wxNOT_FOUND
)
2429 case 1: replacement
=r
; break;
2430 case 2: replacement2
=r
; break;
2431 case 3: replacement3
=r
; break;
2432 case 4: replacement4
=r
; break;
2435 // replace all occurrences of year with it
2436 bool wasReplaced
= fmt2
.Replace(strYear
, replacement
) > 0;
2437 // evaluation order ensures we always attempt the replacement.
2438 wasReplaced
= (fmt2
.Replace(strYear2
, replacement2
) > 0) | wasReplaced
;
2440 // use strftime() to format the same date but in supported
2443 // NB: we assume that strftime() doesn't check for the
2444 // date validity and will happily format the date
2445 // corresponding to Feb 29 of a non leap year (which
2446 // may happen if yearReal was leap and year is not)
2447 struct tm tmAdjusted
;
2449 tmAdjusted
.tm_hour
= tm
.hour
;
2450 tmAdjusted
.tm_min
= tm
.min
;
2451 tmAdjusted
.tm_sec
= tm
.sec
;
2452 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
2453 tmAdjusted
.tm_yday
= GetDayOfYear();
2454 tmAdjusted
.tm_mday
= tm
.mday
;
2455 tmAdjusted
.tm_mon
= tm
.mon
;
2456 tmAdjusted
.tm_year
= year
- 1900;
2457 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
2458 wxString str
= CallStrftime(*p
== _T('c') ? _T("%c")
2462 // now replace the occurrence of 1999 with the real year
2463 // we do this in two stages to stop the 2 digit year
2464 // matching any substring of the 4 digit year.
2465 // Any day,month hours and minutes components should be safe due
2466 // to ensuring the range of the years.
2467 wxString strYearReal
, strYearReal2
;
2468 strYearReal
.Printf(_T("%04d"), yearReal
);
2469 strYearReal2
.Printf(_T("%02d"), yearReal
% 100);
2470 str
.Replace(strYear
, replacement3
);
2471 str
.Replace(strYear2
,replacement4
);
2472 str
.Replace(replacement3
, strYearReal
);
2473 str
.Replace(replacement4
, strYearReal2
);
2475 // and replace back all occurrences of replacement string
2478 str
.Replace(replacement2
, strYear2
);
2479 str
.Replace(replacement
, strYear
);
2485 //Use "%m/%d/%y %H:%M:%S" format instead
2486 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
2487 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
2491 case _T('d'): // day of a month (01-31)
2492 res
+= wxString::Format(fmt
, tm
.mday
);
2495 case _T('H'): // hour in 24h format (00-23)
2496 res
+= wxString::Format(fmt
, tm
.hour
);
2499 case _T('I'): // hour in 12h format (01-12)
2501 // 24h -> 12h, 0h -> 12h too
2502 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
2503 : tm
.hour
? tm
.hour
: 12;
2504 res
+= wxString::Format(fmt
, hour12
);
2508 case _T('j'): // day of the year
2509 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
2512 case _T('l'): // milliseconds (NOT STANDARD)
2513 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
2516 case _T('m'): // month as a number (01-12)
2517 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
2520 case _T('M'): // minute as a decimal number (00-59)
2521 res
+= wxString::Format(fmt
, tm
.min
);
2524 case _T('p'): // AM or PM string
2526 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
2528 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
2532 case _T('S'): // second as a decimal number (00-61)
2533 res
+= wxString::Format(fmt
, tm
.sec
);
2536 case _T('U'): // week number in the year (Sunday 1st week day)
2537 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
2540 case _T('W'): // week number in the year (Monday 1st week day)
2541 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
2544 case _T('w'): // weekday as a number (0-6), Sunday = 0
2545 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
2548 // case _T('x'): -- handled with "%c"
2550 case _T('X'): // locale default time representation
2551 // just use strftime() to format the time for us
2553 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
2555 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
2559 case _T('y'): // year without century (00-99)
2560 res
+= wxString::Format(fmt
, tm
.year
% 100);
2563 case _T('Y'): // year with century
2564 res
+= wxString::Format(fmt
, tm
.year
);
2567 case _T('Z'): // timezone name
2569 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
2574 // is it the format width?
2576 while ( *p
== _T('-') || *p
== _T('+') ||
2577 *p
== _T(' ') || wxIsdigit(*p
) )
2584 // we've only got the flags and width so far in fmt
2585 fmt
.Prepend(_T('%'));
2586 fmt
.Append(_T('d'));
2593 // no, it wasn't the width
2594 wxFAIL_MSG(_T("unknown format specificator"));
2596 // fall through and just copy it nevertheless
2598 case _T('%'): // a percent sign
2602 case 0: // the end of string
2603 wxFAIL_MSG(_T("missing format at the end of string"));
2605 // just put the '%' which was the last char in format
2615 // this function parses a string in (strict) RFC 822 format: see the section 5
2616 // of the RFC for the detailed description, but briefly it's something of the
2617 // form "Sat, 18 Dec 1999 00:48:30 +0100"
2619 // this function is "strict" by design - it must reject anything except true
2620 // RFC822 time specs.
2622 // TODO a great candidate for using reg exps
2623 const wxChar
*wxDateTime::ParseRfc822Date(const wxChar
* date
)
2625 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
2627 const wxChar
*p
= date
;
2628 const wxChar
*comma
= wxStrchr(p
, _T(','));
2631 // the part before comma is the weekday
2633 // skip it for now - we don't use but might check that it really
2634 // corresponds to the specfied date
2637 if ( *p
!= _T(' ') )
2639 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
2641 return (wxChar
*)NULL
;
2647 // the following 1 or 2 digits are the day number
2648 if ( !wxIsdigit(*p
) )
2650 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
2652 return (wxChar
*)NULL
;
2655 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - _T('0'));
2656 if ( wxIsdigit(*p
) )
2659 day
= (wxDateTime_t
)(day
+ (*p
++ - _T('0')));
2662 if ( *p
++ != _T(' ') )
2664 return (wxChar
*)NULL
;
2667 // the following 3 letters specify the month
2668 wxString
monName(p
, 3);
2670 if ( monName
== _T("Jan") )
2672 else if ( monName
== _T("Feb") )
2674 else if ( monName
== _T("Mar") )
2676 else if ( monName
== _T("Apr") )
2678 else if ( monName
== _T("May") )
2680 else if ( monName
== _T("Jun") )
2682 else if ( monName
== _T("Jul") )
2684 else if ( monName
== _T("Aug") )
2686 else if ( monName
== _T("Sep") )
2688 else if ( monName
== _T("Oct") )
2690 else if ( monName
== _T("Nov") )
2692 else if ( monName
== _T("Dec") )
2696 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str());
2698 return (wxChar
*)NULL
;
2703 if ( *p
++ != _T(' ') )
2705 return (wxChar
*)NULL
;
2709 if ( !wxIsdigit(*p
) )
2712 return (wxChar
*)NULL
;
2715 int year
= *p
++ - _T('0');
2717 if ( !wxIsdigit(*p
) )
2719 // should have at least 2 digits in the year
2720 return (wxChar
*)NULL
;
2724 year
+= *p
++ - _T('0');
2726 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
2727 if ( wxIsdigit(*p
) )
2730 year
+= *p
++ - _T('0');
2732 if ( !wxIsdigit(*p
) )
2734 // no 3 digit years please
2735 return (wxChar
*)NULL
;
2739 year
+= *p
++ - _T('0');
2742 if ( *p
++ != _T(' ') )
2744 return (wxChar
*)NULL
;
2747 // time is in the format hh:mm:ss and seconds are optional
2748 if ( !wxIsdigit(*p
) )
2750 return (wxChar
*)NULL
;
2753 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - _T('0'));
2755 if ( !wxIsdigit(*p
) )
2757 return (wxChar
*)NULL
;
2761 hour
= (wxDateTime_t
)(hour
+ (*p
++ - _T('0')));
2763 if ( *p
++ != _T(':') )
2765 return (wxChar
*)NULL
;
2768 if ( !wxIsdigit(*p
) )
2770 return (wxChar
*)NULL
;
2773 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - _T('0'));
2775 if ( !wxIsdigit(*p
) )
2777 return (wxChar
*)NULL
;
2781 min
= (wxDateTime_t
)(min
+ *p
++ - _T('0'));
2783 wxDateTime_t sec
= 0;
2784 if ( *p
++ == _T(':') )
2786 if ( !wxIsdigit(*p
) )
2788 return (wxChar
*)NULL
;
2791 sec
= (wxDateTime_t
)(*p
++ - _T('0'));
2793 if ( !wxIsdigit(*p
) )
2795 return (wxChar
*)NULL
;
2799 sec
= (wxDateTime_t
)(sec
+ *p
++ - _T('0'));
2802 if ( *p
++ != _T(' ') )
2804 return (wxChar
*)NULL
;
2807 // and now the interesting part: the timezone
2808 int offset
wxDUMMY_INITIALIZE(0);
2809 if ( *p
== _T('-') || *p
== _T('+') )
2811 // the explicit offset given: it has the form of hhmm
2812 bool plus
= *p
++ == _T('+');
2814 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2816 return (wxChar
*)NULL
;
2820 offset
= MIN_PER_HOUR
*(10*(*p
- _T('0')) + (*(p
+ 1) - _T('0')));
2824 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2826 return (wxChar
*)NULL
;
2830 offset
+= 10*(*p
- _T('0')) + (*(p
+ 1) - _T('0'));
2841 // the symbolic timezone given: may be either military timezone or one
2842 // of standard abbreviations
2845 // military: Z = UTC, J unused, A = -1, ..., Y = +12
2846 static const int offsets
[26] =
2848 //A B C D E F G H I J K L M
2849 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
2850 //N O P R Q S T U V W Z Y Z
2851 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
2854 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
2856 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
);
2858 return (wxChar
*)NULL
;
2861 offset
= offsets
[*p
++ - _T('A')];
2867 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
2869 else if ( tz
== _T("AST") )
2870 offset
= AST
- GMT0
;
2871 else if ( tz
== _T("ADT") )
2872 offset
= ADT
- GMT0
;
2873 else if ( tz
== _T("EST") )
2874 offset
= EST
- GMT0
;
2875 else if ( tz
== _T("EDT") )
2876 offset
= EDT
- GMT0
;
2877 else if ( tz
== _T("CST") )
2878 offset
= CST
- GMT0
;
2879 else if ( tz
== _T("CDT") )
2880 offset
= CDT
- GMT0
;
2881 else if ( tz
== _T("MST") )
2882 offset
= MST
- GMT0
;
2883 else if ( tz
== _T("MDT") )
2884 offset
= MDT
- GMT0
;
2885 else if ( tz
== _T("PST") )
2886 offset
= PST
- GMT0
;
2887 else if ( tz
== _T("PDT") )
2888 offset
= PDT
- GMT0
;
2891 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
);
2893 return (wxChar
*)NULL
;
2900 offset
*= MIN_PER_HOUR
;
2903 // the spec was correct, construct the date from the values we found
2904 Set(day
, mon
, year
, hour
, min
, sec
);
2905 MakeFromTimezone(TimeZone((wxDateTime_t
)(offset
*SEC_PER_MIN
)));
2912 // returns the string containing strftime() format used for short dates in the
2913 // current locale or an empty string
2914 static wxString
GetLocaleDateFormat()
2918 // there is no setlocale() under Windows CE, so just always query the
2921 if ( strcmp(setlocale(LC_ALL
, NULL
), "C") != 0 )
2924 // The locale was programatically set to non-C. We assume that this was
2925 // done using wxLocale, in which case thread's current locale is also
2926 // set to correct LCID value and we can use GetLocaleInfo to determine
2927 // the correct formatting string:
2929 LCID lcid
= LOCALE_USER_DEFAULT
;
2931 LCID lcid
= GetThreadLocale();
2933 // according to MSDN 80 chars is max allowed for short date format
2935 if ( ::GetLocaleInfo(lcid
, LOCALE_SSHORTDATE
, fmt
, WXSIZEOF(fmt
)) )
2937 wxChar chLast
= _T('\0');
2938 size_t lastCount
= 0;
2939 for ( const wxChar
*p
= fmt
; /* NUL handled inside */; p
++ )
2949 // these characters come in groups, start counting them
2959 // first deal with any special characters we have had
2965 switch ( lastCount
)
2969 // these two are the same as we
2970 // don't distinguish between 1 and
2971 // 2 digits for days
2984 wxFAIL_MSG( _T("too many 'd's") );
2989 switch ( lastCount
)
2993 // as for 'd' and 'dd' above
3006 wxFAIL_MSG( _T("too many 'M's") );
3011 switch ( lastCount
)
3023 wxFAIL_MSG( _T("wrong number of 'y's") );
3028 // strftime() doesn't have era string,
3029 // ignore this format
3030 wxASSERT_MSG( lastCount
<= 2,
3031 _T("too many 'g's") );
3035 wxFAIL_MSG( _T("unreachable") );
3042 // not a special character so must be just a separator,
3044 if ( *p
!= _T('\0') )
3046 if ( *p
== _T('%') )
3048 // this one needs to be escaped
3056 if ( *p
== _T('\0') )
3060 //else: GetLocaleInfo() failed, leave fmtDate value unchanged and
3061 // try our luck with the default formats
3063 //else: default C locale, default formats should work
3068 #endif // __WINDOWS__
3070 const wxChar
*wxDateTime::ParseFormat(const wxChar
*date
,
3071 const wxChar
*format
,
3072 const wxDateTime
& dateDef
)
3074 wxCHECK_MSG( date
&& format
, (wxChar
*)NULL
,
3075 _T("NULL pointer in wxDateTime::ParseFormat()") );
3080 // what fields have we found?
3081 bool haveWDay
= false,
3090 bool hourIsIn12hFormat
= false, // or in 24h one?
3091 isPM
= false; // AM by default
3093 // and the value of the items we have (init them to get rid of warnings)
3094 wxDateTime_t sec
= 0,
3097 WeekDay wday
= Inv_WeekDay
;
3098 wxDateTime_t yday
= 0,
3100 wxDateTime::Month mon
= Inv_Month
;
3103 const wxChar
*input
= date
;
3104 for ( const wxChar
*fmt
= format
; *fmt
; fmt
++ )
3106 if ( *fmt
!= _T('%') )
3108 if ( wxIsspace(*fmt
) )
3110 // a white space in the format string matches 0 or more white
3111 // spaces in the input
3112 while ( wxIsspace(*input
) )
3119 // any other character (not whitespace, not '%') must be
3120 // matched by itself in the input
3121 if ( *input
++ != *fmt
)
3124 return (wxChar
*)NULL
;
3128 // done with this format char
3132 // start of a format specification
3134 // parse the optional width
3136 while ( wxIsdigit(*++fmt
) )
3139 width
+= *fmt
- _T('0');
3142 // the default widths for the various fields
3147 case _T('Y'): // year has 4 digits
3151 case _T('j'): // day of year has 3 digits
3152 case _T('l'): // milliseconds have 3 digits
3156 case _T('w'): // week day as number has only one
3161 // default for all other fields
3166 // then the format itself
3169 case _T('a'): // a weekday name
3172 int flag
= *fmt
== _T('a') ? Name_Abbr
: Name_Full
;
3173 wday
= GetWeekDayFromName(GetAlphaToken(input
), flag
);
3174 if ( wday
== Inv_WeekDay
)
3177 return (wxChar
*)NULL
;
3183 case _T('b'): // a month name
3186 int flag
= *fmt
== _T('b') ? Name_Abbr
: Name_Full
;
3187 mon
= GetMonthFromName(GetAlphaToken(input
), flag
);
3188 if ( mon
== Inv_Month
)
3191 return (wxChar
*)NULL
;
3197 case _T('c'): // locale default date and time representation
3201 // this is the format which corresponds to ctime() output
3202 // and strptime("%c") should parse it, so try it first
3203 static const wxChar
*fmtCtime
= _T("%a %b %d %H:%M:%S %Y");
3205 const wxChar
*result
= dt
.ParseFormat(input
, fmtCtime
);
3208 result
= dt
.ParseFormat(input
, _T("%x %X"));
3213 result
= dt
.ParseFormat(input
, _T("%X %x"));
3218 // we've tried everything and still no match
3219 return (wxChar
*)NULL
;
3224 haveDay
= haveMon
= haveYear
=
3225 haveHour
= haveMin
= haveSec
= true;
3239 case _T('d'): // day of a month (01-31)
3240 if ( !GetNumericToken(width
, input
, &num
) ||
3241 (num
> 31) || (num
< 1) )
3244 return (wxChar
*)NULL
;
3247 // we can't check whether the day range is correct yet, will
3248 // do it later - assume ok for now
3250 mday
= (wxDateTime_t
)num
;
3253 case _T('H'): // hour in 24h format (00-23)
3254 if ( !GetNumericToken(width
, input
, &num
) || (num
> 23) )
3257 return (wxChar
*)NULL
;
3261 hour
= (wxDateTime_t
)num
;
3264 case _T('I'): // hour in 12h format (01-12)
3265 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3268 return (wxChar
*)NULL
;
3272 hourIsIn12hFormat
= true;
3273 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
3276 case _T('j'): // day of the year
3277 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 366) )
3280 return (wxChar
*)NULL
;
3284 yday
= (wxDateTime_t
)num
;
3287 case _T('m'): // month as a number (01-12)
3288 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3291 return (wxChar
*)NULL
;
3295 mon
= (Month
)(num
- 1);
3298 case _T('M'): // minute as a decimal number (00-59)
3299 if ( !GetNumericToken(width
, input
, &num
) || (num
> 59) )
3302 return (wxChar
*)NULL
;
3306 min
= (wxDateTime_t
)num
;
3309 case _T('p'): // AM or PM string
3311 wxString am
, pm
, token
= GetAlphaToken(input
);
3313 GetAmPmStrings(&am
, &pm
);
3314 if (am
.empty() && pm
.empty())
3315 return (wxChar
*)NULL
; // no am/pm strings defined
3316 if ( token
.CmpNoCase(pm
) == 0 )
3320 else if ( token
.CmpNoCase(am
) != 0 )
3323 return (wxChar
*)NULL
;
3328 case _T('r'): // time as %I:%M:%S %p
3331 input
= dt
.ParseFormat(input
, _T("%I:%M:%S %p"));
3335 return (wxChar
*)NULL
;
3338 haveHour
= haveMin
= haveSec
= true;
3347 case _T('R'): // time as %H:%M
3350 input
= dt
.ParseFormat(input
, _T("%H:%M"));
3354 return (wxChar
*)NULL
;
3357 haveHour
= haveMin
= true;
3365 case _T('S'): // second as a decimal number (00-61)
3366 if ( !GetNumericToken(width
, input
, &num
) || (num
> 61) )
3369 return (wxChar
*)NULL
;
3373 sec
= (wxDateTime_t
)num
;
3376 case _T('T'): // time as %H:%M:%S
3379 input
= dt
.ParseFormat(input
, _T("%H:%M:%S"));
3383 return (wxChar
*)NULL
;
3386 haveHour
= haveMin
= haveSec
= true;
3395 case _T('w'): // weekday as a number (0-6), Sunday = 0
3396 if ( !GetNumericToken(width
, input
, &num
) || (wday
> 6) )
3399 return (wxChar
*)NULL
;
3403 wday
= (WeekDay
)num
;
3406 case _T('x'): // locale default date representation
3407 #ifdef HAVE_STRPTIME
3408 // try using strptime() -- it may fail even if the input is
3409 // correct but the date is out of range, so we will fall back
3410 // to our generic code anyhow
3414 const wxChar
*result
= CallStrptime(input
, "%x", &tm
);
3419 haveDay
= haveMon
= haveYear
= true;
3421 year
= 1900 + tm
.tm_year
;
3422 mon
= (Month
)tm
.tm_mon
;
3428 #endif // HAVE_STRPTIME
3436 // The above doesn't work for all locales, try to query
3437 // Windows for the right way of formatting the date:
3438 fmtDate
= GetLocaleDateFormat();
3439 if ( fmtDate
.empty() )
3442 if ( IsWestEuropeanCountry(GetCountry()) ||
3443 GetCountry() == Russia
)
3445 fmtDate
= _T("%d/%m/%y");
3446 fmtDateAlt
= _T("%m/%d/%y");
3450 fmtDate
= _T("%m/%d/%y");
3451 fmtDateAlt
= _T("%d/%m/%y");
3455 const wxChar
*result
= dt
.ParseFormat(input
, fmtDate
);
3457 if ( !result
&& !fmtDateAlt
.empty() )
3459 // ok, be nice and try another one
3460 result
= dt
.ParseFormat(input
, fmtDateAlt
);
3466 return (wxChar
*)NULL
;
3471 haveDay
= haveMon
= haveYear
= true;
3482 case _T('X'): // locale default time representation
3483 #ifdef HAVE_STRPTIME
3485 // use strptime() to do it for us (FIXME !Unicode friendly)
3487 input
= CallStrptime(input
, "%X", &tm
);
3490 return (wxChar
*)NULL
;
3493 haveHour
= haveMin
= haveSec
= true;
3499 #else // !HAVE_STRPTIME
3500 // TODO under Win32 we can query the LOCALE_ITIME system
3501 // setting which says whether the default time format is
3504 // try to parse what follows as "%H:%M:%S" and, if this
3505 // fails, as "%I:%M:%S %p" - this should catch the most
3509 const wxChar
*result
= dt
.ParseFormat(input
, _T("%T"));
3512 result
= dt
.ParseFormat(input
, _T("%r"));
3518 return (wxChar
*)NULL
;
3521 haveHour
= haveMin
= haveSec
= true;
3530 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
3533 case _T('y'): // year without century (00-99)
3534 if ( !GetNumericToken(width
, input
, &num
) || (num
> 99) )
3537 return (wxChar
*)NULL
;
3542 // TODO should have an option for roll over date instead of
3543 // hard coding it here
3544 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
3547 case _T('Y'): // year with century
3548 if ( !GetNumericToken(width
, input
, &num
) )
3551 return (wxChar
*)NULL
;
3555 year
= (wxDateTime_t
)num
;
3558 case _T('Z'): // timezone name
3559 wxFAIL_MSG(_T("TODO"));
3562 case _T('%'): // a percent sign
3563 if ( *input
++ != _T('%') )
3566 return (wxChar
*)NULL
;
3570 case 0: // the end of string
3571 wxFAIL_MSG(_T("unexpected format end"));
3575 default: // not a known format spec
3576 return (wxChar
*)NULL
;
3580 // format matched, try to construct a date from what we have now
3582 if ( dateDef
.IsValid() )
3584 // take this date as default
3585 tmDef
= dateDef
.GetTm();
3587 else if ( IsValid() )
3589 // if this date is valid, don't change it
3594 // no default and this date is invalid - fall back to Today()
3595 tmDef
= Today().GetTm();
3606 // TODO we don't check here that the values are consistent, if both year
3607 // day and month/day were found, we just ignore the year day and we
3608 // also always ignore the week day
3609 if ( haveMon
&& haveDay
)
3611 if ( mday
> GetNumOfDaysInMonth(tm
.year
, mon
) )
3613 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
3615 return (wxChar
*)NULL
;
3621 else if ( haveYDay
)
3623 if ( yday
> GetNumberOfDays(tm
.year
) )
3625 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
3627 return (wxChar
*)NULL
;
3630 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
3637 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
3639 // translate to 24hour format
3642 //else: either already in 24h format or no translation needed
3662 // finally check that the week day is consistent -- if we had it
3663 if ( haveWDay
&& GetWeekDay() != wday
)
3665 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
3673 const wxChar
*wxDateTime::ParseDateTime(const wxChar
*date
)
3675 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3677 // Set to current day and hour, so strings like '14:00' becomes today at
3678 // 14, not some other random date
3679 wxDateTime dtDate
= wxDateTime::Today();
3680 wxDateTime dtTime
= wxDateTime::Today();
3682 const wxChar
* pchTime
;
3684 // Try to parse the beginning of the string as a date
3685 const wxChar
* pchDate
= dtDate
.ParseDate(date
);
3687 // We got a date in the beginning, see if there is a time specified after the date
3690 // Skip spaces, as the ParseTime() function fails on spaces
3691 while ( wxIsspace(*pchDate
) )
3694 pchTime
= dtTime
.ParseTime(pchDate
);
3696 else // no date in the beginning
3698 // check and see if we have a time followed by a date
3699 pchTime
= dtTime
.ParseTime(date
);
3702 while ( wxIsspace(*pchTime
) )
3705 pchDate
= dtDate
.ParseDate(pchTime
);
3709 // If we have a date specified, set our own data to the same date
3710 if ( !pchDate
|| !pchTime
)
3713 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
3714 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
3715 dtTime
.GetMillisecond());
3717 // Return endpoint of scan
3718 return pchDate
> pchTime
? pchDate
: pchTime
;
3721 const wxChar
*wxDateTime::ParseDate(const wxChar
*date
)
3723 // this is a simplified version of ParseDateTime() which understands only
3724 // "today" (for wxDate compatibility) and digits only otherwise (and not
3725 // all esoteric constructions ParseDateTime() knows about)
3727 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3729 const wxChar
*p
= date
;
3730 while ( wxIsspace(*p
) )
3733 // some special cases
3737 int dayDiffFromToday
;
3740 { wxTRANSLATE("today"), 0 },
3741 { wxTRANSLATE("yesterday"), -1 },
3742 { wxTRANSLATE("tomorrow"), 1 },
3745 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
3747 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
3748 size_t len
= dateStr
.length();
3749 if ( wxStrlen(p
) >= len
)
3751 wxString
str(p
, len
);
3752 if ( str
.CmpNoCase(dateStr
) == 0 )
3754 // nothing can follow this, so stop here
3757 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
3759 if ( dayDiffFromToday
)
3761 *this += wxDateSpan::Days(dayDiffFromToday
);
3769 // We try to guess what we have here: for each new (numeric) token, we
3770 // determine if it can be a month, day or a year. Of course, there is an
3771 // ambiguity as some numbers may be days as well as months, so we also
3772 // have the ability to back track.
3775 bool haveDay
= false, // the months day?
3776 haveWDay
= false, // the day of week?
3777 haveMon
= false, // the month?
3778 haveYear
= false; // the year?
3780 // and the value of the items we have (init them to get rid of warnings)
3781 WeekDay wday
= Inv_WeekDay
;
3782 wxDateTime_t day
= 0;
3783 wxDateTime::Month mon
= Inv_Month
;
3786 // tokenize the string
3788 static const wxChar
*dateDelimiters
= _T(".,/-\t\r\n ");
3789 wxStringTokenizer
tok(p
, dateDelimiters
);
3790 while ( tok
.HasMoreTokens() )
3792 wxString token
= tok
.GetNextToken();
3798 if ( token
.ToULong(&val
) )
3800 // guess what this number is
3806 if ( !haveMon
&& val
> 0 && val
<= 12 )
3808 // assume it is month
3811 else // not the month
3815 // this can only be the year
3818 else // may be either day or year
3820 // use a leap year if we don't have the year yet to allow
3821 // dates like 2/29/1976 which would be rejected otherwise
3822 wxDateTime_t max_days
= (wxDateTime_t
)(
3824 ? GetNumOfDaysInMonth(haveYear
? year
: 1976, mon
)
3829 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
3834 else // yes, suppose it's the day
3848 year
= (wxDateTime_t
)val
;
3857 day
= (wxDateTime_t
)val
;
3863 mon
= (Month
)(val
- 1);
3866 else // not a number
3868 // be careful not to overwrite the current mon value
3869 Month mon2
= GetMonthFromName(token
, Name_Full
| Name_Abbr
);
3870 if ( mon2
!= Inv_Month
)
3875 // but we already have a month - maybe we guessed wrong?
3878 // no need to check in month range as always < 12, but
3879 // the days are counted from 1 unlike the months
3880 day
= (wxDateTime_t
)(mon
+ 1);
3885 // could possible be the year (doesn't the year come
3886 // before the month in the japanese format?) (FIXME)
3895 else // not a valid month name
3897 wday
= GetWeekDayFromName(token
, Name_Full
| Name_Abbr
);
3898 if ( wday
!= Inv_WeekDay
)
3908 else // not a valid weekday name
3911 static const wxChar
*ordinals
[] =
3913 wxTRANSLATE("first"),
3914 wxTRANSLATE("second"),
3915 wxTRANSLATE("third"),
3916 wxTRANSLATE("fourth"),
3917 wxTRANSLATE("fifth"),
3918 wxTRANSLATE("sixth"),
3919 wxTRANSLATE("seventh"),
3920 wxTRANSLATE("eighth"),
3921 wxTRANSLATE("ninth"),
3922 wxTRANSLATE("tenth"),
3923 wxTRANSLATE("eleventh"),
3924 wxTRANSLATE("twelfth"),
3925 wxTRANSLATE("thirteenth"),
3926 wxTRANSLATE("fourteenth"),
3927 wxTRANSLATE("fifteenth"),
3928 wxTRANSLATE("sixteenth"),
3929 wxTRANSLATE("seventeenth"),
3930 wxTRANSLATE("eighteenth"),
3931 wxTRANSLATE("nineteenth"),
3932 wxTRANSLATE("twentieth"),
3933 // that's enough - otherwise we'd have problems with
3934 // composite (or not) ordinals
3938 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
3940 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
3946 if ( n
== WXSIZEOF(ordinals
) )
3948 // stop here - something unknown
3955 // don't try anything here (as in case of numeric day
3956 // above) - the symbolic day spec should always
3957 // precede the month/year
3963 day
= (wxDateTime_t
)(n
+ 1);
3968 nPosCur
= tok
.GetPosition();
3971 // either no more tokens or the scan was stopped by something we couldn't
3972 // parse - in any case, see if we can construct a date from what we have
3973 if ( !haveDay
&& !haveWDay
)
3975 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
3980 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
3981 !(haveDay
&& haveMon
&& haveYear
) )
3983 // without adjectives (which we don't support here) the week day only
3984 // makes sense completely separately or with the full date
3985 // specification (what would "Wed 1999" mean?)
3989 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
3991 // may be we have month and day instead of day and year?
3992 if ( haveDay
&& !haveMon
)
3996 // exchange day and month
3997 mon
= (wxDateTime::Month
)(day
- 1);
3999 // we're in the current year then
4000 if ( (year
> 0) && (year
<= (int)GetNumOfDaysInMonth(Inv_Year
, mon
)) )
4002 day
= (wxDateTime_t
)year
;
4007 //else: no, can't exchange, leave haveMon == false
4013 // if we give the year, month and day must be given too
4014 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
4022 mon
= GetCurrentMonth();
4027 year
= GetCurrentYear();
4032 // normally we check the day above but the check is optimistic in case
4033 // we find the day before its month/year so we have to redo it now
4034 if ( day
> GetNumOfDaysInMonth(year
, mon
) )
4037 Set(day
, mon
, year
);
4041 // check that it is really the same
4042 if ( GetWeekDay() != wday
)
4044 // inconsistency detected
4045 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
4047 return (wxChar
*)NULL
;
4055 SetToWeekDayInSameWeek(wday
);
4058 // return the pointer to the first unparsed char
4060 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
4062 // if we couldn't parse the token after the delimiter, put back the
4063 // delimiter as well
4070 const wxChar
*wxDateTime::ParseTime(const wxChar
*time
)
4072 wxCHECK_MSG( time
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
4074 // first try some extra things
4081 { wxTRANSLATE("noon"), 12 },
4082 { wxTRANSLATE("midnight"), 00 },
4086 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
4088 wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
4089 size_t len
= timeString
.length();
4090 if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 )
4092 // casts required by DigitalMars
4093 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
4099 // try all time formats we may think about in the order from longest to
4102 // 12hour with AM/PM?
4103 const wxChar
*result
= ParseFormat(time
, _T("%I:%M:%S %p"));
4107 // normally, it's the same, but why not try it?
4108 result
= ParseFormat(time
, _T("%H:%M:%S"));
4113 // 12hour with AM/PM but without seconds?
4114 result
= ParseFormat(time
, _T("%I:%M %p"));
4120 result
= ParseFormat(time
, _T("%H:%M"));
4125 // just the hour and AM/PM?
4126 result
= ParseFormat(time
, _T("%I %p"));
4132 result
= ParseFormat(time
, _T("%H"));
4137 // parse the standard format: normally it is one of the formats above
4138 // but it may be set to something completely different by the user
4139 result
= ParseFormat(time
, _T("%X"));
4142 // TODO: parse timezones
4147 // ----------------------------------------------------------------------------
4148 // Workdays and holidays support
4149 // ----------------------------------------------------------------------------
4151 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
4153 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
4156 // ============================================================================
4158 // ============================================================================
4160 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
4163 return ds1
.Multiply(n
);
4166 // ============================================================================
4168 // ============================================================================
4170 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
4172 return wxTimeSpan(ts
).Multiply(n
);
4175 // this enum is only used in wxTimeSpan::Format() below but we can't declare
4176 // it locally to the method as it provokes an internal compiler error in egcs
4177 // 2.91.60 when building with -O2
4188 // not all strftime(3) format specifiers make sense here because, for example,
4189 // a time span doesn't have a year nor a timezone
4191 // Here are the ones which are supported (all of them are supported by strftime
4193 // %H hour in 24 hour format
4194 // %M minute (00 - 59)
4195 // %S second (00 - 59)
4198 // Also, for MFC CTimeSpan compatibility, we support
4199 // %D number of days
4201 // And, to be better than MFC :-), we also have
4202 // %E number of wEeks
4203 // %l milliseconds (000 - 999)
4204 wxString
wxTimeSpan::Format(const wxChar
*format
) const
4206 wxCHECK_MSG( format
, wxEmptyString
, _T("NULL format in wxTimeSpan::Format") );
4209 str
.Alloc(wxStrlen(format
));
4211 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
4213 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
4214 // question is what should ts.Format("%S") do? The code here returns "3273"
4215 // in this case (i.e. the total number of seconds, not just seconds % 60)
4216 // because, for me, this call means "give me entire time interval in
4217 // seconds" and not "give me the seconds part of the time interval"
4219 // If we agree that it should behave like this, it is clear that the
4220 // interpretation of each format specifier depends on the presence of the
4221 // other format specs in the string: if there was "%H" before "%M", we
4222 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
4224 // we remember the most important unit found so far
4225 TimeSpanPart partBiggest
= Part_MSec
;
4227 for ( const wxChar
*pch
= format
; *pch
; pch
++ )
4231 if ( ch
== _T('%') )
4233 // the start of the format specification of the printf() below
4234 wxString fmtPrefix
= _T('%');
4239 ch
= *++pch
; // get the format spec char
4243 wxFAIL_MSG( _T("invalid format character") );
4249 // skip the part below switch
4254 if ( partBiggest
< Part_Day
)
4260 partBiggest
= Part_Day
;
4265 partBiggest
= Part_Week
;
4271 if ( partBiggest
< Part_Hour
)
4277 partBiggest
= Part_Hour
;
4280 fmtPrefix
+= _T("02");
4284 n
= GetMilliseconds().ToLong();
4285 if ( partBiggest
< Part_MSec
)
4289 //else: no need to reset partBiggest to Part_MSec, it is
4290 // the least significant one anyhow
4292 fmtPrefix
+= _T("03");
4297 if ( partBiggest
< Part_Min
)
4303 partBiggest
= Part_Min
;
4306 fmtPrefix
+= _T("02");
4310 n
= GetSeconds().ToLong();
4311 if ( partBiggest
< Part_Sec
)
4317 partBiggest
= Part_Sec
;
4320 fmtPrefix
+= _T("02");
4324 str
+= wxString::Format(fmtPrefix
+ _T("ld"), n
);
4328 // normal character, just copy
4336 // ============================================================================
4337 // wxDateTimeHolidayAuthority and related classes
4338 // ============================================================================
4340 #include "wx/arrimpl.cpp"
4342 WX_DEFINE_OBJARRAY(wxDateTimeArray
)
4344 static int wxCMPFUNC_CONV
4345 wxDateTimeCompareFunc(wxDateTime
**first
, wxDateTime
**second
)
4347 wxDateTime dt1
= **first
,
4350 return dt1
== dt2
? 0 : dt1
< dt2
? -1 : +1;
4353 // ----------------------------------------------------------------------------
4354 // wxDateTimeHolidayAuthority
4355 // ----------------------------------------------------------------------------
4357 wxHolidayAuthoritiesArray
wxDateTimeHolidayAuthority::ms_authorities
;
4360 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime
& dt
)
4362 size_t count
= ms_authorities
.size();
4363 for ( size_t n
= 0; n
< count
; n
++ )
4365 if ( ms_authorities
[n
]->DoIsHoliday(dt
) )
4376 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime
& dtStart
,
4377 const wxDateTime
& dtEnd
,
4378 wxDateTimeArray
& holidays
)
4380 wxDateTimeArray hol
;
4384 const size_t countAuth
= ms_authorities
.size();
4385 for ( size_t nAuth
= 0; nAuth
< countAuth
; nAuth
++ )
4387 ms_authorities
[nAuth
]->DoGetHolidaysInRange(dtStart
, dtEnd
, hol
);
4389 WX_APPEND_ARRAY(holidays
, hol
);
4392 holidays
.Sort(wxDateTimeCompareFunc
);
4394 return holidays
.size();
4398 void wxDateTimeHolidayAuthority::ClearAllAuthorities()
4400 WX_CLEAR_ARRAY(ms_authorities
);
4404 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority
*auth
)
4406 ms_authorities
.push_back(auth
);
4409 wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
4411 // required here for Darwin
4414 // ----------------------------------------------------------------------------
4415 // wxDateTimeWorkDays
4416 // ----------------------------------------------------------------------------
4418 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime
& dt
) const
4420 wxDateTime::WeekDay wd
= dt
.GetWeekDay();
4422 return (wd
== wxDateTime::Sun
) || (wd
== wxDateTime::Sat
);
4425 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime
& dtStart
,
4426 const wxDateTime
& dtEnd
,
4427 wxDateTimeArray
& holidays
) const
4429 if ( dtStart
> dtEnd
)
4431 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
4438 // instead of checking all days, start with the first Sat after dtStart and
4439 // end with the last Sun before dtEnd
4440 wxDateTime dtSatFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sat
),
4441 dtSatLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sat
),
4442 dtSunFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sun
),
4443 dtSunLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sun
),
4446 for ( dt
= dtSatFirst
; dt
<= dtSatLast
; dt
+= wxDateSpan::Week() )
4451 for ( dt
= dtSunFirst
; dt
<= dtSunLast
; dt
+= wxDateSpan::Week() )
4456 return holidays
.GetCount();
4459 // ============================================================================
4460 // other helper functions
4461 // ============================================================================
4463 // ----------------------------------------------------------------------------
4464 // iteration helpers: can be used to write a for loop over enum variable like
4466 // for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
4467 // ----------------------------------------------------------------------------
4469 WXDLLIMPEXP_BASE
void wxNextMonth(wxDateTime::Month
& m
)
4471 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4473 // no wrapping or the for loop above would never end!
4474 m
= (wxDateTime::Month
)(m
+ 1);
4477 WXDLLIMPEXP_BASE
void wxPrevMonth(wxDateTime::Month
& m
)
4479 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4481 m
= m
== wxDateTime::Jan
? wxDateTime::Inv_Month
4482 : (wxDateTime::Month
)(m
- 1);
4485 WXDLLIMPEXP_BASE
void wxNextWDay(wxDateTime::WeekDay
& wd
)
4487 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4489 // no wrapping or the for loop above would never end!
4490 wd
= (wxDateTime::WeekDay
)(wd
+ 1);
4493 WXDLLIMPEXP_BASE
void wxPrevWDay(wxDateTime::WeekDay
& wd
)
4495 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4497 wd
= wd
== wxDateTime::Sun
? wxDateTime::Inv_WeekDay
4498 : (wxDateTime::WeekDay
)(wd
- 1);
4501 #endif // wxUSE_DATETIME