1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/datetime.cpp
3 // Purpose: implementation of time/date related classes
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // parts of code taken from sndcal library by Scott E. Lee:
11 // Copyright 1993-1995, Scott E. Lee, all rights reserved.
12 // Permission granted to use, copy, modify, distribute and sell
13 // so long as the above copyright and this permission statement
14 // are retained in all copies.
16 // Licence: wxWindows licence
17 ///////////////////////////////////////////////////////////////////////////////
19 // TODO: for $DEITY sake, someone please fix the #ifdef __WXWINCE__ everywhere,
20 // the proper way to do it is to implement (subset of) wxStrftime() for
21 // CE instead of this horror!!
24 * Implementation notes:
26 * 1. the time is stored as a 64bit integer containing the signed number of
27 * milliseconds since Jan 1. 1970 (the Unix Epoch) - so it is always
30 * 2. the range is thus something about 580 million years, but due to current
31 * algorithms limitations, only dates from Nov 24, 4714BC are handled
33 * 3. standard ANSI C functions are used to do time calculations whenever
34 * possible, i.e. when the date is in the range Jan 1, 1970 to 2038
36 * 4. otherwise, the calculations are done by converting the date to/from JDN
37 * first (the range limitation mentioned above comes from here: the
38 * algorithm used by Scott E. Lee's code only works for positive JDNs, more
41 * 5. the object constructed for the given DD-MM-YYYY HH:MM:SS corresponds to
42 * this moment in local time and may be converted to the object
43 * corresponding to the same date/time in another time zone by using
46 * 6. the conversions to the current (or any other) timezone are done when the
47 * internal time representation is converted to the broken-down one in
51 // ============================================================================
53 // ============================================================================
55 // ----------------------------------------------------------------------------
57 // ----------------------------------------------------------------------------
59 // For compilers that support precompilation, includes "wx.h".
60 #include "wx/wxprec.h"
66 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
70 #include "wx/msw/wrapwin.h"
72 #include "wx/string.h"
75 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
76 #include "wx/module.h"
79 #include "wx/thread.h"
80 #include "wx/tokenzr.h"
91 #include "wx/datetime.h"
93 const long wxDateTime::TIME_T_FACTOR
= 1000l;
95 #if wxUSE_EXTENDED_RTTI
97 template<> void wxStringReadValue(const wxString
&s
, wxDateTime
&data
)
99 data
.ParseFormat(s
,wxT("%Y-%m-%d %H:%M:%S")) ;
102 template<> void wxStringWriteValue(wxString
&s
, const wxDateTime
&data
)
104 s
= data
.Format(wxT("%Y-%m-%d %H:%M:%S")) ;
107 wxCUSTOM_TYPE_INFO(wxDateTime
, wxToStringConverter
<wxDateTime
> , wxFromStringConverter
<wxDateTime
>)
112 // ----------------------------------------------------------------------------
113 // conditional compilation
114 // ----------------------------------------------------------------------------
116 #if defined(HAVE_STRPTIME) && defined(__GLIBC__) && \
117 ((__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0))
118 // glibc 2.0.7 strptime() is broken - the following snippet causes it to
119 // crash (instead of just failing):
121 // strncpy(buf, "Tue Dec 21 20:25:40 1999", 128);
122 // strptime(buf, "%x", &tm);
126 #endif // broken strptime()
128 #if defined(HAVE_STRPTIME) && defined(__DARWIN__) && defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
129 // configure detects strptime as linkable because it's in the OS X
130 // System library but MSL headers don't declare it.
132 // char *strptime(const char *, const char *, struct tm *);
133 // However, we DON'T want to just provide it here because we would
134 // crash and/or overwrite data when strptime from OS X tries
135 // to fill in MW's struct tm which is two fields shorter (no TZ stuff)
136 // So for now let's just say we don't have strptime
140 #if defined(__MWERKS__) && wxUSE_UNICODE
144 #if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM)
145 #if defined(__WXPALMOS__)
146 #define WX_GMTOFF_IN_TM
147 #elif defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
148 #define WX_TIMEZONE _timezone
149 #elif defined(__MWERKS__)
150 long wxmw_timezone
= 28800;
151 #define WX_TIMEZONE wxmw_timezone
152 #elif defined(__DJGPP__) || defined(__WINE__)
153 #include <sys/timeb.h>
155 static long wxGetTimeZone()
157 static long timezone
= MAXLONG
; // invalid timezone
158 if (timezone
== MAXLONG
)
162 timezone
= tb
.timezone
;
166 #define WX_TIMEZONE wxGetTimeZone()
167 #elif defined(__DARWIN__)
168 #define WX_GMTOFF_IN_TM
169 #else // unknown platform - try timezone
170 #define WX_TIMEZONE timezone
172 #endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
174 #if (!defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)) && wxUSE_THREADS && !defined(__WINDOWS__)
175 static wxMutex timeLock
;
178 #ifndef HAVE_LOCALTIME_R
179 struct tm
*wxLocaltime_r(const time_t* ticks
, struct tm
* temp
)
181 #if wxUSE_THREADS && !defined(__WINDOWS__)
182 // No need to waste time with a mutex on windows since it's using
183 // thread local storage for localtime anyway.
184 wxMutexLocker
locker(timeLock
);
186 memcpy(temp
, localtime(ticks
), sizeof(struct tm
));
191 #ifndef HAVE_GMTIME_R
192 struct tm
*wxGmtime_r(const time_t* ticks
, struct tm
* temp
)
194 #if wxUSE_THREADS && !defined(__WINDOWS__)
195 // No need to waste time with a mutex on windows since it's
196 // using thread local storage for gmtime anyway.
197 wxMutexLocker
locker(timeLock
);
199 memcpy(temp
, gmtime(ticks
), sizeof(struct tm
));
204 // ----------------------------------------------------------------------------
206 // ----------------------------------------------------------------------------
208 // debugging helper: just a convenient replacement of wxCHECK()
209 #define wxDATETIME_CHECK(expr, msg) \
210 wxCHECK2_MSG(expr, *this = wxInvalidDateTime; return *this, msg)
212 // ----------------------------------------------------------------------------
214 // ----------------------------------------------------------------------------
216 class wxDateTimeHolidaysModule
: public wxModule
219 virtual bool OnInit()
221 wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays
);
226 virtual void OnExit()
228 wxDateTimeHolidayAuthority::ClearAllAuthorities();
229 wxDateTimeHolidayAuthority::ms_authorities
.clear();
233 DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule
)
236 IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule
, wxModule
)
238 // ----------------------------------------------------------------------------
240 // ----------------------------------------------------------------------------
243 static const int MONTHS_IN_YEAR
= 12;
245 static const int SEC_PER_MIN
= 60;
247 static const int MIN_PER_HOUR
= 60;
249 static const int HOURS_PER_DAY
= 24;
251 static const long SECONDS_PER_DAY
= 86400l;
253 static const int DAYS_PER_WEEK
= 7;
255 static const long MILLISECONDS_PER_DAY
= 86400000l;
257 // this is the integral part of JDN of the midnight of Jan 1, 1970
258 // (i.e. JDN(Jan 1, 1970) = 2440587.5)
259 static const long EPOCH_JDN
= 2440587l;
261 // used only in asserts
263 // the date of JDN -0.5 (as we don't work with fractional parts, this is the
264 // reference date for us) is Nov 24, 4714BC
265 static const int JDN_0_YEAR
= -4713;
266 static const int JDN_0_MONTH
= wxDateTime::Nov
;
267 static const int JDN_0_DAY
= 24;
268 #endif // __WXDEBUG__
270 // the constants used for JDN calculations
271 static const long JDN_OFFSET
= 32046l;
272 static const long DAYS_PER_5_MONTHS
= 153l;
273 static const long DAYS_PER_4_YEARS
= 1461l;
274 static const long DAYS_PER_400_YEARS
= 146097l;
276 // this array contains the cumulated number of days in all previous months for
277 // normal and leap years
278 static const wxDateTime::wxDateTime_t gs_cumulatedDays
[2][MONTHS_IN_YEAR
] =
280 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
281 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
284 // ----------------------------------------------------------------------------
286 // ----------------------------------------------------------------------------
288 const wxChar
* wxDefaultDateTimeFormat
= wxT("%c");
289 const wxChar
* wxDefaultTimeSpanFormat
= wxT("%H:%M:%S");
291 // in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
292 // indicate an invalid wxDateTime object
293 const wxDateTime wxDefaultDateTime
;
295 wxDateTime::Country
wxDateTime::ms_country
= wxDateTime::Country_Unknown
;
297 // ----------------------------------------------------------------------------
299 // ----------------------------------------------------------------------------
301 // debugger helper: shows what the date really is
303 extern const wxChar
*wxDumpDate(const wxDateTime
* dt
)
305 static wxChar buf
[128];
307 wxStrcpy(buf
, dt
->Format(_T("%Y-%m-%d (%a) %H:%M:%S")));
313 // get the number of days in the given month of the given year
315 wxDateTime::wxDateTime_t
GetNumOfDaysInMonth(int year
, wxDateTime::Month month
)
317 // the number of days in month in Julian/Gregorian calendar: the first line
318 // is for normal years, the second one is for the leap ones
319 static wxDateTime::wxDateTime_t daysInMonth
[2][MONTHS_IN_YEAR
] =
321 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
322 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
325 return daysInMonth
[wxDateTime::IsLeapYear(year
)][month
];
328 // returns the time zone in the C sense, i.e. the difference UTC - local
330 static int GetTimeZone()
332 // set to true when the timezone is set
333 static bool s_timezoneSet
= false;
334 static long gmtoffset
= LONG_MAX
; // invalid timezone
336 // ensure that the timezone variable is set by calling wxLocaltime_r
337 if ( !s_timezoneSet
)
339 // just call wxLocaltime_r() instead of figuring out whether this
340 // system supports tzset(), _tzset() or something else
344 wxLocaltime_r(&t
, &tm
);
345 s_timezoneSet
= true;
347 #ifdef WX_GMTOFF_IN_TM
348 // note that GMT offset is the opposite of time zone and so to return
349 // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM
350 // cases we have to negate it
351 gmtoffset
= -tm
.tm_gmtoff
;
352 #else // !WX_GMTOFF_IN_TM
353 gmtoffset
= WX_TIMEZONE
;
354 #endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
357 return (int)gmtoffset
;
360 // return the integral part of the JDN for the midnight of the given date (to
361 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
362 // noon of the previous day)
363 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day
,
364 wxDateTime::Month mon
,
367 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
369 // check the date validity
371 (year
> JDN_0_YEAR
) ||
372 ((year
== JDN_0_YEAR
) && (mon
> JDN_0_MONTH
)) ||
373 ((year
== JDN_0_YEAR
) && (mon
== JDN_0_MONTH
) && (day
>= JDN_0_DAY
)),
374 _T("date out of range - can't convert to JDN")
377 // make the year positive to avoid problems with negative numbers division
380 // months are counted from March here
382 if ( mon
>= wxDateTime::Mar
)
392 // now we can simply add all the contributions together
393 return ((year
/ 100) * DAYS_PER_400_YEARS
) / 4
394 + ((year
% 100) * DAYS_PER_4_YEARS
) / 4
395 + (month
* DAYS_PER_5_MONTHS
+ 2) / 5
401 // this function is a wrapper around strftime(3) adding error checking
402 static wxString
CallStrftime(const wxChar
*format
, const tm
* tm
)
405 // Create temp wxString here to work around mingw/cygwin bug 1046059
406 // http://sourceforge.net/tracker/?func=detail&atid=102435&aid=1046059&group_id=2435
409 if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) )
411 // buffer is too small?
412 wxFAIL_MSG(_T("strftime() failed"));
422 #if wxUSE_UNIX && !defined(HAVE_STRPTIME_DECL)
423 // configure detected that we had strptime() but not its declaration,
424 // provide it ourselves
425 extern "C" char *strptime(const char *, const char *, struct tm
*);
428 // Unicode-friendly strptime() wrapper
429 static const wxChar
*
430 CallStrptime(const wxChar
*input
, const char *fmt
, tm
*tm
)
432 // the problem here is that strptime() returns pointer into the string we
433 // passed to it while we're really interested in the pointer into the
434 // original, Unicode, string so we try to transform the pointer back
436 wxCharBuffer
inputMB(wxConvertWX2MB(input
));
438 const char * const inputMB
= input
;
439 #endif // Unicode/Ascii
441 const char *result
= strptime(inputMB
, fmt
, tm
);
446 // FIXME: this is wrong in presence of surrogates &c
447 return input
+ (result
- inputMB
.data());
450 #endif // Unicode/Ascii
453 #endif // HAVE_STRPTIME
455 // if year and/or month have invalid values, replace them with the current ones
456 static void ReplaceDefaultYearMonthWithCurrent(int *year
,
457 wxDateTime::Month
*month
)
459 struct tm
*tmNow
= NULL
;
462 if ( *year
== wxDateTime::Inv_Year
)
464 tmNow
= wxDateTime::GetTmNow(&tmstruct
);
466 *year
= 1900 + tmNow
->tm_year
;
469 if ( *month
== wxDateTime::Inv_Month
)
472 tmNow
= wxDateTime::GetTmNow(&tmstruct
);
474 *month
= (wxDateTime::Month
)tmNow
->tm_mon
;
478 // fll the struct tm with default values
479 static void InitTm(struct tm
& tm
)
481 // struct tm may have etxra fields (undocumented and with unportable
482 // names) which, nevertheless, must be set to 0
483 memset(&tm
, 0, sizeof(struct tm
));
485 tm
.tm_mday
= 1; // mday 0 is invalid
486 tm
.tm_year
= 76; // any valid year
487 tm
.tm_isdst
= -1; // auto determine
493 // return the month if the string is a month name or Inv_Month otherwise
494 static wxDateTime::Month
GetMonthFromName(const wxString
& name
, int flags
)
496 wxDateTime::Month mon
;
497 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
499 // case-insensitive comparison either one of or with both abbreviated
501 if ( flags
& wxDateTime::Name_Full
)
503 if ( name
.CmpNoCase(wxDateTime::
504 GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 )
510 if ( flags
& wxDateTime::Name_Abbr
)
512 if ( name
.CmpNoCase(wxDateTime::
513 GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 )
523 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
524 static wxDateTime::WeekDay
GetWeekDayFromName(const wxString
& name
, int flags
)
526 wxDateTime::WeekDay wd
;
527 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
529 // case-insensitive comparison either one of or with both abbreviated
531 if ( flags
& wxDateTime::Name_Full
)
533 if ( name
.CmpNoCase(wxDateTime::
534 GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 )
540 if ( flags
& wxDateTime::Name_Abbr
)
542 if ( name
.CmpNoCase(wxDateTime::
543 GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 )
554 struct tm
*wxDateTime::GetTmNow(struct tm
*tmstruct
)
556 time_t t
= GetTimeNow();
557 return wxLocaltime_r(&t
, tmstruct
);
560 // scans all digits (but no more than len) and returns the resulting number
561 static bool GetNumericToken(size_t len
, const wxChar
*& p
, unsigned long *number
)
565 while ( wxIsdigit(*p
) )
569 if ( len
&& ++n
> len
)
573 return !s
.empty() && s
.ToULong(number
);
576 // scans all alphabetic characters and returns the resulting string
577 static wxString
GetAlphaToken(const wxChar
*& p
)
580 while ( wxIsalpha(*p
) )
588 // ============================================================================
589 // implementation of wxDateTime
590 // ============================================================================
592 // ----------------------------------------------------------------------------
594 // ----------------------------------------------------------------------------
598 year
= (wxDateTime_t
)wxDateTime::Inv_Year
;
599 mon
= wxDateTime::Inv_Month
;
601 hour
= min
= sec
= msec
= 0;
602 wday
= wxDateTime::Inv_WeekDay
;
605 wxDateTime::Tm::Tm(const struct tm
& tm
, const TimeZone
& tz
)
609 sec
= (wxDateTime::wxDateTime_t
)tm
.tm_sec
;
610 min
= (wxDateTime::wxDateTime_t
)tm
.tm_min
;
611 hour
= (wxDateTime::wxDateTime_t
)tm
.tm_hour
;
612 mday
= (wxDateTime::wxDateTime_t
)tm
.tm_mday
;
613 mon
= (wxDateTime::Month
)tm
.tm_mon
;
614 year
= 1900 + tm
.tm_year
;
615 wday
= (wxDateTime::wxDateTime_t
)tm
.tm_wday
;
616 yday
= (wxDateTime::wxDateTime_t
)tm
.tm_yday
;
619 bool wxDateTime::Tm::IsValid() const
621 // we allow for the leap seconds, although we don't use them (yet)
622 return (year
!= wxDateTime::Inv_Year
) && (mon
!= wxDateTime::Inv_Month
) &&
623 (mday
<= GetNumOfDaysInMonth(year
, mon
)) &&
624 (hour
< 24) && (min
< 60) && (sec
< 62) && (msec
< 1000);
627 void wxDateTime::Tm::ComputeWeekDay()
629 // compute the week day from day/month/year: we use the dumbest algorithm
630 // possible: just compute our JDN and then use the (simple to derive)
631 // formula: weekday = (JDN + 1.5) % 7
632 wday
= (wxDateTime::wxDateTime_t
)((wxDateTime::WeekDay
)(GetTruncatedJDN(mday
, mon
, year
) + 2) % 7);
635 void wxDateTime::Tm::AddMonths(int monDiff
)
637 // normalize the months field
638 while ( monDiff
< -mon
)
642 monDiff
+= MONTHS_IN_YEAR
;
645 while ( monDiff
+ mon
>= MONTHS_IN_YEAR
)
649 monDiff
-= MONTHS_IN_YEAR
;
652 mon
= (wxDateTime::Month
)(mon
+ monDiff
);
654 wxASSERT_MSG( mon
>= 0 && mon
< MONTHS_IN_YEAR
, _T("logic error") );
656 // NB: we don't check here that the resulting date is valid, this function
657 // is private and the caller must check it if needed
660 void wxDateTime::Tm::AddDays(int dayDiff
)
662 // normalize the days field
663 while ( dayDiff
+ mday
< 1 )
667 dayDiff
+= GetNumOfDaysInMonth(year
, mon
);
670 mday
= (wxDateTime::wxDateTime_t
)( mday
+ dayDiff
);
671 while ( mday
> GetNumOfDaysInMonth(year
, mon
) )
673 mday
-= GetNumOfDaysInMonth(year
, mon
);
678 wxASSERT_MSG( mday
> 0 && mday
<= GetNumOfDaysInMonth(year
, mon
),
682 // ----------------------------------------------------------------------------
684 // ----------------------------------------------------------------------------
686 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz
)
690 case wxDateTime::Local
:
691 // get the offset from C RTL: it returns the difference GMT-local
692 // while we want to have the offset _from_ GMT, hence the '-'
693 m_offset
= -GetTimeZone();
696 case wxDateTime::GMT_12
:
697 case wxDateTime::GMT_11
:
698 case wxDateTime::GMT_10
:
699 case wxDateTime::GMT_9
:
700 case wxDateTime::GMT_8
:
701 case wxDateTime::GMT_7
:
702 case wxDateTime::GMT_6
:
703 case wxDateTime::GMT_5
:
704 case wxDateTime::GMT_4
:
705 case wxDateTime::GMT_3
:
706 case wxDateTime::GMT_2
:
707 case wxDateTime::GMT_1
:
708 m_offset
= -3600*(wxDateTime::GMT0
- tz
);
711 case wxDateTime::GMT0
:
712 case wxDateTime::GMT1
:
713 case wxDateTime::GMT2
:
714 case wxDateTime::GMT3
:
715 case wxDateTime::GMT4
:
716 case wxDateTime::GMT5
:
717 case wxDateTime::GMT6
:
718 case wxDateTime::GMT7
:
719 case wxDateTime::GMT8
:
720 case wxDateTime::GMT9
:
721 case wxDateTime::GMT10
:
722 case wxDateTime::GMT11
:
723 case wxDateTime::GMT12
:
724 case wxDateTime::GMT13
:
725 m_offset
= 3600*(tz
- wxDateTime::GMT0
);
728 case wxDateTime::A_CST
:
729 // Central Standard Time in use in Australia = UTC + 9.5
730 m_offset
= 60l*(9*MIN_PER_HOUR
+ MIN_PER_HOUR
/2);
734 wxFAIL_MSG( _T("unknown time zone") );
738 // ----------------------------------------------------------------------------
740 // ----------------------------------------------------------------------------
743 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
)
745 if ( year
== Inv_Year
)
746 year
= GetCurrentYear();
748 if ( cal
== Gregorian
)
750 // in Gregorian calendar leap years are those divisible by 4 except
751 // those divisible by 100 unless they're also divisible by 400
752 // (in some countries, like Russia and Greece, additional corrections
753 // exist, but they won't manifest themselves until 2700)
754 return (year
% 4 == 0) && ((year
% 100 != 0) || (year
% 400 == 0));
756 else if ( cal
== Julian
)
758 // in Julian calendar the rule is simpler
759 return year
% 4 == 0;
763 wxFAIL_MSG(_T("unknown calendar"));
770 int wxDateTime::GetCentury(int year
)
772 return year
> 0 ? year
/ 100 : year
/ 100 - 1;
776 int wxDateTime::ConvertYearToBC(int year
)
779 return year
> 0 ? year
: year
- 1;
783 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
)
788 return Now().GetYear();
791 wxFAIL_MSG(_T("TODO"));
795 wxFAIL_MSG(_T("unsupported calendar"));
803 wxDateTime::Month
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
)
808 return Now().GetMonth();
811 wxFAIL_MSG(_T("TODO"));
815 wxFAIL_MSG(_T("unsupported calendar"));
823 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(int year
, Calendar cal
)
825 if ( year
== Inv_Year
)
827 // take the current year if none given
828 year
= GetCurrentYear();
835 return IsLeapYear(year
) ? 366 : 365;
838 wxFAIL_MSG(_T("unsupported calendar"));
846 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(wxDateTime::Month month
,
848 wxDateTime::Calendar cal
)
850 wxCHECK_MSG( month
< MONTHS_IN_YEAR
, 0, _T("invalid month") );
852 if ( cal
== Gregorian
|| cal
== Julian
)
854 if ( year
== Inv_Year
)
856 // take the current year if none given
857 year
= GetCurrentYear();
860 return GetNumOfDaysInMonth(year
, month
);
864 wxFAIL_MSG(_T("unsupported calendar"));
871 wxString
wxDateTime::GetMonthName(wxDateTime::Month month
,
872 wxDateTime::NameFlags flags
)
874 wxCHECK_MSG( month
!= Inv_Month
, wxEmptyString
, _T("invalid month") );
876 // notice that we must set all the fields to avoid confusing libc (GNU one
877 // gets confused to a crash if we don't do this)
882 return CallStrftime(flags
== Name_Abbr
? _T("%b") : _T("%B"), &tm
);
888 ret
= (flags
== Name_Abbr
? wxT("Jan"): wxT("January"));
891 ret
= (flags
== Name_Abbr
? wxT("Feb"): wxT("Febuary"));
894 ret
= (flags
== Name_Abbr
? wxT("Mar"): wxT("March"));
897 ret
= (flags
== Name_Abbr
? wxT("Apr"): wxT("April"));
900 ret
= (flags
== Name_Abbr
? wxT("May"): wxT("May"));
903 ret
= (flags
== Name_Abbr
? wxT("Jun"): wxT("June"));
906 ret
= (flags
== Name_Abbr
? wxT("Jul"): wxT("July"));
909 ret
= (flags
== Name_Abbr
? wxT("Aug"): wxT("August"));
912 ret
= (flags
== Name_Abbr
? wxT("Sep"): wxT("September"));
915 ret
= (flags
== Name_Abbr
? wxT("Oct"): wxT("October"));
918 ret
= (flags
== Name_Abbr
? wxT("Nov"): wxT("November"));
921 ret
= (flags
== Name_Abbr
? wxT("Dec"): wxT("December"));
929 wxString
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
,
930 wxDateTime::NameFlags flags
)
932 wxCHECK_MSG( wday
!= Inv_WeekDay
, wxEmptyString
, _T("invalid weekday") );
934 // take some arbitrary Sunday (but notice that the day should be such that
935 // after adding wday to it below we still have a valid date, e.g. don't
943 // and offset it by the number of days needed to get the correct wday
946 // call mktime() to normalize it...
949 // ... and call strftime()
950 return CallStrftime(flags
== Name_Abbr
? _T("%a") : _T("%A"), &tm
);
956 ret
= (flags
== Name_Abbr
? wxT("Sun") : wxT("Sunday"));
959 ret
= (flags
== Name_Abbr
? wxT("Mon") : wxT("Monday"));
962 ret
= (flags
== Name_Abbr
? wxT("Tue") : wxT("Tuesday"));
965 ret
= (flags
== Name_Abbr
? wxT("Wed") : wxT("Wednesday"));
968 ret
= (flags
== Name_Abbr
? wxT("Thu") : wxT("Thursday"));
971 ret
= (flags
== Name_Abbr
? wxT("Fri") : wxT("Friday"));
974 ret
= (flags
== Name_Abbr
? wxT("Sat") : wxT("Saturday"));
983 void wxDateTime::GetAmPmStrings(wxString
*am
, wxString
*pm
)
988 // @Note: Do not call 'CallStrftime' here! CallStrftime checks the return code
989 // and causes an assertion failed if the buffer is to small (which is good) - OR -
990 // if strftime does not return anything because the format string is invalid - OR -
991 // if there are no 'am' / 'pm' tokens defined for the current locale (which is not good).
992 // wxDateTime::ParseTime will try several different formats to parse the time.
993 // As a result, GetAmPmStrings might get called, even if the current locale
994 // does not define any 'am' / 'pm' tokens. In this case, wxStrftime would
995 // assert, even though it is a perfectly legal use.
998 if (wxStrftime(buffer
, sizeof(buffer
)/sizeof(wxChar
), _T("%p"), &tm
) > 0)
999 *am
= wxString(buffer
);
1006 if (wxStrftime(buffer
, sizeof(buffer
)/sizeof(wxChar
), _T("%p"), &tm
) > 0)
1007 *pm
= wxString(buffer
);
1013 // ----------------------------------------------------------------------------
1014 // Country stuff: date calculations depend on the country (DST, work days,
1015 // ...), so we need to know which rules to follow.
1016 // ----------------------------------------------------------------------------
1019 wxDateTime::Country
wxDateTime::GetCountry()
1021 // TODO use LOCALE_ICOUNTRY setting under Win32
1023 if ( ms_country
== Country_Unknown
)
1025 // try to guess from the time zone name
1026 time_t t
= time(NULL
);
1028 struct tm
*tm
= wxLocaltime_r(&t
, &tmstruct
);
1030 wxString tz
= CallStrftime(_T("%Z"), tm
);
1031 if ( tz
== _T("WET") || tz
== _T("WEST") )
1035 else if ( tz
== _T("CET") || tz
== _T("CEST") )
1037 ms_country
= Country_EEC
;
1039 else if ( tz
== _T("MSK") || tz
== _T("MSD") )
1041 ms_country
= Russia
;
1043 else if ( tz
== _T("AST") || tz
== _T("ADT") ||
1044 tz
== _T("EST") || tz
== _T("EDT") ||
1045 tz
== _T("CST") || tz
== _T("CDT") ||
1046 tz
== _T("MST") || tz
== _T("MDT") ||
1047 tz
== _T("PST") || tz
== _T("PDT") )
1053 // well, choose a default one
1065 void wxDateTime::SetCountry(wxDateTime::Country country
)
1067 ms_country
= country
;
1071 bool wxDateTime::IsWestEuropeanCountry(Country country
)
1073 if ( country
== Country_Default
)
1075 country
= GetCountry();
1078 return (Country_WesternEurope_Start
<= country
) &&
1079 (country
<= Country_WesternEurope_End
);
1082 // ----------------------------------------------------------------------------
1083 // DST calculations: we use 3 different rules for the West European countries,
1084 // USA and for the rest of the world. This is undoubtedly false for many
1085 // countries, but I lack the necessary info (and the time to gather it),
1086 // please add the other rules here!
1087 // ----------------------------------------------------------------------------
1090 bool wxDateTime::IsDSTApplicable(int year
, Country country
)
1092 if ( year
== Inv_Year
)
1094 // take the current year if none given
1095 year
= GetCurrentYear();
1098 if ( country
== Country_Default
)
1100 country
= GetCountry();
1107 // DST was first observed in the US and UK during WWI, reused
1108 // during WWII and used again since 1966
1109 return year
>= 1966 ||
1110 (year
>= 1942 && year
<= 1945) ||
1111 (year
== 1918 || year
== 1919);
1114 // assume that it started after WWII
1120 wxDateTime
wxDateTime::GetBeginDST(int year
, Country country
)
1122 if ( year
== Inv_Year
)
1124 // take the current year if none given
1125 year
= GetCurrentYear();
1128 if ( country
== Country_Default
)
1130 country
= GetCountry();
1133 if ( !IsDSTApplicable(year
, country
) )
1135 return wxInvalidDateTime
;
1140 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1142 // DST begins at 1 a.m. GMT on the last Sunday of March
1143 if ( !dt
.SetToLastWeekDay(Sun
, Mar
, year
) )
1146 wxFAIL_MSG( _T("no last Sunday in March?") );
1149 dt
+= wxTimeSpan::Hours(1);
1151 // disable DST tests because it could result in an infinite recursion!
1154 else switch ( country
)
1161 // don't know for sure - assume it was in effect all year
1166 dt
.Set(1, Jan
, year
);
1170 // DST was installed Feb 2, 1942 by the Congress
1171 dt
.Set(2, Feb
, year
);
1174 // Oil embargo changed the DST period in the US
1176 dt
.Set(6, Jan
, 1974);
1180 dt
.Set(23, Feb
, 1975);
1184 // before 1986, DST begun on the last Sunday of April, but
1185 // in 1986 Reagan changed it to begin at 2 a.m. of the
1186 // first Sunday in April
1189 if ( !dt
.SetToLastWeekDay(Sun
, Apr
, year
) )
1192 wxFAIL_MSG( _T("no first Sunday in April?") );
1197 if ( !dt
.SetToWeekDay(Sun
, 1, Apr
, year
) )
1200 wxFAIL_MSG( _T("no first Sunday in April?") );
1204 dt
+= wxTimeSpan::Hours(2);
1206 // TODO what about timezone??
1212 // assume Mar 30 as the start of the DST for the rest of the world
1213 // - totally bogus, of course
1214 dt
.Set(30, Mar
, year
);
1221 wxDateTime
wxDateTime::GetEndDST(int year
, Country country
)
1223 if ( year
== Inv_Year
)
1225 // take the current year if none given
1226 year
= GetCurrentYear();
1229 if ( country
== Country_Default
)
1231 country
= GetCountry();
1234 if ( !IsDSTApplicable(year
, country
) )
1236 return wxInvalidDateTime
;
1241 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1243 // DST ends at 1 a.m. GMT on the last Sunday of October
1244 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1246 // weirder and weirder...
1247 wxFAIL_MSG( _T("no last Sunday in October?") );
1250 dt
+= wxTimeSpan::Hours(1);
1252 // disable DST tests because it could result in an infinite recursion!
1255 else switch ( country
)
1262 // don't know for sure - assume it was in effect all year
1266 dt
.Set(31, Dec
, year
);
1270 // the time was reset after the end of the WWII
1271 dt
.Set(30, Sep
, year
);
1275 // DST ends at 2 a.m. on the last Sunday of October
1276 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1278 // weirder and weirder...
1279 wxFAIL_MSG( _T("no last Sunday in October?") );
1282 dt
+= wxTimeSpan::Hours(2);
1284 // TODO what about timezone??
1289 // assume October 26th as the end of the DST - totally bogus too
1290 dt
.Set(26, Oct
, year
);
1296 // ----------------------------------------------------------------------------
1297 // constructors and assignment operators
1298 // ----------------------------------------------------------------------------
1300 // return the current time with ms precision
1301 /* static */ wxDateTime
wxDateTime::UNow()
1303 return wxDateTime(wxGetLocalTimeMillis());
1306 // the values in the tm structure contain the local time
1307 wxDateTime
& wxDateTime::Set(const struct tm
& tm
)
1310 time_t timet
= mktime(&tm2
);
1312 if ( timet
== (time_t)-1 )
1314 // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
1315 // less than timezone - try to make it work for this case
1316 if ( tm2
.tm_year
== 70 && tm2
.tm_mon
== 0 && tm2
.tm_mday
== 1 )
1318 return Set((time_t)(
1320 tm2
.tm_hour
* MIN_PER_HOUR
* SEC_PER_MIN
+
1321 tm2
.tm_min
* SEC_PER_MIN
+
1325 wxFAIL_MSG( _T("mktime() failed") );
1327 *this = wxInvalidDateTime
;
1337 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
,
1338 wxDateTime_t minute
,
1339 wxDateTime_t second
,
1340 wxDateTime_t millisec
)
1342 // we allow seconds to be 61 to account for the leap seconds, even if we
1343 // don't use them really
1344 wxDATETIME_CHECK( hour
< 24 &&
1348 _T("Invalid time in wxDateTime::Set()") );
1350 // get the current date from system
1352 struct tm
*tm
= GetTmNow(&tmstruct
);
1354 wxDATETIME_CHECK( tm
, _T("wxLocaltime_r() failed") );
1356 // make a copy so it isn't clobbered by the call to mktime() below
1361 tm1
.tm_min
= minute
;
1362 tm1
.tm_sec
= second
;
1364 // and the DST in case it changes on this date
1367 if ( tm2
.tm_isdst
!= tm1
.tm_isdst
)
1368 tm1
.tm_isdst
= tm2
.tm_isdst
;
1372 // and finally adjust milliseconds
1373 return SetMillisecond(millisec
);
1376 wxDateTime
& wxDateTime::Set(wxDateTime_t day
,
1380 wxDateTime_t minute
,
1381 wxDateTime_t second
,
1382 wxDateTime_t millisec
)
1384 wxDATETIME_CHECK( hour
< 24 &&
1388 _T("Invalid time in wxDateTime::Set()") );
1390 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1392 wxDATETIME_CHECK( (0 < day
) && (day
<= GetNumberOfDays(month
, year
)),
1393 _T("Invalid date in wxDateTime::Set()") );
1395 // the range of time_t type (inclusive)
1396 static const int yearMinInRange
= 1970;
1397 static const int yearMaxInRange
= 2037;
1399 // test only the year instead of testing for the exact end of the Unix
1400 // time_t range - it doesn't bring anything to do more precise checks
1401 if ( year
>= yearMinInRange
&& year
<= yearMaxInRange
)
1403 // use the standard library version if the date is in range - this is
1404 // probably more efficient than our code
1406 tm
.tm_year
= year
- 1900;
1412 tm
.tm_isdst
= -1; // mktime() will guess it
1416 // and finally adjust milliseconds
1418 SetMillisecond(millisec
);
1424 // do time calculations ourselves: we want to calculate the number of
1425 // milliseconds between the given date and the epoch
1427 // get the JDN for the midnight of this day
1428 m_time
= GetTruncatedJDN(day
, month
, year
);
1429 m_time
-= EPOCH_JDN
;
1430 m_time
*= SECONDS_PER_DAY
* TIME_T_FACTOR
;
1432 // JDN corresponds to GMT, we take localtime
1433 Add(wxTimeSpan(hour
, minute
, second
+ GetTimeZone(), millisec
));
1439 wxDateTime
& wxDateTime::Set(double jdn
)
1441 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1443 jdn
-= EPOCH_JDN
+ 0.5;
1445 m_time
.Assign(jdn
*MILLISECONDS_PER_DAY
);
1447 // JDNs always are in UTC, so we don't need any adjustments for time zone
1452 wxDateTime
& wxDateTime::ResetTime()
1456 if ( tm
.hour
|| tm
.min
|| tm
.sec
|| tm
.msec
)
1469 // ----------------------------------------------------------------------------
1470 // DOS Date and Time Format functions
1471 // ----------------------------------------------------------------------------
1472 // the dos date and time value is an unsigned 32 bit value in the format:
1473 // YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
1475 // Y = year offset from 1980 (0-127)
1477 // D = day of month (1-31)
1479 // m = minute (0-59)
1480 // s = bisecond (0-29) each bisecond indicates two seconds
1481 // ----------------------------------------------------------------------------
1483 wxDateTime
& wxDateTime::SetFromDOS(unsigned long ddt
)
1488 long year
= ddt
& 0xFE000000;
1493 long month
= ddt
& 0x1E00000;
1498 long day
= ddt
& 0x1F0000;
1502 long hour
= ddt
& 0xF800;
1506 long minute
= ddt
& 0x7E0;
1510 long second
= ddt
& 0x1F;
1511 tm
.tm_sec
= second
* 2;
1513 return Set(mktime(&tm
));
1516 unsigned long wxDateTime::GetAsDOS() const
1519 time_t ticks
= GetTicks();
1521 struct tm
*tm
= wxLocaltime_r(&ticks
, &tmstruct
);
1523 long year
= tm
->tm_year
;
1527 long month
= tm
->tm_mon
;
1531 long day
= tm
->tm_mday
;
1534 long hour
= tm
->tm_hour
;
1537 long minute
= tm
->tm_min
;
1540 long second
= tm
->tm_sec
;
1543 ddt
= year
| month
| day
| hour
| minute
| second
;
1547 // ----------------------------------------------------------------------------
1548 // time_t <-> broken down time conversions
1549 // ----------------------------------------------------------------------------
1551 wxDateTime::Tm
wxDateTime::GetTm(const TimeZone
& tz
) const
1553 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1555 time_t time
= GetTicks();
1556 if ( time
!= (time_t)-1 )
1558 // use C RTL functions
1561 if ( tz
.GetOffset() == -GetTimeZone() )
1563 // we are working with local time
1564 tm
= wxLocaltime_r(&time
, &tmstruct
);
1566 // should never happen
1567 wxCHECK_MSG( tm
, Tm(), _T("wxLocaltime_r() failed") );
1571 time
+= (time_t)tz
.GetOffset();
1572 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
1573 int time2
= (int) time
;
1579 tm
= wxGmtime_r(&time
, &tmstruct
);
1581 // should never happen
1582 wxCHECK_MSG( tm
, Tm(), _T("wxGmtime_r() failed") );
1586 tm
= (struct tm
*)NULL
;
1592 // adjust the milliseconds
1594 long timeOnly
= (m_time
% MILLISECONDS_PER_DAY
).ToLong();
1595 tm2
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1598 //else: use generic code below
1601 // remember the time and do the calculations with the date only - this
1602 // eliminates rounding errors of the floating point arithmetics
1604 wxLongLong timeMidnight
= m_time
+ tz
.GetOffset() * 1000;
1606 long timeOnly
= (timeMidnight
% MILLISECONDS_PER_DAY
).ToLong();
1608 // we want to always have positive time and timeMidnight to be really
1609 // the midnight before it
1612 timeOnly
= MILLISECONDS_PER_DAY
+ timeOnly
;
1615 timeMidnight
-= timeOnly
;
1617 // calculate the Gregorian date from JDN for the midnight of our date:
1618 // this will yield day, month (in 1..12 range) and year
1620 // actually, this is the JDN for the noon of the previous day
1621 long jdn
= (timeMidnight
/ MILLISECONDS_PER_DAY
).ToLong() + EPOCH_JDN
;
1623 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1625 wxASSERT_MSG( jdn
> -2, _T("JDN out of range") );
1627 // calculate the century
1628 long temp
= (jdn
+ JDN_OFFSET
) * 4 - 1;
1629 long century
= temp
/ DAYS_PER_400_YEARS
;
1631 // then the year and day of year (1 <= dayOfYear <= 366)
1632 temp
= ((temp
% DAYS_PER_400_YEARS
) / 4) * 4 + 3;
1633 long year
= (century
* 100) + (temp
/ DAYS_PER_4_YEARS
);
1634 long dayOfYear
= (temp
% DAYS_PER_4_YEARS
) / 4 + 1;
1636 // and finally the month and day of the month
1637 temp
= dayOfYear
* 5 - 3;
1638 long month
= temp
/ DAYS_PER_5_MONTHS
;
1639 long day
= (temp
% DAYS_PER_5_MONTHS
) / 5 + 1;
1641 // month is counted from March - convert to normal
1652 // year is offset by 4800
1655 // check that the algorithm gave us something reasonable
1656 wxASSERT_MSG( (0 < month
) && (month
<= 12), _T("invalid month") );
1657 wxASSERT_MSG( (1 <= day
) && (day
< 32), _T("invalid day") );
1659 // construct Tm from these values
1661 tm
.year
= (int)year
;
1662 tm
.mon
= (Month
)(month
- 1); // algorithm yields 1 for January, not 0
1663 tm
.mday
= (wxDateTime_t
)day
;
1664 tm
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1665 timeOnly
-= tm
.msec
;
1666 timeOnly
/= 1000; // now we have time in seconds
1668 tm
.sec
= (wxDateTime_t
)(timeOnly
% SEC_PER_MIN
);
1670 timeOnly
/= SEC_PER_MIN
; // now we have time in minutes
1672 tm
.min
= (wxDateTime_t
)(timeOnly
% MIN_PER_HOUR
);
1675 tm
.hour
= (wxDateTime_t
)(timeOnly
/ MIN_PER_HOUR
);
1680 wxDateTime
& wxDateTime::SetYear(int year
)
1682 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1691 wxDateTime
& wxDateTime::SetMonth(Month month
)
1693 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1702 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
)
1704 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1713 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
)
1715 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1724 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
)
1726 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1735 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
)
1737 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1746 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
)
1748 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1750 // we don't need to use GetTm() for this one
1751 m_time
-= m_time
% 1000l;
1752 m_time
+= millisecond
;
1757 // ----------------------------------------------------------------------------
1758 // wxDateTime arithmetics
1759 // ----------------------------------------------------------------------------
1761 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
)
1765 tm
.year
+= diff
.GetYears();
1766 tm
.AddMonths(diff
.GetMonths());
1768 // check that the resulting date is valid
1769 if ( tm
.mday
> GetNumOfDaysInMonth(tm
.year
, tm
.mon
) )
1771 // We suppose that when adding one month to Jan 31 we want to get Feb
1772 // 28 (or 29), i.e. adding a month to the last day of the month should
1773 // give the last day of the next month which is quite logical.
1775 // Unfortunately, there is no logic way to understand what should
1776 // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1777 // We make it Feb 28 (last day too), but it is highly questionable.
1778 tm
.mday
= GetNumOfDaysInMonth(tm
.year
, tm
.mon
);
1781 tm
.AddDays(diff
.GetTotalDays());
1785 wxASSERT_MSG( IsSameTime(tm
),
1786 _T("Add(wxDateSpan) shouldn't modify time") );
1791 // ----------------------------------------------------------------------------
1792 // Weekday and monthday stuff
1793 // ----------------------------------------------------------------------------
1795 // convert Sun, Mon, ..., Sat into 6, 0, ..., 5
1796 static inline int ConvertWeekDayToMondayBase(int wd
)
1798 return wd
== wxDateTime::Sun
? 6 : wd
- 1;
1803 wxDateTime::SetToWeekOfYear(int year
, wxDateTime_t numWeek
, WeekDay wd
)
1805 wxASSERT_MSG( numWeek
> 0,
1806 _T("invalid week number: weeks are counted from 1") );
1808 // Jan 4 always lies in the 1st week of the year
1809 wxDateTime
dt(4, Jan
, year
);
1810 dt
.SetToWeekDayInSameWeek(wd
);
1811 dt
+= wxDateSpan::Weeks(numWeek
- 1);
1816 #if WXWIN_COMPATIBILITY_2_6
1817 // use a separate function to avoid warnings about using deprecated
1818 // SetToTheWeek in GetWeek below
1820 SetToTheWeek(int year
,
1821 wxDateTime::wxDateTime_t numWeek
,
1822 wxDateTime::WeekDay weekday
,
1823 wxDateTime::WeekFlags flags
)
1825 // Jan 4 always lies in the 1st week of the year
1826 wxDateTime
dt(4, wxDateTime::Jan
, year
);
1827 dt
.SetToWeekDayInSameWeek(weekday
, flags
);
1828 dt
+= wxDateSpan::Weeks(numWeek
- 1);
1833 bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek
,
1837 int year
= GetYear();
1838 *this = ::SetToTheWeek(year
, numWeek
, weekday
, flags
);
1839 if ( GetYear() != year
)
1841 // oops... numWeek was too big
1848 wxDateTime
wxDateTime::GetWeek(wxDateTime_t numWeek
,
1850 WeekFlags flags
) const
1852 return ::SetToTheWeek(GetYear(), numWeek
, weekday
, flags
);
1854 #endif // WXWIN_COMPATIBILITY_2_6
1856 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
,
1859 // take the current month/year if none specified
1860 if ( year
== Inv_Year
)
1862 if ( month
== Inv_Month
)
1865 return Set(GetNumOfDaysInMonth(year
, month
), month
, year
);
1868 wxDateTime
& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday
, WeekFlags flags
)
1870 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1872 int wdayDst
= weekday
,
1873 wdayThis
= GetWeekDay();
1874 if ( wdayDst
== wdayThis
)
1880 if ( flags
== Default_First
)
1882 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
1885 // the logic below based on comparing weekday and wdayThis works if Sun (0)
1886 // is the first day in the week, but breaks down for Monday_First case so
1887 // we adjust the week days in this case
1888 if ( flags
== Monday_First
)
1890 if ( wdayThis
== Sun
)
1892 if ( wdayDst
== Sun
)
1895 //else: Sunday_First, nothing to do
1897 // go forward or back in time to the day we want
1898 if ( wdayDst
< wdayThis
)
1900 return Subtract(wxDateSpan::Days(wdayThis
- wdayDst
));
1902 else // weekday > wdayThis
1904 return Add(wxDateSpan::Days(wdayDst
- wdayThis
));
1908 wxDateTime
& wxDateTime::SetToNextWeekDay(WeekDay weekday
)
1910 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1913 WeekDay wdayThis
= GetWeekDay();
1914 if ( weekday
== wdayThis
)
1919 else if ( weekday
< wdayThis
)
1921 // need to advance a week
1922 diff
= 7 - (wdayThis
- weekday
);
1924 else // weekday > wdayThis
1926 diff
= weekday
- wdayThis
;
1929 return Add(wxDateSpan::Days(diff
));
1932 wxDateTime
& wxDateTime::SetToPrevWeekDay(WeekDay weekday
)
1934 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1937 WeekDay wdayThis
= GetWeekDay();
1938 if ( weekday
== wdayThis
)
1943 else if ( weekday
> wdayThis
)
1945 // need to go to previous week
1946 diff
= 7 - (weekday
- wdayThis
);
1948 else // weekday < wdayThis
1950 diff
= wdayThis
- weekday
;
1953 return Subtract(wxDateSpan::Days(diff
));
1956 bool wxDateTime::SetToWeekDay(WeekDay weekday
,
1961 wxCHECK_MSG( weekday
!= Inv_WeekDay
, false, _T("invalid weekday") );
1963 // we don't check explicitly that -5 <= n <= 5 because we will return false
1964 // anyhow in such case - but may be should still give an assert for it?
1966 // take the current month/year if none specified
1967 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1971 // TODO this probably could be optimised somehow...
1975 // get the first day of the month
1976 dt
.Set(1, month
, year
);
1979 WeekDay wdayFirst
= dt
.GetWeekDay();
1981 // go to the first weekday of the month
1982 int diff
= weekday
- wdayFirst
;
1986 // add advance n-1 weeks more
1989 dt
+= wxDateSpan::Days(diff
);
1991 else // count from the end of the month
1993 // get the last day of the month
1994 dt
.SetToLastMonthDay(month
, year
);
1997 WeekDay wdayLast
= dt
.GetWeekDay();
1999 // go to the last weekday of the month
2000 int diff
= wdayLast
- weekday
;
2004 // and rewind n-1 weeks from there
2007 dt
-= wxDateSpan::Days(diff
);
2010 // check that it is still in the same month
2011 if ( dt
.GetMonth() == month
)
2019 // no such day in this month
2025 wxDateTime::wxDateTime_t
GetDayOfYearFromTm(const wxDateTime::Tm
& tm
)
2027 return (wxDateTime::wxDateTime_t
)(gs_cumulatedDays
[wxDateTime::IsLeapYear(tm
.year
)][tm
.mon
] + tm
.mday
);
2030 wxDateTime::wxDateTime_t
wxDateTime::GetDayOfYear(const TimeZone
& tz
) const
2032 return GetDayOfYearFromTm(GetTm(tz
));
2035 wxDateTime::wxDateTime_t
2036 wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags
, const TimeZone
& tz
) const
2038 if ( flags
== Default_First
)
2040 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
2044 wxDateTime_t nDayInYear
= GetDayOfYearFromTm(tm
);
2046 int wdTarget
= GetWeekDay(tz
);
2047 int wdYearStart
= wxDateTime(1, Jan
, GetYear()).GetWeekDay();
2049 if ( flags
== Sunday_First
)
2051 // FIXME: First week is not calculated correctly.
2052 week
= (nDayInYear
- wdTarget
+ 7) / 7;
2053 if ( wdYearStart
== Wed
|| wdYearStart
== Thu
)
2056 else // week starts with monday
2058 // adjust the weekdays to non-US style.
2059 wdYearStart
= ConvertWeekDayToMondayBase(wdYearStart
);
2060 wdTarget
= ConvertWeekDayToMondayBase(wdTarget
);
2062 // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html:
2064 // Week 01 of a year is per definition the first week that has the
2065 // Thursday in this year, which is equivalent to the week that
2066 // contains the fourth day of January. In other words, the first
2067 // week of a new year is the week that has the majority of its
2068 // days in the new year. Week 01 might also contain days from the
2069 // previous year and the week before week 01 of a year is the last
2070 // week (52 or 53) of the previous year even if it contains days
2071 // from the new year. A week starts with Monday (day 1) and ends
2072 // with Sunday (day 7).
2075 // if Jan 1 is Thursday or less, it is in the first week of this year
2076 if ( wdYearStart
< 4 )
2078 // count the number of entire weeks between Jan 1 and this date
2079 week
= (nDayInYear
+ wdYearStart
+ 6 - wdTarget
)/7;
2081 // be careful to check for overflow in the next year
2082 if ( week
== 53 && tm
.mday
- wdTarget
> 28 )
2085 else // Jan 1 is in the last week of the previous year
2087 // check if we happen to be at the last week of previous year:
2088 if ( tm
.mon
== Jan
&& tm
.mday
< 8 - wdYearStart
)
2089 week
= wxDateTime(31, Dec
, GetYear()-1).GetWeekOfYear();
2091 week
= (nDayInYear
+ wdYearStart
- 1 - wdTarget
)/7;
2095 return (wxDateTime::wxDateTime_t
)week
;
2098 wxDateTime::wxDateTime_t
wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags
,
2099 const TimeZone
& tz
) const
2102 wxDateTime dtMonthStart
= wxDateTime(1, tm
.mon
, tm
.year
);
2103 int nWeek
= GetWeekOfYear(flags
) - dtMonthStart
.GetWeekOfYear(flags
) + 1;
2106 // this may happen for January when Jan, 1 is the last week of the
2108 nWeek
+= IsLeapYear(tm
.year
- 1) ? 53 : 52;
2111 return (wxDateTime::wxDateTime_t
)nWeek
;
2114 wxDateTime
& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday
)
2116 int year
= GetYear();
2117 wxDATETIME_CHECK( (0 < yday
) && (yday
<= GetNumberOfDays(year
)),
2118 _T("invalid year day") );
2120 bool isLeap
= IsLeapYear(year
);
2121 for ( Month mon
= Jan
; mon
< Inv_Month
; wxNextMonth(mon
) )
2123 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
2124 // don't need it neither - because of the CHECK above we know that
2125 // yday lies in December then
2126 if ( (mon
== Dec
) || (yday
<= gs_cumulatedDays
[isLeap
][mon
+ 1]) )
2128 Set((wxDateTime::wxDateTime_t
)(yday
- gs_cumulatedDays
[isLeap
][mon
]), mon
, year
);
2137 // ----------------------------------------------------------------------------
2138 // Julian day number conversion and related stuff
2139 // ----------------------------------------------------------------------------
2141 double wxDateTime::GetJulianDayNumber() const
2143 return m_time
.ToDouble() / MILLISECONDS_PER_DAY
+ EPOCH_JDN
+ 0.5;
2146 double wxDateTime::GetRataDie() const
2148 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
2149 return GetJulianDayNumber() - 1721119.5 - 306;
2152 // ----------------------------------------------------------------------------
2153 // timezone and DST stuff
2154 // ----------------------------------------------------------------------------
2156 int wxDateTime::IsDST(wxDateTime::Country country
) const
2158 wxCHECK_MSG( country
== Country_Default
, -1,
2159 _T("country support not implemented") );
2161 // use the C RTL for the dates in the standard range
2162 time_t timet
= GetTicks();
2163 if ( timet
!= (time_t)-1 )
2166 tm
*tm
= wxLocaltime_r(&timet
, &tmstruct
);
2168 wxCHECK_MSG( tm
, -1, _T("wxLocaltime_r() failed") );
2170 return tm
->tm_isdst
;
2174 int year
= GetYear();
2176 if ( !IsDSTApplicable(year
, country
) )
2178 // no DST time in this year in this country
2182 return IsBetween(GetBeginDST(year
, country
), GetEndDST(year
, country
));
2186 wxDateTime
& wxDateTime::MakeTimezone(const TimeZone
& tz
, bool noDST
)
2188 long secDiff
= GetTimeZone() + tz
.GetOffset();
2190 // we need to know whether DST is or not in effect for this date unless
2191 // the test disabled by the caller
2192 if ( !noDST
&& (IsDST() == 1) )
2194 // FIXME we assume that the DST is always shifted by 1 hour
2198 return Add(wxTimeSpan::Seconds(secDiff
));
2201 wxDateTime
& wxDateTime::MakeFromTimezone(const TimeZone
& tz
, bool noDST
)
2203 long secDiff
= GetTimeZone() + tz
.GetOffset();
2205 // we need to know whether DST is or not in effect for this date unless
2206 // the test disabled by the caller
2207 if ( !noDST
&& (IsDST() == 1) )
2209 // FIXME we assume that the DST is always shifted by 1 hour
2213 return Subtract(wxTimeSpan::Seconds(secDiff
));
2216 // ----------------------------------------------------------------------------
2217 // wxDateTime to/from text representations
2218 // ----------------------------------------------------------------------------
2220 wxString
wxDateTime::Format(const wxChar
*format
, const TimeZone
& tz
) const
2222 wxCHECK_MSG( format
, wxEmptyString
, _T("NULL format in wxDateTime::Format") );
2224 // we have to use our own implementation if the date is out of range of
2225 // strftime() or if we use non standard specificators
2226 time_t time
= GetTicks();
2227 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
2232 if ( tz
.GetOffset() == -GetTimeZone() )
2234 // we are working with local time
2235 tm
= wxLocaltime_r(&time
, &tmstruct
);
2237 // should never happen
2238 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxLocaltime_r() failed") );
2242 time
+= (int)tz
.GetOffset();
2244 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
2245 int time2
= (int) time
;
2251 tm
= wxGmtime_r(&time
, &tmstruct
);
2253 // should never happen
2254 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxGmtime_r() failed") );
2258 tm
= (struct tm
*)NULL
;
2262 //Windows CE doesn't support strftime or wcsftime, so we use the generic implementation
2265 return CallStrftime(format
, tm
);
2268 //else: use generic code below
2271 // we only parse ANSI C format specifications here, no POSIX 2
2272 // complications, no GNU extensions but we do add support for a "%l" format
2273 // specifier allowing to get the number of milliseconds
2276 // used for calls to strftime() when we only deal with time
2277 struct tm tmTimeOnly
;
2278 tmTimeOnly
.tm_hour
= tm
.hour
;
2279 tmTimeOnly
.tm_min
= tm
.min
;
2280 tmTimeOnly
.tm_sec
= tm
.sec
;
2281 tmTimeOnly
.tm_wday
= 0;
2282 tmTimeOnly
.tm_yday
= 0;
2283 tmTimeOnly
.tm_mday
= 1; // any date will do
2284 tmTimeOnly
.tm_mon
= 0;
2285 tmTimeOnly
.tm_year
= 76;
2286 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
2288 wxString tmp
, res
, fmt
;
2289 for ( const wxChar
*p
= format
; *p
; p
++ )
2291 if ( *p
!= _T('%') )
2299 // set the default format
2302 case _T('Y'): // year has 4 digits
2306 case _T('j'): // day of year has 3 digits
2307 case _T('l'): // milliseconds have 3 digits
2311 case _T('w'): // week day as number has only one
2316 // it's either another valid format specifier in which case
2317 // the format is "%02d" (for all the rest) or we have the
2318 // field width preceding the format in which case it will
2319 // override the default format anyhow
2323 bool restart
= true;
2328 // start of the format specification
2331 case _T('a'): // a weekday name
2333 // second parameter should be true for abbreviated names
2334 res
+= GetWeekDayName(tm
.GetWeekDay(),
2335 *p
== _T('a') ? Name_Abbr
: Name_Full
);
2338 case _T('b'): // a month name
2340 res
+= GetMonthName(tm
.mon
,
2341 *p
== _T('b') ? Name_Abbr
: Name_Full
);
2344 case _T('c'): // locale default date and time representation
2345 case _T('x'): // locale default date representation
2348 // the problem: there is no way to know what do these format
2349 // specifications correspond to for the current locale.
2351 // the solution: use a hack and still use strftime(): first
2352 // find the YEAR which is a year in the strftime() range (1970
2353 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
2354 // of the real year. Then make a copy of the format and
2355 // replace all occurrences of YEAR in it with some unique
2356 // string not appearing anywhere else in it, then use
2357 // strftime() to format the date in year YEAR and then replace
2358 // YEAR back by the real year and the unique replacement
2359 // string back with YEAR. Notice that "all occurrences of YEAR"
2360 // means all occurrences of 4 digit as well as 2 digit form!
2362 // the bugs: we assume that neither of %c nor %x contains any
2363 // fields which may change between the YEAR and real year. For
2364 // example, the week number (%U, %W) and the day number (%j)
2365 // will change if one of these years is leap and the other one
2368 // find the YEAR: normally, for any year X, Jan 1 or the
2369 // year X + 28 is the same weekday as Jan 1 of X (because
2370 // the weekday advances by 1 for each normal X and by 2
2371 // for each leap X, hence by 5 every 4 years or by 35
2372 // which is 0 mod 7 every 28 years) but this rule breaks
2373 // down if there are years between X and Y which are
2374 // divisible by 4 but not leap (i.e. divisible by 100 but
2375 // not 400), hence the correction.
2377 int yearReal
= GetYear(tz
);
2378 int mod28
= yearReal
% 28;
2380 // be careful to not go too far - we risk to leave the
2385 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
2389 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
2392 int nCentury
= year
/ 100,
2393 nCenturyReal
= yearReal
/ 100;
2395 // need to adjust for the years divisble by 400 which are
2396 // not leap but are counted like leap ones if we just take
2397 // the number of centuries in between for nLostWeekDays
2398 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
2399 (nCentury
/ 4 - nCenturyReal
/ 4);
2401 // we have to gain back the "lost" weekdays: note that the
2402 // effect of this loop is to not do anything to
2403 // nLostWeekDays (which we won't use any more), but to
2404 // (indirectly) set the year correctly
2405 while ( (nLostWeekDays
% 7) != 0 )
2407 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
2410 // Keep year below 2000 so the 2digit year number
2411 // can never match the month or day of the month
2412 if (year
>=2000) year
-=28;
2413 // at any rate, we couldn't go further than 1988 + 9 + 28!
2414 wxASSERT_MSG( year
< 2030,
2415 _T("logic error in wxDateTime::Format") );
2417 wxString strYear
, strYear2
;
2418 strYear
.Printf(_T("%d"), year
);
2419 strYear2
.Printf(_T("%d"), year
% 100);
2421 // find four strings not occurring in format (this is surely
2422 // not the optimal way of doing it... improvements welcome!)
2423 wxString fmt2
= format
;
2424 wxString replacement
,replacement2
,replacement3
,replacement4
;
2425 for (int rnr
=1; rnr
<5 ; rnr
++)
2427 wxString r
= (wxChar
)-rnr
;
2428 while ( fmt2
.Find(r
) != wxNOT_FOUND
)
2435 case 1: replacement
=r
; break;
2436 case 2: replacement2
=r
; break;
2437 case 3: replacement3
=r
; break;
2438 case 4: replacement4
=r
; break;
2441 // replace all occurrences of year with it
2442 bool wasReplaced
= fmt2
.Replace(strYear
, replacement
) > 0;
2443 // evaluation order ensures we always attempt the replacement.
2444 wasReplaced
= (fmt2
.Replace(strYear2
, replacement2
) > 0) | wasReplaced
;
2446 // use strftime() to format the same date but in supported
2449 // NB: we assume that strftime() doesn't check for the
2450 // date validity and will happily format the date
2451 // corresponding to Feb 29 of a non leap year (which
2452 // may happen if yearReal was leap and year is not)
2453 struct tm tmAdjusted
;
2455 tmAdjusted
.tm_hour
= tm
.hour
;
2456 tmAdjusted
.tm_min
= tm
.min
;
2457 tmAdjusted
.tm_sec
= tm
.sec
;
2458 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
2459 tmAdjusted
.tm_yday
= GetDayOfYear();
2460 tmAdjusted
.tm_mday
= tm
.mday
;
2461 tmAdjusted
.tm_mon
= tm
.mon
;
2462 tmAdjusted
.tm_year
= year
- 1900;
2463 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
2464 wxString str
= CallStrftime(*p
== _T('c') ? _T("%c")
2468 // now replace the occurrence of 1999 with the real year
2469 // we do this in two stages to stop the 2 digit year
2470 // matching any substring of the 4 digit year.
2471 // Any day,month hours and minutes components should be safe due
2472 // to ensuring the range of the years.
2473 wxString strYearReal
, strYearReal2
;
2474 strYearReal
.Printf(_T("%04d"), yearReal
);
2475 strYearReal2
.Printf(_T("%02d"), yearReal
% 100);
2476 str
.Replace(strYear
, replacement3
);
2477 str
.Replace(strYear2
,replacement4
);
2478 str
.Replace(replacement3
, strYearReal
);
2479 str
.Replace(replacement4
, strYearReal2
);
2481 // and replace back all occurrences of replacement string
2484 str
.Replace(replacement2
, strYear2
);
2485 str
.Replace(replacement
, strYear
);
2491 //Use "%m/%d/%y %H:%M:%S" format instead
2492 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
2493 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
2497 case _T('d'): // day of a month (01-31)
2498 res
+= wxString::Format(fmt
, tm
.mday
);
2501 case _T('H'): // hour in 24h format (00-23)
2502 res
+= wxString::Format(fmt
, tm
.hour
);
2505 case _T('I'): // hour in 12h format (01-12)
2507 // 24h -> 12h, 0h -> 12h too
2508 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
2509 : tm
.hour
? tm
.hour
: 12;
2510 res
+= wxString::Format(fmt
, hour12
);
2514 case _T('j'): // day of the year
2515 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
2518 case _T('l'): // milliseconds (NOT STANDARD)
2519 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
2522 case _T('m'): // month as a number (01-12)
2523 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
2526 case _T('M'): // minute as a decimal number (00-59)
2527 res
+= wxString::Format(fmt
, tm
.min
);
2530 case _T('p'): // AM or PM string
2532 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
2534 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
2538 case _T('S'): // second as a decimal number (00-61)
2539 res
+= wxString::Format(fmt
, tm
.sec
);
2542 case _T('U'): // week number in the year (Sunday 1st week day)
2543 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
2546 case _T('W'): // week number in the year (Monday 1st week day)
2547 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
2550 case _T('w'): // weekday as a number (0-6), Sunday = 0
2551 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
2554 // case _T('x'): -- handled with "%c"
2556 case _T('X'): // locale default time representation
2557 // just use strftime() to format the time for us
2559 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
2561 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
2565 case _T('y'): // year without century (00-99)
2566 res
+= wxString::Format(fmt
, tm
.year
% 100);
2569 case _T('Y'): // year with century
2570 res
+= wxString::Format(fmt
, tm
.year
);
2573 case _T('Z'): // timezone name
2575 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
2580 // is it the format width?
2582 while ( *p
== _T('-') || *p
== _T('+') ||
2583 *p
== _T(' ') || wxIsdigit(*p
) )
2590 // we've only got the flags and width so far in fmt
2591 fmt
.Prepend(_T('%'));
2592 fmt
.Append(_T('d'));
2599 // no, it wasn't the width
2600 wxFAIL_MSG(_T("unknown format specificator"));
2602 // fall through and just copy it nevertheless
2604 case _T('%'): // a percent sign
2608 case 0: // the end of string
2609 wxFAIL_MSG(_T("missing format at the end of string"));
2611 // just put the '%' which was the last char in format
2621 // this function parses a string in (strict) RFC 822 format: see the section 5
2622 // of the RFC for the detailed description, but briefly it's something of the
2623 // form "Sat, 18 Dec 1999 00:48:30 +0100"
2625 // this function is "strict" by design - it must reject anything except true
2626 // RFC822 time specs.
2628 // TODO a great candidate for using reg exps
2629 const wxChar
*wxDateTime::ParseRfc822Date(const wxChar
* date
)
2631 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
2633 const wxChar
*p
= date
;
2634 const wxChar
*comma
= wxStrchr(p
, _T(','));
2637 // the part before comma is the weekday
2639 // skip it for now - we don't use but might check that it really
2640 // corresponds to the specfied date
2643 if ( *p
!= _T(' ') )
2645 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
2647 return (wxChar
*)NULL
;
2653 // the following 1 or 2 digits are the day number
2654 if ( !wxIsdigit(*p
) )
2656 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
2658 return (wxChar
*)NULL
;
2661 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - _T('0'));
2662 if ( wxIsdigit(*p
) )
2665 day
= (wxDateTime_t
)(day
+ (*p
++ - _T('0')));
2668 if ( *p
++ != _T(' ') )
2670 return (wxChar
*)NULL
;
2673 // the following 3 letters specify the month
2674 wxString
monName(p
, 3);
2676 if ( monName
== _T("Jan") )
2678 else if ( monName
== _T("Feb") )
2680 else if ( monName
== _T("Mar") )
2682 else if ( monName
== _T("Apr") )
2684 else if ( monName
== _T("May") )
2686 else if ( monName
== _T("Jun") )
2688 else if ( monName
== _T("Jul") )
2690 else if ( monName
== _T("Aug") )
2692 else if ( monName
== _T("Sep") )
2694 else if ( monName
== _T("Oct") )
2696 else if ( monName
== _T("Nov") )
2698 else if ( monName
== _T("Dec") )
2702 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str());
2704 return (wxChar
*)NULL
;
2709 if ( *p
++ != _T(' ') )
2711 return (wxChar
*)NULL
;
2715 if ( !wxIsdigit(*p
) )
2718 return (wxChar
*)NULL
;
2721 int year
= *p
++ - _T('0');
2723 if ( !wxIsdigit(*p
) )
2725 // should have at least 2 digits in the year
2726 return (wxChar
*)NULL
;
2730 year
+= *p
++ - _T('0');
2732 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
2733 if ( wxIsdigit(*p
) )
2736 year
+= *p
++ - _T('0');
2738 if ( !wxIsdigit(*p
) )
2740 // no 3 digit years please
2741 return (wxChar
*)NULL
;
2745 year
+= *p
++ - _T('0');
2748 if ( *p
++ != _T(' ') )
2750 return (wxChar
*)NULL
;
2753 // time is in the format hh:mm:ss and seconds are optional
2754 if ( !wxIsdigit(*p
) )
2756 return (wxChar
*)NULL
;
2759 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - _T('0'));
2761 if ( !wxIsdigit(*p
) )
2763 return (wxChar
*)NULL
;
2767 hour
= (wxDateTime_t
)(hour
+ (*p
++ - _T('0')));
2769 if ( *p
++ != _T(':') )
2771 return (wxChar
*)NULL
;
2774 if ( !wxIsdigit(*p
) )
2776 return (wxChar
*)NULL
;
2779 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - _T('0'));
2781 if ( !wxIsdigit(*p
) )
2783 return (wxChar
*)NULL
;
2787 min
= (wxDateTime_t
)(min
+ *p
++ - _T('0'));
2789 wxDateTime_t sec
= 0;
2790 if ( *p
++ == _T(':') )
2792 if ( !wxIsdigit(*p
) )
2794 return (wxChar
*)NULL
;
2797 sec
= (wxDateTime_t
)(*p
++ - _T('0'));
2799 if ( !wxIsdigit(*p
) )
2801 return (wxChar
*)NULL
;
2805 sec
= (wxDateTime_t
)(sec
+ *p
++ - _T('0'));
2808 if ( *p
++ != _T(' ') )
2810 return (wxChar
*)NULL
;
2813 // and now the interesting part: the timezone
2814 int offset
wxDUMMY_INITIALIZE(0);
2815 if ( *p
== _T('-') || *p
== _T('+') )
2817 // the explicit offset given: it has the form of hhmm
2818 bool plus
= *p
++ == _T('+');
2820 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2822 return (wxChar
*)NULL
;
2826 offset
= MIN_PER_HOUR
*(10*(*p
- _T('0')) + (*(p
+ 1) - _T('0')));
2830 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2832 return (wxChar
*)NULL
;
2836 offset
+= 10*(*p
- _T('0')) + (*(p
+ 1) - _T('0'));
2847 // the symbolic timezone given: may be either military timezone or one
2848 // of standard abbreviations
2851 // military: Z = UTC, J unused, A = -1, ..., Y = +12
2852 static const int offsets
[26] =
2854 //A B C D E F G H I J K L M
2855 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
2856 //N O P R Q S T U V W Z Y Z
2857 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
2860 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
2862 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
);
2864 return (wxChar
*)NULL
;
2867 offset
= offsets
[*p
++ - _T('A')];
2873 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
2875 else if ( tz
== _T("AST") )
2876 offset
= AST
- GMT0
;
2877 else if ( tz
== _T("ADT") )
2878 offset
= ADT
- GMT0
;
2879 else if ( tz
== _T("EST") )
2880 offset
= EST
- GMT0
;
2881 else if ( tz
== _T("EDT") )
2882 offset
= EDT
- GMT0
;
2883 else if ( tz
== _T("CST") )
2884 offset
= CST
- GMT0
;
2885 else if ( tz
== _T("CDT") )
2886 offset
= CDT
- GMT0
;
2887 else if ( tz
== _T("MST") )
2888 offset
= MST
- GMT0
;
2889 else if ( tz
== _T("MDT") )
2890 offset
= MDT
- GMT0
;
2891 else if ( tz
== _T("PST") )
2892 offset
= PST
- GMT0
;
2893 else if ( tz
== _T("PDT") )
2894 offset
= PDT
- GMT0
;
2897 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
);
2899 return (wxChar
*)NULL
;
2906 offset
*= MIN_PER_HOUR
;
2909 // the spec was correct, construct the date from the values we found
2910 Set(day
, mon
, year
, hour
, min
, sec
);
2911 MakeFromTimezone(TimeZone((wxDateTime_t
)(offset
*SEC_PER_MIN
)));
2918 // returns the string containing strftime() format used for short dates in the
2919 // current locale or an empty string
2920 static wxString
GetLocaleDateFormat()
2924 // there is no setlocale() under Windows CE, so just always query the
2927 if ( strcmp(setlocale(LC_ALL
, NULL
), "C") != 0 )
2930 // The locale was programatically set to non-C. We assume that this was
2931 // done using wxLocale, in which case thread's current locale is also
2932 // set to correct LCID value and we can use GetLocaleInfo to determine
2933 // the correct formatting string:
2935 LCID lcid
= LOCALE_USER_DEFAULT
;
2937 LCID lcid
= GetThreadLocale();
2939 // according to MSDN 80 chars is max allowed for short date format
2941 if ( ::GetLocaleInfo(lcid
, LOCALE_SSHORTDATE
, fmt
, WXSIZEOF(fmt
)) )
2943 wxChar chLast
= _T('\0');
2944 size_t lastCount
= 0;
2945 for ( const wxChar
*p
= fmt
; /* NUL handled inside */; p
++ )
2955 // these characters come in groups, start counting them
2965 // first deal with any special characters we have had
2971 switch ( lastCount
)
2975 // these two are the same as we
2976 // don't distinguish between 1 and
2977 // 2 digits for days
2990 wxFAIL_MSG( _T("too many 'd's") );
2995 switch ( lastCount
)
2999 // as for 'd' and 'dd' above
3012 wxFAIL_MSG( _T("too many 'M's") );
3017 switch ( lastCount
)
3029 wxFAIL_MSG( _T("wrong number of 'y's") );
3034 // strftime() doesn't have era string,
3035 // ignore this format
3036 wxASSERT_MSG( lastCount
<= 2,
3037 _T("too many 'g's") );
3041 wxFAIL_MSG( _T("unreachable") );
3048 // not a special character so must be just a separator,
3050 if ( *p
!= _T('\0') )
3052 if ( *p
== _T('%') )
3054 // this one needs to be escaped
3062 if ( *p
== _T('\0') )
3066 //else: GetLocaleInfo() failed, leave fmtDate value unchanged and
3067 // try our luck with the default formats
3069 //else: default C locale, default formats should work
3074 #endif // __WINDOWS__
3076 const wxChar
*wxDateTime::ParseFormat(const wxChar
*date
,
3077 const wxChar
*format
,
3078 const wxDateTime
& dateDef
)
3080 wxCHECK_MSG( date
&& format
, (wxChar
*)NULL
,
3081 _T("NULL pointer in wxDateTime::ParseFormat()") );
3086 // what fields have we found?
3087 bool haveWDay
= false,
3096 bool hourIsIn12hFormat
= false, // or in 24h one?
3097 isPM
= false; // AM by default
3099 // and the value of the items we have (init them to get rid of warnings)
3100 wxDateTime_t sec
= 0,
3103 WeekDay wday
= Inv_WeekDay
;
3104 wxDateTime_t yday
= 0,
3106 wxDateTime::Month mon
= Inv_Month
;
3109 const wxChar
*input
= date
;
3110 for ( const wxChar
*fmt
= format
; *fmt
; fmt
++ )
3112 if ( *fmt
!= _T('%') )
3114 if ( wxIsspace(*fmt
) )
3116 // a white space in the format string matches 0 or more white
3117 // spaces in the input
3118 while ( wxIsspace(*input
) )
3125 // any other character (not whitespace, not '%') must be
3126 // matched by itself in the input
3127 if ( *input
++ != *fmt
)
3130 return (wxChar
*)NULL
;
3134 // done with this format char
3138 // start of a format specification
3140 // parse the optional width
3142 while ( wxIsdigit(*++fmt
) )
3145 width
+= *fmt
- _T('0');
3148 // the default widths for the various fields
3153 case _T('Y'): // year has 4 digits
3157 case _T('j'): // day of year has 3 digits
3158 case _T('l'): // milliseconds have 3 digits
3162 case _T('w'): // week day as number has only one
3167 // default for all other fields
3172 // then the format itself
3175 case _T('a'): // a weekday name
3178 int flag
= *fmt
== _T('a') ? Name_Abbr
: Name_Full
;
3179 wday
= GetWeekDayFromName(GetAlphaToken(input
), flag
);
3180 if ( wday
== Inv_WeekDay
)
3183 return (wxChar
*)NULL
;
3189 case _T('b'): // a month name
3192 int flag
= *fmt
== _T('b') ? Name_Abbr
: Name_Full
;
3193 mon
= GetMonthFromName(GetAlphaToken(input
), flag
);
3194 if ( mon
== Inv_Month
)
3197 return (wxChar
*)NULL
;
3203 case _T('c'): // locale default date and time representation
3207 // this is the format which corresponds to ctime() output
3208 // and strptime("%c") should parse it, so try it first
3209 static const wxChar
*fmtCtime
= _T("%a %b %d %H:%M:%S %Y");
3211 const wxChar
*result
= dt
.ParseFormat(input
, fmtCtime
);
3214 result
= dt
.ParseFormat(input
, _T("%x %X"));
3219 result
= dt
.ParseFormat(input
, _T("%X %x"));
3224 // we've tried everything and still no match
3225 return (wxChar
*)NULL
;
3230 haveDay
= haveMon
= haveYear
=
3231 haveHour
= haveMin
= haveSec
= true;
3245 case _T('d'): // day of a month (01-31)
3246 if ( !GetNumericToken(width
, input
, &num
) ||
3247 (num
> 31) || (num
< 1) )
3250 return (wxChar
*)NULL
;
3253 // we can't check whether the day range is correct yet, will
3254 // do it later - assume ok for now
3256 mday
= (wxDateTime_t
)num
;
3259 case _T('H'): // hour in 24h format (00-23)
3260 if ( !GetNumericToken(width
, input
, &num
) || (num
> 23) )
3263 return (wxChar
*)NULL
;
3267 hour
= (wxDateTime_t
)num
;
3270 case _T('I'): // hour in 12h format (01-12)
3271 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3274 return (wxChar
*)NULL
;
3278 hourIsIn12hFormat
= true;
3279 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
3282 case _T('j'): // day of the year
3283 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 366) )
3286 return (wxChar
*)NULL
;
3290 yday
= (wxDateTime_t
)num
;
3293 case _T('m'): // month as a number (01-12)
3294 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3297 return (wxChar
*)NULL
;
3301 mon
= (Month
)(num
- 1);
3304 case _T('M'): // minute as a decimal number (00-59)
3305 if ( !GetNumericToken(width
, input
, &num
) || (num
> 59) )
3308 return (wxChar
*)NULL
;
3312 min
= (wxDateTime_t
)num
;
3315 case _T('p'): // AM or PM string
3317 wxString am
, pm
, token
= GetAlphaToken(input
);
3319 GetAmPmStrings(&am
, &pm
);
3320 if (am
.empty() && pm
.empty())
3321 return (wxChar
*)NULL
; // no am/pm strings defined
3322 if ( token
.CmpNoCase(pm
) == 0 )
3326 else if ( token
.CmpNoCase(am
) != 0 )
3329 return (wxChar
*)NULL
;
3334 case _T('r'): // time as %I:%M:%S %p
3337 input
= dt
.ParseFormat(input
, _T("%I:%M:%S %p"));
3341 return (wxChar
*)NULL
;
3344 haveHour
= haveMin
= haveSec
= true;
3353 case _T('R'): // time as %H:%M
3356 input
= dt
.ParseFormat(input
, _T("%H:%M"));
3360 return (wxChar
*)NULL
;
3363 haveHour
= haveMin
= true;
3371 case _T('S'): // second as a decimal number (00-61)
3372 if ( !GetNumericToken(width
, input
, &num
) || (num
> 61) )
3375 return (wxChar
*)NULL
;
3379 sec
= (wxDateTime_t
)num
;
3382 case _T('T'): // time as %H:%M:%S
3385 input
= dt
.ParseFormat(input
, _T("%H:%M:%S"));
3389 return (wxChar
*)NULL
;
3392 haveHour
= haveMin
= haveSec
= true;
3401 case _T('w'): // weekday as a number (0-6), Sunday = 0
3402 if ( !GetNumericToken(width
, input
, &num
) || (wday
> 6) )
3405 return (wxChar
*)NULL
;
3409 wday
= (WeekDay
)num
;
3412 case _T('x'): // locale default date representation
3413 #ifdef HAVE_STRPTIME
3414 // try using strptime() -- it may fail even if the input is
3415 // correct but the date is out of range, so we will fall back
3416 // to our generic code anyhow
3420 const wxChar
*result
= CallStrptime(input
, "%x", &tm
);
3425 haveDay
= haveMon
= haveYear
= true;
3427 year
= 1900 + tm
.tm_year
;
3428 mon
= (Month
)tm
.tm_mon
;
3434 #endif // HAVE_STRPTIME
3442 // The above doesn't work for all locales, try to query
3443 // Windows for the right way of formatting the date:
3444 fmtDate
= GetLocaleDateFormat();
3445 if ( fmtDate
.empty() )
3448 if ( IsWestEuropeanCountry(GetCountry()) ||
3449 GetCountry() == Russia
)
3451 fmtDate
= _T("%d/%m/%y");
3452 fmtDateAlt
= _T("%m/%d/%y");
3456 fmtDate
= _T("%m/%d/%y");
3457 fmtDateAlt
= _T("%d/%m/%y");
3461 const wxChar
*result
= dt
.ParseFormat(input
, fmtDate
);
3463 if ( !result
&& !fmtDateAlt
.empty() )
3465 // ok, be nice and try another one
3466 result
= dt
.ParseFormat(input
, fmtDateAlt
);
3472 return (wxChar
*)NULL
;
3477 haveDay
= haveMon
= haveYear
= true;
3488 case _T('X'): // locale default time representation
3489 #ifdef HAVE_STRPTIME
3491 // use strptime() to do it for us (FIXME !Unicode friendly)
3493 input
= CallStrptime(input
, "%X", &tm
);
3496 return (wxChar
*)NULL
;
3499 haveHour
= haveMin
= haveSec
= true;
3505 #else // !HAVE_STRPTIME
3506 // TODO under Win32 we can query the LOCALE_ITIME system
3507 // setting which says whether the default time format is
3510 // try to parse what follows as "%H:%M:%S" and, if this
3511 // fails, as "%I:%M:%S %p" - this should catch the most
3515 const wxChar
*result
= dt
.ParseFormat(input
, _T("%T"));
3518 result
= dt
.ParseFormat(input
, _T("%r"));
3524 return (wxChar
*)NULL
;
3527 haveHour
= haveMin
= haveSec
= true;
3536 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
3539 case _T('y'): // year without century (00-99)
3540 if ( !GetNumericToken(width
, input
, &num
) || (num
> 99) )
3543 return (wxChar
*)NULL
;
3548 // TODO should have an option for roll over date instead of
3549 // hard coding it here
3550 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
3553 case _T('Y'): // year with century
3554 if ( !GetNumericToken(width
, input
, &num
) )
3557 return (wxChar
*)NULL
;
3561 year
= (wxDateTime_t
)num
;
3564 case _T('Z'): // timezone name
3565 wxFAIL_MSG(_T("TODO"));
3568 case _T('%'): // a percent sign
3569 if ( *input
++ != _T('%') )
3572 return (wxChar
*)NULL
;
3576 case 0: // the end of string
3577 wxFAIL_MSG(_T("unexpected format end"));
3581 default: // not a known format spec
3582 return (wxChar
*)NULL
;
3586 // format matched, try to construct a date from what we have now
3588 if ( dateDef
.IsValid() )
3590 // take this date as default
3591 tmDef
= dateDef
.GetTm();
3593 else if ( IsValid() )
3595 // if this date is valid, don't change it
3600 // no default and this date is invalid - fall back to Today()
3601 tmDef
= Today().GetTm();
3612 // TODO we don't check here that the values are consistent, if both year
3613 // day and month/day were found, we just ignore the year day and we
3614 // also always ignore the week day
3615 if ( haveMon
&& haveDay
)
3617 if ( mday
> GetNumOfDaysInMonth(tm
.year
, mon
) )
3619 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
3621 return (wxChar
*)NULL
;
3627 else if ( haveYDay
)
3629 if ( yday
> GetNumberOfDays(tm
.year
) )
3631 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
3633 return (wxChar
*)NULL
;
3636 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
3643 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
3645 // translate to 24hour format
3648 //else: either already in 24h format or no translation needed
3668 // finally check that the week day is consistent -- if we had it
3669 if ( haveWDay
&& GetWeekDay() != wday
)
3671 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
3679 const wxChar
*wxDateTime::ParseDateTime(const wxChar
*date
)
3681 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3683 // Set to current day and hour, so strings like '14:00' becomes today at
3684 // 14, not some other random date
3685 wxDateTime dtDate
= wxDateTime::Today();
3686 wxDateTime dtTime
= wxDateTime::Today();
3688 const wxChar
* pchTime
;
3690 // Try to parse the beginning of the string as a date
3691 const wxChar
* pchDate
= dtDate
.ParseDate(date
);
3693 // We got a date in the beginning, see if there is a time specified after the date
3696 // Skip spaces, as the ParseTime() function fails on spaces
3697 while ( wxIsspace(*pchDate
) )
3700 pchTime
= dtTime
.ParseTime(pchDate
);
3702 else // no date in the beginning
3704 // check and see if we have a time followed by a date
3705 pchTime
= dtTime
.ParseTime(date
);
3708 while ( wxIsspace(*pchTime
) )
3711 pchDate
= dtDate
.ParseDate(pchTime
);
3715 // If we have a date specified, set our own data to the same date
3716 if ( !pchDate
|| !pchTime
)
3719 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
3720 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
3721 dtTime
.GetMillisecond());
3723 // Return endpoint of scan
3724 return pchDate
> pchTime
? pchDate
: pchTime
;
3727 const wxChar
*wxDateTime::ParseDate(const wxChar
*date
)
3729 // this is a simplified version of ParseDateTime() which understands only
3730 // "today" (for wxDate compatibility) and digits only otherwise (and not
3731 // all esoteric constructions ParseDateTime() knows about)
3733 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3735 const wxChar
*p
= date
;
3736 while ( wxIsspace(*p
) )
3739 // some special cases
3743 int dayDiffFromToday
;
3746 { wxTRANSLATE("today"), 0 },
3747 { wxTRANSLATE("yesterday"), -1 },
3748 { wxTRANSLATE("tomorrow"), 1 },
3751 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
3753 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
3754 size_t len
= dateStr
.length();
3755 if ( wxStrlen(p
) >= len
)
3757 wxString
str(p
, len
);
3758 if ( str
.CmpNoCase(dateStr
) == 0 )
3760 // nothing can follow this, so stop here
3763 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
3765 if ( dayDiffFromToday
)
3767 *this += wxDateSpan::Days(dayDiffFromToday
);
3775 // We try to guess what we have here: for each new (numeric) token, we
3776 // determine if it can be a month, day or a year. Of course, there is an
3777 // ambiguity as some numbers may be days as well as months, so we also
3778 // have the ability to back track.
3781 bool haveDay
= false, // the months day?
3782 haveWDay
= false, // the day of week?
3783 haveMon
= false, // the month?
3784 haveYear
= false; // the year?
3786 // and the value of the items we have (init them to get rid of warnings)
3787 WeekDay wday
= Inv_WeekDay
;
3788 wxDateTime_t day
= 0;
3789 wxDateTime::Month mon
= Inv_Month
;
3792 // tokenize the string
3794 static const wxChar
*dateDelimiters
= _T(".,/-\t\r\n ");
3795 wxStringTokenizer
tok(p
, dateDelimiters
);
3796 while ( tok
.HasMoreTokens() )
3798 wxString token
= tok
.GetNextToken();
3804 if ( token
.ToULong(&val
) )
3806 // guess what this number is
3812 if ( !haveMon
&& val
> 0 && val
<= 12 )
3814 // assume it is month
3817 else // not the month
3821 // this can only be the year
3824 else // may be either day or year
3826 // use a leap year if we don't have the year yet to allow
3827 // dates like 2/29/1976 which would be rejected otherwise
3828 wxDateTime_t max_days
= (wxDateTime_t
)(
3830 ? GetNumOfDaysInMonth(haveYear
? year
: 1976, mon
)
3835 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
3840 else // yes, suppose it's the day
3854 year
= (wxDateTime_t
)val
;
3863 day
= (wxDateTime_t
)val
;
3869 mon
= (Month
)(val
- 1);
3872 else // not a number
3874 // be careful not to overwrite the current mon value
3875 Month mon2
= GetMonthFromName(token
, Name_Full
| Name_Abbr
);
3876 if ( mon2
!= Inv_Month
)
3881 // but we already have a month - maybe we guessed wrong?
3884 // no need to check in month range as always < 12, but
3885 // the days are counted from 1 unlike the months
3886 day
= (wxDateTime_t
)(mon
+ 1);
3891 // could possible be the year (doesn't the year come
3892 // before the month in the japanese format?) (FIXME)
3901 else // not a valid month name
3903 wday
= GetWeekDayFromName(token
, Name_Full
| Name_Abbr
);
3904 if ( wday
!= Inv_WeekDay
)
3914 else // not a valid weekday name
3917 static const wxChar
*ordinals
[] =
3919 wxTRANSLATE("first"),
3920 wxTRANSLATE("second"),
3921 wxTRANSLATE("third"),
3922 wxTRANSLATE("fourth"),
3923 wxTRANSLATE("fifth"),
3924 wxTRANSLATE("sixth"),
3925 wxTRANSLATE("seventh"),
3926 wxTRANSLATE("eighth"),
3927 wxTRANSLATE("ninth"),
3928 wxTRANSLATE("tenth"),
3929 wxTRANSLATE("eleventh"),
3930 wxTRANSLATE("twelfth"),
3931 wxTRANSLATE("thirteenth"),
3932 wxTRANSLATE("fourteenth"),
3933 wxTRANSLATE("fifteenth"),
3934 wxTRANSLATE("sixteenth"),
3935 wxTRANSLATE("seventeenth"),
3936 wxTRANSLATE("eighteenth"),
3937 wxTRANSLATE("nineteenth"),
3938 wxTRANSLATE("twentieth"),
3939 // that's enough - otherwise we'd have problems with
3940 // composite (or not) ordinals
3944 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
3946 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
3952 if ( n
== WXSIZEOF(ordinals
) )
3954 // stop here - something unknown
3961 // don't try anything here (as in case of numeric day
3962 // above) - the symbolic day spec should always
3963 // precede the month/year
3969 day
= (wxDateTime_t
)(n
+ 1);
3974 nPosCur
= tok
.GetPosition();
3977 // either no more tokens or the scan was stopped by something we couldn't
3978 // parse - in any case, see if we can construct a date from what we have
3979 if ( !haveDay
&& !haveWDay
)
3981 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
3986 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
3987 !(haveDay
&& haveMon
&& haveYear
) )
3989 // without adjectives (which we don't support here) the week day only
3990 // makes sense completely separately or with the full date
3991 // specification (what would "Wed 1999" mean?)
3995 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
3997 // may be we have month and day instead of day and year?
3998 if ( haveDay
&& !haveMon
)
4002 // exchange day and month
4003 mon
= (wxDateTime::Month
)(day
- 1);
4005 // we're in the current year then
4006 if ( (year
> 0) && (year
<= (int)GetNumOfDaysInMonth(Inv_Year
, mon
)) )
4008 day
= (wxDateTime_t
)year
;
4013 //else: no, can't exchange, leave haveMon == false
4019 // if we give the year, month and day must be given too
4020 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
4028 mon
= GetCurrentMonth();
4033 year
= GetCurrentYear();
4038 // normally we check the day above but the check is optimistic in case
4039 // we find the day before its month/year so we have to redo it now
4040 if ( day
> GetNumOfDaysInMonth(year
, mon
) )
4043 Set(day
, mon
, year
);
4047 // check that it is really the same
4048 if ( GetWeekDay() != wday
)
4050 // inconsistency detected
4051 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
4053 return (wxChar
*)NULL
;
4061 SetToWeekDayInSameWeek(wday
);
4064 // return the pointer to the first unparsed char
4066 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
4068 // if we couldn't parse the token after the delimiter, put back the
4069 // delimiter as well
4076 const wxChar
*wxDateTime::ParseTime(const wxChar
*time
)
4078 wxCHECK_MSG( time
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
4080 // first try some extra things
4087 { wxTRANSLATE("noon"), 12 },
4088 { wxTRANSLATE("midnight"), 00 },
4092 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
4094 wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
4095 size_t len
= timeString
.length();
4096 if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 )
4098 // casts required by DigitalMars
4099 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
4105 // try all time formats we may think about in the order from longest to
4108 // 12hour with AM/PM?
4109 const wxChar
*result
= ParseFormat(time
, _T("%I:%M:%S %p"));
4113 // normally, it's the same, but why not try it?
4114 result
= ParseFormat(time
, _T("%H:%M:%S"));
4119 // 12hour with AM/PM but without seconds?
4120 result
= ParseFormat(time
, _T("%I:%M %p"));
4126 result
= ParseFormat(time
, _T("%H:%M"));
4131 // just the hour and AM/PM?
4132 result
= ParseFormat(time
, _T("%I %p"));
4138 result
= ParseFormat(time
, _T("%H"));
4143 // parse the standard format: normally it is one of the formats above
4144 // but it may be set to something completely different by the user
4145 result
= ParseFormat(time
, _T("%X"));
4148 // TODO: parse timezones
4153 // ----------------------------------------------------------------------------
4154 // Workdays and holidays support
4155 // ----------------------------------------------------------------------------
4157 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
4159 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
4162 // ============================================================================
4164 // ============================================================================
4166 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
4169 return ds1
.Multiply(n
);
4172 // ============================================================================
4174 // ============================================================================
4176 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
4178 return wxTimeSpan(ts
).Multiply(n
);
4181 // this enum is only used in wxTimeSpan::Format() below but we can't declare
4182 // it locally to the method as it provokes an internal compiler error in egcs
4183 // 2.91.60 when building with -O2
4194 // not all strftime(3) format specifiers make sense here because, for example,
4195 // a time span doesn't have a year nor a timezone
4197 // Here are the ones which are supported (all of them are supported by strftime
4199 // %H hour in 24 hour format
4200 // %M minute (00 - 59)
4201 // %S second (00 - 59)
4204 // Also, for MFC CTimeSpan compatibility, we support
4205 // %D number of days
4207 // And, to be better than MFC :-), we also have
4208 // %E number of wEeks
4209 // %l milliseconds (000 - 999)
4210 wxString
wxTimeSpan::Format(const wxChar
*format
) const
4212 wxCHECK_MSG( format
, wxEmptyString
, _T("NULL format in wxTimeSpan::Format") );
4215 str
.Alloc(wxStrlen(format
));
4217 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
4219 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
4220 // question is what should ts.Format("%S") do? The code here returns "3273"
4221 // in this case (i.e. the total number of seconds, not just seconds % 60)
4222 // because, for me, this call means "give me entire time interval in
4223 // seconds" and not "give me the seconds part of the time interval"
4225 // If we agree that it should behave like this, it is clear that the
4226 // interpretation of each format specifier depends on the presence of the
4227 // other format specs in the string: if there was "%H" before "%M", we
4228 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
4230 // we remember the most important unit found so far
4231 TimeSpanPart partBiggest
= Part_MSec
;
4233 for ( const wxChar
*pch
= format
; *pch
; pch
++ )
4237 if ( ch
== _T('%') )
4239 // the start of the format specification of the printf() below
4240 wxString
fmtPrefix(_T('%'));
4245 // the number of digits for the format string, 0 if unused
4246 unsigned digits
= 0;
4248 ch
= *++pch
; // get the format spec char
4252 wxFAIL_MSG( _T("invalid format character") );
4258 // skip the part below switch
4263 if ( partBiggest
< Part_Day
)
4269 partBiggest
= Part_Day
;
4274 partBiggest
= Part_Week
;
4280 if ( partBiggest
< Part_Hour
)
4284 // the sign has already been taken into account
4285 // when outputting the biggest part
4293 partBiggest
= Part_Hour
;
4300 n
= GetMilliseconds().ToLong();
4301 if ( partBiggest
< Part_MSec
)
4308 //else: no need to reset partBiggest to Part_MSec, it is
4309 // the least significant one anyhow
4316 if ( partBiggest
< Part_Min
)
4325 partBiggest
= Part_Min
;
4332 n
= GetSeconds().ToLong();
4333 if ( partBiggest
< Part_Sec
)
4342 partBiggest
= Part_Sec
;
4351 // negative numbers need one extra position for '-' display
4355 fmtPrefix
<< _T("0") << digits
;
4358 str
+= wxString::Format(fmtPrefix
+ _T("ld"), n
);
4362 // normal character, just copy
4370 // ============================================================================
4371 // wxDateTimeHolidayAuthority and related classes
4372 // ============================================================================
4374 #include "wx/arrimpl.cpp"
4376 WX_DEFINE_OBJARRAY(wxDateTimeArray
)
4378 static int wxCMPFUNC_CONV
4379 wxDateTimeCompareFunc(wxDateTime
**first
, wxDateTime
**second
)
4381 wxDateTime dt1
= **first
,
4384 return dt1
== dt2
? 0 : dt1
< dt2
? -1 : +1;
4387 // ----------------------------------------------------------------------------
4388 // wxDateTimeHolidayAuthority
4389 // ----------------------------------------------------------------------------
4391 wxHolidayAuthoritiesArray
wxDateTimeHolidayAuthority::ms_authorities
;
4394 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime
& dt
)
4396 size_t count
= ms_authorities
.size();
4397 for ( size_t n
= 0; n
< count
; n
++ )
4399 if ( ms_authorities
[n
]->DoIsHoliday(dt
) )
4410 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime
& dtStart
,
4411 const wxDateTime
& dtEnd
,
4412 wxDateTimeArray
& holidays
)
4414 wxDateTimeArray hol
;
4418 const size_t countAuth
= ms_authorities
.size();
4419 for ( size_t nAuth
= 0; nAuth
< countAuth
; nAuth
++ )
4421 ms_authorities
[nAuth
]->DoGetHolidaysInRange(dtStart
, dtEnd
, hol
);
4423 WX_APPEND_ARRAY(holidays
, hol
);
4426 holidays
.Sort(wxDateTimeCompareFunc
);
4428 return holidays
.size();
4432 void wxDateTimeHolidayAuthority::ClearAllAuthorities()
4434 WX_CLEAR_ARRAY(ms_authorities
);
4438 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority
*auth
)
4440 ms_authorities
.push_back(auth
);
4443 wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
4445 // required here for Darwin
4448 // ----------------------------------------------------------------------------
4449 // wxDateTimeWorkDays
4450 // ----------------------------------------------------------------------------
4452 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime
& dt
) const
4454 wxDateTime::WeekDay wd
= dt
.GetWeekDay();
4456 return (wd
== wxDateTime::Sun
) || (wd
== wxDateTime::Sat
);
4459 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime
& dtStart
,
4460 const wxDateTime
& dtEnd
,
4461 wxDateTimeArray
& holidays
) const
4463 if ( dtStart
> dtEnd
)
4465 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
4472 // instead of checking all days, start with the first Sat after dtStart and
4473 // end with the last Sun before dtEnd
4474 wxDateTime dtSatFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sat
),
4475 dtSatLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sat
),
4476 dtSunFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sun
),
4477 dtSunLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sun
),
4480 for ( dt
= dtSatFirst
; dt
<= dtSatLast
; dt
+= wxDateSpan::Week() )
4485 for ( dt
= dtSunFirst
; dt
<= dtSunLast
; dt
+= wxDateSpan::Week() )
4490 return holidays
.GetCount();
4493 // ============================================================================
4494 // other helper functions
4495 // ============================================================================
4497 // ----------------------------------------------------------------------------
4498 // iteration helpers: can be used to write a for loop over enum variable like
4500 // for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
4501 // ----------------------------------------------------------------------------
4503 WXDLLIMPEXP_BASE
void wxNextMonth(wxDateTime::Month
& m
)
4505 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4507 // no wrapping or the for loop above would never end!
4508 m
= (wxDateTime::Month
)(m
+ 1);
4511 WXDLLIMPEXP_BASE
void wxPrevMonth(wxDateTime::Month
& m
)
4513 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4515 m
= m
== wxDateTime::Jan
? wxDateTime::Inv_Month
4516 : (wxDateTime::Month
)(m
- 1);
4519 WXDLLIMPEXP_BASE
void wxNextWDay(wxDateTime::WeekDay
& wd
)
4521 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4523 // no wrapping or the for loop above would never end!
4524 wd
= (wxDateTime::WeekDay
)(wd
+ 1);
4527 WXDLLIMPEXP_BASE
void wxPrevWDay(wxDateTime::WeekDay
& wd
)
4529 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4531 wd
= wd
== wxDateTime::Sun
? wxDateTime::Inv_WeekDay
4532 : (wxDateTime::WeekDay
)(wd
- 1);
4535 #endif // wxUSE_DATETIME