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 // set to true when the timezone is set
331 static bool s_timezoneSet
= false;
332 static long gmtoffset
= LONG_MAX
; // invalid timezone
334 // ensure that the timezone variable is set by calling wxLocaltime_r
335 if ( !s_timezoneSet
)
337 // just call wxLocaltime_r() instead of figuring out whether this
338 // system supports tzset(), _tzset() or something else
342 wxLocaltime_r(&t
, &tm
);
343 s_timezoneSet
= true;
345 #ifdef WX_GMTOFF_IN_TM
346 // note that GMT offset is the opposite of time zone and so to return
347 // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM
348 // cases we have to negate it
349 gmtoffset
= -tm
.tm_gmtoff
;
350 #else // !WX_GMTOFF_IN_TM
351 gmtoffset
= WX_TIMEZONE
;
352 #endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
355 return (int)gmtoffset
;
358 // return the integral part of the JDN for the midnight of the given date (to
359 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
360 // noon of the previous day)
361 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day
,
362 wxDateTime::Month mon
,
365 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
367 // check the date validity
369 (year
> JDN_0_YEAR
) ||
370 ((year
== JDN_0_YEAR
) && (mon
> JDN_0_MONTH
)) ||
371 ((year
== JDN_0_YEAR
) && (mon
== JDN_0_MONTH
) && (day
>= JDN_0_DAY
)),
372 _T("date out of range - can't convert to JDN")
375 // make the year positive to avoid problems with negative numbers division
378 // months are counted from March here
380 if ( mon
>= wxDateTime::Mar
)
390 // now we can simply add all the contributions together
391 return ((year
/ 100) * DAYS_PER_400_YEARS
) / 4
392 + ((year
% 100) * DAYS_PER_4_YEARS
) / 4
393 + (month
* DAYS_PER_5_MONTHS
+ 2) / 5
399 // this function is a wrapper around strftime(3) adding error checking
400 static wxString
CallStrftime(const wxChar
*format
, const tm
* tm
)
403 // Create temp wxString here to work around mingw/cygwin bug 1046059
404 // http://sourceforge.net/tracker/?func=detail&atid=102435&aid=1046059&group_id=2435
407 if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) )
409 // buffer is too small?
410 wxFAIL_MSG(_T("strftime() failed"));
420 #if wxUSE_UNIX && !defined(HAVE_STRPTIME_DECL)
421 // configure detected that we had strptime() but not its declaration,
422 // provide it ourselves
423 extern "C" char *strptime(const char *, const char *, struct tm
*);
426 // Unicode-friendly strptime() wrapper
427 static const wxChar
*
428 CallStrptime(const wxChar
*input
, const char *fmt
, tm
*tm
)
430 // the problem here is that strptime() returns pointer into the string we
431 // passed to it while we're really interested in the pointer into the
432 // original, Unicode, string so we try to transform the pointer back
434 wxCharBuffer
inputMB(wxConvertWX2MB(input
));
436 const char * const inputMB
= input
;
437 #endif // Unicode/Ascii
439 const char *result
= strptime(inputMB
, fmt
, tm
);
444 // FIXME: this is wrong in presence of surrogates &c
445 return input
+ (result
- inputMB
.data());
448 #endif // Unicode/Ascii
451 #endif // HAVE_STRPTIME
453 // if year and/or month have invalid values, replace them with the current ones
454 static void ReplaceDefaultYearMonthWithCurrent(int *year
,
455 wxDateTime::Month
*month
)
457 struct tm
*tmNow
= NULL
;
460 if ( *year
== wxDateTime::Inv_Year
)
462 tmNow
= wxDateTime::GetTmNow(&tmstruct
);
464 *year
= 1900 + tmNow
->tm_year
;
467 if ( *month
== wxDateTime::Inv_Month
)
470 tmNow
= wxDateTime::GetTmNow(&tmstruct
);
472 *month
= (wxDateTime::Month
)tmNow
->tm_mon
;
476 // fll the struct tm with default values
477 static void InitTm(struct tm
& tm
)
479 // struct tm may have etxra fields (undocumented and with unportable
480 // names) which, nevertheless, must be set to 0
481 memset(&tm
, 0, sizeof(struct tm
));
483 tm
.tm_mday
= 1; // mday 0 is invalid
484 tm
.tm_year
= 76; // any valid year
485 tm
.tm_isdst
= -1; // auto determine
491 // return the month if the string is a month name or Inv_Month otherwise
492 static wxDateTime::Month
GetMonthFromName(const wxString
& name
, int flags
)
494 wxDateTime::Month mon
;
495 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
497 // case-insensitive comparison either one of or with both abbreviated
499 if ( flags
& wxDateTime::Name_Full
)
501 if ( name
.CmpNoCase(wxDateTime::
502 GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 )
508 if ( flags
& wxDateTime::Name_Abbr
)
510 if ( name
.CmpNoCase(wxDateTime::
511 GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 )
521 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
522 static wxDateTime::WeekDay
GetWeekDayFromName(const wxString
& name
, int flags
)
524 wxDateTime::WeekDay wd
;
525 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
527 // case-insensitive comparison either one of or with both abbreviated
529 if ( flags
& wxDateTime::Name_Full
)
531 if ( name
.CmpNoCase(wxDateTime::
532 GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 )
538 if ( flags
& wxDateTime::Name_Abbr
)
540 if ( name
.CmpNoCase(wxDateTime::
541 GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 )
552 struct tm
*wxDateTime::GetTmNow(struct tm
*tmstruct
)
554 time_t t
= GetTimeNow();
555 return wxLocaltime_r(&t
, tmstruct
);
558 // scans all digits (but no more than len) and returns the resulting number
559 static bool GetNumericToken(size_t len
, const wxChar
*& p
, unsigned long *number
)
563 while ( wxIsdigit(*p
) )
567 if ( len
&& ++n
> len
)
571 return !s
.empty() && s
.ToULong(number
);
574 // scans all alphabetic characters and returns the resulting string
575 static wxString
GetAlphaToken(const wxChar
*& p
)
578 while ( wxIsalpha(*p
) )
586 // ============================================================================
587 // implementation of wxDateTime
588 // ============================================================================
590 // ----------------------------------------------------------------------------
592 // ----------------------------------------------------------------------------
596 year
= (wxDateTime_t
)wxDateTime::Inv_Year
;
597 mon
= wxDateTime::Inv_Month
;
599 hour
= min
= sec
= msec
= 0;
600 wday
= wxDateTime::Inv_WeekDay
;
603 wxDateTime::Tm::Tm(const struct tm
& tm
, const TimeZone
& tz
)
607 sec
= (wxDateTime::wxDateTime_t
)tm
.tm_sec
;
608 min
= (wxDateTime::wxDateTime_t
)tm
.tm_min
;
609 hour
= (wxDateTime::wxDateTime_t
)tm
.tm_hour
;
610 mday
= (wxDateTime::wxDateTime_t
)tm
.tm_mday
;
611 mon
= (wxDateTime::Month
)tm
.tm_mon
;
612 year
= 1900 + tm
.tm_year
;
613 wday
= (wxDateTime::wxDateTime_t
)tm
.tm_wday
;
614 yday
= (wxDateTime::wxDateTime_t
)tm
.tm_yday
;
617 bool wxDateTime::Tm::IsValid() const
619 // we allow for the leap seconds, although we don't use them (yet)
620 return (year
!= wxDateTime::Inv_Year
) && (mon
!= wxDateTime::Inv_Month
) &&
621 (mday
<= GetNumOfDaysInMonth(year
, mon
)) &&
622 (hour
< 24) && (min
< 60) && (sec
< 62) && (msec
< 1000);
625 void wxDateTime::Tm::ComputeWeekDay()
627 // compute the week day from day/month/year: we use the dumbest algorithm
628 // possible: just compute our JDN and then use the (simple to derive)
629 // formula: weekday = (JDN + 1.5) % 7
630 wday
= (wxDateTime::wxDateTime_t
)((wxDateTime::WeekDay
)(GetTruncatedJDN(mday
, mon
, year
) + 2) % 7);
633 void wxDateTime::Tm::AddMonths(int monDiff
)
635 // normalize the months field
636 while ( monDiff
< -mon
)
640 monDiff
+= MONTHS_IN_YEAR
;
643 while ( monDiff
+ mon
>= MONTHS_IN_YEAR
)
647 monDiff
-= MONTHS_IN_YEAR
;
650 mon
= (wxDateTime::Month
)(mon
+ monDiff
);
652 wxASSERT_MSG( mon
>= 0 && mon
< MONTHS_IN_YEAR
, _T("logic error") );
654 // NB: we don't check here that the resulting date is valid, this function
655 // is private and the caller must check it if needed
658 void wxDateTime::Tm::AddDays(int dayDiff
)
660 // normalize the days field
661 while ( dayDiff
+ mday
< 1 )
665 dayDiff
+= GetNumOfDaysInMonth(year
, mon
);
668 mday
= (wxDateTime::wxDateTime_t
)( mday
+ dayDiff
);
669 while ( mday
> GetNumOfDaysInMonth(year
, mon
) )
671 mday
-= GetNumOfDaysInMonth(year
, mon
);
676 wxASSERT_MSG( mday
> 0 && mday
<= GetNumOfDaysInMonth(year
, mon
),
680 // ----------------------------------------------------------------------------
682 // ----------------------------------------------------------------------------
684 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz
)
688 case wxDateTime::Local
:
689 // get the offset from C RTL: it returns the difference GMT-local
690 // while we want to have the offset _from_ GMT, hence the '-'
691 m_offset
= -GetTimeZone();
694 case wxDateTime::GMT_12
:
695 case wxDateTime::GMT_11
:
696 case wxDateTime::GMT_10
:
697 case wxDateTime::GMT_9
:
698 case wxDateTime::GMT_8
:
699 case wxDateTime::GMT_7
:
700 case wxDateTime::GMT_6
:
701 case wxDateTime::GMT_5
:
702 case wxDateTime::GMT_4
:
703 case wxDateTime::GMT_3
:
704 case wxDateTime::GMT_2
:
705 case wxDateTime::GMT_1
:
706 m_offset
= -3600*(wxDateTime::GMT0
- tz
);
709 case wxDateTime::GMT0
:
710 case wxDateTime::GMT1
:
711 case wxDateTime::GMT2
:
712 case wxDateTime::GMT3
:
713 case wxDateTime::GMT4
:
714 case wxDateTime::GMT5
:
715 case wxDateTime::GMT6
:
716 case wxDateTime::GMT7
:
717 case wxDateTime::GMT8
:
718 case wxDateTime::GMT9
:
719 case wxDateTime::GMT10
:
720 case wxDateTime::GMT11
:
721 case wxDateTime::GMT12
:
722 m_offset
= 3600*(tz
- wxDateTime::GMT0
);
725 case wxDateTime::A_CST
:
726 // Central Standard Time in use in Australia = UTC + 9.5
727 m_offset
= 60l*(9*MIN_PER_HOUR
+ MIN_PER_HOUR
/2);
731 wxFAIL_MSG( _T("unknown time zone") );
735 // ----------------------------------------------------------------------------
737 // ----------------------------------------------------------------------------
740 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
)
742 if ( year
== Inv_Year
)
743 year
= GetCurrentYear();
745 if ( cal
== Gregorian
)
747 // in Gregorian calendar leap years are those divisible by 4 except
748 // those divisible by 100 unless they're also divisible by 400
749 // (in some countries, like Russia and Greece, additional corrections
750 // exist, but they won't manifest themselves until 2700)
751 return (year
% 4 == 0) && ((year
% 100 != 0) || (year
% 400 == 0));
753 else if ( cal
== Julian
)
755 // in Julian calendar the rule is simpler
756 return year
% 4 == 0;
760 wxFAIL_MSG(_T("unknown calendar"));
767 int wxDateTime::GetCentury(int year
)
769 return year
> 0 ? year
/ 100 : year
/ 100 - 1;
773 int wxDateTime::ConvertYearToBC(int year
)
776 return year
> 0 ? year
: year
- 1;
780 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
)
785 return Now().GetYear();
788 wxFAIL_MSG(_T("TODO"));
792 wxFAIL_MSG(_T("unsupported calendar"));
800 wxDateTime::Month
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
)
805 return Now().GetMonth();
808 wxFAIL_MSG(_T("TODO"));
812 wxFAIL_MSG(_T("unsupported calendar"));
820 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(int year
, Calendar cal
)
822 if ( year
== Inv_Year
)
824 // take the current year if none given
825 year
= GetCurrentYear();
832 return IsLeapYear(year
) ? 366 : 365;
835 wxFAIL_MSG(_T("unsupported calendar"));
843 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(wxDateTime::Month month
,
845 wxDateTime::Calendar cal
)
847 wxCHECK_MSG( month
< MONTHS_IN_YEAR
, 0, _T("invalid month") );
849 if ( cal
== Gregorian
|| cal
== Julian
)
851 if ( year
== Inv_Year
)
853 // take the current year if none given
854 year
= GetCurrentYear();
857 return GetNumOfDaysInMonth(year
, month
);
861 wxFAIL_MSG(_T("unsupported calendar"));
868 wxString
wxDateTime::GetMonthName(wxDateTime::Month month
,
869 wxDateTime::NameFlags flags
)
871 wxCHECK_MSG( month
!= Inv_Month
, wxEmptyString
, _T("invalid month") );
873 // notice that we must set all the fields to avoid confusing libc (GNU one
874 // gets confused to a crash if we don't do this)
879 return CallStrftime(flags
== Name_Abbr
? _T("%b") : _T("%B"), &tm
);
885 ret
= (flags
== Name_Abbr
? wxT("Jan"): wxT("January"));
888 ret
= (flags
== Name_Abbr
? wxT("Feb"): wxT("Febuary"));
891 ret
= (flags
== Name_Abbr
? wxT("Mar"): wxT("March"));
894 ret
= (flags
== Name_Abbr
? wxT("Apr"): wxT("April"));
897 ret
= (flags
== Name_Abbr
? wxT("May"): wxT("May"));
900 ret
= (flags
== Name_Abbr
? wxT("Jun"): wxT("June"));
903 ret
= (flags
== Name_Abbr
? wxT("Jul"): wxT("July"));
906 ret
= (flags
== Name_Abbr
? wxT("Aug"): wxT("August"));
909 ret
= (flags
== Name_Abbr
? wxT("Sep"): wxT("September"));
912 ret
= (flags
== Name_Abbr
? wxT("Oct"): wxT("October"));
915 ret
= (flags
== Name_Abbr
? wxT("Nov"): wxT("November"));
918 ret
= (flags
== Name_Abbr
? wxT("Dec"): wxT("December"));
926 wxString
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
,
927 wxDateTime::NameFlags flags
)
929 wxCHECK_MSG( wday
!= Inv_WeekDay
, wxEmptyString
, _T("invalid weekday") );
931 // take some arbitrary Sunday (but notice that the day should be such that
932 // after adding wday to it below we still have a valid date, e.g. don't
940 // and offset it by the number of days needed to get the correct wday
943 // call mktime() to normalize it...
946 // ... and call strftime()
947 return CallStrftime(flags
== Name_Abbr
? _T("%a") : _T("%A"), &tm
);
953 ret
= (flags
== Name_Abbr
? wxT("Sun") : wxT("Sunday"));
956 ret
= (flags
== Name_Abbr
? wxT("Mon") : wxT("Monday"));
959 ret
= (flags
== Name_Abbr
? wxT("Tue") : wxT("Tuesday"));
962 ret
= (flags
== Name_Abbr
? wxT("Wed") : wxT("Wednesday"));
965 ret
= (flags
== Name_Abbr
? wxT("Thu") : wxT("Thursday"));
968 ret
= (flags
== Name_Abbr
? wxT("Fri") : wxT("Friday"));
971 ret
= (flags
== Name_Abbr
? wxT("Sat") : wxT("Saturday"));
980 void wxDateTime::GetAmPmStrings(wxString
*am
, wxString
*pm
)
985 // @Note: Do not call 'CallStrftime' here! CallStrftime checks the return code
986 // and causes an assertion failed if the buffer is to small (which is good) - OR -
987 // if strftime does not return anything because the format string is invalid - OR -
988 // if there are no 'am' / 'pm' tokens defined for the current locale (which is not good).
989 // wxDateTime::ParseTime will try several different formats to parse the time.
990 // As a result, GetAmPmStrings might get called, even if the current locale
991 // does not define any 'am' / 'pm' tokens. In this case, wxStrftime would
992 // assert, even though it is a perfectly legal use.
995 if (wxStrftime(buffer
, sizeof(buffer
)/sizeof(wxChar
), _T("%p"), &tm
) > 0)
996 *am
= wxString(buffer
);
1003 if (wxStrftime(buffer
, sizeof(buffer
)/sizeof(wxChar
), _T("%p"), &tm
) > 0)
1004 *pm
= wxString(buffer
);
1010 // ----------------------------------------------------------------------------
1011 // Country stuff: date calculations depend on the country (DST, work days,
1012 // ...), so we need to know which rules to follow.
1013 // ----------------------------------------------------------------------------
1016 wxDateTime::Country
wxDateTime::GetCountry()
1018 // TODO use LOCALE_ICOUNTRY setting under Win32
1020 if ( ms_country
== Country_Unknown
)
1022 // try to guess from the time zone name
1023 time_t t
= time(NULL
);
1025 struct tm
*tm
= wxLocaltime_r(&t
, &tmstruct
);
1027 wxString tz
= CallStrftime(_T("%Z"), tm
);
1028 if ( tz
== _T("WET") || tz
== _T("WEST") )
1032 else if ( tz
== _T("CET") || tz
== _T("CEST") )
1034 ms_country
= Country_EEC
;
1036 else if ( tz
== _T("MSK") || tz
== _T("MSD") )
1038 ms_country
= Russia
;
1040 else if ( tz
== _T("AST") || tz
== _T("ADT") ||
1041 tz
== _T("EST") || tz
== _T("EDT") ||
1042 tz
== _T("CST") || tz
== _T("CDT") ||
1043 tz
== _T("MST") || tz
== _T("MDT") ||
1044 tz
== _T("PST") || tz
== _T("PDT") )
1050 // well, choose a default one
1062 void wxDateTime::SetCountry(wxDateTime::Country country
)
1064 ms_country
= country
;
1068 bool wxDateTime::IsWestEuropeanCountry(Country country
)
1070 if ( country
== Country_Default
)
1072 country
= GetCountry();
1075 return (Country_WesternEurope_Start
<= country
) &&
1076 (country
<= Country_WesternEurope_End
);
1079 // ----------------------------------------------------------------------------
1080 // DST calculations: we use 3 different rules for the West European countries,
1081 // USA and for the rest of the world. This is undoubtedly false for many
1082 // countries, but I lack the necessary info (and the time to gather it),
1083 // please add the other rules here!
1084 // ----------------------------------------------------------------------------
1087 bool wxDateTime::IsDSTApplicable(int year
, Country country
)
1089 if ( year
== Inv_Year
)
1091 // take the current year if none given
1092 year
= GetCurrentYear();
1095 if ( country
== Country_Default
)
1097 country
= GetCountry();
1104 // DST was first observed in the US and UK during WWI, reused
1105 // during WWII and used again since 1966
1106 return year
>= 1966 ||
1107 (year
>= 1942 && year
<= 1945) ||
1108 (year
== 1918 || year
== 1919);
1111 // assume that it started after WWII
1117 wxDateTime
wxDateTime::GetBeginDST(int year
, Country country
)
1119 if ( year
== Inv_Year
)
1121 // take the current year if none given
1122 year
= GetCurrentYear();
1125 if ( country
== Country_Default
)
1127 country
= GetCountry();
1130 if ( !IsDSTApplicable(year
, country
) )
1132 return wxInvalidDateTime
;
1137 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1139 // DST begins at 1 a.m. GMT on the last Sunday of March
1140 if ( !dt
.SetToLastWeekDay(Sun
, Mar
, year
) )
1143 wxFAIL_MSG( _T("no last Sunday in March?") );
1146 dt
+= wxTimeSpan::Hours(1);
1148 // disable DST tests because it could result in an infinite recursion!
1151 else switch ( country
)
1158 // don't know for sure - assume it was in effect all year
1163 dt
.Set(1, Jan
, year
);
1167 // DST was installed Feb 2, 1942 by the Congress
1168 dt
.Set(2, Feb
, year
);
1171 // Oil embargo changed the DST period in the US
1173 dt
.Set(6, Jan
, 1974);
1177 dt
.Set(23, Feb
, 1975);
1181 // before 1986, DST begun on the last Sunday of April, but
1182 // in 1986 Reagan changed it to begin at 2 a.m. of the
1183 // first Sunday in April
1186 if ( !dt
.SetToLastWeekDay(Sun
, Apr
, year
) )
1189 wxFAIL_MSG( _T("no first Sunday in April?") );
1194 if ( !dt
.SetToWeekDay(Sun
, 1, Apr
, year
) )
1197 wxFAIL_MSG( _T("no first Sunday in April?") );
1201 dt
+= wxTimeSpan::Hours(2);
1203 // TODO what about timezone??
1209 // assume Mar 30 as the start of the DST for the rest of the world
1210 // - totally bogus, of course
1211 dt
.Set(30, Mar
, year
);
1218 wxDateTime
wxDateTime::GetEndDST(int year
, Country country
)
1220 if ( year
== Inv_Year
)
1222 // take the current year if none given
1223 year
= GetCurrentYear();
1226 if ( country
== Country_Default
)
1228 country
= GetCountry();
1231 if ( !IsDSTApplicable(year
, country
) )
1233 return wxInvalidDateTime
;
1238 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1240 // DST ends at 1 a.m. GMT on the last Sunday of October
1241 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1243 // weirder and weirder...
1244 wxFAIL_MSG( _T("no last Sunday in October?") );
1247 dt
+= wxTimeSpan::Hours(1);
1249 // disable DST tests because it could result in an infinite recursion!
1252 else switch ( country
)
1259 // don't know for sure - assume it was in effect all year
1263 dt
.Set(31, Dec
, year
);
1267 // the time was reset after the end of the WWII
1268 dt
.Set(30, Sep
, year
);
1272 // DST ends at 2 a.m. on the last Sunday of October
1273 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1275 // weirder and weirder...
1276 wxFAIL_MSG( _T("no last Sunday in October?") );
1279 dt
+= wxTimeSpan::Hours(2);
1281 // TODO what about timezone??
1286 // assume October 26th as the end of the DST - totally bogus too
1287 dt
.Set(26, Oct
, year
);
1293 // ----------------------------------------------------------------------------
1294 // constructors and assignment operators
1295 // ----------------------------------------------------------------------------
1297 // return the current time with ms precision
1298 /* static */ wxDateTime
wxDateTime::UNow()
1300 return wxDateTime(wxGetLocalTimeMillis());
1303 // the values in the tm structure contain the local time
1304 wxDateTime
& wxDateTime::Set(const struct tm
& tm
)
1307 time_t timet
= mktime(&tm2
);
1309 if ( timet
== (time_t)-1 )
1311 // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
1312 // less than timezone - try to make it work for this case
1313 if ( tm2
.tm_year
== 70 && tm2
.tm_mon
== 0 && tm2
.tm_mday
== 1 )
1315 return Set((time_t)(
1317 tm2
.tm_hour
* MIN_PER_HOUR
* SEC_PER_MIN
+
1318 tm2
.tm_min
* SEC_PER_MIN
+
1322 wxFAIL_MSG( _T("mktime() failed") );
1324 *this = wxInvalidDateTime
;
1334 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
,
1335 wxDateTime_t minute
,
1336 wxDateTime_t second
,
1337 wxDateTime_t millisec
)
1339 // we allow seconds to be 61 to account for the leap seconds, even if we
1340 // don't use them really
1341 wxDATETIME_CHECK( hour
< 24 &&
1345 _T("Invalid time in wxDateTime::Set()") );
1347 // get the current date from system
1349 struct tm
*tm
= GetTmNow(&tmstruct
);
1351 wxDATETIME_CHECK( tm
, _T("wxLocaltime_r() failed") );
1353 // make a copy so it isn't clobbered by the call to mktime() below
1358 tm1
.tm_min
= minute
;
1359 tm1
.tm_sec
= second
;
1361 // and the DST in case it changes on this date
1364 if ( tm2
.tm_isdst
!= tm1
.tm_isdst
)
1365 tm1
.tm_isdst
= tm2
.tm_isdst
;
1369 // and finally adjust milliseconds
1370 return SetMillisecond(millisec
);
1373 wxDateTime
& wxDateTime::Set(wxDateTime_t day
,
1377 wxDateTime_t minute
,
1378 wxDateTime_t second
,
1379 wxDateTime_t millisec
)
1381 wxDATETIME_CHECK( hour
< 24 &&
1385 _T("Invalid time in wxDateTime::Set()") );
1387 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1389 wxDATETIME_CHECK( (0 < day
) && (day
<= GetNumberOfDays(month
, year
)),
1390 _T("Invalid date in wxDateTime::Set()") );
1392 // the range of time_t type (inclusive)
1393 static const int yearMinInRange
= 1970;
1394 static const int yearMaxInRange
= 2037;
1396 // test only the year instead of testing for the exact end of the Unix
1397 // time_t range - it doesn't bring anything to do more precise checks
1398 if ( year
>= yearMinInRange
&& year
<= yearMaxInRange
)
1400 // use the standard library version if the date is in range - this is
1401 // probably more efficient than our code
1403 tm
.tm_year
= year
- 1900;
1409 tm
.tm_isdst
= -1; // mktime() will guess it
1413 // and finally adjust milliseconds
1415 SetMillisecond(millisec
);
1421 // do time calculations ourselves: we want to calculate the number of
1422 // milliseconds between the given date and the epoch
1424 // get the JDN for the midnight of this day
1425 m_time
= GetTruncatedJDN(day
, month
, year
);
1426 m_time
-= EPOCH_JDN
;
1427 m_time
*= SECONDS_PER_DAY
* TIME_T_FACTOR
;
1429 // JDN corresponds to GMT, we take localtime
1430 Add(wxTimeSpan(hour
, minute
, second
+ GetTimeZone(), millisec
));
1436 wxDateTime
& wxDateTime::Set(double jdn
)
1438 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1440 jdn
-= EPOCH_JDN
+ 0.5;
1442 m_time
.Assign(jdn
*MILLISECONDS_PER_DAY
);
1444 // JDNs always are in UTC, so we don't need any adjustments for time zone
1449 wxDateTime
& wxDateTime::ResetTime()
1453 if ( tm
.hour
|| tm
.min
|| tm
.sec
|| tm
.msec
)
1466 // ----------------------------------------------------------------------------
1467 // DOS Date and Time Format functions
1468 // ----------------------------------------------------------------------------
1469 // the dos date and time value is an unsigned 32 bit value in the format:
1470 // YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
1472 // Y = year offset from 1980 (0-127)
1474 // D = day of month (1-31)
1476 // m = minute (0-59)
1477 // s = bisecond (0-29) each bisecond indicates two seconds
1478 // ----------------------------------------------------------------------------
1480 wxDateTime
& wxDateTime::SetFromDOS(unsigned long ddt
)
1485 long year
= ddt
& 0xFE000000;
1490 long month
= ddt
& 0x1E00000;
1495 long day
= ddt
& 0x1F0000;
1499 long hour
= ddt
& 0xF800;
1503 long minute
= ddt
& 0x7E0;
1507 long second
= ddt
& 0x1F;
1508 tm
.tm_sec
= second
* 2;
1510 return Set(mktime(&tm
));
1513 unsigned long wxDateTime::GetAsDOS() const
1516 time_t ticks
= GetTicks();
1518 struct tm
*tm
= wxLocaltime_r(&ticks
, &tmstruct
);
1520 long year
= tm
->tm_year
;
1524 long month
= tm
->tm_mon
;
1528 long day
= tm
->tm_mday
;
1531 long hour
= tm
->tm_hour
;
1534 long minute
= tm
->tm_min
;
1537 long second
= tm
->tm_sec
;
1540 ddt
= year
| month
| day
| hour
| minute
| second
;
1544 // ----------------------------------------------------------------------------
1545 // time_t <-> broken down time conversions
1546 // ----------------------------------------------------------------------------
1548 wxDateTime::Tm
wxDateTime::GetTm(const TimeZone
& tz
) const
1550 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1552 time_t time
= GetTicks();
1553 if ( time
!= (time_t)-1 )
1555 // use C RTL functions
1558 if ( tz
.GetOffset() == -GetTimeZone() )
1560 // we are working with local time
1561 tm
= wxLocaltime_r(&time
, &tmstruct
);
1563 // should never happen
1564 wxCHECK_MSG( tm
, Tm(), _T("wxLocaltime_r() failed") );
1568 time
+= (time_t)tz
.GetOffset();
1569 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
1570 int time2
= (int) time
;
1576 tm
= wxGmtime_r(&time
, &tmstruct
);
1578 // should never happen
1579 wxCHECK_MSG( tm
, Tm(), _T("wxGmtime_r() failed") );
1583 tm
= (struct tm
*)NULL
;
1589 // adjust the milliseconds
1591 long timeOnly
= (m_time
% MILLISECONDS_PER_DAY
).ToLong();
1592 tm2
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1595 //else: use generic code below
1598 // remember the time and do the calculations with the date only - this
1599 // eliminates rounding errors of the floating point arithmetics
1601 wxLongLong timeMidnight
= m_time
+ tz
.GetOffset() * 1000;
1603 long timeOnly
= (timeMidnight
% MILLISECONDS_PER_DAY
).ToLong();
1605 // we want to always have positive time and timeMidnight to be really
1606 // the midnight before it
1609 timeOnly
= MILLISECONDS_PER_DAY
+ timeOnly
;
1612 timeMidnight
-= timeOnly
;
1614 // calculate the Gregorian date from JDN for the midnight of our date:
1615 // this will yield day, month (in 1..12 range) and year
1617 // actually, this is the JDN for the noon of the previous day
1618 long jdn
= (timeMidnight
/ MILLISECONDS_PER_DAY
).ToLong() + EPOCH_JDN
;
1620 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1622 wxASSERT_MSG( jdn
> -2, _T("JDN out of range") );
1624 // calculate the century
1625 long temp
= (jdn
+ JDN_OFFSET
) * 4 - 1;
1626 long century
= temp
/ DAYS_PER_400_YEARS
;
1628 // then the year and day of year (1 <= dayOfYear <= 366)
1629 temp
= ((temp
% DAYS_PER_400_YEARS
) / 4) * 4 + 3;
1630 long year
= (century
* 100) + (temp
/ DAYS_PER_4_YEARS
);
1631 long dayOfYear
= (temp
% DAYS_PER_4_YEARS
) / 4 + 1;
1633 // and finally the month and day of the month
1634 temp
= dayOfYear
* 5 - 3;
1635 long month
= temp
/ DAYS_PER_5_MONTHS
;
1636 long day
= (temp
% DAYS_PER_5_MONTHS
) / 5 + 1;
1638 // month is counted from March - convert to normal
1649 // year is offset by 4800
1652 // check that the algorithm gave us something reasonable
1653 wxASSERT_MSG( (0 < month
) && (month
<= 12), _T("invalid month") );
1654 wxASSERT_MSG( (1 <= day
) && (day
< 32), _T("invalid day") );
1656 // construct Tm from these values
1658 tm
.year
= (int)year
;
1659 tm
.mon
= (Month
)(month
- 1); // algorithm yields 1 for January, not 0
1660 tm
.mday
= (wxDateTime_t
)day
;
1661 tm
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1662 timeOnly
-= tm
.msec
;
1663 timeOnly
/= 1000; // now we have time in seconds
1665 tm
.sec
= (wxDateTime_t
)(timeOnly
% SEC_PER_MIN
);
1667 timeOnly
/= SEC_PER_MIN
; // now we have time in minutes
1669 tm
.min
= (wxDateTime_t
)(timeOnly
% MIN_PER_HOUR
);
1672 tm
.hour
= (wxDateTime_t
)(timeOnly
/ MIN_PER_HOUR
);
1677 wxDateTime
& wxDateTime::SetYear(int year
)
1679 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1688 wxDateTime
& wxDateTime::SetMonth(Month month
)
1690 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1699 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
)
1701 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1710 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
)
1712 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1721 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
)
1723 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1732 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
)
1734 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1743 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
)
1745 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1747 // we don't need to use GetTm() for this one
1748 m_time
-= m_time
% 1000l;
1749 m_time
+= millisecond
;
1754 // ----------------------------------------------------------------------------
1755 // wxDateTime arithmetics
1756 // ----------------------------------------------------------------------------
1758 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
)
1762 tm
.year
+= diff
.GetYears();
1763 tm
.AddMonths(diff
.GetMonths());
1765 // check that the resulting date is valid
1766 if ( tm
.mday
> GetNumOfDaysInMonth(tm
.year
, tm
.mon
) )
1768 // We suppose that when adding one month to Jan 31 we want to get Feb
1769 // 28 (or 29), i.e. adding a month to the last day of the month should
1770 // give the last day of the next month which is quite logical.
1772 // Unfortunately, there is no logic way to understand what should
1773 // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1774 // We make it Feb 28 (last day too), but it is highly questionable.
1775 tm
.mday
= GetNumOfDaysInMonth(tm
.year
, tm
.mon
);
1778 tm
.AddDays(diff
.GetTotalDays());
1782 wxASSERT_MSG( IsSameTime(tm
),
1783 _T("Add(wxDateSpan) shouldn't modify time") );
1788 // ----------------------------------------------------------------------------
1789 // Weekday and monthday stuff
1790 // ----------------------------------------------------------------------------
1792 // convert Sun, Mon, ..., Sat into 6, 0, ..., 5
1793 static inline int ConvertWeekDayToMondayBase(int wd
)
1795 return wd
== wxDateTime::Sun
? 6 : wd
- 1;
1800 wxDateTime::SetToWeekOfYear(int year
, wxDateTime_t numWeek
, WeekDay wd
)
1802 wxASSERT_MSG( numWeek
> 0,
1803 _T("invalid week number: weeks are counted from 1") );
1805 // Jan 4 always lies in the 1st week of the year
1806 wxDateTime
dt(4, Jan
, year
);
1807 dt
.SetToWeekDayInSameWeek(wd
);
1808 dt
+= wxDateSpan::Weeks(numWeek
- 1);
1813 #if WXWIN_COMPATIBILITY_2_6
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
);
1851 #endif // WXWIN_COMPATIBILITY_2_6
1853 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
,
1856 // take the current month/year if none specified
1857 if ( year
== Inv_Year
)
1859 if ( month
== Inv_Month
)
1862 return Set(GetNumOfDaysInMonth(year
, month
), month
, year
);
1865 wxDateTime
& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday
, WeekFlags flags
)
1867 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1869 int wdayDst
= weekday
,
1870 wdayThis
= GetWeekDay();
1871 if ( wdayDst
== wdayThis
)
1877 if ( flags
== Default_First
)
1879 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
1882 // the logic below based on comparing weekday and wdayThis works if Sun (0)
1883 // is the first day in the week, but breaks down for Monday_First case so
1884 // we adjust the week days in this case
1885 if ( flags
== Monday_First
)
1887 if ( wdayThis
== Sun
)
1889 if ( wdayDst
== Sun
)
1892 //else: Sunday_First, nothing to do
1894 // go forward or back in time to the day we want
1895 if ( wdayDst
< wdayThis
)
1897 return Subtract(wxDateSpan::Days(wdayThis
- wdayDst
));
1899 else // weekday > wdayThis
1901 return Add(wxDateSpan::Days(wdayDst
- wdayThis
));
1905 wxDateTime
& wxDateTime::SetToNextWeekDay(WeekDay weekday
)
1907 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1910 WeekDay wdayThis
= GetWeekDay();
1911 if ( weekday
== wdayThis
)
1916 else if ( weekday
< wdayThis
)
1918 // need to advance a week
1919 diff
= 7 - (wdayThis
- weekday
);
1921 else // weekday > wdayThis
1923 diff
= weekday
- wdayThis
;
1926 return Add(wxDateSpan::Days(diff
));
1929 wxDateTime
& wxDateTime::SetToPrevWeekDay(WeekDay weekday
)
1931 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1934 WeekDay wdayThis
= GetWeekDay();
1935 if ( weekday
== wdayThis
)
1940 else if ( weekday
> wdayThis
)
1942 // need to go to previous week
1943 diff
= 7 - (weekday
- wdayThis
);
1945 else // weekday < wdayThis
1947 diff
= wdayThis
- weekday
;
1950 return Subtract(wxDateSpan::Days(diff
));
1953 bool wxDateTime::SetToWeekDay(WeekDay weekday
,
1958 wxCHECK_MSG( weekday
!= Inv_WeekDay
, false, _T("invalid weekday") );
1960 // we don't check explicitly that -5 <= n <= 5 because we will return false
1961 // anyhow in such case - but may be should still give an assert for it?
1963 // take the current month/year if none specified
1964 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1968 // TODO this probably could be optimised somehow...
1972 // get the first day of the month
1973 dt
.Set(1, month
, year
);
1976 WeekDay wdayFirst
= dt
.GetWeekDay();
1978 // go to the first weekday of the month
1979 int diff
= weekday
- wdayFirst
;
1983 // add advance n-1 weeks more
1986 dt
+= wxDateSpan::Days(diff
);
1988 else // count from the end of the month
1990 // get the last day of the month
1991 dt
.SetToLastMonthDay(month
, year
);
1994 WeekDay wdayLast
= dt
.GetWeekDay();
1996 // go to the last weekday of the month
1997 int diff
= wdayLast
- weekday
;
2001 // and rewind n-1 weeks from there
2004 dt
-= wxDateSpan::Days(diff
);
2007 // check that it is still in the same month
2008 if ( dt
.GetMonth() == month
)
2016 // no such day in this month
2022 wxDateTime::wxDateTime_t
GetDayOfYearFromTm(const wxDateTime::Tm
& tm
)
2024 return (wxDateTime::wxDateTime_t
)(gs_cumulatedDays
[wxDateTime::IsLeapYear(tm
.year
)][tm
.mon
] + tm
.mday
);
2027 wxDateTime::wxDateTime_t
wxDateTime::GetDayOfYear(const TimeZone
& tz
) const
2029 return GetDayOfYearFromTm(GetTm(tz
));
2032 wxDateTime::wxDateTime_t
2033 wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags
, const TimeZone
& tz
) const
2035 if ( flags
== Default_First
)
2037 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
2041 wxDateTime_t nDayInYear
= GetDayOfYearFromTm(tm
);
2043 int wdTarget
= GetWeekDay(tz
);
2044 int wdYearStart
= wxDateTime(1, Jan
, GetYear()).GetWeekDay();
2046 if ( flags
== Sunday_First
)
2048 // FIXME: First week is not calculated correctly.
2049 week
= (nDayInYear
- wdTarget
+ 7) / 7;
2050 if ( wdYearStart
== Wed
|| wdYearStart
== Thu
)
2053 else // week starts with monday
2055 // adjust the weekdays to non-US style.
2056 wdYearStart
= ConvertWeekDayToMondayBase(wdYearStart
);
2057 wdTarget
= ConvertWeekDayToMondayBase(wdTarget
);
2059 // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html:
2061 // Week 01 of a year is per definition the first week that has the
2062 // Thursday in this year, which is equivalent to the week that
2063 // contains the fourth day of January. In other words, the first
2064 // week of a new year is the week that has the majority of its
2065 // days in the new year. Week 01 might also contain days from the
2066 // previous year and the week before week 01 of a year is the last
2067 // week (52 or 53) of the previous year even if it contains days
2068 // from the new year. A week starts with Monday (day 1) and ends
2069 // with Sunday (day 7).
2072 // if Jan 1 is Thursday or less, it is in the first week of this year
2073 if ( wdYearStart
< 4 )
2075 // count the number of entire weeks between Jan 1 and this date
2076 week
= (nDayInYear
+ wdYearStart
+ 6 - wdTarget
)/7;
2078 // be careful to check for overflow in the next year
2079 if ( week
== 53 && tm
.mday
- wdTarget
> 28 )
2082 else // Jan 1 is in the last week of the previous year
2084 // check if we happen to be at the last week of previous year:
2085 if ( tm
.mon
== Jan
&& tm
.mday
< 8 - wdYearStart
)
2086 week
= wxDateTime(31, Dec
, GetYear()-1).GetWeekOfYear();
2088 week
= (nDayInYear
+ wdYearStart
- 1 - wdTarget
)/7;
2092 return (wxDateTime::wxDateTime_t
)week
;
2095 wxDateTime::wxDateTime_t
wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags
,
2096 const TimeZone
& tz
) const
2099 wxDateTime dtMonthStart
= wxDateTime(1, tm
.mon
, tm
.year
);
2100 int nWeek
= GetWeekOfYear(flags
) - dtMonthStart
.GetWeekOfYear(flags
) + 1;
2103 // this may happen for January when Jan, 1 is the last week of the
2105 nWeek
+= IsLeapYear(tm
.year
- 1) ? 53 : 52;
2108 return (wxDateTime::wxDateTime_t
)nWeek
;
2111 wxDateTime
& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday
)
2113 int year
= GetYear();
2114 wxDATETIME_CHECK( (0 < yday
) && (yday
<= GetNumberOfDays(year
)),
2115 _T("invalid year day") );
2117 bool isLeap
= IsLeapYear(year
);
2118 for ( Month mon
= Jan
; mon
< Inv_Month
; wxNextMonth(mon
) )
2120 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
2121 // don't need it neither - because of the CHECK above we know that
2122 // yday lies in December then
2123 if ( (mon
== Dec
) || (yday
<= gs_cumulatedDays
[isLeap
][mon
+ 1]) )
2125 Set((wxDateTime::wxDateTime_t
)(yday
- gs_cumulatedDays
[isLeap
][mon
]), mon
, year
);
2134 // ----------------------------------------------------------------------------
2135 // Julian day number conversion and related stuff
2136 // ----------------------------------------------------------------------------
2138 double wxDateTime::GetJulianDayNumber() const
2140 return m_time
.ToDouble() / MILLISECONDS_PER_DAY
+ EPOCH_JDN
+ 0.5;
2143 double wxDateTime::GetRataDie() const
2145 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
2146 return GetJulianDayNumber() - 1721119.5 - 306;
2149 // ----------------------------------------------------------------------------
2150 // timezone and DST stuff
2151 // ----------------------------------------------------------------------------
2153 int wxDateTime::IsDST(wxDateTime::Country country
) const
2155 wxCHECK_MSG( country
== Country_Default
, -1,
2156 _T("country support not implemented") );
2158 // use the C RTL for the dates in the standard range
2159 time_t timet
= GetTicks();
2160 if ( timet
!= (time_t)-1 )
2163 tm
*tm
= wxLocaltime_r(&timet
, &tmstruct
);
2165 wxCHECK_MSG( tm
, -1, _T("wxLocaltime_r() failed") );
2167 return tm
->tm_isdst
;
2171 int year
= GetYear();
2173 if ( !IsDSTApplicable(year
, country
) )
2175 // no DST time in this year in this country
2179 return IsBetween(GetBeginDST(year
, country
), GetEndDST(year
, country
));
2183 wxDateTime
& wxDateTime::MakeTimezone(const TimeZone
& tz
, bool noDST
)
2185 long secDiff
= GetTimeZone() + tz
.GetOffset();
2187 // we need to know whether DST is or not in effect for this date unless
2188 // the test disabled by the caller
2189 if ( !noDST
&& (IsDST() == 1) )
2191 // FIXME we assume that the DST is always shifted by 1 hour
2195 return Add(wxTimeSpan::Seconds(secDiff
));
2198 wxDateTime
& wxDateTime::MakeFromTimezone(const TimeZone
& tz
, bool noDST
)
2200 long secDiff
= GetTimeZone() + tz
.GetOffset();
2202 // we need to know whether DST is or not in effect for this date unless
2203 // the test disabled by the caller
2204 if ( !noDST
&& (IsDST() == 1) )
2206 // FIXME we assume that the DST is always shifted by 1 hour
2210 return Subtract(wxTimeSpan::Seconds(secDiff
));
2213 // ----------------------------------------------------------------------------
2214 // wxDateTime to/from text representations
2215 // ----------------------------------------------------------------------------
2217 wxString
wxDateTime::Format(const wxChar
*format
, const TimeZone
& tz
) const
2219 wxCHECK_MSG( format
, wxEmptyString
, _T("NULL format in wxDateTime::Format") );
2221 // we have to use our own implementation if the date is out of range of
2222 // strftime() or if we use non standard specificators
2223 time_t time
= GetTicks();
2224 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
2229 if ( tz
.GetOffset() == -GetTimeZone() )
2231 // we are working with local time
2232 tm
= wxLocaltime_r(&time
, &tmstruct
);
2234 // should never happen
2235 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxLocaltime_r() failed") );
2239 time
+= (int)tz
.GetOffset();
2241 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
2242 int time2
= (int) time
;
2248 tm
= wxGmtime_r(&time
, &tmstruct
);
2250 // should never happen
2251 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxGmtime_r() failed") );
2255 tm
= (struct tm
*)NULL
;
2259 //Windows CE doesn't support strftime or wcsftime, so we use the generic implementation
2262 return CallStrftime(format
, tm
);
2265 //else: use generic code below
2268 // we only parse ANSI C format specifications here, no POSIX 2
2269 // complications, no GNU extensions but we do add support for a "%l" format
2270 // specifier allowing to get the number of milliseconds
2273 // used for calls to strftime() when we only deal with time
2274 struct tm tmTimeOnly
;
2275 tmTimeOnly
.tm_hour
= tm
.hour
;
2276 tmTimeOnly
.tm_min
= tm
.min
;
2277 tmTimeOnly
.tm_sec
= tm
.sec
;
2278 tmTimeOnly
.tm_wday
= 0;
2279 tmTimeOnly
.tm_yday
= 0;
2280 tmTimeOnly
.tm_mday
= 1; // any date will do
2281 tmTimeOnly
.tm_mon
= 0;
2282 tmTimeOnly
.tm_year
= 76;
2283 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
2285 wxString tmp
, res
, fmt
;
2286 for ( const wxChar
*p
= format
; *p
; p
++ )
2288 if ( *p
!= _T('%') )
2296 // set the default format
2299 case _T('Y'): // year has 4 digits
2303 case _T('j'): // day of year has 3 digits
2304 case _T('l'): // milliseconds have 3 digits
2308 case _T('w'): // week day as number has only one
2313 // it's either another valid format specifier in which case
2314 // the format is "%02d" (for all the rest) or we have the
2315 // field width preceding the format in which case it will
2316 // override the default format anyhow
2320 bool restart
= true;
2325 // start of the format specification
2328 case _T('a'): // a weekday name
2330 // second parameter should be true for abbreviated names
2331 res
+= GetWeekDayName(tm
.GetWeekDay(),
2332 *p
== _T('a') ? Name_Abbr
: Name_Full
);
2335 case _T('b'): // a month name
2337 res
+= GetMonthName(tm
.mon
,
2338 *p
== _T('b') ? Name_Abbr
: Name_Full
);
2341 case _T('c'): // locale default date and time representation
2342 case _T('x'): // locale default date representation
2345 // the problem: there is no way to know what do these format
2346 // specifications correspond to for the current locale.
2348 // the solution: use a hack and still use strftime(): first
2349 // find the YEAR which is a year in the strftime() range (1970
2350 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
2351 // of the real year. Then make a copy of the format and
2352 // replace all occurrences of YEAR in it with some unique
2353 // string not appearing anywhere else in it, then use
2354 // strftime() to format the date in year YEAR and then replace
2355 // YEAR back by the real year and the unique replacement
2356 // string back with YEAR. Notice that "all occurrences of YEAR"
2357 // means all occurrences of 4 digit as well as 2 digit form!
2359 // the bugs: we assume that neither of %c nor %x contains any
2360 // fields which may change between the YEAR and real year. For
2361 // example, the week number (%U, %W) and the day number (%j)
2362 // will change if one of these years is leap and the other one
2365 // find the YEAR: normally, for any year X, Jan 1 or the
2366 // year X + 28 is the same weekday as Jan 1 of X (because
2367 // the weekday advances by 1 for each normal X and by 2
2368 // for each leap X, hence by 5 every 4 years or by 35
2369 // which is 0 mod 7 every 28 years) but this rule breaks
2370 // down if there are years between X and Y which are
2371 // divisible by 4 but not leap (i.e. divisible by 100 but
2372 // not 400), hence the correction.
2374 int yearReal
= GetYear(tz
);
2375 int mod28
= yearReal
% 28;
2377 // be careful to not go too far - we risk to leave the
2382 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
2386 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
2389 int nCentury
= year
/ 100,
2390 nCenturyReal
= yearReal
/ 100;
2392 // need to adjust for the years divisble by 400 which are
2393 // not leap but are counted like leap ones if we just take
2394 // the number of centuries in between for nLostWeekDays
2395 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
2396 (nCentury
/ 4 - nCenturyReal
/ 4);
2398 // we have to gain back the "lost" weekdays: note that the
2399 // effect of this loop is to not do anything to
2400 // nLostWeekDays (which we won't use any more), but to
2401 // (indirectly) set the year correctly
2402 while ( (nLostWeekDays
% 7) != 0 )
2404 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
2407 // Keep year below 2000 so the 2digit year number
2408 // can never match the month or day of the month
2409 if (year
>=2000) year
-=28;
2410 // at any rate, we couldn't go further than 1988 + 9 + 28!
2411 wxASSERT_MSG( year
< 2030,
2412 _T("logic error in wxDateTime::Format") );
2414 wxString strYear
, strYear2
;
2415 strYear
.Printf(_T("%d"), year
);
2416 strYear2
.Printf(_T("%d"), year
% 100);
2418 // find four strings not occurring in format (this is surely
2419 // not the optimal way of doing it... improvements welcome!)
2420 wxString fmt2
= format
;
2421 wxString replacement
,replacement2
,replacement3
,replacement4
;
2422 for (int rnr
=1; rnr
<5 ; rnr
++) {
2423 wxString r
= (wxChar
)-rnr
;
2424 while ( fmt2
.Find(r
) != wxNOT_FOUND
)
2430 case 1: replacement
=r
; break;
2431 case 2: replacement2
=r
; break;
2432 case 3: replacement3
=r
; break;
2433 case 4: replacement4
=r
; break;
2436 // replace all occurrences of year with it
2437 bool wasReplaced
= fmt2
.Replace(strYear
, replacement
) > 0;
2438 // evaluation order ensures we always attempt the replacement.
2439 wasReplaced
= (fmt2
.Replace(strYear2
, replacement2
) > 0) | wasReplaced
;
2441 // use strftime() to format the same date but in supported
2444 // NB: we assume that strftime() doesn't check for the
2445 // date validity and will happily format the date
2446 // corresponding to Feb 29 of a non leap year (which
2447 // may happen if yearReal was leap and year is not)
2448 struct tm tmAdjusted
;
2450 tmAdjusted
.tm_hour
= tm
.hour
;
2451 tmAdjusted
.tm_min
= tm
.min
;
2452 tmAdjusted
.tm_sec
= tm
.sec
;
2453 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
2454 tmAdjusted
.tm_yday
= GetDayOfYear();
2455 tmAdjusted
.tm_mday
= tm
.mday
;
2456 tmAdjusted
.tm_mon
= tm
.mon
;
2457 tmAdjusted
.tm_year
= year
- 1900;
2458 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
2459 wxString str
= CallStrftime(*p
== _T('c') ? _T("%c")
2463 // now replace the occurrence of 1999 with the real year
2464 // we do this in two stages to stop the 2 digit year
2465 // matching any substring of the 4 digit year.
2466 // Any day,month hours and minutes components should be safe due
2467 // to ensuring the range of the years.
2468 wxString strYearReal
, strYearReal2
;
2469 strYearReal
.Printf(_T("%04d"), yearReal
);
2470 strYearReal2
.Printf(_T("%02d"), yearReal
% 100);
2471 str
.Replace(strYear
, replacement3
);
2472 str
.Replace(strYear2
,replacement4
);
2473 str
.Replace(replacement3
, strYearReal
);
2474 str
.Replace(replacement4
, strYearReal2
);
2476 // and replace back all occurrences of replacement string
2479 str
.Replace(replacement2
, strYear2
);
2480 str
.Replace(replacement
, strYear
);
2486 //Use "%m/%d/%y %H:%M:%S" format instead
2487 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
2488 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
2492 case _T('d'): // day of a month (01-31)
2493 res
+= wxString::Format(fmt
, tm
.mday
);
2496 case _T('H'): // hour in 24h format (00-23)
2497 res
+= wxString::Format(fmt
, tm
.hour
);
2500 case _T('I'): // hour in 12h format (01-12)
2502 // 24h -> 12h, 0h -> 12h too
2503 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
2504 : tm
.hour
? tm
.hour
: 12;
2505 res
+= wxString::Format(fmt
, hour12
);
2509 case _T('j'): // day of the year
2510 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
2513 case _T('l'): // milliseconds (NOT STANDARD)
2514 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
2517 case _T('m'): // month as a number (01-12)
2518 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
2521 case _T('M'): // minute as a decimal number (00-59)
2522 res
+= wxString::Format(fmt
, tm
.min
);
2525 case _T('p'): // AM or PM string
2527 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
2529 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
2533 case _T('S'): // second as a decimal number (00-61)
2534 res
+= wxString::Format(fmt
, tm
.sec
);
2537 case _T('U'): // week number in the year (Sunday 1st week day)
2538 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
2541 case _T('W'): // week number in the year (Monday 1st week day)
2542 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
2545 case _T('w'): // weekday as a number (0-6), Sunday = 0
2546 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
2549 // case _T('x'): -- handled with "%c"
2551 case _T('X'): // locale default time representation
2552 // just use strftime() to format the time for us
2554 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
2556 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
2560 case _T('y'): // year without century (00-99)
2561 res
+= wxString::Format(fmt
, tm
.year
% 100);
2564 case _T('Y'): // year with century
2565 res
+= wxString::Format(fmt
, tm
.year
);
2568 case _T('Z'): // timezone name
2570 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
2575 // is it the format width?
2577 while ( *p
== _T('-') || *p
== _T('+') ||
2578 *p
== _T(' ') || wxIsdigit(*p
) )
2585 // we've only got the flags and width so far in fmt
2586 fmt
.Prepend(_T('%'));
2587 fmt
.Append(_T('d'));
2594 // no, it wasn't the width
2595 wxFAIL_MSG(_T("unknown format specificator"));
2597 // fall through and just copy it nevertheless
2599 case _T('%'): // a percent sign
2603 case 0: // the end of string
2604 wxFAIL_MSG(_T("missing format at the end of string"));
2606 // just put the '%' which was the last char in format
2616 // this function parses a string in (strict) RFC 822 format: see the section 5
2617 // of the RFC for the detailed description, but briefly it's something of the
2618 // form "Sat, 18 Dec 1999 00:48:30 +0100"
2620 // this function is "strict" by design - it must reject anything except true
2621 // RFC822 time specs.
2623 // TODO a great candidate for using reg exps
2624 const wxChar
*wxDateTime::ParseRfc822Date(const wxChar
* date
)
2626 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
2628 const wxChar
*p
= date
;
2629 const wxChar
*comma
= wxStrchr(p
, _T(','));
2632 // the part before comma is the weekday
2634 // skip it for now - we don't use but might check that it really
2635 // corresponds to the specfied date
2638 if ( *p
!= _T(' ') )
2640 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
2642 return (wxChar
*)NULL
;
2648 // the following 1 or 2 digits are the day number
2649 if ( !wxIsdigit(*p
) )
2651 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
2653 return (wxChar
*)NULL
;
2656 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - _T('0'));
2657 if ( wxIsdigit(*p
) )
2660 day
= (wxDateTime_t
)(day
+ (*p
++ - _T('0')));
2663 if ( *p
++ != _T(' ') )
2665 return (wxChar
*)NULL
;
2668 // the following 3 letters specify the month
2669 wxString
monName(p
, 3);
2671 if ( monName
== _T("Jan") )
2673 else if ( monName
== _T("Feb") )
2675 else if ( monName
== _T("Mar") )
2677 else if ( monName
== _T("Apr") )
2679 else if ( monName
== _T("May") )
2681 else if ( monName
== _T("Jun") )
2683 else if ( monName
== _T("Jul") )
2685 else if ( monName
== _T("Aug") )
2687 else if ( monName
== _T("Sep") )
2689 else if ( monName
== _T("Oct") )
2691 else if ( monName
== _T("Nov") )
2693 else if ( monName
== _T("Dec") )
2697 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str());
2699 return (wxChar
*)NULL
;
2704 if ( *p
++ != _T(' ') )
2706 return (wxChar
*)NULL
;
2710 if ( !wxIsdigit(*p
) )
2713 return (wxChar
*)NULL
;
2716 int year
= *p
++ - _T('0');
2718 if ( !wxIsdigit(*p
) )
2720 // should have at least 2 digits in the year
2721 return (wxChar
*)NULL
;
2725 year
+= *p
++ - _T('0');
2727 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
2728 if ( wxIsdigit(*p
) )
2731 year
+= *p
++ - _T('0');
2733 if ( !wxIsdigit(*p
) )
2735 // no 3 digit years please
2736 return (wxChar
*)NULL
;
2740 year
+= *p
++ - _T('0');
2743 if ( *p
++ != _T(' ') )
2745 return (wxChar
*)NULL
;
2748 // time is in the format hh:mm:ss and seconds are optional
2749 if ( !wxIsdigit(*p
) )
2751 return (wxChar
*)NULL
;
2754 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - _T('0'));
2756 if ( !wxIsdigit(*p
) )
2758 return (wxChar
*)NULL
;
2762 hour
= (wxDateTime_t
)(hour
+ (*p
++ - _T('0')));
2764 if ( *p
++ != _T(':') )
2766 return (wxChar
*)NULL
;
2769 if ( !wxIsdigit(*p
) )
2771 return (wxChar
*)NULL
;
2774 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - _T('0'));
2776 if ( !wxIsdigit(*p
) )
2778 return (wxChar
*)NULL
;
2782 min
= (wxDateTime_t
)(min
+ *p
++ - _T('0'));
2784 wxDateTime_t sec
= 0;
2785 if ( *p
++ == _T(':') )
2787 if ( !wxIsdigit(*p
) )
2789 return (wxChar
*)NULL
;
2792 sec
= (wxDateTime_t
)(*p
++ - _T('0'));
2794 if ( !wxIsdigit(*p
) )
2796 return (wxChar
*)NULL
;
2800 sec
= (wxDateTime_t
)(sec
+ *p
++ - _T('0'));
2803 if ( *p
++ != _T(' ') )
2805 return (wxChar
*)NULL
;
2808 // and now the interesting part: the timezone
2809 int offset
wxDUMMY_INITIALIZE(0);
2810 if ( *p
== _T('-') || *p
== _T('+') )
2812 // the explicit offset given: it has the form of hhmm
2813 bool plus
= *p
++ == _T('+');
2815 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2817 return (wxChar
*)NULL
;
2821 offset
= MIN_PER_HOUR
*(10*(*p
- _T('0')) + (*(p
+ 1) - _T('0')));
2825 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2827 return (wxChar
*)NULL
;
2831 offset
+= 10*(*p
- _T('0')) + (*(p
+ 1) - _T('0'));
2842 // the symbolic timezone given: may be either military timezone or one
2843 // of standard abbreviations
2846 // military: Z = UTC, J unused, A = -1, ..., Y = +12
2847 static const int offsets
[26] =
2849 //A B C D E F G H I J K L M
2850 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
2851 //N O P R Q S T U V W Z Y Z
2852 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
2855 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
2857 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
);
2859 return (wxChar
*)NULL
;
2862 offset
= offsets
[*p
++ - _T('A')];
2868 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
2870 else if ( tz
== _T("AST") )
2871 offset
= AST
- GMT0
;
2872 else if ( tz
== _T("ADT") )
2873 offset
= ADT
- GMT0
;
2874 else if ( tz
== _T("EST") )
2875 offset
= EST
- GMT0
;
2876 else if ( tz
== _T("EDT") )
2877 offset
= EDT
- GMT0
;
2878 else if ( tz
== _T("CST") )
2879 offset
= CST
- GMT0
;
2880 else if ( tz
== _T("CDT") )
2881 offset
= CDT
- GMT0
;
2882 else if ( tz
== _T("MST") )
2883 offset
= MST
- GMT0
;
2884 else if ( tz
== _T("MDT") )
2885 offset
= MDT
- GMT0
;
2886 else if ( tz
== _T("PST") )
2887 offset
= PST
- GMT0
;
2888 else if ( tz
== _T("PDT") )
2889 offset
= PDT
- GMT0
;
2892 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
);
2894 return (wxChar
*)NULL
;
2901 offset
*= MIN_PER_HOUR
;
2904 // the spec was correct, construct the date from the values we found
2905 Set(day
, mon
, year
, hour
, min
, sec
);
2906 MakeFromTimezone(TimeZone((wxDateTime_t
)(offset
*SEC_PER_MIN
)));
2913 // returns the string containing strftime() format used for short dates in the
2914 // current locale or an empty string
2915 static wxString
GetLocaleDateFormat()
2919 // there is no setlocale() under Windows CE, so just always query the
2922 if ( strcmp(setlocale(LC_ALL
, NULL
), "C") != 0 )
2925 // The locale was programatically set to non-C. We assume that this was
2926 // done using wxLocale, in which case thread's current locale is also
2927 // set to correct LCID value and we can use GetLocaleInfo to determine
2928 // the correct formatting string:
2930 LCID lcid
= LOCALE_USER_DEFAULT
;
2932 LCID lcid
= GetThreadLocale();
2934 // according to MSDN 80 chars is max allowed for short date format
2936 if ( ::GetLocaleInfo(lcid
, LOCALE_SSHORTDATE
, fmt
, WXSIZEOF(fmt
)) )
2938 wxChar chLast
= _T('\0');
2939 size_t lastCount
= 0;
2940 for ( const wxChar
*p
= fmt
; /* NUL handled inside */; p
++ )
2950 // these characters come in groups, start counting them
2960 // first deal with any special characters we have had
2966 switch ( lastCount
)
2970 // these two are the same as we
2971 // don't distinguish between 1 and
2972 // 2 digits for days
2985 wxFAIL_MSG( _T("too many 'd's") );
2990 switch ( lastCount
)
2994 // as for 'd' and 'dd' above
3007 wxFAIL_MSG( _T("too many 'M's") );
3012 switch ( lastCount
)
3024 wxFAIL_MSG( _T("wrong number of 'y's") );
3029 // strftime() doesn't have era string,
3030 // ignore this format
3031 wxASSERT_MSG( lastCount
<= 2,
3032 _T("too many 'g's") );
3036 wxFAIL_MSG( _T("unreachable") );
3043 // not a special character so must be just a separator,
3045 if ( *p
!= _T('\0') )
3047 if ( *p
== _T('%') )
3049 // this one needs to be escaped
3057 if ( *p
== _T('\0') )
3061 //else: GetLocaleInfo() failed, leave fmtDate value unchanged and
3062 // try our luck with the default formats
3064 //else: default C locale, default formats should work
3069 #endif // __WINDOWS__
3071 const wxChar
*wxDateTime::ParseFormat(const wxChar
*date
,
3072 const wxChar
*format
,
3073 const wxDateTime
& dateDef
)
3075 wxCHECK_MSG( date
&& format
, (wxChar
*)NULL
,
3076 _T("NULL pointer in wxDateTime::ParseFormat()") );
3081 // what fields have we found?
3082 bool haveWDay
= false,
3091 bool hourIsIn12hFormat
= false, // or in 24h one?
3092 isPM
= false; // AM by default
3094 // and the value of the items we have (init them to get rid of warnings)
3095 wxDateTime_t sec
= 0,
3098 WeekDay wday
= Inv_WeekDay
;
3099 wxDateTime_t yday
= 0,
3101 wxDateTime::Month mon
= Inv_Month
;
3104 const wxChar
*input
= date
;
3105 for ( const wxChar
*fmt
= format
; *fmt
; fmt
++ )
3107 if ( *fmt
!= _T('%') )
3109 if ( wxIsspace(*fmt
) )
3111 // a white space in the format string matches 0 or more white
3112 // spaces in the input
3113 while ( wxIsspace(*input
) )
3120 // any other character (not whitespace, not '%') must be
3121 // matched by itself in the input
3122 if ( *input
++ != *fmt
)
3125 return (wxChar
*)NULL
;
3129 // done with this format char
3133 // start of a format specification
3135 // parse the optional width
3137 while ( wxIsdigit(*++fmt
) )
3140 width
+= *fmt
- _T('0');
3143 // the default widths for the various fields
3148 case _T('Y'): // year has 4 digits
3152 case _T('j'): // day of year has 3 digits
3153 case _T('l'): // milliseconds have 3 digits
3157 case _T('w'): // week day as number has only one
3162 // default for all other fields
3167 // then the format itself
3170 case _T('a'): // a weekday name
3173 int flag
= *fmt
== _T('a') ? Name_Abbr
: Name_Full
;
3174 wday
= GetWeekDayFromName(GetAlphaToken(input
), flag
);
3175 if ( wday
== Inv_WeekDay
)
3178 return (wxChar
*)NULL
;
3184 case _T('b'): // a month name
3187 int flag
= *fmt
== _T('b') ? Name_Abbr
: Name_Full
;
3188 mon
= GetMonthFromName(GetAlphaToken(input
), flag
);
3189 if ( mon
== Inv_Month
)
3192 return (wxChar
*)NULL
;
3198 case _T('c'): // locale default date and time representation
3202 // this is the format which corresponds to ctime() output
3203 // and strptime("%c") should parse it, so try it first
3204 static const wxChar
*fmtCtime
= _T("%a %b %d %H:%M:%S %Y");
3206 const wxChar
*result
= dt
.ParseFormat(input
, fmtCtime
);
3209 result
= dt
.ParseFormat(input
, _T("%x %X"));
3214 result
= dt
.ParseFormat(input
, _T("%X %x"));
3219 // we've tried everything and still no match
3220 return (wxChar
*)NULL
;
3225 haveDay
= haveMon
= haveYear
=
3226 haveHour
= haveMin
= haveSec
= true;
3240 case _T('d'): // day of a month (01-31)
3241 if ( !GetNumericToken(width
, input
, &num
) ||
3242 (num
> 31) || (num
< 1) )
3245 return (wxChar
*)NULL
;
3248 // we can't check whether the day range is correct yet, will
3249 // do it later - assume ok for now
3251 mday
= (wxDateTime_t
)num
;
3254 case _T('H'): // hour in 24h format (00-23)
3255 if ( !GetNumericToken(width
, input
, &num
) || (num
> 23) )
3258 return (wxChar
*)NULL
;
3262 hour
= (wxDateTime_t
)num
;
3265 case _T('I'): // hour in 12h format (01-12)
3266 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3269 return (wxChar
*)NULL
;
3273 hourIsIn12hFormat
= true;
3274 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
3277 case _T('j'): // day of the year
3278 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 366) )
3281 return (wxChar
*)NULL
;
3285 yday
= (wxDateTime_t
)num
;
3288 case _T('m'): // month as a number (01-12)
3289 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3292 return (wxChar
*)NULL
;
3296 mon
= (Month
)(num
- 1);
3299 case _T('M'): // minute as a decimal number (00-59)
3300 if ( !GetNumericToken(width
, input
, &num
) || (num
> 59) )
3303 return (wxChar
*)NULL
;
3307 min
= (wxDateTime_t
)num
;
3310 case _T('p'): // AM or PM string
3312 wxString am
, pm
, token
= GetAlphaToken(input
);
3314 GetAmPmStrings(&am
, &pm
);
3315 if (am
.empty() && pm
.empty())
3316 return (wxChar
*)NULL
; // no am/pm strings defined
3317 if ( token
.CmpNoCase(pm
) == 0 )
3321 else if ( token
.CmpNoCase(am
) != 0 )
3324 return (wxChar
*)NULL
;
3329 case _T('r'): // time as %I:%M:%S %p
3332 input
= dt
.ParseFormat(input
, _T("%I:%M:%S %p"));
3336 return (wxChar
*)NULL
;
3339 haveHour
= haveMin
= haveSec
= true;
3348 case _T('R'): // time as %H:%M
3351 input
= dt
.ParseFormat(input
, _T("%H:%M"));
3355 return (wxChar
*)NULL
;
3358 haveHour
= haveMin
= true;
3366 case _T('S'): // second as a decimal number (00-61)
3367 if ( !GetNumericToken(width
, input
, &num
) || (num
> 61) )
3370 return (wxChar
*)NULL
;
3374 sec
= (wxDateTime_t
)num
;
3377 case _T('T'): // time as %H:%M:%S
3380 input
= dt
.ParseFormat(input
, _T("%H:%M:%S"));
3384 return (wxChar
*)NULL
;
3387 haveHour
= haveMin
= haveSec
= true;
3396 case _T('w'): // weekday as a number (0-6), Sunday = 0
3397 if ( !GetNumericToken(width
, input
, &num
) || (wday
> 6) )
3400 return (wxChar
*)NULL
;
3404 wday
= (WeekDay
)num
;
3407 case _T('x'): // locale default date representation
3408 #ifdef HAVE_STRPTIME
3409 // try using strptime() -- it may fail even if the input is
3410 // correct but the date is out of range, so we will fall back
3411 // to our generic code anyhow
3415 const wxChar
*result
= CallStrptime(input
, "%x", &tm
);
3420 haveDay
= haveMon
= haveYear
= true;
3422 year
= 1900 + tm
.tm_year
;
3423 mon
= (Month
)tm
.tm_mon
;
3429 #endif // HAVE_STRPTIME
3437 // The above doesn't work for all locales, try to query
3438 // Windows for the right way of formatting the date:
3439 fmtDate
= GetLocaleDateFormat();
3440 if ( fmtDate
.empty() )
3443 if ( IsWestEuropeanCountry(GetCountry()) ||
3444 GetCountry() == Russia
)
3446 fmtDate
= _T("%d/%m/%y");
3447 fmtDateAlt
= _T("%m/%d/%y");
3451 fmtDate
= _T("%m/%d/%y");
3452 fmtDateAlt
= _T("%d/%m/%y");
3456 const wxChar
*result
= dt
.ParseFormat(input
, fmtDate
);
3458 if ( !result
&& !fmtDateAlt
.empty() )
3460 // ok, be nice and try another one
3461 result
= dt
.ParseFormat(input
, fmtDateAlt
);
3467 return (wxChar
*)NULL
;
3472 haveDay
= haveMon
= haveYear
= true;
3483 case _T('X'): // locale default time representation
3484 #ifdef HAVE_STRPTIME
3486 // use strptime() to do it for us (FIXME !Unicode friendly)
3488 input
= CallStrptime(input
, "%X", &tm
);
3491 return (wxChar
*)NULL
;
3494 haveHour
= haveMin
= haveSec
= true;
3500 #else // !HAVE_STRPTIME
3501 // TODO under Win32 we can query the LOCALE_ITIME system
3502 // setting which says whether the default time format is
3505 // try to parse what follows as "%H:%M:%S" and, if this
3506 // fails, as "%I:%M:%S %p" - this should catch the most
3510 const wxChar
*result
= dt
.ParseFormat(input
, _T("%T"));
3513 result
= dt
.ParseFormat(input
, _T("%r"));
3519 return (wxChar
*)NULL
;
3522 haveHour
= haveMin
= haveSec
= true;
3531 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
3534 case _T('y'): // year without century (00-99)
3535 if ( !GetNumericToken(width
, input
, &num
) || (num
> 99) )
3538 return (wxChar
*)NULL
;
3543 // TODO should have an option for roll over date instead of
3544 // hard coding it here
3545 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
3548 case _T('Y'): // year with century
3549 if ( !GetNumericToken(width
, input
, &num
) )
3552 return (wxChar
*)NULL
;
3556 year
= (wxDateTime_t
)num
;
3559 case _T('Z'): // timezone name
3560 wxFAIL_MSG(_T("TODO"));
3563 case _T('%'): // a percent sign
3564 if ( *input
++ != _T('%') )
3567 return (wxChar
*)NULL
;
3571 case 0: // the end of string
3572 wxFAIL_MSG(_T("unexpected format end"));
3576 default: // not a known format spec
3577 return (wxChar
*)NULL
;
3581 // format matched, try to construct a date from what we have now
3583 if ( dateDef
.IsValid() )
3585 // take this date as default
3586 tmDef
= dateDef
.GetTm();
3588 else if ( IsValid() )
3590 // if this date is valid, don't change it
3595 // no default and this date is invalid - fall back to Today()
3596 tmDef
= Today().GetTm();
3607 // TODO we don't check here that the values are consistent, if both year
3608 // day and month/day were found, we just ignore the year day and we
3609 // also always ignore the week day
3610 if ( haveMon
&& haveDay
)
3612 if ( mday
> GetNumOfDaysInMonth(tm
.year
, mon
) )
3614 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
3616 return (wxChar
*)NULL
;
3622 else if ( haveYDay
)
3624 if ( yday
> GetNumberOfDays(tm
.year
) )
3626 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
3628 return (wxChar
*)NULL
;
3631 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
3638 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
3640 // translate to 24hour format
3643 //else: either already in 24h format or no translation needed
3663 // finally check that the week day is consistent -- if we had it
3664 if ( haveWDay
&& GetWeekDay() != wday
)
3666 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
3674 const wxChar
*wxDateTime::ParseDateTime(const wxChar
*date
)
3676 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3678 // Set to current day and hour, so strings like '14:00' becomes today at
3679 // 14, not some other random date
3680 wxDateTime dtDate
= wxDateTime::Today();
3681 wxDateTime dtTime
= wxDateTime::Today();
3683 const wxChar
* pchTime
;
3685 // Try to parse the beginning of the string as a date
3686 const wxChar
* pchDate
= dtDate
.ParseDate(date
);
3688 // We got a date in the beginning, see if there is a time specified after the date
3691 // Skip spaces, as the ParseTime() function fails on spaces
3692 while ( wxIsspace(*pchDate
) )
3695 pchTime
= dtTime
.ParseTime(pchDate
);
3697 else // no date in the beginning
3699 // check and see if we have a time followed by a date
3700 pchTime
= dtTime
.ParseTime(date
);
3703 while ( wxIsspace(*pchTime
) )
3706 pchDate
= dtDate
.ParseDate(pchTime
);
3710 // If we have a date specified, set our own data to the same date
3711 if ( !pchDate
|| !pchTime
)
3714 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
3715 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
3716 dtTime
.GetMillisecond());
3718 // Return endpoint of scan
3719 return pchDate
> pchTime
? pchDate
: pchTime
;
3722 const wxChar
*wxDateTime::ParseDate(const wxChar
*date
)
3724 // this is a simplified version of ParseDateTime() which understands only
3725 // "today" (for wxDate compatibility) and digits only otherwise (and not
3726 // all esoteric constructions ParseDateTime() knows about)
3728 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3730 const wxChar
*p
= date
;
3731 while ( wxIsspace(*p
) )
3734 // some special cases
3738 int dayDiffFromToday
;
3741 { wxTRANSLATE("today"), 0 },
3742 { wxTRANSLATE("yesterday"), -1 },
3743 { wxTRANSLATE("tomorrow"), 1 },
3746 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
3748 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
3749 size_t len
= dateStr
.length();
3750 if ( wxStrlen(p
) >= len
)
3752 wxString
str(p
, len
);
3753 if ( str
.CmpNoCase(dateStr
) == 0 )
3755 // nothing can follow this, so stop here
3758 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
3760 if ( dayDiffFromToday
)
3762 *this += wxDateSpan::Days(dayDiffFromToday
);
3770 // We try to guess what we have here: for each new (numeric) token, we
3771 // determine if it can be a month, day or a year. Of course, there is an
3772 // ambiguity as some numbers may be days as well as months, so we also
3773 // have the ability to back track.
3776 bool haveDay
= false, // the months day?
3777 haveWDay
= false, // the day of week?
3778 haveMon
= false, // the month?
3779 haveYear
= false; // the year?
3781 // and the value of the items we have (init them to get rid of warnings)
3782 WeekDay wday
= Inv_WeekDay
;
3783 wxDateTime_t day
= 0;
3784 wxDateTime::Month mon
= Inv_Month
;
3787 // tokenize the string
3789 static const wxChar
*dateDelimiters
= _T(".,/-\t\r\n ");
3790 wxStringTokenizer
tok(p
, dateDelimiters
);
3791 while ( tok
.HasMoreTokens() )
3793 wxString token
= tok
.GetNextToken();
3799 if ( token
.ToULong(&val
) )
3801 // guess what this number is
3807 if ( !haveMon
&& val
> 0 && val
<= 12 )
3809 // assume it is month
3812 else // not the month
3816 // this can only be the year
3819 else // may be either day or year
3821 // use a leap year if we don't have the year yet to allow
3822 // dates like 2/29/1976 which would be rejected otherwise
3823 wxDateTime_t max_days
= (wxDateTime_t
)(
3825 ? GetNumOfDaysInMonth(haveYear
? year
: 1976, mon
)
3830 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
3835 else // yes, suppose it's the day
3849 year
= (wxDateTime_t
)val
;
3858 day
= (wxDateTime_t
)val
;
3864 mon
= (Month
)(val
- 1);
3867 else // not a number
3869 // be careful not to overwrite the current mon value
3870 Month mon2
= GetMonthFromName(token
, Name_Full
| Name_Abbr
);
3871 if ( mon2
!= Inv_Month
)
3876 // but we already have a month - maybe we guessed wrong?
3879 // no need to check in month range as always < 12, but
3880 // the days are counted from 1 unlike the months
3881 day
= (wxDateTime_t
)(mon
+ 1);
3886 // could possible be the year (doesn't the year come
3887 // before the month in the japanese format?) (FIXME)
3896 else // not a valid month name
3898 wday
= GetWeekDayFromName(token
, Name_Full
| Name_Abbr
);
3899 if ( wday
!= Inv_WeekDay
)
3909 else // not a valid weekday name
3912 static const wxChar
*ordinals
[] =
3914 wxTRANSLATE("first"),
3915 wxTRANSLATE("second"),
3916 wxTRANSLATE("third"),
3917 wxTRANSLATE("fourth"),
3918 wxTRANSLATE("fifth"),
3919 wxTRANSLATE("sixth"),
3920 wxTRANSLATE("seventh"),
3921 wxTRANSLATE("eighth"),
3922 wxTRANSLATE("ninth"),
3923 wxTRANSLATE("tenth"),
3924 wxTRANSLATE("eleventh"),
3925 wxTRANSLATE("twelfth"),
3926 wxTRANSLATE("thirteenth"),
3927 wxTRANSLATE("fourteenth"),
3928 wxTRANSLATE("fifteenth"),
3929 wxTRANSLATE("sixteenth"),
3930 wxTRANSLATE("seventeenth"),
3931 wxTRANSLATE("eighteenth"),
3932 wxTRANSLATE("nineteenth"),
3933 wxTRANSLATE("twentieth"),
3934 // that's enough - otherwise we'd have problems with
3935 // composite (or not) ordinals
3939 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
3941 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
3947 if ( n
== WXSIZEOF(ordinals
) )
3949 // stop here - something unknown
3956 // don't try anything here (as in case of numeric day
3957 // above) - the symbolic day spec should always
3958 // precede the month/year
3964 day
= (wxDateTime_t
)(n
+ 1);
3969 nPosCur
= tok
.GetPosition();
3972 // either no more tokens or the scan was stopped by something we couldn't
3973 // parse - in any case, see if we can construct a date from what we have
3974 if ( !haveDay
&& !haveWDay
)
3976 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
3981 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
3982 !(haveDay
&& haveMon
&& haveYear
) )
3984 // without adjectives (which we don't support here) the week day only
3985 // makes sense completely separately or with the full date
3986 // specification (what would "Wed 1999" mean?)
3990 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
3992 // may be we have month and day instead of day and year?
3993 if ( haveDay
&& !haveMon
)
3997 // exchange day and month
3998 mon
= (wxDateTime::Month
)(day
- 1);
4000 // we're in the current year then
4001 if ( (year
> 0) && (year
<= (int)GetNumOfDaysInMonth(Inv_Year
, mon
)) )
4003 day
= (wxDateTime_t
)year
;
4008 //else: no, can't exchange, leave haveMon == false
4014 // if we give the year, month and day must be given too
4015 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
4023 mon
= GetCurrentMonth();
4028 year
= GetCurrentYear();
4033 // normally we check the day above but the check is optimistic in case
4034 // we find the day before its month/year so we have to redo it now
4035 if ( day
> GetNumOfDaysInMonth(year
, mon
) )
4038 Set(day
, mon
, year
);
4042 // check that it is really the same
4043 if ( GetWeekDay() != wday
)
4045 // inconsistency detected
4046 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
4048 return (wxChar
*)NULL
;
4056 SetToWeekDayInSameWeek(wday
);
4059 // return the pointer to the first unparsed char
4061 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
4063 // if we couldn't parse the token after the delimiter, put back the
4064 // delimiter as well
4071 const wxChar
*wxDateTime::ParseTime(const wxChar
*time
)
4073 wxCHECK_MSG( time
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
4075 // first try some extra things
4082 { wxTRANSLATE("noon"), 12 },
4083 { wxTRANSLATE("midnight"), 00 },
4087 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
4089 wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
4090 size_t len
= timeString
.length();
4091 if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 )
4093 // casts required by DigitalMars
4094 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
4100 // try all time formats we may think about in the order from longest to
4103 // 12hour with AM/PM?
4104 const wxChar
*result
= ParseFormat(time
, _T("%I:%M:%S %p"));
4108 // normally, it's the same, but why not try it?
4109 result
= ParseFormat(time
, _T("%H:%M:%S"));
4114 // 12hour with AM/PM but without seconds?
4115 result
= ParseFormat(time
, _T("%I:%M %p"));
4121 result
= ParseFormat(time
, _T("%H:%M"));
4126 // just the hour and AM/PM?
4127 result
= ParseFormat(time
, _T("%I %p"));
4133 result
= ParseFormat(time
, _T("%H"));
4138 // parse the standard format: normally it is one of the formats above
4139 // but it may be set to something completely different by the user
4140 result
= ParseFormat(time
, _T("%X"));
4143 // TODO: parse timezones
4148 // ----------------------------------------------------------------------------
4149 // Workdays and holidays support
4150 // ----------------------------------------------------------------------------
4152 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
4154 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
4157 // ============================================================================
4159 // ============================================================================
4161 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
4164 return ds1
.Multiply(n
);
4167 // ============================================================================
4169 // ============================================================================
4171 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
4173 return wxTimeSpan(ts
).Multiply(n
);
4176 // this enum is only used in wxTimeSpan::Format() below but we can't declare
4177 // it locally to the method as it provokes an internal compiler error in egcs
4178 // 2.91.60 when building with -O2
4189 // not all strftime(3) format specifiers make sense here because, for example,
4190 // a time span doesn't have a year nor a timezone
4192 // Here are the ones which are supported (all of them are supported by strftime
4194 // %H hour in 24 hour format
4195 // %M minute (00 - 59)
4196 // %S second (00 - 59)
4199 // Also, for MFC CTimeSpan compatibility, we support
4200 // %D number of days
4202 // And, to be better than MFC :-), we also have
4203 // %E number of wEeks
4204 // %l milliseconds (000 - 999)
4205 wxString
wxTimeSpan::Format(const wxChar
*format
) const
4207 wxCHECK_MSG( format
, wxEmptyString
, _T("NULL format in wxTimeSpan::Format") );
4210 str
.Alloc(wxStrlen(format
));
4212 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
4214 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
4215 // question is what should ts.Format("%S") do? The code here returns "3273"
4216 // in this case (i.e. the total number of seconds, not just seconds % 60)
4217 // because, for me, this call means "give me entire time interval in
4218 // seconds" and not "give me the seconds part of the time interval"
4220 // If we agree that it should behave like this, it is clear that the
4221 // interpretation of each format specifier depends on the presence of the
4222 // other format specs in the string: if there was "%H" before "%M", we
4223 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
4225 // we remember the most important unit found so far
4226 TimeSpanPart partBiggest
= Part_MSec
;
4228 for ( const wxChar
*pch
= format
; *pch
; pch
++ )
4232 if ( ch
== _T('%') )
4234 // the start of the format specification of the printf() below
4235 wxString fmtPrefix
= _T('%');
4240 ch
= *++pch
; // get the format spec char
4244 wxFAIL_MSG( _T("invalid format character") );
4250 // skip the part below switch
4255 if ( partBiggest
< Part_Day
)
4261 partBiggest
= Part_Day
;
4266 partBiggest
= Part_Week
;
4272 if ( partBiggest
< Part_Hour
)
4278 partBiggest
= Part_Hour
;
4281 fmtPrefix
+= _T("02");
4285 n
= GetMilliseconds().ToLong();
4286 if ( partBiggest
< Part_MSec
)
4290 //else: no need to reset partBiggest to Part_MSec, it is
4291 // the least significant one anyhow
4293 fmtPrefix
+= _T("03");
4298 if ( partBiggest
< Part_Min
)
4304 partBiggest
= Part_Min
;
4307 fmtPrefix
+= _T("02");
4311 n
= GetSeconds().ToLong();
4312 if ( partBiggest
< Part_Sec
)
4318 partBiggest
= Part_Sec
;
4321 fmtPrefix
+= _T("02");
4325 str
+= wxString::Format(fmtPrefix
+ _T("ld"), n
);
4329 // normal character, just copy
4337 // ============================================================================
4338 // wxDateTimeHolidayAuthority and related classes
4339 // ============================================================================
4341 #include "wx/arrimpl.cpp"
4343 WX_DEFINE_OBJARRAY(wxDateTimeArray
)
4345 static int wxCMPFUNC_CONV
4346 wxDateTimeCompareFunc(wxDateTime
**first
, wxDateTime
**second
)
4348 wxDateTime dt1
= **first
,
4351 return dt1
== dt2
? 0 : dt1
< dt2
? -1 : +1;
4354 // ----------------------------------------------------------------------------
4355 // wxDateTimeHolidayAuthority
4356 // ----------------------------------------------------------------------------
4358 wxHolidayAuthoritiesArray
wxDateTimeHolidayAuthority::ms_authorities
;
4361 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime
& dt
)
4363 size_t count
= ms_authorities
.size();
4364 for ( size_t n
= 0; n
< count
; n
++ )
4366 if ( ms_authorities
[n
]->DoIsHoliday(dt
) )
4377 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime
& dtStart
,
4378 const wxDateTime
& dtEnd
,
4379 wxDateTimeArray
& holidays
)
4381 wxDateTimeArray hol
;
4385 const size_t countAuth
= ms_authorities
.size();
4386 for ( size_t nAuth
= 0; nAuth
< countAuth
; nAuth
++ )
4388 ms_authorities
[nAuth
]->DoGetHolidaysInRange(dtStart
, dtEnd
, hol
);
4390 WX_APPEND_ARRAY(holidays
, hol
);
4393 holidays
.Sort(wxDateTimeCompareFunc
);
4395 return holidays
.size();
4399 void wxDateTimeHolidayAuthority::ClearAllAuthorities()
4401 WX_CLEAR_ARRAY(ms_authorities
);
4405 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority
*auth
)
4407 ms_authorities
.push_back(auth
);
4410 wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
4412 // required here for Darwin
4415 // ----------------------------------------------------------------------------
4416 // wxDateTimeWorkDays
4417 // ----------------------------------------------------------------------------
4419 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime
& dt
) const
4421 wxDateTime::WeekDay wd
= dt
.GetWeekDay();
4423 return (wd
== wxDateTime::Sun
) || (wd
== wxDateTime::Sat
);
4426 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime
& dtStart
,
4427 const wxDateTime
& dtEnd
,
4428 wxDateTimeArray
& holidays
) const
4430 if ( dtStart
> dtEnd
)
4432 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
4439 // instead of checking all days, start with the first Sat after dtStart and
4440 // end with the last Sun before dtEnd
4441 wxDateTime dtSatFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sat
),
4442 dtSatLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sat
),
4443 dtSunFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sun
),
4444 dtSunLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sun
),
4447 for ( dt
= dtSatFirst
; dt
<= dtSatLast
; dt
+= wxDateSpan::Week() )
4452 for ( dt
= dtSunFirst
; dt
<= dtSunLast
; dt
+= wxDateSpan::Week() )
4457 return holidays
.GetCount();
4460 // ============================================================================
4461 // other helper functions
4462 // ============================================================================
4464 // ----------------------------------------------------------------------------
4465 // iteration helpers: can be used to write a for loop over enum variable like
4467 // for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
4468 // ----------------------------------------------------------------------------
4470 WXDLLIMPEXP_BASE
void wxNextMonth(wxDateTime::Month
& m
)
4472 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4474 // no wrapping or the for loop above would never end!
4475 m
= (wxDateTime::Month
)(m
+ 1);
4478 WXDLLIMPEXP_BASE
void wxPrevMonth(wxDateTime::Month
& m
)
4480 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4482 m
= m
== wxDateTime::Jan
? wxDateTime::Inv_Month
4483 : (wxDateTime::Month
)(m
- 1);
4486 WXDLLIMPEXP_BASE
void wxNextWDay(wxDateTime::WeekDay
& wd
)
4488 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4490 // no wrapping or the for loop above would never end!
4491 wd
= (wxDateTime::WeekDay
)(wd
+ 1);
4494 WXDLLIMPEXP_BASE
void wxPrevWDay(wxDateTime::WeekDay
& wd
)
4496 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4498 wd
= wd
== wxDateTime::Sun
? wxDateTime::Inv_WeekDay
4499 : (wxDateTime::WeekDay
)(wd
- 1);
4502 #endif // wxUSE_DATETIME