1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/datetime.cpp
3 // Purpose: implementation of time/date related classes
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // parts of code taken from sndcal library by Scott E. Lee:
11 // Copyright 1993-1995, Scott E. Lee, all rights reserved.
12 // Permission granted to use, copy, modify, distribute and sell
13 // so long as the above copyright and this permission statement
14 // are retained in all copies.
16 // Licence: wxWindows licence
17 ///////////////////////////////////////////////////////////////////////////////
20 * Implementation notes:
22 * 1. the time is stored as a 64bit integer containing the signed number of
23 * milliseconds since Jan 1. 1970 (the Unix Epoch) - so it is always
26 * 2. the range is thus something about 580 million years, but due to current
27 * algorithms limitations, only dates from Nov 24, 4714BC are handled
29 * 3. standard ANSI C functions are used to do time calculations whenever
30 * possible, i.e. when the date is in the range Jan 1, 1970 to 2038
32 * 4. otherwise, the calculations are done by converting the date to/from JDN
33 * first (the range limitation mentioned above comes from here: the
34 * algorithm used by Scott E. Lee's code only works for positive JDNs, more
37 * 5. the object constructed for the given DD-MM-YYYY HH:MM:SS corresponds to
38 * this moment in local time and may be converted to the object
39 * corresponding to the same date/time in another time zone by using
42 * 6. the conversions to the current (or any other) timezone are done when the
43 * internal time representation is converted to the broken-down one in
47 // ============================================================================
49 // ============================================================================
51 // ----------------------------------------------------------------------------
53 // ----------------------------------------------------------------------------
55 // For compilers that support precompilation, includes "wx.h".
56 #include "wx/wxprec.h"
62 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
66 #include "wx/msw/wrapwin.h"
68 #include "wx/string.h"
71 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
72 #include "wx/module.h"
75 #include "wx/thread.h"
76 #include "wx/tokenzr.h"
87 #include "wx/datetime.h"
89 const long wxDateTime::TIME_T_FACTOR
= 1000l;
91 #if wxUSE_EXTENDED_RTTI
93 template<> void wxStringReadValue(const wxString
&s
, wxDateTime
&data
)
95 data
.ParseFormat(s
,wxT("%Y-%m-%d %H:%M:%S")) ;
98 template<> void wxStringWriteValue(wxString
&s
, const wxDateTime
&data
)
100 s
= data
.Format(wxT("%Y-%m-%d %H:%M:%S")) ;
103 wxCUSTOM_TYPE_INFO(wxDateTime
, wxToStringConverter
<wxDateTime
> , wxFromStringConverter
<wxDateTime
>)
108 // ----------------------------------------------------------------------------
109 // conditional compilation
110 // ----------------------------------------------------------------------------
112 #if defined(HAVE_STRPTIME) && defined(__GLIBC__) && \
113 ((__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0))
114 // glibc 2.0.7 strptime() is broken - the following snippet causes it to
115 // crash (instead of just failing):
117 // strncpy(buf, "Tue Dec 21 20:25:40 1999", 128);
118 // strptime(buf, "%x", &tm);
122 #endif // broken strptime()
124 #if defined(HAVE_STRPTIME) && defined(__DARWIN__) && defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
125 // configure detects strptime as linkable because it's in the OS X
126 // System library but MSL headers don't declare it.
128 // char *strptime(const char *, const char *, struct tm *);
129 // However, we DON'T want to just provide it here because we would
130 // crash and/or overwrite data when strptime from OS X tries
131 // to fill in MW's struct tm which is two fields shorter (no TZ stuff)
132 // So for now let's just say we don't have strptime
136 #if defined(__MWERKS__) && wxUSE_UNICODE
140 // define a special symbol for VC8 instead of writing tests for 1400 repeatedly
142 #if __VISUALC__ >= 1400
147 #if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM)
148 #if defined(__WXPALMOS__)
149 #define WX_GMTOFF_IN_TM
150 #elif defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
151 #define WX_TIMEZONE _timezone
152 #elif defined(__MWERKS__)
153 long wxmw_timezone
= 28800;
154 #define WX_TIMEZONE wxmw_timezone
155 #elif defined(__DJGPP__) || defined(__WINE__)
156 #include <sys/timeb.h>
158 static long wxGetTimeZone()
160 static long timezone
= MAXLONG
; // invalid timezone
161 if (timezone
== MAXLONG
)
165 timezone
= tb
.timezone
;
169 #define WX_TIMEZONE wxGetTimeZone()
170 #elif defined(__DARWIN__)
171 #define WX_GMTOFF_IN_TM
172 #elif defined(__WXWINCE__) && defined(__VISUALC8__)
173 #define WX_TIMEZONE _timezone
174 #else // unknown platform - try timezone
175 #define WX_TIMEZONE timezone
177 #endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
179 // everyone has strftime except Win CE unless VC8 is used
180 #if !defined(__WXWINCE__) || defined(__VISUALC8__)
181 #define HAVE_STRFTIME
184 // NB: VC8 safe time functions could/should be used for wxMSW as well probably
185 #if defined(__WXWINCE__) && defined(__VISUALC8__)
187 struct tm
*wxLocaltime_r(const time_t *t
, struct tm
* tm
)
190 return _localtime64_s(tm
, &t64
) == 0 ? tm
: NULL
;
193 struct tm
*wxGmtime_r(const time_t* t
, struct tm
* tm
)
196 return _gmtime64_s(tm
, &t64
) == 0 ? tm
: NULL
;
199 #else // !wxWinCE with VC8
201 #if (!defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)) && wxUSE_THREADS && !defined(__WINDOWS__)
202 static wxMutex timeLock
;
205 #ifndef HAVE_LOCALTIME_R
206 struct tm
*wxLocaltime_r(const time_t* ticks
, struct tm
* temp
)
208 #if wxUSE_THREADS && !defined(__WINDOWS__)
209 // No need to waste time with a mutex on windows since it's using
210 // thread local storage for localtime anyway.
211 wxMutexLocker
locker(timeLock
);
213 memcpy(temp
, localtime(ticks
), sizeof(struct tm
));
216 #endif // !HAVE_LOCALTIME_R
218 #ifndef HAVE_GMTIME_R
219 struct tm
*wxGmtime_r(const time_t* ticks
, struct tm
* temp
)
221 #if wxUSE_THREADS && !defined(__WINDOWS__)
222 // No need to waste time with a mutex on windows since it's
223 // using thread local storage for gmtime anyway.
224 wxMutexLocker
locker(timeLock
);
226 memcpy(temp
, gmtime(ticks
), sizeof(struct tm
));
229 #endif // !HAVE_GMTIME_R
231 #endif // wxWinCE with VC8/other platforms
233 // ----------------------------------------------------------------------------
235 // ----------------------------------------------------------------------------
237 // debugging helper: just a convenient replacement of wxCHECK()
238 #define wxDATETIME_CHECK(expr, msg) \
239 wxCHECK2_MSG(expr, *this = wxInvalidDateTime; return *this, msg)
241 // ----------------------------------------------------------------------------
243 // ----------------------------------------------------------------------------
245 class wxDateTimeHolidaysModule
: public wxModule
248 virtual bool OnInit()
250 wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays
);
255 virtual void OnExit()
257 wxDateTimeHolidayAuthority::ClearAllAuthorities();
258 wxDateTimeHolidayAuthority::ms_authorities
.clear();
262 DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule
)
265 IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule
, wxModule
)
267 // ----------------------------------------------------------------------------
269 // ----------------------------------------------------------------------------
272 static const int MONTHS_IN_YEAR
= 12;
274 static const int SEC_PER_MIN
= 60;
276 static const int MIN_PER_HOUR
= 60;
278 static const int HOURS_PER_DAY
= 24;
280 static const long SECONDS_PER_DAY
= 86400l;
282 static const int DAYS_PER_WEEK
= 7;
284 static const long MILLISECONDS_PER_DAY
= 86400000l;
286 // this is the integral part of JDN of the midnight of Jan 1, 1970
287 // (i.e. JDN(Jan 1, 1970) = 2440587.5)
288 static const long EPOCH_JDN
= 2440587l;
290 // used only in asserts
292 // the date of JDN -0.5 (as we don't work with fractional parts, this is the
293 // reference date for us) is Nov 24, 4714BC
294 static const int JDN_0_YEAR
= -4713;
295 static const int JDN_0_MONTH
= wxDateTime::Nov
;
296 static const int JDN_0_DAY
= 24;
297 #endif // __WXDEBUG__
299 // the constants used for JDN calculations
300 static const long JDN_OFFSET
= 32046l;
301 static const long DAYS_PER_5_MONTHS
= 153l;
302 static const long DAYS_PER_4_YEARS
= 1461l;
303 static const long DAYS_PER_400_YEARS
= 146097l;
305 // this array contains the cumulated number of days in all previous months for
306 // normal and leap years
307 static const wxDateTime::wxDateTime_t gs_cumulatedDays
[2][MONTHS_IN_YEAR
] =
309 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
310 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
313 // ----------------------------------------------------------------------------
315 // ----------------------------------------------------------------------------
317 const wxChar
* wxDefaultDateTimeFormat
= wxT("%c");
318 const wxChar
* wxDefaultTimeSpanFormat
= wxT("%H:%M:%S");
320 // in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
321 // indicate an invalid wxDateTime object
322 const wxDateTime wxDefaultDateTime
;
324 wxDateTime::Country
wxDateTime::ms_country
= wxDateTime::Country_Unknown
;
326 // ----------------------------------------------------------------------------
328 // ----------------------------------------------------------------------------
330 // debugger helper: shows what the date really is
332 extern const wxChar
*wxDumpDate(const wxDateTime
* dt
)
334 static wxChar buf
[128];
336 wxStrcpy(buf
, dt
->Format(_T("%Y-%m-%d (%a) %H:%M:%S")));
342 // get the number of days in the given month of the given year
344 wxDateTime::wxDateTime_t
GetNumOfDaysInMonth(int year
, wxDateTime::Month month
)
346 // the number of days in month in Julian/Gregorian calendar: the first line
347 // is for normal years, the second one is for the leap ones
348 static wxDateTime::wxDateTime_t daysInMonth
[2][MONTHS_IN_YEAR
] =
350 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
351 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
354 return daysInMonth
[wxDateTime::IsLeapYear(year
)][month
];
357 // returns the time zone in the C sense, i.e. the difference UTC - local
359 static int GetTimeZone()
361 // set to true when the timezone is set
362 static bool s_timezoneSet
= false;
363 static long gmtoffset
= LONG_MAX
; // invalid timezone
365 // ensure that the timezone variable is set by calling wxLocaltime_r
366 if ( !s_timezoneSet
)
368 // just call wxLocaltime_r() instead of figuring out whether this
369 // system supports tzset(), _tzset() or something else
373 wxLocaltime_r(&t
, &tm
);
374 s_timezoneSet
= true;
376 #ifdef WX_GMTOFF_IN_TM
377 // note that GMT offset is the opposite of time zone and so to return
378 // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM
379 // cases we have to negate it
380 gmtoffset
= -tm
.tm_gmtoff
;
381 #else // !WX_GMTOFF_IN_TM
382 gmtoffset
= WX_TIMEZONE
;
383 #endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
386 return (int)gmtoffset
;
389 // return the integral part of the JDN for the midnight of the given date (to
390 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
391 // noon of the previous day)
392 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day
,
393 wxDateTime::Month mon
,
396 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
398 // check the date validity
400 (year
> JDN_0_YEAR
) ||
401 ((year
== JDN_0_YEAR
) && (mon
> JDN_0_MONTH
)) ||
402 ((year
== JDN_0_YEAR
) && (mon
== JDN_0_MONTH
) && (day
>= JDN_0_DAY
)),
403 _T("date out of range - can't convert to JDN")
406 // make the year positive to avoid problems with negative numbers division
409 // months are counted from March here
411 if ( mon
>= wxDateTime::Mar
)
421 // now we can simply add all the contributions together
422 return ((year
/ 100) * DAYS_PER_400_YEARS
) / 4
423 + ((year
% 100) * DAYS_PER_4_YEARS
) / 4
424 + (month
* DAYS_PER_5_MONTHS
+ 2) / 5
431 // this function is a wrapper around strftime(3) adding error checking
432 static wxString
CallStrftime(const wxChar
*format
, const tm
* tm
)
435 // Create temp wxString here to work around mingw/cygwin bug 1046059
436 // http://sourceforge.net/tracker/?func=detail&atid=102435&aid=1046059&group_id=2435
439 if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) )
441 // buffer is too small?
442 wxFAIL_MSG(_T("strftime() failed"));
449 #endif // HAVE_STRFTIME
453 #if wxUSE_UNIX && !defined(HAVE_STRPTIME_DECL)
454 // configure detected that we had strptime() but not its declaration,
455 // provide it ourselves
456 extern "C" char *strptime(const char *, const char *, struct tm
*);
459 // Unicode-friendly strptime() wrapper
460 static const wxChar
*
461 CallStrptime(const wxChar
*input
, const char *fmt
, tm
*tm
)
463 // the problem here is that strptime() returns pointer into the string we
464 // passed to it while we're really interested in the pointer into the
465 // original, Unicode, string so we try to transform the pointer back
467 wxCharBuffer
inputMB(wxConvertWX2MB(input
));
469 const char * const inputMB
= input
;
470 #endif // Unicode/Ascii
472 const char *result
= strptime(inputMB
, fmt
, tm
);
477 // FIXME: this is wrong in presence of surrogates &c
478 return input
+ (result
- inputMB
.data());
481 #endif // Unicode/Ascii
484 #endif // HAVE_STRPTIME
486 // if year and/or month have invalid values, replace them with the current ones
487 static void ReplaceDefaultYearMonthWithCurrent(int *year
,
488 wxDateTime::Month
*month
)
490 struct tm
*tmNow
= NULL
;
493 if ( *year
== wxDateTime::Inv_Year
)
495 tmNow
= wxDateTime::GetTmNow(&tmstruct
);
497 *year
= 1900 + tmNow
->tm_year
;
500 if ( *month
== wxDateTime::Inv_Month
)
503 tmNow
= wxDateTime::GetTmNow(&tmstruct
);
505 *month
= (wxDateTime::Month
)tmNow
->tm_mon
;
509 // fll the struct tm with default values
510 static void InitTm(struct tm
& tm
)
512 // struct tm may have etxra fields (undocumented and with unportable
513 // names) which, nevertheless, must be set to 0
514 memset(&tm
, 0, sizeof(struct tm
));
516 tm
.tm_mday
= 1; // mday 0 is invalid
517 tm
.tm_year
= 76; // any valid year
518 tm
.tm_isdst
= -1; // auto determine
524 // return the month if the string is a month name or Inv_Month otherwise
525 static wxDateTime::Month
GetMonthFromName(const wxString
& name
, int flags
)
527 wxDateTime::Month mon
;
528 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
530 // case-insensitive comparison either one of or with both abbreviated
532 if ( flags
& wxDateTime::Name_Full
)
534 if ( name
.CmpNoCase(wxDateTime::
535 GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 )
541 if ( flags
& wxDateTime::Name_Abbr
)
543 if ( name
.CmpNoCase(wxDateTime::
544 GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 )
554 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
555 static wxDateTime::WeekDay
GetWeekDayFromName(const wxString
& name
, int flags
)
557 wxDateTime::WeekDay wd
;
558 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
560 // case-insensitive comparison either one of or with both abbreviated
562 if ( flags
& wxDateTime::Name_Full
)
564 if ( name
.CmpNoCase(wxDateTime::
565 GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 )
571 if ( flags
& wxDateTime::Name_Abbr
)
573 if ( name
.CmpNoCase(wxDateTime::
574 GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 )
585 struct tm
*wxDateTime::GetTmNow(struct tm
*tmstruct
)
587 time_t t
= GetTimeNow();
588 return wxLocaltime_r(&t
, tmstruct
);
591 // scans all digits (but no more than len) and returns the resulting number
592 static bool GetNumericToken(size_t len
, const wxChar
*& p
, unsigned long *number
)
596 while ( wxIsdigit(*p
) )
600 if ( len
&& ++n
> len
)
604 return !s
.empty() && s
.ToULong(number
);
607 // scans all alphabetic characters and returns the resulting string
608 static wxString
GetAlphaToken(const wxChar
*& p
)
611 while ( wxIsalpha(*p
) )
619 // ============================================================================
620 // implementation of wxDateTime
621 // ============================================================================
623 // ----------------------------------------------------------------------------
625 // ----------------------------------------------------------------------------
629 year
= (wxDateTime_t
)wxDateTime::Inv_Year
;
630 mon
= wxDateTime::Inv_Month
;
632 hour
= min
= sec
= msec
= 0;
633 wday
= wxDateTime::Inv_WeekDay
;
636 wxDateTime::Tm::Tm(const struct tm
& tm
, const TimeZone
& tz
)
640 sec
= (wxDateTime::wxDateTime_t
)tm
.tm_sec
;
641 min
= (wxDateTime::wxDateTime_t
)tm
.tm_min
;
642 hour
= (wxDateTime::wxDateTime_t
)tm
.tm_hour
;
643 mday
= (wxDateTime::wxDateTime_t
)tm
.tm_mday
;
644 mon
= (wxDateTime::Month
)tm
.tm_mon
;
645 year
= 1900 + tm
.tm_year
;
646 wday
= (wxDateTime::wxDateTime_t
)tm
.tm_wday
;
647 yday
= (wxDateTime::wxDateTime_t
)tm
.tm_yday
;
650 bool wxDateTime::Tm::IsValid() const
652 // we allow for the leap seconds, although we don't use them (yet)
653 return (year
!= wxDateTime::Inv_Year
) && (mon
!= wxDateTime::Inv_Month
) &&
654 (mday
<= GetNumOfDaysInMonth(year
, mon
)) &&
655 (hour
< 24) && (min
< 60) && (sec
< 62) && (msec
< 1000);
658 void wxDateTime::Tm::ComputeWeekDay()
660 // compute the week day from day/month/year: we use the dumbest algorithm
661 // possible: just compute our JDN and then use the (simple to derive)
662 // formula: weekday = (JDN + 1.5) % 7
663 wday
= (wxDateTime::wxDateTime_t
)((wxDateTime::WeekDay
)(GetTruncatedJDN(mday
, mon
, year
) + 2) % 7);
666 void wxDateTime::Tm::AddMonths(int monDiff
)
668 // normalize the months field
669 while ( monDiff
< -mon
)
673 monDiff
+= MONTHS_IN_YEAR
;
676 while ( monDiff
+ mon
>= MONTHS_IN_YEAR
)
680 monDiff
-= MONTHS_IN_YEAR
;
683 mon
= (wxDateTime::Month
)(mon
+ monDiff
);
685 wxASSERT_MSG( mon
>= 0 && mon
< MONTHS_IN_YEAR
, _T("logic error") );
687 // NB: we don't check here that the resulting date is valid, this function
688 // is private and the caller must check it if needed
691 void wxDateTime::Tm::AddDays(int dayDiff
)
693 // normalize the days field
694 while ( dayDiff
+ mday
< 1 )
698 dayDiff
+= GetNumOfDaysInMonth(year
, mon
);
701 mday
= (wxDateTime::wxDateTime_t
)( mday
+ dayDiff
);
702 while ( mday
> GetNumOfDaysInMonth(year
, mon
) )
704 mday
-= GetNumOfDaysInMonth(year
, mon
);
709 wxASSERT_MSG( mday
> 0 && mday
<= GetNumOfDaysInMonth(year
, mon
),
713 // ----------------------------------------------------------------------------
715 // ----------------------------------------------------------------------------
717 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz
)
721 case wxDateTime::Local
:
722 // get the offset from C RTL: it returns the difference GMT-local
723 // while we want to have the offset _from_ GMT, hence the '-'
724 m_offset
= -GetTimeZone();
727 case wxDateTime::GMT_12
:
728 case wxDateTime::GMT_11
:
729 case wxDateTime::GMT_10
:
730 case wxDateTime::GMT_9
:
731 case wxDateTime::GMT_8
:
732 case wxDateTime::GMT_7
:
733 case wxDateTime::GMT_6
:
734 case wxDateTime::GMT_5
:
735 case wxDateTime::GMT_4
:
736 case wxDateTime::GMT_3
:
737 case wxDateTime::GMT_2
:
738 case wxDateTime::GMT_1
:
739 m_offset
= -3600*(wxDateTime::GMT0
- tz
);
742 case wxDateTime::GMT0
:
743 case wxDateTime::GMT1
:
744 case wxDateTime::GMT2
:
745 case wxDateTime::GMT3
:
746 case wxDateTime::GMT4
:
747 case wxDateTime::GMT5
:
748 case wxDateTime::GMT6
:
749 case wxDateTime::GMT7
:
750 case wxDateTime::GMT8
:
751 case wxDateTime::GMT9
:
752 case wxDateTime::GMT10
:
753 case wxDateTime::GMT11
:
754 case wxDateTime::GMT12
:
755 case wxDateTime::GMT13
:
756 m_offset
= 3600*(tz
- wxDateTime::GMT0
);
759 case wxDateTime::A_CST
:
760 // Central Standard Time in use in Australia = UTC + 9.5
761 m_offset
= 60l*(9*MIN_PER_HOUR
+ MIN_PER_HOUR
/2);
765 wxFAIL_MSG( _T("unknown time zone") );
769 // ----------------------------------------------------------------------------
771 // ----------------------------------------------------------------------------
774 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
)
776 if ( year
== Inv_Year
)
777 year
= GetCurrentYear();
779 if ( cal
== Gregorian
)
781 // in Gregorian calendar leap years are those divisible by 4 except
782 // those divisible by 100 unless they're also divisible by 400
783 // (in some countries, like Russia and Greece, additional corrections
784 // exist, but they won't manifest themselves until 2700)
785 return (year
% 4 == 0) && ((year
% 100 != 0) || (year
% 400 == 0));
787 else if ( cal
== Julian
)
789 // in Julian calendar the rule is simpler
790 return year
% 4 == 0;
794 wxFAIL_MSG(_T("unknown calendar"));
801 int wxDateTime::GetCentury(int year
)
803 return year
> 0 ? year
/ 100 : year
/ 100 - 1;
807 int wxDateTime::ConvertYearToBC(int year
)
810 return year
> 0 ? year
: year
- 1;
814 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
)
819 return Now().GetYear();
822 wxFAIL_MSG(_T("TODO"));
826 wxFAIL_MSG(_T("unsupported calendar"));
834 wxDateTime::Month
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
)
839 return Now().GetMonth();
842 wxFAIL_MSG(_T("TODO"));
846 wxFAIL_MSG(_T("unsupported calendar"));
854 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(int year
, Calendar cal
)
856 if ( year
== Inv_Year
)
858 // take the current year if none given
859 year
= GetCurrentYear();
866 return IsLeapYear(year
) ? 366 : 365;
869 wxFAIL_MSG(_T("unsupported calendar"));
877 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(wxDateTime::Month month
,
879 wxDateTime::Calendar cal
)
881 wxCHECK_MSG( month
< MONTHS_IN_YEAR
, 0, _T("invalid month") );
883 if ( cal
== Gregorian
|| cal
== Julian
)
885 if ( year
== Inv_Year
)
887 // take the current year if none given
888 year
= GetCurrentYear();
891 return GetNumOfDaysInMonth(year
, month
);
895 wxFAIL_MSG(_T("unsupported calendar"));
902 wxString
wxDateTime::GetMonthName(wxDateTime::Month month
,
903 wxDateTime::NameFlags flags
)
905 wxCHECK_MSG( month
!= Inv_Month
, wxEmptyString
, _T("invalid month") );
907 // notice that we must set all the fields to avoid confusing libc (GNU one
908 // gets confused to a crash if we don't do this)
913 return CallStrftime(flags
== Name_Abbr
? _T("%b") : _T("%B"), &tm
);
914 #else // !HAVE_STRFTIME
919 ret
= (flags
== Name_Abbr
? wxT("Jan"): wxT("January"));
922 ret
= (flags
== Name_Abbr
? wxT("Feb"): wxT("Febuary"));
925 ret
= (flags
== Name_Abbr
? wxT("Mar"): wxT("March"));
928 ret
= (flags
== Name_Abbr
? wxT("Apr"): wxT("April"));
931 ret
= (flags
== Name_Abbr
? wxT("May"): wxT("May"));
934 ret
= (flags
== Name_Abbr
? wxT("Jun"): wxT("June"));
937 ret
= (flags
== Name_Abbr
? wxT("Jul"): wxT("July"));
940 ret
= (flags
== Name_Abbr
? wxT("Aug"): wxT("August"));
943 ret
= (flags
== Name_Abbr
? wxT("Sep"): wxT("September"));
946 ret
= (flags
== Name_Abbr
? wxT("Oct"): wxT("October"));
949 ret
= (flags
== Name_Abbr
? wxT("Nov"): wxT("November"));
952 ret
= (flags
== Name_Abbr
? wxT("Dec"): wxT("December"));
956 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
960 wxString
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
,
961 wxDateTime::NameFlags flags
)
963 wxCHECK_MSG( wday
!= Inv_WeekDay
, wxEmptyString
, _T("invalid weekday") );
965 // take some arbitrary Sunday (but notice that the day should be such that
966 // after adding wday to it below we still have a valid date, e.g. don't
974 // and offset it by the number of days needed to get the correct wday
977 // call mktime() to normalize it...
980 // ... and call strftime()
981 return CallStrftime(flags
== Name_Abbr
? _T("%a") : _T("%A"), &tm
);
982 #else // !HAVE_STRFTIME
987 ret
= (flags
== Name_Abbr
? wxT("Sun") : wxT("Sunday"));
990 ret
= (flags
== Name_Abbr
? wxT("Mon") : wxT("Monday"));
993 ret
= (flags
== Name_Abbr
? wxT("Tue") : wxT("Tuesday"));
996 ret
= (flags
== Name_Abbr
? wxT("Wed") : wxT("Wednesday"));
999 ret
= (flags
== Name_Abbr
? wxT("Thu") : wxT("Thursday"));
1002 ret
= (flags
== Name_Abbr
? wxT("Fri") : wxT("Friday"));
1005 ret
= (flags
== Name_Abbr
? wxT("Sat") : wxT("Saturday"));
1009 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
1013 void wxDateTime::GetAmPmStrings(wxString
*am
, wxString
*pm
)
1018 // @Note: Do not call 'CallStrftime' here! CallStrftime checks the return code
1019 // and causes an assertion failed if the buffer is to small (which is good) - OR -
1020 // if strftime does not return anything because the format string is invalid - OR -
1021 // if there are no 'am' / 'pm' tokens defined for the current locale (which is not good).
1022 // wxDateTime::ParseTime will try several different formats to parse the time.
1023 // As a result, GetAmPmStrings might get called, even if the current locale
1024 // does not define any 'am' / 'pm' tokens. In this case, wxStrftime would
1025 // assert, even though it is a perfectly legal use.
1028 if (wxStrftime(buffer
, sizeof(buffer
)/sizeof(wxChar
), _T("%p"), &tm
) > 0)
1029 *am
= wxString(buffer
);
1036 if (wxStrftime(buffer
, sizeof(buffer
)/sizeof(wxChar
), _T("%p"), &tm
) > 0)
1037 *pm
= wxString(buffer
);
1043 // ----------------------------------------------------------------------------
1044 // Country stuff: date calculations depend on the country (DST, work days,
1045 // ...), so we need to know which rules to follow.
1046 // ----------------------------------------------------------------------------
1049 wxDateTime::Country
wxDateTime::GetCountry()
1051 // TODO use LOCALE_ICOUNTRY setting under Win32
1053 if ( ms_country
== Country_Unknown
)
1055 // try to guess from the time zone name
1056 time_t t
= time(NULL
);
1058 struct tm
*tm
= wxLocaltime_r(&t
, &tmstruct
);
1060 wxString tz
= CallStrftime(_T("%Z"), tm
);
1061 if ( tz
== _T("WET") || tz
== _T("WEST") )
1065 else if ( tz
== _T("CET") || tz
== _T("CEST") )
1067 ms_country
= Country_EEC
;
1069 else if ( tz
== _T("MSK") || tz
== _T("MSD") )
1071 ms_country
= Russia
;
1073 else if ( tz
== _T("AST") || tz
== _T("ADT") ||
1074 tz
== _T("EST") || tz
== _T("EDT") ||
1075 tz
== _T("CST") || tz
== _T("CDT") ||
1076 tz
== _T("MST") || tz
== _T("MDT") ||
1077 tz
== _T("PST") || tz
== _T("PDT") )
1083 // well, choose a default one
1087 #else // __WXWINCE__
1089 #endif // !__WXWINCE__/__WXWINCE__
1095 void wxDateTime::SetCountry(wxDateTime::Country country
)
1097 ms_country
= country
;
1101 bool wxDateTime::IsWestEuropeanCountry(Country country
)
1103 if ( country
== Country_Default
)
1105 country
= GetCountry();
1108 return (Country_WesternEurope_Start
<= country
) &&
1109 (country
<= Country_WesternEurope_End
);
1112 // ----------------------------------------------------------------------------
1113 // DST calculations: we use 3 different rules for the West European countries,
1114 // USA and for the rest of the world. This is undoubtedly false for many
1115 // countries, but I lack the necessary info (and the time to gather it),
1116 // please add the other rules here!
1117 // ----------------------------------------------------------------------------
1120 bool wxDateTime::IsDSTApplicable(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();
1137 // DST was first observed in the US and UK during WWI, reused
1138 // during WWII and used again since 1966
1139 return year
>= 1966 ||
1140 (year
>= 1942 && year
<= 1945) ||
1141 (year
== 1918 || year
== 1919);
1144 // assume that it started after WWII
1150 wxDateTime
wxDateTime::GetBeginDST(int year
, Country country
)
1152 if ( year
== Inv_Year
)
1154 // take the current year if none given
1155 year
= GetCurrentYear();
1158 if ( country
== Country_Default
)
1160 country
= GetCountry();
1163 if ( !IsDSTApplicable(year
, country
) )
1165 return wxInvalidDateTime
;
1170 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1172 // DST begins at 1 a.m. GMT on the last Sunday of March
1173 if ( !dt
.SetToLastWeekDay(Sun
, Mar
, year
) )
1176 wxFAIL_MSG( _T("no last Sunday in March?") );
1179 dt
+= wxTimeSpan::Hours(1);
1181 // disable DST tests because it could result in an infinite recursion!
1184 else switch ( country
)
1191 // don't know for sure - assume it was in effect all year
1196 dt
.Set(1, Jan
, year
);
1200 // DST was installed Feb 2, 1942 by the Congress
1201 dt
.Set(2, Feb
, year
);
1204 // Oil embargo changed the DST period in the US
1206 dt
.Set(6, Jan
, 1974);
1210 dt
.Set(23, Feb
, 1975);
1214 // before 1986, DST begun on the last Sunday of April, but
1215 // in 1986 Reagan changed it to begin at 2 a.m. of the
1216 // first Sunday in April
1219 if ( !dt
.SetToLastWeekDay(Sun
, Apr
, year
) )
1222 wxFAIL_MSG( _T("no first Sunday in April?") );
1227 if ( !dt
.SetToWeekDay(Sun
, 1, Apr
, year
) )
1230 wxFAIL_MSG( _T("no first Sunday in April?") );
1234 dt
+= wxTimeSpan::Hours(2);
1236 // TODO what about timezone??
1242 // assume Mar 30 as the start of the DST for the rest of the world
1243 // - totally bogus, of course
1244 dt
.Set(30, Mar
, year
);
1251 wxDateTime
wxDateTime::GetEndDST(int year
, Country country
)
1253 if ( year
== Inv_Year
)
1255 // take the current year if none given
1256 year
= GetCurrentYear();
1259 if ( country
== Country_Default
)
1261 country
= GetCountry();
1264 if ( !IsDSTApplicable(year
, country
) )
1266 return wxInvalidDateTime
;
1271 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1273 // DST ends at 1 a.m. GMT on the last Sunday of October
1274 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1276 // weirder and weirder...
1277 wxFAIL_MSG( _T("no last Sunday in October?") );
1280 dt
+= wxTimeSpan::Hours(1);
1282 // disable DST tests because it could result in an infinite recursion!
1285 else switch ( country
)
1292 // don't know for sure - assume it was in effect all year
1296 dt
.Set(31, Dec
, year
);
1300 // the time was reset after the end of the WWII
1301 dt
.Set(30, Sep
, year
);
1305 // DST ends at 2 a.m. on the last Sunday of October
1306 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1308 // weirder and weirder...
1309 wxFAIL_MSG( _T("no last Sunday in October?") );
1312 dt
+= wxTimeSpan::Hours(2);
1314 // TODO what about timezone??
1319 // assume October 26th as the end of the DST - totally bogus too
1320 dt
.Set(26, Oct
, year
);
1326 // ----------------------------------------------------------------------------
1327 // constructors and assignment operators
1328 // ----------------------------------------------------------------------------
1330 // return the current time with ms precision
1331 /* static */ wxDateTime
wxDateTime::UNow()
1333 return wxDateTime(wxGetLocalTimeMillis());
1336 // the values in the tm structure contain the local time
1337 wxDateTime
& wxDateTime::Set(const struct tm
& tm
)
1340 time_t timet
= mktime(&tm2
);
1342 if ( timet
== (time_t)-1 )
1344 // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
1345 // less than timezone - try to make it work for this case
1346 if ( tm2
.tm_year
== 70 && tm2
.tm_mon
== 0 && tm2
.tm_mday
== 1 )
1348 return Set((time_t)(
1350 tm2
.tm_hour
* MIN_PER_HOUR
* SEC_PER_MIN
+
1351 tm2
.tm_min
* SEC_PER_MIN
+
1355 wxFAIL_MSG( _T("mktime() failed") );
1357 *this = wxInvalidDateTime
;
1367 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
,
1368 wxDateTime_t minute
,
1369 wxDateTime_t second
,
1370 wxDateTime_t millisec
)
1372 // we allow seconds to be 61 to account for the leap seconds, even if we
1373 // don't use them really
1374 wxDATETIME_CHECK( hour
< 24 &&
1378 _T("Invalid time in wxDateTime::Set()") );
1380 // get the current date from system
1382 struct tm
*tm
= GetTmNow(&tmstruct
);
1384 wxDATETIME_CHECK( tm
, _T("wxLocaltime_r() failed") );
1386 // make a copy so it isn't clobbered by the call to mktime() below
1391 tm1
.tm_min
= minute
;
1392 tm1
.tm_sec
= second
;
1394 // and the DST in case it changes on this date
1397 if ( tm2
.tm_isdst
!= tm1
.tm_isdst
)
1398 tm1
.tm_isdst
= tm2
.tm_isdst
;
1402 // and finally adjust milliseconds
1403 return SetMillisecond(millisec
);
1406 wxDateTime
& wxDateTime::Set(wxDateTime_t day
,
1410 wxDateTime_t minute
,
1411 wxDateTime_t second
,
1412 wxDateTime_t millisec
)
1414 wxDATETIME_CHECK( hour
< 24 &&
1418 _T("Invalid time in wxDateTime::Set()") );
1420 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1422 wxDATETIME_CHECK( (0 < day
) && (day
<= GetNumberOfDays(month
, year
)),
1423 _T("Invalid date in wxDateTime::Set()") );
1425 // the range of time_t type (inclusive)
1426 static const int yearMinInRange
= 1970;
1427 static const int yearMaxInRange
= 2037;
1429 // test only the year instead of testing for the exact end of the Unix
1430 // time_t range - it doesn't bring anything to do more precise checks
1431 if ( year
>= yearMinInRange
&& year
<= yearMaxInRange
)
1433 // use the standard library version if the date is in range - this is
1434 // probably more efficient than our code
1436 tm
.tm_year
= year
- 1900;
1442 tm
.tm_isdst
= -1; // mktime() will guess it
1446 // and finally adjust milliseconds
1448 SetMillisecond(millisec
);
1454 // do time calculations ourselves: we want to calculate the number of
1455 // milliseconds between the given date and the epoch
1457 // get the JDN for the midnight of this day
1458 m_time
= GetTruncatedJDN(day
, month
, year
);
1459 m_time
-= EPOCH_JDN
;
1460 m_time
*= SECONDS_PER_DAY
* TIME_T_FACTOR
;
1462 // JDN corresponds to GMT, we take localtime
1463 Add(wxTimeSpan(hour
, minute
, second
+ GetTimeZone(), millisec
));
1469 wxDateTime
& wxDateTime::Set(double jdn
)
1471 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1473 jdn
-= EPOCH_JDN
+ 0.5;
1475 m_time
.Assign(jdn
*MILLISECONDS_PER_DAY
);
1477 // JDNs always are in UTC, so we don't need any adjustments for time zone
1482 wxDateTime
& wxDateTime::ResetTime()
1486 if ( tm
.hour
|| tm
.min
|| tm
.sec
|| tm
.msec
)
1499 wxDateTime
wxDateTime::GetDateOnly() const
1506 return wxDateTime(tm
);
1509 // ----------------------------------------------------------------------------
1510 // DOS Date and Time Format functions
1511 // ----------------------------------------------------------------------------
1512 // the dos date and time value is an unsigned 32 bit value in the format:
1513 // YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
1515 // Y = year offset from 1980 (0-127)
1517 // D = day of month (1-31)
1519 // m = minute (0-59)
1520 // s = bisecond (0-29) each bisecond indicates two seconds
1521 // ----------------------------------------------------------------------------
1523 wxDateTime
& wxDateTime::SetFromDOS(unsigned long ddt
)
1528 long year
= ddt
& 0xFE000000;
1533 long month
= ddt
& 0x1E00000;
1538 long day
= ddt
& 0x1F0000;
1542 long hour
= ddt
& 0xF800;
1546 long minute
= ddt
& 0x7E0;
1550 long second
= ddt
& 0x1F;
1551 tm
.tm_sec
= second
* 2;
1553 return Set(mktime(&tm
));
1556 unsigned long wxDateTime::GetAsDOS() const
1559 time_t ticks
= GetTicks();
1561 struct tm
*tm
= wxLocaltime_r(&ticks
, &tmstruct
);
1563 long year
= tm
->tm_year
;
1567 long month
= tm
->tm_mon
;
1571 long day
= tm
->tm_mday
;
1574 long hour
= tm
->tm_hour
;
1577 long minute
= tm
->tm_min
;
1580 long second
= tm
->tm_sec
;
1583 ddt
= year
| month
| day
| hour
| minute
| second
;
1587 // ----------------------------------------------------------------------------
1588 // time_t <-> broken down time conversions
1589 // ----------------------------------------------------------------------------
1591 wxDateTime::Tm
wxDateTime::GetTm(const TimeZone
& tz
) const
1593 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1595 time_t time
= GetTicks();
1596 if ( time
!= (time_t)-1 )
1598 // use C RTL functions
1601 if ( tz
.GetOffset() == -GetTimeZone() )
1603 // we are working with local time
1604 tm
= wxLocaltime_r(&time
, &tmstruct
);
1606 // should never happen
1607 wxCHECK_MSG( tm
, Tm(), _T("wxLocaltime_r() failed") );
1611 time
+= (time_t)tz
.GetOffset();
1612 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
1613 int time2
= (int) time
;
1619 tm
= wxGmtime_r(&time
, &tmstruct
);
1621 // should never happen
1622 wxCHECK_MSG( tm
, Tm(), _T("wxGmtime_r() failed") );
1626 tm
= (struct tm
*)NULL
;
1632 // adjust the milliseconds
1634 long timeOnly
= (m_time
% MILLISECONDS_PER_DAY
).ToLong();
1635 tm2
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1638 //else: use generic code below
1641 // remember the time and do the calculations with the date only - this
1642 // eliminates rounding errors of the floating point arithmetics
1644 wxLongLong timeMidnight
= m_time
+ tz
.GetOffset() * 1000;
1646 long timeOnly
= (timeMidnight
% MILLISECONDS_PER_DAY
).ToLong();
1648 // we want to always have positive time and timeMidnight to be really
1649 // the midnight before it
1652 timeOnly
= MILLISECONDS_PER_DAY
+ timeOnly
;
1655 timeMidnight
-= timeOnly
;
1657 // calculate the Gregorian date from JDN for the midnight of our date:
1658 // this will yield day, month (in 1..12 range) and year
1660 // actually, this is the JDN for the noon of the previous day
1661 long jdn
= (timeMidnight
/ MILLISECONDS_PER_DAY
).ToLong() + EPOCH_JDN
;
1663 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1665 wxASSERT_MSG( jdn
> -2, _T("JDN out of range") );
1667 // calculate the century
1668 long temp
= (jdn
+ JDN_OFFSET
) * 4 - 1;
1669 long century
= temp
/ DAYS_PER_400_YEARS
;
1671 // then the year and day of year (1 <= dayOfYear <= 366)
1672 temp
= ((temp
% DAYS_PER_400_YEARS
) / 4) * 4 + 3;
1673 long year
= (century
* 100) + (temp
/ DAYS_PER_4_YEARS
);
1674 long dayOfYear
= (temp
% DAYS_PER_4_YEARS
) / 4 + 1;
1676 // and finally the month and day of the month
1677 temp
= dayOfYear
* 5 - 3;
1678 long month
= temp
/ DAYS_PER_5_MONTHS
;
1679 long day
= (temp
% DAYS_PER_5_MONTHS
) / 5 + 1;
1681 // month is counted from March - convert to normal
1692 // year is offset by 4800
1695 // check that the algorithm gave us something reasonable
1696 wxASSERT_MSG( (0 < month
) && (month
<= 12), _T("invalid month") );
1697 wxASSERT_MSG( (1 <= day
) && (day
< 32), _T("invalid day") );
1699 // construct Tm from these values
1701 tm
.year
= (int)year
;
1702 tm
.mon
= (Month
)(month
- 1); // algorithm yields 1 for January, not 0
1703 tm
.mday
= (wxDateTime_t
)day
;
1704 tm
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1705 timeOnly
-= tm
.msec
;
1706 timeOnly
/= 1000; // now we have time in seconds
1708 tm
.sec
= (wxDateTime_t
)(timeOnly
% SEC_PER_MIN
);
1710 timeOnly
/= SEC_PER_MIN
; // now we have time in minutes
1712 tm
.min
= (wxDateTime_t
)(timeOnly
% MIN_PER_HOUR
);
1715 tm
.hour
= (wxDateTime_t
)(timeOnly
/ MIN_PER_HOUR
);
1720 wxDateTime
& wxDateTime::SetYear(int year
)
1722 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1731 wxDateTime
& wxDateTime::SetMonth(Month month
)
1733 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1742 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
)
1744 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1753 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
)
1755 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1764 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
)
1766 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1775 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
)
1777 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1786 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
)
1788 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1790 // we don't need to use GetTm() for this one
1791 m_time
-= m_time
% 1000l;
1792 m_time
+= millisecond
;
1797 // ----------------------------------------------------------------------------
1798 // wxDateTime arithmetics
1799 // ----------------------------------------------------------------------------
1801 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
)
1805 tm
.year
+= diff
.GetYears();
1806 tm
.AddMonths(diff
.GetMonths());
1808 // check that the resulting date is valid
1809 if ( tm
.mday
> GetNumOfDaysInMonth(tm
.year
, tm
.mon
) )
1811 // We suppose that when adding one month to Jan 31 we want to get Feb
1812 // 28 (or 29), i.e. adding a month to the last day of the month should
1813 // give the last day of the next month which is quite logical.
1815 // Unfortunately, there is no logic way to understand what should
1816 // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1817 // We make it Feb 28 (last day too), but it is highly questionable.
1818 tm
.mday
= GetNumOfDaysInMonth(tm
.year
, tm
.mon
);
1821 tm
.AddDays(diff
.GetTotalDays());
1825 wxASSERT_MSG( IsSameTime(tm
),
1826 _T("Add(wxDateSpan) shouldn't modify time") );
1831 // ----------------------------------------------------------------------------
1832 // Weekday and monthday stuff
1833 // ----------------------------------------------------------------------------
1835 // convert Sun, Mon, ..., Sat into 6, 0, ..., 5
1836 static inline int ConvertWeekDayToMondayBase(int wd
)
1838 return wd
== wxDateTime::Sun
? 6 : wd
- 1;
1843 wxDateTime::SetToWeekOfYear(int year
, wxDateTime_t numWeek
, WeekDay wd
)
1845 wxASSERT_MSG( numWeek
> 0,
1846 _T("invalid week number: weeks are counted from 1") );
1848 // Jan 4 always lies in the 1st week of the year
1849 wxDateTime
dt(4, Jan
, year
);
1850 dt
.SetToWeekDayInSameWeek(wd
);
1851 dt
+= wxDateSpan::Weeks(numWeek
- 1);
1856 #if WXWIN_COMPATIBILITY_2_6
1857 // use a separate function to avoid warnings about using deprecated
1858 // SetToTheWeek in GetWeek below
1860 SetToTheWeek(int year
,
1861 wxDateTime::wxDateTime_t numWeek
,
1862 wxDateTime::WeekDay weekday
,
1863 wxDateTime::WeekFlags flags
)
1865 // Jan 4 always lies in the 1st week of the year
1866 wxDateTime
dt(4, wxDateTime::Jan
, year
);
1867 dt
.SetToWeekDayInSameWeek(weekday
, flags
);
1868 dt
+= wxDateSpan::Weeks(numWeek
- 1);
1873 bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek
,
1877 int year
= GetYear();
1878 *this = ::SetToTheWeek(year
, numWeek
, weekday
, flags
);
1879 if ( GetYear() != year
)
1881 // oops... numWeek was too big
1888 wxDateTime
wxDateTime::GetWeek(wxDateTime_t numWeek
,
1890 WeekFlags flags
) const
1892 return ::SetToTheWeek(GetYear(), numWeek
, weekday
, flags
);
1894 #endif // WXWIN_COMPATIBILITY_2_6
1896 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
,
1899 // take the current month/year if none specified
1900 if ( year
== Inv_Year
)
1902 if ( month
== Inv_Month
)
1905 return Set(GetNumOfDaysInMonth(year
, month
), month
, year
);
1908 wxDateTime
& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday
, WeekFlags flags
)
1910 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1912 int wdayDst
= weekday
,
1913 wdayThis
= GetWeekDay();
1914 if ( wdayDst
== wdayThis
)
1920 if ( flags
== Default_First
)
1922 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
1925 // the logic below based on comparing weekday and wdayThis works if Sun (0)
1926 // is the first day in the week, but breaks down for Monday_First case so
1927 // we adjust the week days in this case
1928 if ( flags
== Monday_First
)
1930 if ( wdayThis
== Sun
)
1932 if ( wdayDst
== Sun
)
1935 //else: Sunday_First, nothing to do
1937 // go forward or back in time to the day we want
1938 if ( wdayDst
< wdayThis
)
1940 return Subtract(wxDateSpan::Days(wdayThis
- wdayDst
));
1942 else // weekday > wdayThis
1944 return Add(wxDateSpan::Days(wdayDst
- wdayThis
));
1948 wxDateTime
& wxDateTime::SetToNextWeekDay(WeekDay weekday
)
1950 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1953 WeekDay wdayThis
= GetWeekDay();
1954 if ( weekday
== wdayThis
)
1959 else if ( weekday
< wdayThis
)
1961 // need to advance a week
1962 diff
= 7 - (wdayThis
- weekday
);
1964 else // weekday > wdayThis
1966 diff
= weekday
- wdayThis
;
1969 return Add(wxDateSpan::Days(diff
));
1972 wxDateTime
& wxDateTime::SetToPrevWeekDay(WeekDay weekday
)
1974 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1977 WeekDay wdayThis
= GetWeekDay();
1978 if ( weekday
== wdayThis
)
1983 else if ( weekday
> wdayThis
)
1985 // need to go to previous week
1986 diff
= 7 - (weekday
- wdayThis
);
1988 else // weekday < wdayThis
1990 diff
= wdayThis
- weekday
;
1993 return Subtract(wxDateSpan::Days(diff
));
1996 bool wxDateTime::SetToWeekDay(WeekDay weekday
,
2001 wxCHECK_MSG( weekday
!= Inv_WeekDay
, false, _T("invalid weekday") );
2003 // we don't check explicitly that -5 <= n <= 5 because we will return false
2004 // anyhow in such case - but may be should still give an assert for it?
2006 // take the current month/year if none specified
2007 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
2011 // TODO this probably could be optimised somehow...
2015 // get the first day of the month
2016 dt
.Set(1, month
, year
);
2019 WeekDay wdayFirst
= dt
.GetWeekDay();
2021 // go to the first weekday of the month
2022 int diff
= weekday
- wdayFirst
;
2026 // add advance n-1 weeks more
2029 dt
+= wxDateSpan::Days(diff
);
2031 else // count from the end of the month
2033 // get the last day of the month
2034 dt
.SetToLastMonthDay(month
, year
);
2037 WeekDay wdayLast
= dt
.GetWeekDay();
2039 // go to the last weekday of the month
2040 int diff
= wdayLast
- weekday
;
2044 // and rewind n-1 weeks from there
2047 dt
-= wxDateSpan::Days(diff
);
2050 // check that it is still in the same month
2051 if ( dt
.GetMonth() == month
)
2059 // no such day in this month
2065 wxDateTime::wxDateTime_t
GetDayOfYearFromTm(const wxDateTime::Tm
& tm
)
2067 return (wxDateTime::wxDateTime_t
)(gs_cumulatedDays
[wxDateTime::IsLeapYear(tm
.year
)][tm
.mon
] + tm
.mday
);
2070 wxDateTime::wxDateTime_t
wxDateTime::GetDayOfYear(const TimeZone
& tz
) const
2072 return GetDayOfYearFromTm(GetTm(tz
));
2075 wxDateTime::wxDateTime_t
2076 wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags
, const TimeZone
& tz
) const
2078 if ( flags
== Default_First
)
2080 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
2084 wxDateTime_t nDayInYear
= GetDayOfYearFromTm(tm
);
2086 int wdTarget
= GetWeekDay(tz
);
2087 int wdYearStart
= wxDateTime(1, Jan
, GetYear()).GetWeekDay();
2089 if ( flags
== Sunday_First
)
2091 // FIXME: First week is not calculated correctly.
2092 week
= (nDayInYear
- wdTarget
+ 7) / 7;
2093 if ( wdYearStart
== Wed
|| wdYearStart
== Thu
)
2096 else // week starts with monday
2098 // adjust the weekdays to non-US style.
2099 wdYearStart
= ConvertWeekDayToMondayBase(wdYearStart
);
2100 wdTarget
= ConvertWeekDayToMondayBase(wdTarget
);
2102 // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html:
2104 // Week 01 of a year is per definition the first week that has the
2105 // Thursday in this year, which is equivalent to the week that
2106 // contains the fourth day of January. In other words, the first
2107 // week of a new year is the week that has the majority of its
2108 // days in the new year. Week 01 might also contain days from the
2109 // previous year and the week before week 01 of a year is the last
2110 // week (52 or 53) of the previous year even if it contains days
2111 // from the new year. A week starts with Monday (day 1) and ends
2112 // with Sunday (day 7).
2115 // if Jan 1 is Thursday or less, it is in the first week of this year
2116 if ( wdYearStart
< 4 )
2118 // count the number of entire weeks between Jan 1 and this date
2119 week
= (nDayInYear
+ wdYearStart
+ 6 - wdTarget
)/7;
2121 // be careful to check for overflow in the next year
2122 if ( week
== 53 && tm
.mday
- wdTarget
> 28 )
2125 else // Jan 1 is in the last week of the previous year
2127 // check if we happen to be at the last week of previous year:
2128 if ( tm
.mon
== Jan
&& tm
.mday
< 8 - wdYearStart
)
2129 week
= wxDateTime(31, Dec
, GetYear()-1).GetWeekOfYear();
2131 week
= (nDayInYear
+ wdYearStart
- 1 - wdTarget
)/7;
2135 return (wxDateTime::wxDateTime_t
)week
;
2138 wxDateTime::wxDateTime_t
wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags
,
2139 const TimeZone
& tz
) const
2142 wxDateTime dtMonthStart
= wxDateTime(1, tm
.mon
, tm
.year
);
2143 int nWeek
= GetWeekOfYear(flags
) - dtMonthStart
.GetWeekOfYear(flags
) + 1;
2146 // this may happen for January when Jan, 1 is the last week of the
2148 nWeek
+= IsLeapYear(tm
.year
- 1) ? 53 : 52;
2151 return (wxDateTime::wxDateTime_t
)nWeek
;
2154 wxDateTime
& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday
)
2156 int year
= GetYear();
2157 wxDATETIME_CHECK( (0 < yday
) && (yday
<= GetNumberOfDays(year
)),
2158 _T("invalid year day") );
2160 bool isLeap
= IsLeapYear(year
);
2161 for ( Month mon
= Jan
; mon
< Inv_Month
; wxNextMonth(mon
) )
2163 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
2164 // don't need it neither - because of the CHECK above we know that
2165 // yday lies in December then
2166 if ( (mon
== Dec
) || (yday
<= gs_cumulatedDays
[isLeap
][mon
+ 1]) )
2168 Set((wxDateTime::wxDateTime_t
)(yday
- gs_cumulatedDays
[isLeap
][mon
]), mon
, year
);
2177 // ----------------------------------------------------------------------------
2178 // Julian day number conversion and related stuff
2179 // ----------------------------------------------------------------------------
2181 double wxDateTime::GetJulianDayNumber() const
2183 return m_time
.ToDouble() / MILLISECONDS_PER_DAY
+ EPOCH_JDN
+ 0.5;
2186 double wxDateTime::GetRataDie() const
2188 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
2189 return GetJulianDayNumber() - 1721119.5 - 306;
2192 // ----------------------------------------------------------------------------
2193 // timezone and DST stuff
2194 // ----------------------------------------------------------------------------
2196 int wxDateTime::IsDST(wxDateTime::Country country
) const
2198 wxCHECK_MSG( country
== Country_Default
, -1,
2199 _T("country support not implemented") );
2201 // use the C RTL for the dates in the standard range
2202 time_t timet
= GetTicks();
2203 if ( timet
!= (time_t)-1 )
2206 tm
*tm
= wxLocaltime_r(&timet
, &tmstruct
);
2208 wxCHECK_MSG( tm
, -1, _T("wxLocaltime_r() failed") );
2210 return tm
->tm_isdst
;
2214 int year
= GetYear();
2216 if ( !IsDSTApplicable(year
, country
) )
2218 // no DST time in this year in this country
2222 return IsBetween(GetBeginDST(year
, country
), GetEndDST(year
, country
));
2226 wxDateTime
& wxDateTime::MakeTimezone(const TimeZone
& tz
, bool noDST
)
2228 long secDiff
= GetTimeZone() + tz
.GetOffset();
2230 // we need to know whether DST is or not in effect for this date unless
2231 // the test disabled by the caller
2232 if ( !noDST
&& (IsDST() == 1) )
2234 // FIXME we assume that the DST is always shifted by 1 hour
2238 return Add(wxTimeSpan::Seconds(secDiff
));
2241 wxDateTime
& wxDateTime::MakeFromTimezone(const TimeZone
& tz
, bool noDST
)
2243 long secDiff
= GetTimeZone() + tz
.GetOffset();
2245 // we need to know whether DST is or not in effect for this date unless
2246 // the test disabled by the caller
2247 if ( !noDST
&& (IsDST() == 1) )
2249 // FIXME we assume that the DST is always shifted by 1 hour
2253 return Subtract(wxTimeSpan::Seconds(secDiff
));
2256 // ----------------------------------------------------------------------------
2257 // wxDateTime to/from text representations
2258 // ----------------------------------------------------------------------------
2260 wxString
wxDateTime::Format(const wxChar
*format
, const TimeZone
& tz
) const
2262 wxCHECK_MSG( format
, wxEmptyString
, _T("NULL format in wxDateTime::Format") );
2264 time_t time
= GetTicks();
2266 // we have to use our own implementation if the date is out of range of
2267 // strftime() or if we use non standard specificators
2268 #ifdef HAVE_STRFTIME
2269 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
2274 if ( tz
.GetOffset() == -GetTimeZone() )
2276 // we are working with local time
2277 tm
= wxLocaltime_r(&time
, &tmstruct
);
2279 // should never happen
2280 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxLocaltime_r() failed") );
2284 time
+= (int)tz
.GetOffset();
2286 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
2287 int time2
= (int) time
;
2293 tm
= wxGmtime_r(&time
, &tmstruct
);
2295 // should never happen
2296 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxGmtime_r() failed") );
2300 tm
= (struct tm
*)NULL
;
2306 return CallStrftime(format
, tm
);
2309 //else: use generic code below
2310 #endif // HAVE_STRFTIME
2312 // we only parse ANSI C format specifications here, no POSIX 2
2313 // complications, no GNU extensions but we do add support for a "%l" format
2314 // specifier allowing to get the number of milliseconds
2317 // used for calls to strftime() when we only deal with time
2318 struct tm tmTimeOnly
;
2319 tmTimeOnly
.tm_hour
= tm
.hour
;
2320 tmTimeOnly
.tm_min
= tm
.min
;
2321 tmTimeOnly
.tm_sec
= tm
.sec
;
2322 tmTimeOnly
.tm_wday
= 0;
2323 tmTimeOnly
.tm_yday
= 0;
2324 tmTimeOnly
.tm_mday
= 1; // any date will do
2325 tmTimeOnly
.tm_mon
= 0;
2326 tmTimeOnly
.tm_year
= 76;
2327 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
2329 wxString tmp
, res
, fmt
;
2330 for ( const wxChar
*p
= format
; *p
; p
++ )
2332 if ( *p
!= _T('%') )
2340 // set the default format
2343 case _T('Y'): // year has 4 digits
2347 case _T('j'): // day of year has 3 digits
2348 case _T('l'): // milliseconds have 3 digits
2352 case _T('w'): // week day as number has only one
2357 // it's either another valid format specifier in which case
2358 // the format is "%02d" (for all the rest) or we have the
2359 // field width preceding the format in which case it will
2360 // override the default format anyhow
2364 bool restart
= true;
2369 // start of the format specification
2372 case _T('a'): // a weekday name
2374 // second parameter should be true for abbreviated names
2375 res
+= GetWeekDayName(tm
.GetWeekDay(),
2376 *p
== _T('a') ? Name_Abbr
: Name_Full
);
2379 case _T('b'): // a month name
2381 res
+= GetMonthName(tm
.mon
,
2382 *p
== _T('b') ? Name_Abbr
: Name_Full
);
2385 case _T('c'): // locale default date and time representation
2386 case _T('x'): // locale default date representation
2387 #ifdef HAVE_STRFTIME
2389 // the problem: there is no way to know what do these format
2390 // specifications correspond to for the current locale.
2392 // the solution: use a hack and still use strftime(): first
2393 // find the YEAR which is a year in the strftime() range (1970
2394 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
2395 // of the real year. Then make a copy of the format and
2396 // replace all occurrences of YEAR in it with some unique
2397 // string not appearing anywhere else in it, then use
2398 // strftime() to format the date in year YEAR and then replace
2399 // YEAR back by the real year and the unique replacement
2400 // string back with YEAR. Notice that "all occurrences of YEAR"
2401 // means all occurrences of 4 digit as well as 2 digit form!
2403 // the bugs: we assume that neither of %c nor %x contains any
2404 // fields which may change between the YEAR and real year. For
2405 // example, the week number (%U, %W) and the day number (%j)
2406 // will change if one of these years is leap and the other one
2409 // find the YEAR: normally, for any year X, Jan 1 or the
2410 // year X + 28 is the same weekday as Jan 1 of X (because
2411 // the weekday advances by 1 for each normal X and by 2
2412 // for each leap X, hence by 5 every 4 years or by 35
2413 // which is 0 mod 7 every 28 years) but this rule breaks
2414 // down if there are years between X and Y which are
2415 // divisible by 4 but not leap (i.e. divisible by 100 but
2416 // not 400), hence the correction.
2418 int yearReal
= GetYear(tz
);
2419 int mod28
= yearReal
% 28;
2421 // be careful to not go too far - we risk to leave the
2426 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
2430 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
2433 int nCentury
= year
/ 100,
2434 nCenturyReal
= yearReal
/ 100;
2436 // need to adjust for the years divisble by 400 which are
2437 // not leap but are counted like leap ones if we just take
2438 // the number of centuries in between for nLostWeekDays
2439 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
2440 (nCentury
/ 4 - nCenturyReal
/ 4);
2442 // we have to gain back the "lost" weekdays: note that the
2443 // effect of this loop is to not do anything to
2444 // nLostWeekDays (which we won't use any more), but to
2445 // (indirectly) set the year correctly
2446 while ( (nLostWeekDays
% 7) != 0 )
2448 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
2451 // Keep year below 2000 so the 2digit year number
2452 // can never match the month or day of the month
2453 if (year
>=2000) year
-=28;
2454 // at any rate, we couldn't go further than 1988 + 9 + 28!
2455 wxASSERT_MSG( year
< 2030,
2456 _T("logic error in wxDateTime::Format") );
2458 wxString strYear
, strYear2
;
2459 strYear
.Printf(_T("%d"), year
);
2460 strYear2
.Printf(_T("%d"), year
% 100);
2462 // find four strings not occurring in format (this is surely
2463 // not the optimal way of doing it... improvements welcome!)
2464 wxString fmt2
= format
;
2465 wxString replacement
,replacement2
,replacement3
,replacement4
;
2466 for (int rnr
=1; rnr
<5 ; rnr
++)
2468 wxString r
= (wxChar
)-rnr
;
2469 while ( fmt2
.Find(r
) != wxNOT_FOUND
)
2476 case 1: replacement
=r
; break;
2477 case 2: replacement2
=r
; break;
2478 case 3: replacement3
=r
; break;
2479 case 4: replacement4
=r
; break;
2482 // replace all occurrences of year with it
2483 bool wasReplaced
= fmt2
.Replace(strYear
, replacement
) > 0;
2484 // evaluation order ensures we always attempt the replacement.
2485 wasReplaced
= (fmt2
.Replace(strYear2
, replacement2
) > 0) || wasReplaced
;
2487 // use strftime() to format the same date but in supported
2490 // NB: we assume that strftime() doesn't check for the
2491 // date validity and will happily format the date
2492 // corresponding to Feb 29 of a non leap year (which
2493 // may happen if yearReal was leap and year is not)
2494 struct tm tmAdjusted
;
2496 tmAdjusted
.tm_hour
= tm
.hour
;
2497 tmAdjusted
.tm_min
= tm
.min
;
2498 tmAdjusted
.tm_sec
= tm
.sec
;
2499 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
2500 tmAdjusted
.tm_yday
= GetDayOfYear();
2501 tmAdjusted
.tm_mday
= tm
.mday
;
2502 tmAdjusted
.tm_mon
= tm
.mon
;
2503 tmAdjusted
.tm_year
= year
- 1900;
2504 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
2505 wxString str
= CallStrftime(*p
== _T('c') ? _T("%c")
2509 // now replace the occurrence of 1999 with the real year
2510 // we do this in two stages to stop the 2 digit year
2511 // matching any substring of the 4 digit year.
2512 // Any day,month hours and minutes components should be safe due
2513 // to ensuring the range of the years.
2514 wxString strYearReal
, strYearReal2
;
2515 strYearReal
.Printf(_T("%04d"), yearReal
);
2516 strYearReal2
.Printf(_T("%02d"), yearReal
% 100);
2517 str
.Replace(strYear
, replacement3
);
2518 str
.Replace(strYear2
,replacement4
);
2519 str
.Replace(replacement3
, strYearReal
);
2520 str
.Replace(replacement4
, strYearReal2
);
2522 // and replace back all occurrences of replacement string
2525 str
.Replace(replacement2
, strYear2
);
2526 str
.Replace(replacement
, strYear
);
2531 #else // !HAVE_STRFTIME
2532 // Use "%m/%d/%y %H:%M:%S" format instead
2533 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
2534 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
2535 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
2538 case _T('d'): // day of a month (01-31)
2539 res
+= wxString::Format(fmt
, tm
.mday
);
2542 case _T('H'): // hour in 24h format (00-23)
2543 res
+= wxString::Format(fmt
, tm
.hour
);
2546 case _T('I'): // hour in 12h format (01-12)
2548 // 24h -> 12h, 0h -> 12h too
2549 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
2550 : tm
.hour
? tm
.hour
: 12;
2551 res
+= wxString::Format(fmt
, hour12
);
2555 case _T('j'): // day of the year
2556 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
2559 case _T('l'): // milliseconds (NOT STANDARD)
2560 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
2563 case _T('m'): // month as a number (01-12)
2564 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
2567 case _T('M'): // minute as a decimal number (00-59)
2568 res
+= wxString::Format(fmt
, tm
.min
);
2571 case _T('p'): // AM or PM string
2572 #ifdef HAVE_STRFTIME
2573 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
2574 #else // !HAVE_STRFTIME
2575 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
2576 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
2579 case _T('S'): // second as a decimal number (00-61)
2580 res
+= wxString::Format(fmt
, tm
.sec
);
2583 case _T('U'): // week number in the year (Sunday 1st week day)
2584 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
2587 case _T('W'): // week number in the year (Monday 1st week day)
2588 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
2591 case _T('w'): // weekday as a number (0-6), Sunday = 0
2592 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
2595 // case _T('x'): -- handled with "%c"
2597 case _T('X'): // locale default time representation
2598 // just use strftime() to format the time for us
2599 #ifdef HAVE_STRFTIME
2600 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
2601 #else // !HAVE_STRFTIME
2602 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
2603 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
2606 case _T('y'): // year without century (00-99)
2607 res
+= wxString::Format(fmt
, tm
.year
% 100);
2610 case _T('Y'): // year with century
2611 res
+= wxString::Format(fmt
, tm
.year
);
2614 case _T('Z'): // timezone name
2615 #ifdef HAVE_STRFTIME
2616 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
2621 // is it the format width?
2623 while ( *p
== _T('-') || *p
== _T('+') ||
2624 *p
== _T(' ') || wxIsdigit(*p
) )
2631 // we've only got the flags and width so far in fmt
2632 fmt
.Prepend(_T('%'));
2633 fmt
.Append(_T('d'));
2640 // no, it wasn't the width
2641 wxFAIL_MSG(_T("unknown format specificator"));
2643 // fall through and just copy it nevertheless
2645 case _T('%'): // a percent sign
2649 case 0: // the end of string
2650 wxFAIL_MSG(_T("missing format at the end of string"));
2652 // just put the '%' which was the last char in format
2662 // this function parses a string in (strict) RFC 822 format: see the section 5
2663 // of the RFC for the detailed description, but briefly it's something of the
2664 // form "Sat, 18 Dec 1999 00:48:30 +0100"
2666 // this function is "strict" by design - it must reject anything except true
2667 // RFC822 time specs.
2669 // TODO a great candidate for using reg exps
2670 const wxChar
*wxDateTime::ParseRfc822Date(const wxChar
* date
)
2672 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
2674 const wxChar
*p
= date
;
2675 const wxChar
*comma
= wxStrchr(p
, _T(','));
2678 // the part before comma is the weekday
2680 // skip it for now - we don't use but might check that it really
2681 // corresponds to the specfied date
2684 if ( *p
!= _T(' ') )
2686 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
2688 return (wxChar
*)NULL
;
2694 // the following 1 or 2 digits are the day number
2695 if ( !wxIsdigit(*p
) )
2697 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
2699 return (wxChar
*)NULL
;
2702 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - _T('0'));
2703 if ( wxIsdigit(*p
) )
2706 day
= (wxDateTime_t
)(day
+ (*p
++ - _T('0')));
2709 if ( *p
++ != _T(' ') )
2711 return (wxChar
*)NULL
;
2714 // the following 3 letters specify the month
2715 wxString
monName(p
, 3);
2717 if ( monName
== _T("Jan") )
2719 else if ( monName
== _T("Feb") )
2721 else if ( monName
== _T("Mar") )
2723 else if ( monName
== _T("Apr") )
2725 else if ( monName
== _T("May") )
2727 else if ( monName
== _T("Jun") )
2729 else if ( monName
== _T("Jul") )
2731 else if ( monName
== _T("Aug") )
2733 else if ( monName
== _T("Sep") )
2735 else if ( monName
== _T("Oct") )
2737 else if ( monName
== _T("Nov") )
2739 else if ( monName
== _T("Dec") )
2743 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str());
2745 return (wxChar
*)NULL
;
2750 if ( *p
++ != _T(' ') )
2752 return (wxChar
*)NULL
;
2756 if ( !wxIsdigit(*p
) )
2759 return (wxChar
*)NULL
;
2762 int year
= *p
++ - _T('0');
2764 if ( !wxIsdigit(*p
) )
2766 // should have at least 2 digits in the year
2767 return (wxChar
*)NULL
;
2771 year
+= *p
++ - _T('0');
2773 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
2774 if ( wxIsdigit(*p
) )
2777 year
+= *p
++ - _T('0');
2779 if ( !wxIsdigit(*p
) )
2781 // no 3 digit years please
2782 return (wxChar
*)NULL
;
2786 year
+= *p
++ - _T('0');
2789 if ( *p
++ != _T(' ') )
2791 return (wxChar
*)NULL
;
2794 // time is in the format hh:mm:ss and seconds are optional
2795 if ( !wxIsdigit(*p
) )
2797 return (wxChar
*)NULL
;
2800 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - _T('0'));
2802 if ( !wxIsdigit(*p
) )
2804 return (wxChar
*)NULL
;
2808 hour
= (wxDateTime_t
)(hour
+ (*p
++ - _T('0')));
2810 if ( *p
++ != _T(':') )
2812 return (wxChar
*)NULL
;
2815 if ( !wxIsdigit(*p
) )
2817 return (wxChar
*)NULL
;
2820 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - _T('0'));
2822 if ( !wxIsdigit(*p
) )
2824 return (wxChar
*)NULL
;
2828 min
= (wxDateTime_t
)(min
+ *p
++ - _T('0'));
2830 wxDateTime_t sec
= 0;
2831 if ( *p
++ == _T(':') )
2833 if ( !wxIsdigit(*p
) )
2835 return (wxChar
*)NULL
;
2838 sec
= (wxDateTime_t
)(*p
++ - _T('0'));
2840 if ( !wxIsdigit(*p
) )
2842 return (wxChar
*)NULL
;
2846 sec
= (wxDateTime_t
)(sec
+ *p
++ - _T('0'));
2849 if ( *p
++ != _T(' ') )
2851 return (wxChar
*)NULL
;
2854 // and now the interesting part: the timezone
2855 int offset
wxDUMMY_INITIALIZE(0);
2856 if ( *p
== _T('-') || *p
== _T('+') )
2858 // the explicit offset given: it has the form of hhmm
2859 bool plus
= *p
++ == _T('+');
2861 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2863 return (wxChar
*)NULL
;
2867 offset
= MIN_PER_HOUR
*(10*(*p
- _T('0')) + (*(p
+ 1) - _T('0')));
2871 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2873 return (wxChar
*)NULL
;
2877 offset
+= 10*(*p
- _T('0')) + (*(p
+ 1) - _T('0'));
2888 // the symbolic timezone given: may be either military timezone or one
2889 // of standard abbreviations
2892 // military: Z = UTC, J unused, A = -1, ..., Y = +12
2893 static const int offsets
[26] =
2895 //A B C D E F G H I J K L M
2896 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
2897 //N O P R Q S T U V W Z Y Z
2898 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
2901 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
2903 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
);
2905 return (wxChar
*)NULL
;
2908 offset
= offsets
[*p
++ - _T('A')];
2914 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
2916 else if ( tz
== _T("AST") )
2917 offset
= AST
- GMT0
;
2918 else if ( tz
== _T("ADT") )
2919 offset
= ADT
- GMT0
;
2920 else if ( tz
== _T("EST") )
2921 offset
= EST
- GMT0
;
2922 else if ( tz
== _T("EDT") )
2923 offset
= EDT
- GMT0
;
2924 else if ( tz
== _T("CST") )
2925 offset
= CST
- GMT0
;
2926 else if ( tz
== _T("CDT") )
2927 offset
= CDT
- GMT0
;
2928 else if ( tz
== _T("MST") )
2929 offset
= MST
- GMT0
;
2930 else if ( tz
== _T("MDT") )
2931 offset
= MDT
- GMT0
;
2932 else if ( tz
== _T("PST") )
2933 offset
= PST
- GMT0
;
2934 else if ( tz
== _T("PDT") )
2935 offset
= PDT
- GMT0
;
2938 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
);
2940 return (wxChar
*)NULL
;
2947 offset
*= MIN_PER_HOUR
;
2950 // the spec was correct, construct the date from the values we found
2951 Set(day
, mon
, year
, hour
, min
, sec
);
2952 MakeFromTimezone(TimeZone((wxDateTime_t
)(offset
*SEC_PER_MIN
)));
2959 // returns the string containing strftime() format used for short dates in the
2960 // current locale or an empty string
2961 static wxString
GetLocaleDateFormat()
2965 // there is no setlocale() under Windows CE, so just always query the
2968 if ( strcmp(setlocale(LC_ALL
, NULL
), "C") != 0 )
2971 // The locale was programatically set to non-C. We assume that this was
2972 // done using wxLocale, in which case thread's current locale is also
2973 // set to correct LCID value and we can use GetLocaleInfo to determine
2974 // the correct formatting string:
2976 LCID lcid
= LOCALE_USER_DEFAULT
;
2978 LCID lcid
= GetThreadLocale();
2980 // according to MSDN 80 chars is max allowed for short date format
2982 if ( ::GetLocaleInfo(lcid
, LOCALE_SSHORTDATE
, fmt
, WXSIZEOF(fmt
)) )
2984 wxChar chLast
= _T('\0');
2985 size_t lastCount
= 0;
2986 for ( const wxChar
*p
= fmt
; /* NUL handled inside */; p
++ )
2996 // these characters come in groups, start counting them
3006 // first deal with any special characters we have had
3012 switch ( lastCount
)
3016 // these two are the same as we
3017 // don't distinguish between 1 and
3018 // 2 digits for days
3031 wxFAIL_MSG( _T("too many 'd's") );
3036 switch ( lastCount
)
3040 // as for 'd' and 'dd' above
3053 wxFAIL_MSG( _T("too many 'M's") );
3058 switch ( lastCount
)
3070 wxFAIL_MSG( _T("wrong number of 'y's") );
3075 // strftime() doesn't have era string,
3076 // ignore this format
3077 wxASSERT_MSG( lastCount
<= 2,
3078 _T("too many 'g's") );
3082 wxFAIL_MSG( _T("unreachable") );
3089 // not a special character so must be just a separator,
3091 if ( *p
!= _T('\0') )
3093 if ( *p
== _T('%') )
3095 // this one needs to be escaped
3103 if ( *p
== _T('\0') )
3107 //else: GetLocaleInfo() failed, leave fmtDate value unchanged and
3108 // try our luck with the default formats
3110 //else: default C locale, default formats should work
3115 #endif // __WINDOWS__
3117 const wxChar
*wxDateTime::ParseFormat(const wxChar
*date
,
3118 const wxChar
*format
,
3119 const wxDateTime
& dateDef
)
3121 wxCHECK_MSG( date
&& format
, (wxChar
*)NULL
,
3122 _T("NULL pointer in wxDateTime::ParseFormat()") );
3127 // what fields have we found?
3128 bool haveWDay
= false,
3137 bool hourIsIn12hFormat
= false, // or in 24h one?
3138 isPM
= false; // AM by default
3140 // and the value of the items we have (init them to get rid of warnings)
3141 wxDateTime_t sec
= 0,
3144 WeekDay wday
= Inv_WeekDay
;
3145 wxDateTime_t yday
= 0,
3147 wxDateTime::Month mon
= Inv_Month
;
3150 const wxChar
*input
= date
;
3151 for ( const wxChar
*fmt
= format
; *fmt
; fmt
++ )
3153 if ( *fmt
!= _T('%') )
3155 if ( wxIsspace(*fmt
) )
3157 // a white space in the format string matches 0 or more white
3158 // spaces in the input
3159 while ( wxIsspace(*input
) )
3166 // any other character (not whitespace, not '%') must be
3167 // matched by itself in the input
3168 if ( *input
++ != *fmt
)
3171 return (wxChar
*)NULL
;
3175 // done with this format char
3179 // start of a format specification
3181 // parse the optional width
3183 while ( wxIsdigit(*++fmt
) )
3186 width
+= *fmt
- _T('0');
3189 // the default widths for the various fields
3194 case _T('Y'): // year has 4 digits
3198 case _T('j'): // day of year has 3 digits
3199 case _T('l'): // milliseconds have 3 digits
3203 case _T('w'): // week day as number has only one
3208 // default for all other fields
3213 // then the format itself
3216 case _T('a'): // a weekday name
3219 int flag
= *fmt
== _T('a') ? Name_Abbr
: Name_Full
;
3220 wday
= GetWeekDayFromName(GetAlphaToken(input
), flag
);
3221 if ( wday
== Inv_WeekDay
)
3224 return (wxChar
*)NULL
;
3230 case _T('b'): // a month name
3233 int flag
= *fmt
== _T('b') ? Name_Abbr
: Name_Full
;
3234 mon
= GetMonthFromName(GetAlphaToken(input
), flag
);
3235 if ( mon
== Inv_Month
)
3238 return (wxChar
*)NULL
;
3244 case _T('c'): // locale default date and time representation
3248 // this is the format which corresponds to ctime() output
3249 // and strptime("%c") should parse it, so try it first
3250 static const wxChar
*fmtCtime
= _T("%a %b %d %H:%M:%S %Y");
3252 const wxChar
*result
= dt
.ParseFormat(input
, fmtCtime
);
3255 result
= dt
.ParseFormat(input
, _T("%x %X"));
3260 result
= dt
.ParseFormat(input
, _T("%X %x"));
3265 // we've tried everything and still no match
3266 return (wxChar
*)NULL
;
3271 haveDay
= haveMon
= haveYear
=
3272 haveHour
= haveMin
= haveSec
= true;
3286 case _T('d'): // day of a month (01-31)
3287 if ( !GetNumericToken(width
, input
, &num
) ||
3288 (num
> 31) || (num
< 1) )
3291 return (wxChar
*)NULL
;
3294 // we can't check whether the day range is correct yet, will
3295 // do it later - assume ok for now
3297 mday
= (wxDateTime_t
)num
;
3300 case _T('H'): // hour in 24h format (00-23)
3301 if ( !GetNumericToken(width
, input
, &num
) || (num
> 23) )
3304 return (wxChar
*)NULL
;
3308 hour
= (wxDateTime_t
)num
;
3311 case _T('I'): // hour in 12h format (01-12)
3312 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3315 return (wxChar
*)NULL
;
3319 hourIsIn12hFormat
= true;
3320 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
3323 case _T('j'): // day of the year
3324 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 366) )
3327 return (wxChar
*)NULL
;
3331 yday
= (wxDateTime_t
)num
;
3334 case _T('m'): // month as a number (01-12)
3335 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3338 return (wxChar
*)NULL
;
3342 mon
= (Month
)(num
- 1);
3345 case _T('M'): // minute as a decimal number (00-59)
3346 if ( !GetNumericToken(width
, input
, &num
) || (num
> 59) )
3349 return (wxChar
*)NULL
;
3353 min
= (wxDateTime_t
)num
;
3356 case _T('p'): // AM or PM string
3358 wxString am
, pm
, token
= GetAlphaToken(input
);
3360 GetAmPmStrings(&am
, &pm
);
3361 if (am
.empty() && pm
.empty())
3362 return (wxChar
*)NULL
; // no am/pm strings defined
3363 if ( token
.CmpNoCase(pm
) == 0 )
3367 else if ( token
.CmpNoCase(am
) != 0 )
3370 return (wxChar
*)NULL
;
3375 case _T('r'): // time as %I:%M:%S %p
3378 input
= dt
.ParseFormat(input
, _T("%I:%M:%S %p"));
3382 return (wxChar
*)NULL
;
3385 haveHour
= haveMin
= haveSec
= true;
3394 case _T('R'): // time as %H:%M
3397 input
= dt
.ParseFormat(input
, _T("%H:%M"));
3401 return (wxChar
*)NULL
;
3404 haveHour
= haveMin
= true;
3412 case _T('S'): // second as a decimal number (00-61)
3413 if ( !GetNumericToken(width
, input
, &num
) || (num
> 61) )
3416 return (wxChar
*)NULL
;
3420 sec
= (wxDateTime_t
)num
;
3423 case _T('T'): // time as %H:%M:%S
3426 input
= dt
.ParseFormat(input
, _T("%H:%M:%S"));
3430 return (wxChar
*)NULL
;
3433 haveHour
= haveMin
= haveSec
= true;
3442 case _T('w'): // weekday as a number (0-6), Sunday = 0
3443 if ( !GetNumericToken(width
, input
, &num
) || (wday
> 6) )
3446 return (wxChar
*)NULL
;
3450 wday
= (WeekDay
)num
;
3453 case _T('x'): // locale default date representation
3454 #ifdef HAVE_STRPTIME
3455 // try using strptime() -- it may fail even if the input is
3456 // correct but the date is out of range, so we will fall back
3457 // to our generic code anyhow
3461 const wxChar
*result
= CallStrptime(input
, "%x", &tm
);
3466 haveDay
= haveMon
= haveYear
= true;
3468 year
= 1900 + tm
.tm_year
;
3469 mon
= (Month
)tm
.tm_mon
;
3475 #endif // HAVE_STRPTIME
3483 // The above doesn't work for all locales, try to query
3484 // Windows for the right way of formatting the date:
3485 fmtDate
= GetLocaleDateFormat();
3486 if ( fmtDate
.empty() )
3489 if ( IsWestEuropeanCountry(GetCountry()) ||
3490 GetCountry() == Russia
)
3492 fmtDate
= _T("%d/%m/%y");
3493 fmtDateAlt
= _T("%m/%d/%y");
3497 fmtDate
= _T("%m/%d/%y");
3498 fmtDateAlt
= _T("%d/%m/%y");
3502 const wxChar
*result
= dt
.ParseFormat(input
, fmtDate
);
3504 if ( !result
&& !fmtDateAlt
.empty() )
3506 // ok, be nice and try another one
3507 result
= dt
.ParseFormat(input
, fmtDateAlt
);
3513 return (wxChar
*)NULL
;
3518 haveDay
= haveMon
= haveYear
= true;
3529 case _T('X'): // locale default time representation
3530 #ifdef HAVE_STRPTIME
3532 // use strptime() to do it for us (FIXME !Unicode friendly)
3534 input
= CallStrptime(input
, "%X", &tm
);
3537 return (wxChar
*)NULL
;
3540 haveHour
= haveMin
= haveSec
= true;
3546 #else // !HAVE_STRPTIME
3547 // TODO under Win32 we can query the LOCALE_ITIME system
3548 // setting which says whether the default time format is
3551 // try to parse what follows as "%H:%M:%S" and, if this
3552 // fails, as "%I:%M:%S %p" - this should catch the most
3556 const wxChar
*result
= dt
.ParseFormat(input
, _T("%T"));
3559 result
= dt
.ParseFormat(input
, _T("%r"));
3565 return (wxChar
*)NULL
;
3568 haveHour
= haveMin
= haveSec
= true;
3577 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
3580 case _T('y'): // year without century (00-99)
3581 if ( !GetNumericToken(width
, input
, &num
) || (num
> 99) )
3584 return (wxChar
*)NULL
;
3589 // TODO should have an option for roll over date instead of
3590 // hard coding it here
3591 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
3594 case _T('Y'): // year with century
3595 if ( !GetNumericToken(width
, input
, &num
) )
3598 return (wxChar
*)NULL
;
3602 year
= (wxDateTime_t
)num
;
3605 case _T('Z'): // timezone name
3606 wxFAIL_MSG(_T("TODO"));
3609 case _T('%'): // a percent sign
3610 if ( *input
++ != _T('%') )
3613 return (wxChar
*)NULL
;
3617 case 0: // the end of string
3618 wxFAIL_MSG(_T("unexpected format end"));
3622 default: // not a known format spec
3623 return (wxChar
*)NULL
;
3627 // format matched, try to construct a date from what we have now
3629 if ( dateDef
.IsValid() )
3631 // take this date as default
3632 tmDef
= dateDef
.GetTm();
3634 else if ( IsValid() )
3636 // if this date is valid, don't change it
3641 // no default and this date is invalid - fall back to Today()
3642 tmDef
= Today().GetTm();
3653 // TODO we don't check here that the values are consistent, if both year
3654 // day and month/day were found, we just ignore the year day and we
3655 // also always ignore the week day
3656 if ( haveMon
&& haveDay
)
3658 if ( mday
> GetNumOfDaysInMonth(tm
.year
, mon
) )
3660 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
3662 return (wxChar
*)NULL
;
3668 else if ( haveYDay
)
3670 if ( yday
> GetNumberOfDays(tm
.year
) )
3672 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
3674 return (wxChar
*)NULL
;
3677 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
3684 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
3686 // translate to 24hour format
3689 //else: either already in 24h format or no translation needed
3709 // finally check that the week day is consistent -- if we had it
3710 if ( haveWDay
&& GetWeekDay() != wday
)
3712 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
3720 const wxChar
*wxDateTime::ParseDateTime(const wxChar
*date
)
3722 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3724 // Set to current day and hour, so strings like '14:00' becomes today at
3725 // 14, not some other random date
3726 wxDateTime dtDate
= wxDateTime::Today();
3727 wxDateTime dtTime
= wxDateTime::Today();
3729 const wxChar
* pchTime
;
3731 // Try to parse the beginning of the string as a date
3732 const wxChar
* pchDate
= dtDate
.ParseDate(date
);
3734 // We got a date in the beginning, see if there is a time specified after the date
3737 // Skip spaces, as the ParseTime() function fails on spaces
3738 while ( wxIsspace(*pchDate
) )
3741 pchTime
= dtTime
.ParseTime(pchDate
);
3743 else // no date in the beginning
3745 // check and see if we have a time followed by a date
3746 pchTime
= dtTime
.ParseTime(date
);
3749 while ( wxIsspace(*pchTime
) )
3752 pchDate
= dtDate
.ParseDate(pchTime
);
3756 // If we have a date specified, set our own data to the same date
3757 if ( !pchDate
|| !pchTime
)
3760 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
3761 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
3762 dtTime
.GetMillisecond());
3764 // Return endpoint of scan
3765 return pchDate
> pchTime
? pchDate
: pchTime
;
3768 const wxChar
*wxDateTime::ParseDate(const wxChar
*date
)
3770 // this is a simplified version of ParseDateTime() which understands only
3771 // "today" (for wxDate compatibility) and digits only otherwise (and not
3772 // all esoteric constructions ParseDateTime() knows about)
3774 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3776 const wxChar
*p
= date
;
3777 while ( wxIsspace(*p
) )
3780 // some special cases
3784 int dayDiffFromToday
;
3787 { wxTRANSLATE("today"), 0 },
3788 { wxTRANSLATE("yesterday"), -1 },
3789 { wxTRANSLATE("tomorrow"), 1 },
3792 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
3794 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
3795 size_t len
= dateStr
.length();
3796 if ( wxStrlen(p
) >= len
)
3798 wxString
str(p
, len
);
3799 if ( str
.CmpNoCase(dateStr
) == 0 )
3801 // nothing can follow this, so stop here
3804 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
3806 if ( dayDiffFromToday
)
3808 *this += wxDateSpan::Days(dayDiffFromToday
);
3816 // We try to guess what we have here: for each new (numeric) token, we
3817 // determine if it can be a month, day or a year. Of course, there is an
3818 // ambiguity as some numbers may be days as well as months, so we also
3819 // have the ability to back track.
3822 bool haveDay
= false, // the months day?
3823 haveWDay
= false, // the day of week?
3824 haveMon
= false, // the month?
3825 haveYear
= false; // the year?
3827 // and the value of the items we have (init them to get rid of warnings)
3828 WeekDay wday
= Inv_WeekDay
;
3829 wxDateTime_t day
= 0;
3830 wxDateTime::Month mon
= Inv_Month
;
3833 // tokenize the string
3835 static const wxChar
*dateDelimiters
= _T(".,/-\t\r\n ");
3836 wxStringTokenizer
tok(p
, dateDelimiters
);
3837 while ( tok
.HasMoreTokens() )
3839 wxString token
= tok
.GetNextToken();
3845 if ( token
.ToULong(&val
) )
3847 // guess what this number is
3853 if ( !haveMon
&& val
> 0 && val
<= 12 )
3855 // assume it is month
3858 else // not the month
3862 // this can only be the year
3865 else // may be either day or year
3867 // use a leap year if we don't have the year yet to allow
3868 // dates like 2/29/1976 which would be rejected otherwise
3869 wxDateTime_t max_days
= (wxDateTime_t
)(
3871 ? GetNumOfDaysInMonth(haveYear
? year
: 1976, mon
)
3876 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
3881 else // yes, suppose it's the day
3895 year
= (wxDateTime_t
)val
;
3904 day
= (wxDateTime_t
)val
;
3910 mon
= (Month
)(val
- 1);
3913 else // not a number
3915 // be careful not to overwrite the current mon value
3916 Month mon2
= GetMonthFromName(token
, Name_Full
| Name_Abbr
);
3917 if ( mon2
!= Inv_Month
)
3922 // but we already have a month - maybe we guessed wrong?
3925 // no need to check in month range as always < 12, but
3926 // the days are counted from 1 unlike the months
3927 day
= (wxDateTime_t
)(mon
+ 1);
3932 // could possible be the year (doesn't the year come
3933 // before the month in the japanese format?) (FIXME)
3942 else // not a valid month name
3944 wday
= GetWeekDayFromName(token
, Name_Full
| Name_Abbr
);
3945 if ( wday
!= Inv_WeekDay
)
3955 else // not a valid weekday name
3958 static const wxChar
*ordinals
[] =
3960 wxTRANSLATE("first"),
3961 wxTRANSLATE("second"),
3962 wxTRANSLATE("third"),
3963 wxTRANSLATE("fourth"),
3964 wxTRANSLATE("fifth"),
3965 wxTRANSLATE("sixth"),
3966 wxTRANSLATE("seventh"),
3967 wxTRANSLATE("eighth"),
3968 wxTRANSLATE("ninth"),
3969 wxTRANSLATE("tenth"),
3970 wxTRANSLATE("eleventh"),
3971 wxTRANSLATE("twelfth"),
3972 wxTRANSLATE("thirteenth"),
3973 wxTRANSLATE("fourteenth"),
3974 wxTRANSLATE("fifteenth"),
3975 wxTRANSLATE("sixteenth"),
3976 wxTRANSLATE("seventeenth"),
3977 wxTRANSLATE("eighteenth"),
3978 wxTRANSLATE("nineteenth"),
3979 wxTRANSLATE("twentieth"),
3980 // that's enough - otherwise we'd have problems with
3981 // composite (or not) ordinals
3985 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
3987 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
3993 if ( n
== WXSIZEOF(ordinals
) )
3995 // stop here - something unknown
4002 // don't try anything here (as in case of numeric day
4003 // above) - the symbolic day spec should always
4004 // precede the month/year
4010 day
= (wxDateTime_t
)(n
+ 1);
4015 nPosCur
= tok
.GetPosition();
4018 // either no more tokens or the scan was stopped by something we couldn't
4019 // parse - in any case, see if we can construct a date from what we have
4020 if ( !haveDay
&& !haveWDay
)
4022 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
4027 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
4028 !(haveDay
&& haveMon
&& haveYear
) )
4030 // without adjectives (which we don't support here) the week day only
4031 // makes sense completely separately or with the full date
4032 // specification (what would "Wed 1999" mean?)
4036 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
4038 // may be we have month and day instead of day and year?
4039 if ( haveDay
&& !haveMon
)
4043 // exchange day and month
4044 mon
= (wxDateTime::Month
)(day
- 1);
4046 // we're in the current year then
4047 if ( (year
> 0) && (year
<= (int)GetNumOfDaysInMonth(Inv_Year
, mon
)) )
4049 day
= (wxDateTime_t
)year
;
4054 //else: no, can't exchange, leave haveMon == false
4060 // if we give the year, month and day must be given too
4061 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
4069 mon
= GetCurrentMonth();
4074 year
= GetCurrentYear();
4079 // normally we check the day above but the check is optimistic in case
4080 // we find the day before its month/year so we have to redo it now
4081 if ( day
> GetNumOfDaysInMonth(year
, mon
) )
4084 Set(day
, mon
, year
);
4088 // check that it is really the same
4089 if ( GetWeekDay() != wday
)
4091 // inconsistency detected
4092 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
4094 return (wxChar
*)NULL
;
4102 SetToWeekDayInSameWeek(wday
);
4105 // return the pointer to the first unparsed char
4107 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
4109 // if we couldn't parse the token after the delimiter, put back the
4110 // delimiter as well
4117 const wxChar
*wxDateTime::ParseTime(const wxChar
*time
)
4119 wxCHECK_MSG( time
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
4121 // first try some extra things
4128 { wxTRANSLATE("noon"), 12 },
4129 { wxTRANSLATE("midnight"), 00 },
4133 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
4135 wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
4136 size_t len
= timeString
.length();
4137 if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 )
4139 // casts required by DigitalMars
4140 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
4146 // try all time formats we may think about in the order from longest to
4149 // 12hour with AM/PM?
4150 const wxChar
*result
= ParseFormat(time
, _T("%I:%M:%S %p"));
4154 // normally, it's the same, but why not try it?
4155 result
= ParseFormat(time
, _T("%H:%M:%S"));
4160 // 12hour with AM/PM but without seconds?
4161 result
= ParseFormat(time
, _T("%I:%M %p"));
4167 result
= ParseFormat(time
, _T("%H:%M"));
4172 // just the hour and AM/PM?
4173 result
= ParseFormat(time
, _T("%I %p"));
4179 result
= ParseFormat(time
, _T("%H"));
4184 // parse the standard format: normally it is one of the formats above
4185 // but it may be set to something completely different by the user
4186 result
= ParseFormat(time
, _T("%X"));
4189 // TODO: parse timezones
4194 // ----------------------------------------------------------------------------
4195 // Workdays and holidays support
4196 // ----------------------------------------------------------------------------
4198 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
4200 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
4203 // ============================================================================
4205 // ============================================================================
4207 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
4210 return ds1
.Multiply(n
);
4213 // ============================================================================
4215 // ============================================================================
4217 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
4219 return wxTimeSpan(ts
).Multiply(n
);
4222 // this enum is only used in wxTimeSpan::Format() below but we can't declare
4223 // it locally to the method as it provokes an internal compiler error in egcs
4224 // 2.91.60 when building with -O2
4235 // not all strftime(3) format specifiers make sense here because, for example,
4236 // a time span doesn't have a year nor a timezone
4238 // Here are the ones which are supported (all of them are supported by strftime
4240 // %H hour in 24 hour format
4241 // %M minute (00 - 59)
4242 // %S second (00 - 59)
4245 // Also, for MFC CTimeSpan compatibility, we support
4246 // %D number of days
4248 // And, to be better than MFC :-), we also have
4249 // %E number of wEeks
4250 // %l milliseconds (000 - 999)
4251 wxString
wxTimeSpan::Format(const wxChar
*format
) const
4253 wxCHECK_MSG( format
, wxEmptyString
, _T("NULL format in wxTimeSpan::Format") );
4256 str
.Alloc(wxStrlen(format
));
4258 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
4260 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
4261 // question is what should ts.Format("%S") do? The code here returns "3273"
4262 // in this case (i.e. the total number of seconds, not just seconds % 60)
4263 // because, for me, this call means "give me entire time interval in
4264 // seconds" and not "give me the seconds part of the time interval"
4266 // If we agree that it should behave like this, it is clear that the
4267 // interpretation of each format specifier depends on the presence of the
4268 // other format specs in the string: if there was "%H" before "%M", we
4269 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
4271 // we remember the most important unit found so far
4272 TimeSpanPart partBiggest
= Part_MSec
;
4274 for ( const wxChar
*pch
= format
; *pch
; pch
++ )
4278 if ( ch
== _T('%') )
4280 // the start of the format specification of the printf() below
4281 wxString
fmtPrefix(_T('%'));
4286 // the number of digits for the format string, 0 if unused
4287 unsigned digits
= 0;
4289 ch
= *++pch
; // get the format spec char
4293 wxFAIL_MSG( _T("invalid format character") );
4299 // skip the part below switch
4304 if ( partBiggest
< Part_Day
)
4310 partBiggest
= Part_Day
;
4315 partBiggest
= Part_Week
;
4321 if ( partBiggest
< Part_Hour
)
4325 // the sign has already been taken into account
4326 // when outputting the biggest part
4334 partBiggest
= Part_Hour
;
4341 n
= GetMilliseconds().ToLong();
4342 if ( partBiggest
< Part_MSec
)
4349 //else: no need to reset partBiggest to Part_MSec, it is
4350 // the least significant one anyhow
4357 if ( partBiggest
< Part_Min
)
4366 partBiggest
= Part_Min
;
4373 n
= GetSeconds().ToLong();
4374 if ( partBiggest
< Part_Sec
)
4383 partBiggest
= Part_Sec
;
4392 // negative numbers need one extra position for '-' display
4396 fmtPrefix
<< _T("0") << digits
;
4399 str
+= wxString::Format(fmtPrefix
+ _T("ld"), n
);
4403 // normal character, just copy
4411 // ============================================================================
4412 // wxDateTimeHolidayAuthority and related classes
4413 // ============================================================================
4415 #include "wx/arrimpl.cpp"
4417 WX_DEFINE_OBJARRAY(wxDateTimeArray
)
4419 static int wxCMPFUNC_CONV
4420 wxDateTimeCompareFunc(wxDateTime
**first
, wxDateTime
**second
)
4422 wxDateTime dt1
= **first
,
4425 return dt1
== dt2
? 0 : dt1
< dt2
? -1 : +1;
4428 // ----------------------------------------------------------------------------
4429 // wxDateTimeHolidayAuthority
4430 // ----------------------------------------------------------------------------
4432 wxHolidayAuthoritiesArray
wxDateTimeHolidayAuthority::ms_authorities
;
4435 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime
& dt
)
4437 size_t count
= ms_authorities
.size();
4438 for ( size_t n
= 0; n
< count
; n
++ )
4440 if ( ms_authorities
[n
]->DoIsHoliday(dt
) )
4451 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime
& dtStart
,
4452 const wxDateTime
& dtEnd
,
4453 wxDateTimeArray
& holidays
)
4455 wxDateTimeArray hol
;
4459 const size_t countAuth
= ms_authorities
.size();
4460 for ( size_t nAuth
= 0; nAuth
< countAuth
; nAuth
++ )
4462 ms_authorities
[nAuth
]->DoGetHolidaysInRange(dtStart
, dtEnd
, hol
);
4464 WX_APPEND_ARRAY(holidays
, hol
);
4467 holidays
.Sort(wxDateTimeCompareFunc
);
4469 return holidays
.size();
4473 void wxDateTimeHolidayAuthority::ClearAllAuthorities()
4475 WX_CLEAR_ARRAY(ms_authorities
);
4479 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority
*auth
)
4481 ms_authorities
.push_back(auth
);
4484 wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
4486 // required here for Darwin
4489 // ----------------------------------------------------------------------------
4490 // wxDateTimeWorkDays
4491 // ----------------------------------------------------------------------------
4493 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime
& dt
) const
4495 wxDateTime::WeekDay wd
= dt
.GetWeekDay();
4497 return (wd
== wxDateTime::Sun
) || (wd
== wxDateTime::Sat
);
4500 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime
& dtStart
,
4501 const wxDateTime
& dtEnd
,
4502 wxDateTimeArray
& holidays
) const
4504 if ( dtStart
> dtEnd
)
4506 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
4513 // instead of checking all days, start with the first Sat after dtStart and
4514 // end with the last Sun before dtEnd
4515 wxDateTime dtSatFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sat
),
4516 dtSatLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sat
),
4517 dtSunFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sun
),
4518 dtSunLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sun
),
4521 for ( dt
= dtSatFirst
; dt
<= dtSatLast
; dt
+= wxDateSpan::Week() )
4526 for ( dt
= dtSunFirst
; dt
<= dtSunLast
; dt
+= wxDateSpan::Week() )
4531 return holidays
.GetCount();
4534 // ============================================================================
4535 // other helper functions
4536 // ============================================================================
4538 // ----------------------------------------------------------------------------
4539 // iteration helpers: can be used to write a for loop over enum variable like
4541 // for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
4542 // ----------------------------------------------------------------------------
4544 WXDLLIMPEXP_BASE
void wxNextMonth(wxDateTime::Month
& m
)
4546 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4548 // no wrapping or the for loop above would never end!
4549 m
= (wxDateTime::Month
)(m
+ 1);
4552 WXDLLIMPEXP_BASE
void wxPrevMonth(wxDateTime::Month
& m
)
4554 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4556 m
= m
== wxDateTime::Jan
? wxDateTime::Inv_Month
4557 : (wxDateTime::Month
)(m
- 1);
4560 WXDLLIMPEXP_BASE
void wxNextWDay(wxDateTime::WeekDay
& wd
)
4562 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4564 // no wrapping or the for loop above would never end!
4565 wd
= (wxDateTime::WeekDay
)(wd
+ 1);
4568 WXDLLIMPEXP_BASE
void wxPrevWDay(wxDateTime::WeekDay
& wd
)
4570 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4572 wd
= wd
== wxDateTime::Sun
? wxDateTime::Inv_WeekDay
4573 : (wxDateTime::WeekDay
)(wd
- 1);
4576 #endif // wxUSE_DATETIME