1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/datetime.cpp
3 // Purpose: implementation of time/date related classes
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // parts of code taken from sndcal library by Scott E. Lee:
11 // Copyright 1993-1995, Scott E. Lee, all rights reserved.
12 // Permission granted to use, copy, modify, distribute and sell
13 // so long as the above copyright and this permission statement
14 // are retained in all copies.
16 // Licence: wxWindows licence
17 ///////////////////////////////////////////////////////////////////////////////
19 // TODO: for $DEITY sake, someone please fix the #ifdef __WXWINCE__ everywhere,
20 // the proper way to do it is to implement (subset of) wxStrftime() for
21 // CE instead of this horror!!
24 * Implementation notes:
26 * 1. the time is stored as a 64bit integer containing the signed number of
27 * milliseconds since Jan 1. 1970 (the Unix Epoch) - so it is always
30 * 2. the range is thus something about 580 million years, but due to current
31 * algorithms limitations, only dates from Nov 24, 4714BC are handled
33 * 3. standard ANSI C functions are used to do time calculations whenever
34 * possible, i.e. when the date is in the range Jan 1, 1970 to 2038
36 * 4. otherwise, the calculations are done by converting the date to/from JDN
37 * first (the range limitation mentioned above comes from here: the
38 * algorithm used by Scott E. Lee's code only works for positive JDNs, more
41 * 5. the object constructed for the given DD-MM-YYYY HH:MM:SS corresponds to
42 * this moment in local time and may be converted to the object
43 * corresponding to the same date/time in another time zone by using
46 * 6. the conversions to the current (or any other) timezone are done when the
47 * internal time representation is converted to the broken-down one in
51 // ============================================================================
53 // ============================================================================
55 // ----------------------------------------------------------------------------
57 // ----------------------------------------------------------------------------
59 // For compilers that support precompilation, includes "wx.h".
60 #include "wx/wxprec.h"
66 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
69 #include "wx/string.h"
74 #include "wx/thread.h"
75 #include "wx/tokenzr.h"
76 #include "wx/module.h"
81 #include "wx/msw/wrapwin.h"
88 #include "wx/datetime.h"
89 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
91 const long wxDateTime::TIME_T_FACTOR
= 1000l;
93 #if wxUSE_EXTENDED_RTTI
95 template<> void wxStringReadValue(const wxString
&s
, wxDateTime
&data
)
97 data
.ParseFormat(s
,wxT("%Y-%m-%d %H:%M:%S")) ;
100 template<> void wxStringWriteValue(wxString
&s
, const wxDateTime
&data
)
102 s
= data
.Format(wxT("%Y-%m-%d %H:%M:%S")) ;
105 wxCUSTOM_TYPE_INFO(wxDateTime
, wxToStringConverter
<wxDateTime
> , wxFromStringConverter
<wxDateTime
>)
110 // ----------------------------------------------------------------------------
111 // conditional compilation
112 // ----------------------------------------------------------------------------
114 #if defined(HAVE_STRPTIME) && defined(__GLIBC__) && \
115 ((__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0))
116 // glibc 2.0.7 strptime() is broken - the following snippet causes it to
117 // crash (instead of just failing):
119 // strncpy(buf, "Tue Dec 21 20:25:40 1999", 128);
120 // strptime(buf, "%x", &tm);
124 #endif // broken strptime()
126 #if defined(HAVE_STRPTIME) && defined(__DARWIN__) && defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
127 // configure detects strptime as linkable because it's in the OS X
128 // System library but MSL headers don't declare it.
130 // char *strptime(const char *, const char *, struct tm *);
131 // However, we DON'T want to just provide it here because we would
132 // crash and/or overwrite data when strptime from OS X tries
133 // to fill in MW's struct tm which is two fields shorter (no TZ stuff)
134 // So for now let's just say we don't have strptime
138 #if defined(__MWERKS__) && wxUSE_UNICODE
142 #if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM)
143 #if defined(__WXPALMOS__)
144 #define WX_GMTOFF_IN_TM
145 #elif defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
146 #define WX_TIMEZONE _timezone
147 #elif defined(__MWERKS__)
148 long wxmw_timezone
= 28800;
149 #define WX_TIMEZONE wxmw_timezone
150 #elif defined(__DJGPP__) || defined(__WINE__)
151 #include <sys/timeb.h>
153 static long wxGetTimeZone()
155 static long timezone
= MAXLONG
; // invalid timezone
156 if (timezone
== MAXLONG
)
160 timezone
= tb
.timezone
;
164 #define WX_TIMEZONE wxGetTimeZone()
165 #elif defined(__DARWIN__)
166 #define WX_GMTOFF_IN_TM
167 #else // unknown platform - try timezone
168 #define WX_TIMEZONE timezone
170 #endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
172 // ----------------------------------------------------------------------------
174 // ----------------------------------------------------------------------------
176 // debugging helper: just a convenient replacement of wxCHECK()
177 #define wxDATETIME_CHECK(expr, msg) \
181 *this = wxInvalidDateTime; \
185 // ----------------------------------------------------------------------------
187 // ----------------------------------------------------------------------------
189 class wxDateTimeHolidaysModule
: public wxModule
192 virtual bool OnInit()
194 wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays
);
199 virtual void OnExit()
201 wxDateTimeHolidayAuthority::ClearAllAuthorities();
202 wxDateTimeHolidayAuthority::ms_authorities
.clear();
206 DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule
)
209 IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule
, wxModule
)
211 // ----------------------------------------------------------------------------
213 // ----------------------------------------------------------------------------
216 static const int MONTHS_IN_YEAR
= 12;
218 static const int SEC_PER_MIN
= 60;
220 static const int MIN_PER_HOUR
= 60;
222 static const int HOURS_PER_DAY
= 24;
224 static const long SECONDS_PER_DAY
= 86400l;
226 static const int DAYS_PER_WEEK
= 7;
228 static const long MILLISECONDS_PER_DAY
= 86400000l;
230 // this is the integral part of JDN of the midnight of Jan 1, 1970
231 // (i.e. JDN(Jan 1, 1970) = 2440587.5)
232 static const long EPOCH_JDN
= 2440587l;
234 // used only in asserts
236 // the date of JDN -0.5 (as we don't work with fractional parts, this is the
237 // reference date for us) is Nov 24, 4714BC
238 static const int JDN_0_YEAR
= -4713;
239 static const int JDN_0_MONTH
= wxDateTime::Nov
;
240 static const int JDN_0_DAY
= 24;
241 #endif // __WXDEBUG__
243 // the constants used for JDN calculations
244 static const long JDN_OFFSET
= 32046l;
245 static const long DAYS_PER_5_MONTHS
= 153l;
246 static const long DAYS_PER_4_YEARS
= 1461l;
247 static const long DAYS_PER_400_YEARS
= 146097l;
249 // this array contains the cumulated number of days in all previous months for
250 // normal and leap years
251 static const wxDateTime::wxDateTime_t gs_cumulatedDays
[2][MONTHS_IN_YEAR
] =
253 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
254 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
257 // ----------------------------------------------------------------------------
259 // ----------------------------------------------------------------------------
261 const wxChar
* wxDefaultDateTimeFormat
= wxT("%c");
262 const wxChar
* wxDefaultTimeSpanFormat
= wxT("%H:%M:%S");
264 // in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
265 // indicate an invalid wxDateTime object
266 const wxDateTime wxDefaultDateTime
;
268 wxDateTime::Country
wxDateTime::ms_country
= wxDateTime::Country_Unknown
;
270 // ----------------------------------------------------------------------------
272 // ----------------------------------------------------------------------------
274 // debugger helper: shows what the date really is
276 extern const wxChar
*wxDumpDate(const wxDateTime
* dt
)
278 static wxChar buf
[128];
280 wxStrcpy(buf
, dt
->Format(_T("%Y-%m-%d (%a) %H:%M:%S")));
286 // get the number of days in the given month of the given year
288 wxDateTime::wxDateTime_t
GetNumOfDaysInMonth(int year
, wxDateTime::Month month
)
290 // the number of days in month in Julian/Gregorian calendar: the first line
291 // is for normal years, the second one is for the leap ones
292 static wxDateTime::wxDateTime_t daysInMonth
[2][MONTHS_IN_YEAR
] =
294 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
295 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
298 return daysInMonth
[wxDateTime::IsLeapYear(year
)][month
];
301 // returns the time zone in the C sense, i.e. the difference UTC - local
303 static int GetTimeZone()
305 #ifdef WX_GMTOFF_IN_TM
306 // set to true when the timezone is set
307 static bool s_timezoneSet
= false;
308 static long gmtoffset
= LONG_MAX
; // invalid timezone
310 // ensure that the timezone variable is set by calling wxLocaltime_r
311 if ( !s_timezoneSet
)
313 // just call wxLocaltime_r() instead of figuring out whether this
314 // system supports tzset(), _tzset() or something else
319 tm
= wxLocaltime_r(&t
, &tmstruct
);
320 s_timezoneSet
= true;
322 // note that GMT offset is the opposite of time zone and so to return
323 // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM
324 // cases we have to negate it
325 gmtoffset
= -tm
->tm_gmtoff
;
328 return (int)gmtoffset
;
329 #else // !WX_GMTOFF_IN_TM
330 return (int)WX_TIMEZONE
;
331 #endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
334 // return the integral part of the JDN for the midnight of the given date (to
335 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
336 // noon of the previous day)
337 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day
,
338 wxDateTime::Month mon
,
341 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
343 // check the date validity
345 (year
> JDN_0_YEAR
) ||
346 ((year
== JDN_0_YEAR
) && (mon
> JDN_0_MONTH
)) ||
347 ((year
== JDN_0_YEAR
) && (mon
== JDN_0_MONTH
) && (day
>= JDN_0_DAY
)),
348 _T("date out of range - can't convert to JDN")
351 // make the year positive to avoid problems with negative numbers division
354 // months are counted from March here
356 if ( mon
>= wxDateTime::Mar
)
366 // now we can simply add all the contributions together
367 return ((year
/ 100) * DAYS_PER_400_YEARS
) / 4
368 + ((year
% 100) * DAYS_PER_4_YEARS
) / 4
369 + (month
* DAYS_PER_5_MONTHS
+ 2) / 5
375 // this function is a wrapper around strftime(3) adding error checking
376 static wxString
CallStrftime(const wxChar
*format
, const tm
* tm
)
379 // Create temp wxString here to work around mingw/cygwin bug 1046059
380 // http://sourceforge.net/tracker/?func=detail&atid=102435&aid=1046059&group_id=2435
383 if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) )
385 // buffer is too small?
386 wxFAIL_MSG(_T("strftime() failed"));
396 #if wxUSE_UNIX && !defined(HAVE_STRPTIME_DECL)
397 // configure detected that we had strptime() but not its declaration,
398 // provide it ourselves
399 extern "C" char *strptime(const char *, const char *, struct tm
*);
402 // Unicode-friendly strptime() wrapper
403 static const wxChar
*
404 CallStrptime(const wxChar
*input
, const char *fmt
, tm
*tm
)
406 // the problem here is that strptime() returns pointer into the string we
407 // passed to it while we're really interested in the pointer into the
408 // original, Unicode, string so we try to transform the pointer back
410 wxCharBuffer
inputMB(wxConvertWX2MB(input
));
412 const char * const inputMB
= input
;
413 #endif // Unicode/Ascii
415 const char *result
= strptime(inputMB
, fmt
, tm
);
420 // FIXME: this is wrong in presence of surrogates &c
421 return input
+ (result
- inputMB
.data());
424 #endif // Unicode/Ascii
427 #endif // HAVE_STRPTIME
429 // if year and/or month have invalid values, replace them with the current ones
430 static void ReplaceDefaultYearMonthWithCurrent(int *year
,
431 wxDateTime::Month
*month
)
433 struct tm
*tmNow
= NULL
;
436 if ( *year
== wxDateTime::Inv_Year
)
438 tmNow
= wxDateTime::GetTmNow(&tmstruct
);
440 *year
= 1900 + tmNow
->tm_year
;
443 if ( *month
== wxDateTime::Inv_Month
)
446 tmNow
= wxDateTime::GetTmNow(&tmstruct
);
448 *month
= (wxDateTime::Month
)tmNow
->tm_mon
;
452 // fll the struct tm with default values
453 static void InitTm(struct tm
& tm
)
455 // struct tm may have etxra fields (undocumented and with unportable
456 // names) which, nevertheless, must be set to 0
457 memset(&tm
, 0, sizeof(struct tm
));
459 tm
.tm_mday
= 1; // mday 0 is invalid
460 tm
.tm_year
= 76; // any valid year
461 tm
.tm_isdst
= -1; // auto determine
467 // return the month if the string is a month name or Inv_Month otherwise
468 static wxDateTime::Month
GetMonthFromName(const wxString
& name
, int flags
)
470 wxDateTime::Month mon
;
471 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
473 // case-insensitive comparison either one of or with both abbreviated
475 if ( flags
& wxDateTime::Name_Full
)
477 if ( name
.CmpNoCase(wxDateTime::
478 GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 )
484 if ( flags
& wxDateTime::Name_Abbr
)
486 if ( name
.CmpNoCase(wxDateTime::
487 GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 )
497 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
498 static wxDateTime::WeekDay
GetWeekDayFromName(const wxString
& name
, int flags
)
500 wxDateTime::WeekDay wd
;
501 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
503 // case-insensitive comparison either one of or with both abbreviated
505 if ( flags
& wxDateTime::Name_Full
)
507 if ( name
.CmpNoCase(wxDateTime::
508 GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 )
514 if ( flags
& wxDateTime::Name_Abbr
)
516 if ( name
.CmpNoCase(wxDateTime::
517 GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 )
528 struct tm
*wxDateTime::GetTmNow(struct tm
*tmstruct
)
530 time_t t
= GetTimeNow();
531 return wxLocaltime_r(&t
, tmstruct
);
534 // scans all digits (but no more than len) and returns the resulting number
535 static bool GetNumericToken(size_t len
, const wxChar
*& p
, unsigned long *number
)
539 while ( wxIsdigit(*p
) )
543 if ( len
&& ++n
> len
)
547 return !s
.empty() && s
.ToULong(number
);
550 // scans all alphabetic characters and returns the resulting string
551 static wxString
GetAlphaToken(const wxChar
*& p
)
554 while ( wxIsalpha(*p
) )
562 // ============================================================================
563 // implementation of wxDateTime
564 // ============================================================================
566 // ----------------------------------------------------------------------------
568 // ----------------------------------------------------------------------------
572 year
= (wxDateTime_t
)wxDateTime::Inv_Year
;
573 mon
= wxDateTime::Inv_Month
;
575 hour
= min
= sec
= msec
= 0;
576 wday
= wxDateTime::Inv_WeekDay
;
579 wxDateTime::Tm::Tm(const struct tm
& tm
, const TimeZone
& tz
)
583 sec
= (wxDateTime::wxDateTime_t
)tm
.tm_sec
;
584 min
= (wxDateTime::wxDateTime_t
)tm
.tm_min
;
585 hour
= (wxDateTime::wxDateTime_t
)tm
.tm_hour
;
586 mday
= (wxDateTime::wxDateTime_t
)tm
.tm_mday
;
587 mon
= (wxDateTime::Month
)tm
.tm_mon
;
588 year
= 1900 + tm
.tm_year
;
589 wday
= (wxDateTime::wxDateTime_t
)tm
.tm_wday
;
590 yday
= (wxDateTime::wxDateTime_t
)tm
.tm_yday
;
593 bool wxDateTime::Tm::IsValid() const
595 // we allow for the leap seconds, although we don't use them (yet)
596 return (year
!= wxDateTime::Inv_Year
) && (mon
!= wxDateTime::Inv_Month
) &&
597 (mday
<= GetNumOfDaysInMonth(year
, mon
)) &&
598 (hour
< 24) && (min
< 60) && (sec
< 62) && (msec
< 1000);
601 void wxDateTime::Tm::ComputeWeekDay()
603 // compute the week day from day/month/year: we use the dumbest algorithm
604 // possible: just compute our JDN and then use the (simple to derive)
605 // formula: weekday = (JDN + 1.5) % 7
606 wday
= (wxDateTime::wxDateTime_t
)((wxDateTime::WeekDay
)(GetTruncatedJDN(mday
, mon
, year
) + 2) % 7);
609 void wxDateTime::Tm::AddMonths(int monDiff
)
611 // normalize the months field
612 while ( monDiff
< -mon
)
616 monDiff
+= MONTHS_IN_YEAR
;
619 while ( monDiff
+ mon
>= MONTHS_IN_YEAR
)
623 monDiff
-= MONTHS_IN_YEAR
;
626 mon
= (wxDateTime::Month
)(mon
+ monDiff
);
628 wxASSERT_MSG( mon
>= 0 && mon
< MONTHS_IN_YEAR
, _T("logic error") );
630 // NB: we don't check here that the resulting date is valid, this function
631 // is private and the caller must check it if needed
634 void wxDateTime::Tm::AddDays(int dayDiff
)
636 // normalize the days field
637 while ( dayDiff
+ mday
< 1 )
641 dayDiff
+= GetNumOfDaysInMonth(year
, mon
);
644 mday
= (wxDateTime::wxDateTime_t
)( mday
+ dayDiff
);
645 while ( mday
> GetNumOfDaysInMonth(year
, mon
) )
647 mday
-= GetNumOfDaysInMonth(year
, mon
);
652 wxASSERT_MSG( mday
> 0 && mday
<= GetNumOfDaysInMonth(year
, mon
),
656 // ----------------------------------------------------------------------------
658 // ----------------------------------------------------------------------------
660 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz
)
664 case wxDateTime::Local
:
665 // get the offset from C RTL: it returns the difference GMT-local
666 // while we want to have the offset _from_ GMT, hence the '-'
667 m_offset
= -GetTimeZone();
670 case wxDateTime::GMT_12
:
671 case wxDateTime::GMT_11
:
672 case wxDateTime::GMT_10
:
673 case wxDateTime::GMT_9
:
674 case wxDateTime::GMT_8
:
675 case wxDateTime::GMT_7
:
676 case wxDateTime::GMT_6
:
677 case wxDateTime::GMT_5
:
678 case wxDateTime::GMT_4
:
679 case wxDateTime::GMT_3
:
680 case wxDateTime::GMT_2
:
681 case wxDateTime::GMT_1
:
682 m_offset
= -3600*(wxDateTime::GMT0
- tz
);
685 case wxDateTime::GMT0
:
686 case wxDateTime::GMT1
:
687 case wxDateTime::GMT2
:
688 case wxDateTime::GMT3
:
689 case wxDateTime::GMT4
:
690 case wxDateTime::GMT5
:
691 case wxDateTime::GMT6
:
692 case wxDateTime::GMT7
:
693 case wxDateTime::GMT8
:
694 case wxDateTime::GMT9
:
695 case wxDateTime::GMT10
:
696 case wxDateTime::GMT11
:
697 case wxDateTime::GMT12
:
698 m_offset
= 3600*(tz
- wxDateTime::GMT0
);
701 case wxDateTime::A_CST
:
702 // Central Standard Time in use in Australia = UTC + 9.5
703 m_offset
= 60l*(9*MIN_PER_HOUR
+ MIN_PER_HOUR
/2);
707 wxFAIL_MSG( _T("unknown time zone") );
711 // ----------------------------------------------------------------------------
713 // ----------------------------------------------------------------------------
716 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
)
718 if ( year
== Inv_Year
)
719 year
= GetCurrentYear();
721 if ( cal
== Gregorian
)
723 // in Gregorian calendar leap years are those divisible by 4 except
724 // those divisible by 100 unless they're also divisible by 400
725 // (in some countries, like Russia and Greece, additional corrections
726 // exist, but they won't manifest themselves until 2700)
727 return (year
% 4 == 0) && ((year
% 100 != 0) || (year
% 400 == 0));
729 else if ( cal
== Julian
)
731 // in Julian calendar the rule is simpler
732 return year
% 4 == 0;
736 wxFAIL_MSG(_T("unknown calendar"));
743 int wxDateTime::GetCentury(int year
)
745 return year
> 0 ? year
/ 100 : year
/ 100 - 1;
749 int wxDateTime::ConvertYearToBC(int year
)
752 return year
> 0 ? year
: year
- 1;
756 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
)
761 return Now().GetYear();
764 wxFAIL_MSG(_T("TODO"));
768 wxFAIL_MSG(_T("unsupported calendar"));
776 wxDateTime::Month
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
)
781 return Now().GetMonth();
784 wxFAIL_MSG(_T("TODO"));
788 wxFAIL_MSG(_T("unsupported calendar"));
796 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(int year
, Calendar cal
)
798 if ( year
== Inv_Year
)
800 // take the current year if none given
801 year
= GetCurrentYear();
808 return IsLeapYear(year
) ? 366 : 365;
811 wxFAIL_MSG(_T("unsupported calendar"));
819 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(wxDateTime::Month month
,
821 wxDateTime::Calendar cal
)
823 wxCHECK_MSG( month
< MONTHS_IN_YEAR
, 0, _T("invalid month") );
825 if ( cal
== Gregorian
|| cal
== Julian
)
827 if ( year
== Inv_Year
)
829 // take the current year if none given
830 year
= GetCurrentYear();
833 return GetNumOfDaysInMonth(year
, month
);
837 wxFAIL_MSG(_T("unsupported calendar"));
844 wxString
wxDateTime::GetMonthName(wxDateTime::Month month
,
845 wxDateTime::NameFlags flags
)
847 wxCHECK_MSG( month
!= Inv_Month
, wxEmptyString
, _T("invalid month") );
849 // notice that we must set all the fields to avoid confusing libc (GNU one
850 // gets confused to a crash if we don't do this)
855 return CallStrftime(flags
== Name_Abbr
? _T("%b") : _T("%B"), &tm
);
861 ret
= (flags
== Name_Abbr
? wxT("Jan"): wxT("January"));
864 ret
= (flags
== Name_Abbr
? wxT("Feb"): wxT("Febuary"));
867 ret
= (flags
== Name_Abbr
? wxT("Mar"): wxT("March"));
870 ret
= (flags
== Name_Abbr
? wxT("Apr"): wxT("April"));
873 ret
= (flags
== Name_Abbr
? wxT("May"): wxT("May"));
876 ret
= (flags
== Name_Abbr
? wxT("Jun"): wxT("June"));
879 ret
= (flags
== Name_Abbr
? wxT("Jul"): wxT("July"));
882 ret
= (flags
== Name_Abbr
? wxT("Aug"): wxT("August"));
885 ret
= (flags
== Name_Abbr
? wxT("Sep"): wxT("September"));
888 ret
= (flags
== Name_Abbr
? wxT("Oct"): wxT("October"));
891 ret
= (flags
== Name_Abbr
? wxT("Nov"): wxT("November"));
894 ret
= (flags
== Name_Abbr
? wxT("Dec"): wxT("December"));
902 wxString
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
,
903 wxDateTime::NameFlags flags
)
905 wxCHECK_MSG( wday
!= Inv_WeekDay
, wxEmptyString
, _T("invalid weekday") );
907 // take some arbitrary Sunday (but notice that the day should be such that
908 // after adding wday to it below we still have a valid date, e.g. don't
916 // and offset it by the number of days needed to get the correct wday
919 // call mktime() to normalize it...
922 // ... and call strftime()
923 return CallStrftime(flags
== Name_Abbr
? _T("%a") : _T("%A"), &tm
);
929 ret
= (flags
== Name_Abbr
? wxT("Sun") : wxT("Sunday"));
932 ret
= (flags
== Name_Abbr
? wxT("Mon") : wxT("Monday"));
935 ret
= (flags
== Name_Abbr
? wxT("Tue") : wxT("Tuesday"));
938 ret
= (flags
== Name_Abbr
? wxT("Wed") : wxT("Wednesday"));
941 ret
= (flags
== Name_Abbr
? wxT("Thu") : wxT("Thursday"));
944 ret
= (flags
== Name_Abbr
? wxT("Fri") : wxT("Friday"));
947 ret
= (flags
== Name_Abbr
? wxT("Sat") : wxT("Saturday"));
956 void wxDateTime::GetAmPmStrings(wxString
*am
, wxString
*pm
)
961 // @Note: Do not call 'CallStrftime' here! CallStrftime checks the return code
962 // and causes an assertion failed if the buffer is to small (which is good) - OR -
963 // if strftime does not return anything because the format string is invalid - OR -
964 // if there are no 'am' / 'pm' tokens defined for the current locale (which is not good).
965 // wxDateTime::ParseTime will try several different formats to parse the time.
966 // As a result, GetAmPmStrings might get called, even if the current locale
967 // does not define any 'am' / 'pm' tokens. In this case, wxStrftime would
968 // assert, even though it is a perfectly legal use.
971 if (wxStrftime(buffer
, sizeof(buffer
)/sizeof(wxChar
), _T("%p"), &tm
) > 0)
972 *am
= wxString(buffer
);
979 if (wxStrftime(buffer
, sizeof(buffer
)/sizeof(wxChar
), _T("%p"), &tm
) > 0)
980 *pm
= wxString(buffer
);
986 // ----------------------------------------------------------------------------
987 // Country stuff: date calculations depend on the country (DST, work days,
988 // ...), so we need to know which rules to follow.
989 // ----------------------------------------------------------------------------
992 wxDateTime::Country
wxDateTime::GetCountry()
994 // TODO use LOCALE_ICOUNTRY setting under Win32
996 if ( ms_country
== Country_Unknown
)
998 // try to guess from the time zone name
999 time_t t
= time(NULL
);
1001 struct tm
*tm
= wxLocaltime_r(&t
, &tmstruct
);
1003 wxString tz
= CallStrftime(_T("%Z"), tm
);
1004 if ( tz
== _T("WET") || tz
== _T("WEST") )
1008 else if ( tz
== _T("CET") || tz
== _T("CEST") )
1010 ms_country
= Country_EEC
;
1012 else if ( tz
== _T("MSK") || tz
== _T("MSD") )
1014 ms_country
= Russia
;
1016 else if ( tz
== _T("AST") || tz
== _T("ADT") ||
1017 tz
== _T("EST") || tz
== _T("EDT") ||
1018 tz
== _T("CST") || tz
== _T("CDT") ||
1019 tz
== _T("MST") || tz
== _T("MDT") ||
1020 tz
== _T("PST") || tz
== _T("PDT") )
1026 // well, choose a default one
1038 void wxDateTime::SetCountry(wxDateTime::Country country
)
1040 ms_country
= country
;
1044 bool wxDateTime::IsWestEuropeanCountry(Country country
)
1046 if ( country
== Country_Default
)
1048 country
= GetCountry();
1051 return (Country_WesternEurope_Start
<= country
) &&
1052 (country
<= Country_WesternEurope_End
);
1055 // ----------------------------------------------------------------------------
1056 // DST calculations: we use 3 different rules for the West European countries,
1057 // USA and for the rest of the world. This is undoubtedly false for many
1058 // countries, but I lack the necessary info (and the time to gather it),
1059 // please add the other rules here!
1060 // ----------------------------------------------------------------------------
1063 bool wxDateTime::IsDSTApplicable(int year
, Country country
)
1065 if ( year
== Inv_Year
)
1067 // take the current year if none given
1068 year
= GetCurrentYear();
1071 if ( country
== Country_Default
)
1073 country
= GetCountry();
1080 // DST was first observed in the US and UK during WWI, reused
1081 // during WWII and used again since 1966
1082 return year
>= 1966 ||
1083 (year
>= 1942 && year
<= 1945) ||
1084 (year
== 1918 || year
== 1919);
1087 // assume that it started after WWII
1093 wxDateTime
wxDateTime::GetBeginDST(int year
, Country country
)
1095 if ( year
== Inv_Year
)
1097 // take the current year if none given
1098 year
= GetCurrentYear();
1101 if ( country
== Country_Default
)
1103 country
= GetCountry();
1106 if ( !IsDSTApplicable(year
, country
) )
1108 return wxInvalidDateTime
;
1113 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1115 // DST begins at 1 a.m. GMT on the last Sunday of March
1116 if ( !dt
.SetToLastWeekDay(Sun
, Mar
, year
) )
1119 wxFAIL_MSG( _T("no last Sunday in March?") );
1122 dt
+= wxTimeSpan::Hours(1);
1124 // disable DST tests because it could result in an infinite recursion!
1127 else switch ( country
)
1134 // don't know for sure - assume it was in effect all year
1139 dt
.Set(1, Jan
, year
);
1143 // DST was installed Feb 2, 1942 by the Congress
1144 dt
.Set(2, Feb
, year
);
1147 // Oil embargo changed the DST period in the US
1149 dt
.Set(6, Jan
, 1974);
1153 dt
.Set(23, Feb
, 1975);
1157 // before 1986, DST begun on the last Sunday of April, but
1158 // in 1986 Reagan changed it to begin at 2 a.m. of the
1159 // first Sunday in April
1162 if ( !dt
.SetToLastWeekDay(Sun
, Apr
, year
) )
1165 wxFAIL_MSG( _T("no first Sunday in April?") );
1170 if ( !dt
.SetToWeekDay(Sun
, 1, Apr
, year
) )
1173 wxFAIL_MSG( _T("no first Sunday in April?") );
1177 dt
+= wxTimeSpan::Hours(2);
1179 // TODO what about timezone??
1185 // assume Mar 30 as the start of the DST for the rest of the world
1186 // - totally bogus, of course
1187 dt
.Set(30, Mar
, year
);
1194 wxDateTime
wxDateTime::GetEndDST(int year
, Country country
)
1196 if ( year
== Inv_Year
)
1198 // take the current year if none given
1199 year
= GetCurrentYear();
1202 if ( country
== Country_Default
)
1204 country
= GetCountry();
1207 if ( !IsDSTApplicable(year
, country
) )
1209 return wxInvalidDateTime
;
1214 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1216 // DST ends at 1 a.m. GMT on the last Sunday of October
1217 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1219 // weirder and weirder...
1220 wxFAIL_MSG( _T("no last Sunday in October?") );
1223 dt
+= wxTimeSpan::Hours(1);
1225 // disable DST tests because it could result in an infinite recursion!
1228 else switch ( country
)
1235 // don't know for sure - assume it was in effect all year
1239 dt
.Set(31, Dec
, year
);
1243 // the time was reset after the end of the WWII
1244 dt
.Set(30, Sep
, year
);
1248 // DST ends at 2 a.m. on the last Sunday of October
1249 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1251 // weirder and weirder...
1252 wxFAIL_MSG( _T("no last Sunday in October?") );
1255 dt
+= wxTimeSpan::Hours(2);
1257 // TODO what about timezone??
1262 // assume October 26th as the end of the DST - totally bogus too
1263 dt
.Set(26, Oct
, year
);
1269 // ----------------------------------------------------------------------------
1270 // constructors and assignment operators
1271 // ----------------------------------------------------------------------------
1273 // return the current time with ms precision
1274 /* static */ wxDateTime
wxDateTime::UNow()
1276 return wxDateTime(wxGetLocalTimeMillis());
1279 // the values in the tm structure contain the local time
1280 wxDateTime
& wxDateTime::Set(const struct tm
& tm
)
1283 time_t timet
= mktime(&tm2
);
1285 if ( timet
== (time_t)-1 )
1287 // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
1288 // less than timezone - try to make it work for this case
1289 if ( tm2
.tm_year
== 70 && tm2
.tm_mon
== 0 && tm2
.tm_mday
== 1 )
1291 return Set((time_t)(
1293 tm2
.tm_hour
* MIN_PER_HOUR
* SEC_PER_MIN
+
1294 tm2
.tm_min
* SEC_PER_MIN
+
1298 wxFAIL_MSG( _T("mktime() failed") );
1300 *this = wxInvalidDateTime
;
1310 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
,
1311 wxDateTime_t minute
,
1312 wxDateTime_t second
,
1313 wxDateTime_t millisec
)
1315 // we allow seconds to be 61 to account for the leap seconds, even if we
1316 // don't use them really
1317 wxDATETIME_CHECK( hour
< 24 &&
1321 _T("Invalid time in wxDateTime::Set()") );
1323 // get the current date from system
1325 struct tm
*tm
= GetTmNow(&tmstruct
);
1327 wxDATETIME_CHECK( tm
, _T("wxLocaltime_r() failed") );
1329 // make a copy so it isn't clobbered by the call to mktime() below
1334 tm1
.tm_min
= minute
;
1335 tm1
.tm_sec
= second
;
1337 // and the DST in case it changes on this date
1340 if ( tm2
.tm_isdst
!= tm1
.tm_isdst
)
1341 tm1
.tm_isdst
= tm2
.tm_isdst
;
1345 // and finally adjust milliseconds
1346 return SetMillisecond(millisec
);
1349 wxDateTime
& wxDateTime::Set(wxDateTime_t day
,
1353 wxDateTime_t minute
,
1354 wxDateTime_t second
,
1355 wxDateTime_t millisec
)
1357 wxDATETIME_CHECK( hour
< 24 &&
1361 _T("Invalid time in wxDateTime::Set()") );
1363 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1365 wxDATETIME_CHECK( (0 < day
) && (day
<= GetNumberOfDays(month
, year
)),
1366 _T("Invalid date in wxDateTime::Set()") );
1368 // the range of time_t type (inclusive)
1369 static const int yearMinInRange
= 1970;
1370 static const int yearMaxInRange
= 2037;
1372 // test only the year instead of testing for the exact end of the Unix
1373 // time_t range - it doesn't bring anything to do more precise checks
1374 if ( year
>= yearMinInRange
&& year
<= yearMaxInRange
)
1376 // use the standard library version if the date is in range - this is
1377 // probably more efficient than our code
1379 tm
.tm_year
= year
- 1900;
1385 tm
.tm_isdst
= -1; // mktime() will guess it
1389 // and finally adjust milliseconds
1391 SetMillisecond(millisec
);
1397 // do time calculations ourselves: we want to calculate the number of
1398 // milliseconds between the given date and the epoch
1400 // get the JDN for the midnight of this day
1401 m_time
= GetTruncatedJDN(day
, month
, year
);
1402 m_time
-= EPOCH_JDN
;
1403 m_time
*= SECONDS_PER_DAY
* TIME_T_FACTOR
;
1405 // JDN corresponds to GMT, we take localtime
1406 Add(wxTimeSpan(hour
, minute
, second
+ GetTimeZone(), millisec
));
1412 wxDateTime
& wxDateTime::Set(double jdn
)
1414 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1416 jdn
-= EPOCH_JDN
+ 0.5;
1418 m_time
.Assign(jdn
*MILLISECONDS_PER_DAY
);
1420 // JDNs always are in UTC, so we don't need any adjustments for time zone
1425 wxDateTime
& wxDateTime::ResetTime()
1429 if ( tm
.hour
|| tm
.min
|| tm
.sec
|| tm
.msec
)
1442 // ----------------------------------------------------------------------------
1443 // DOS Date and Time Format functions
1444 // ----------------------------------------------------------------------------
1445 // the dos date and time value is an unsigned 32 bit value in the format:
1446 // YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
1448 // Y = year offset from 1980 (0-127)
1450 // D = day of month (1-31)
1452 // m = minute (0-59)
1453 // s = bisecond (0-29) each bisecond indicates two seconds
1454 // ----------------------------------------------------------------------------
1456 wxDateTime
& wxDateTime::SetFromDOS(unsigned long ddt
)
1461 long year
= ddt
& 0xFE000000;
1466 long month
= ddt
& 0x1E00000;
1471 long day
= ddt
& 0x1F0000;
1475 long hour
= ddt
& 0xF800;
1479 long minute
= ddt
& 0x7E0;
1483 long second
= ddt
& 0x1F;
1484 tm
.tm_sec
= second
* 2;
1486 return Set(mktime(&tm
));
1489 unsigned long wxDateTime::GetAsDOS() const
1492 time_t ticks
= GetTicks();
1494 struct tm
*tm
= wxLocaltime_r(&ticks
, &tmstruct
);
1496 long year
= tm
->tm_year
;
1500 long month
= tm
->tm_mon
;
1504 long day
= tm
->tm_mday
;
1507 long hour
= tm
->tm_hour
;
1510 long minute
= tm
->tm_min
;
1513 long second
= tm
->tm_sec
;
1516 ddt
= year
| month
| day
| hour
| minute
| second
;
1520 // ----------------------------------------------------------------------------
1521 // time_t <-> broken down time conversions
1522 // ----------------------------------------------------------------------------
1524 wxDateTime::Tm
wxDateTime::GetTm(const TimeZone
& tz
) const
1526 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1528 time_t time
= GetTicks();
1529 if ( time
!= (time_t)-1 )
1531 // use C RTL functions
1534 if ( tz
.GetOffset() == -GetTimeZone() )
1536 // we are working with local time
1537 tm
= wxLocaltime_r(&time
, &tmstruct
);
1539 // should never happen
1540 wxCHECK_MSG( tm
, Tm(), _T("wxLocaltime_r() failed") );
1544 time
+= (time_t)tz
.GetOffset();
1545 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
1546 int time2
= (int) time
;
1552 tm
= wxGmtime_r(&time
, &tmstruct
);
1554 // should never happen
1555 wxCHECK_MSG( tm
, Tm(), _T("wxGmtime_r() failed") );
1559 tm
= (struct tm
*)NULL
;
1565 // adjust the milliseconds
1567 long timeOnly
= (m_time
% MILLISECONDS_PER_DAY
).ToLong();
1568 tm2
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1571 //else: use generic code below
1574 // remember the time and do the calculations with the date only - this
1575 // eliminates rounding errors of the floating point arithmetics
1577 wxLongLong timeMidnight
= m_time
+ tz
.GetOffset() * 1000;
1579 long timeOnly
= (timeMidnight
% MILLISECONDS_PER_DAY
).ToLong();
1581 // we want to always have positive time and timeMidnight to be really
1582 // the midnight before it
1585 timeOnly
= MILLISECONDS_PER_DAY
+ timeOnly
;
1588 timeMidnight
-= timeOnly
;
1590 // calculate the Gregorian date from JDN for the midnight of our date:
1591 // this will yield day, month (in 1..12 range) and year
1593 // actually, this is the JDN for the noon of the previous day
1594 long jdn
= (timeMidnight
/ MILLISECONDS_PER_DAY
).ToLong() + EPOCH_JDN
;
1596 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1598 wxASSERT_MSG( jdn
> -2, _T("JDN out of range") );
1600 // calculate the century
1601 long temp
= (jdn
+ JDN_OFFSET
) * 4 - 1;
1602 long century
= temp
/ DAYS_PER_400_YEARS
;
1604 // then the year and day of year (1 <= dayOfYear <= 366)
1605 temp
= ((temp
% DAYS_PER_400_YEARS
) / 4) * 4 + 3;
1606 long year
= (century
* 100) + (temp
/ DAYS_PER_4_YEARS
);
1607 long dayOfYear
= (temp
% DAYS_PER_4_YEARS
) / 4 + 1;
1609 // and finally the month and day of the month
1610 temp
= dayOfYear
* 5 - 3;
1611 long month
= temp
/ DAYS_PER_5_MONTHS
;
1612 long day
= (temp
% DAYS_PER_5_MONTHS
) / 5 + 1;
1614 // month is counted from March - convert to normal
1625 // year is offset by 4800
1628 // check that the algorithm gave us something reasonable
1629 wxASSERT_MSG( (0 < month
) && (month
<= 12), _T("invalid month") );
1630 wxASSERT_MSG( (1 <= day
) && (day
< 32), _T("invalid day") );
1632 // construct Tm from these values
1634 tm
.year
= (int)year
;
1635 tm
.mon
= (Month
)(month
- 1); // algorithm yields 1 for January, not 0
1636 tm
.mday
= (wxDateTime_t
)day
;
1637 tm
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1638 timeOnly
-= tm
.msec
;
1639 timeOnly
/= 1000; // now we have time in seconds
1641 tm
.sec
= (wxDateTime_t
)(timeOnly
% SEC_PER_MIN
);
1643 timeOnly
/= SEC_PER_MIN
; // now we have time in minutes
1645 tm
.min
= (wxDateTime_t
)(timeOnly
% MIN_PER_HOUR
);
1648 tm
.hour
= (wxDateTime_t
)(timeOnly
/ MIN_PER_HOUR
);
1653 wxDateTime
& wxDateTime::SetYear(int year
)
1655 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1664 wxDateTime
& wxDateTime::SetMonth(Month month
)
1666 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1675 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
)
1677 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1686 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
)
1688 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1697 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
)
1699 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1708 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
)
1710 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1719 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
)
1721 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1723 // we don't need to use GetTm() for this one
1724 m_time
-= m_time
% 1000l;
1725 m_time
+= millisecond
;
1730 // ----------------------------------------------------------------------------
1731 // wxDateTime arithmetics
1732 // ----------------------------------------------------------------------------
1734 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
)
1738 tm
.year
+= diff
.GetYears();
1739 tm
.AddMonths(diff
.GetMonths());
1741 // check that the resulting date is valid
1742 if ( tm
.mday
> GetNumOfDaysInMonth(tm
.year
, tm
.mon
) )
1744 // We suppose that when adding one month to Jan 31 we want to get Feb
1745 // 28 (or 29), i.e. adding a month to the last day of the month should
1746 // give the last day of the next month which is quite logical.
1748 // Unfortunately, there is no logic way to understand what should
1749 // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1750 // We make it Feb 28 (last day too), but it is highly questionable.
1751 tm
.mday
= GetNumOfDaysInMonth(tm
.year
, tm
.mon
);
1754 tm
.AddDays(diff
.GetTotalDays());
1758 wxASSERT_MSG( IsSameTime(tm
),
1759 _T("Add(wxDateSpan) shouldn't modify time") );
1764 // ----------------------------------------------------------------------------
1765 // Weekday and monthday stuff
1766 // ----------------------------------------------------------------------------
1768 // convert Sun, Mon, ..., Sat into 6, 0, ..., 5
1769 static inline int ConvertWeekDayToMondayBase(int wd
)
1771 return wd
== wxDateTime::Sun
? 6 : wd
- 1;
1776 wxDateTime::SetToWeekOfYear(int year
, wxDateTime_t numWeek
, WeekDay wd
)
1778 wxASSERT_MSG( numWeek
> 0,
1779 _T("invalid week number: weeks are counted from 1") );
1781 // Jan 4 always lies in the 1st week of the year
1782 wxDateTime
dt(4, Jan
, year
);
1783 dt
.SetToWeekDayInSameWeek(wd
);
1784 dt
+= wxDateSpan::Weeks(numWeek
- 1);
1789 // use a separate function to avoid warnings about using deprecated
1790 // SetToTheWeek in GetWeek below
1792 SetToTheWeek(int year
,
1793 wxDateTime::wxDateTime_t numWeek
,
1794 wxDateTime::WeekDay weekday
,
1795 wxDateTime::WeekFlags flags
)
1797 // Jan 4 always lies in the 1st week of the year
1798 wxDateTime
dt(4, wxDateTime::Jan
, year
);
1799 dt
.SetToWeekDayInSameWeek(weekday
, flags
);
1800 dt
+= wxDateSpan::Weeks(numWeek
- 1);
1805 bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek
,
1809 int year
= GetYear();
1810 *this = ::SetToTheWeek(year
, numWeek
, weekday
, flags
);
1811 if ( GetYear() != year
)
1813 // oops... numWeek was too big
1820 wxDateTime
wxDateTime::GetWeek(wxDateTime_t numWeek
,
1822 WeekFlags flags
) const
1824 return ::SetToTheWeek(GetYear(), numWeek
, weekday
, flags
);
1827 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
,
1830 // take the current month/year if none specified
1831 if ( year
== Inv_Year
)
1833 if ( month
== Inv_Month
)
1836 return Set(GetNumOfDaysInMonth(year
, month
), month
, year
);
1839 wxDateTime
& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday
, WeekFlags flags
)
1841 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1843 int wdayDst
= weekday
,
1844 wdayThis
= GetWeekDay();
1845 if ( wdayDst
== wdayThis
)
1851 if ( flags
== Default_First
)
1853 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
1856 // the logic below based on comparing weekday and wdayThis works if Sun (0)
1857 // is the first day in the week, but breaks down for Monday_First case so
1858 // we adjust the week days in this case
1859 if ( flags
== Monday_First
)
1861 if ( wdayThis
== Sun
)
1863 if ( wdayDst
== Sun
)
1866 //else: Sunday_First, nothing to do
1868 // go forward or back in time to the day we want
1869 if ( wdayDst
< wdayThis
)
1871 return Subtract(wxDateSpan::Days(wdayThis
- wdayDst
));
1873 else // weekday > wdayThis
1875 return Add(wxDateSpan::Days(wdayDst
- wdayThis
));
1879 wxDateTime
& wxDateTime::SetToNextWeekDay(WeekDay weekday
)
1881 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1884 WeekDay wdayThis
= GetWeekDay();
1885 if ( weekday
== wdayThis
)
1890 else if ( weekday
< wdayThis
)
1892 // need to advance a week
1893 diff
= 7 - (wdayThis
- weekday
);
1895 else // weekday > wdayThis
1897 diff
= weekday
- wdayThis
;
1900 return Add(wxDateSpan::Days(diff
));
1903 wxDateTime
& wxDateTime::SetToPrevWeekDay(WeekDay weekday
)
1905 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1908 WeekDay wdayThis
= GetWeekDay();
1909 if ( weekday
== wdayThis
)
1914 else if ( weekday
> wdayThis
)
1916 // need to go to previous week
1917 diff
= 7 - (weekday
- wdayThis
);
1919 else // weekday < wdayThis
1921 diff
= wdayThis
- weekday
;
1924 return Subtract(wxDateSpan::Days(diff
));
1927 bool wxDateTime::SetToWeekDay(WeekDay weekday
,
1932 wxCHECK_MSG( weekday
!= Inv_WeekDay
, false, _T("invalid weekday") );
1934 // we don't check explicitly that -5 <= n <= 5 because we will return false
1935 // anyhow in such case - but may be should still give an assert for it?
1937 // take the current month/year if none specified
1938 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1942 // TODO this probably could be optimised somehow...
1946 // get the first day of the month
1947 dt
.Set(1, month
, year
);
1950 WeekDay wdayFirst
= dt
.GetWeekDay();
1952 // go to the first weekday of the month
1953 int diff
= weekday
- wdayFirst
;
1957 // add advance n-1 weeks more
1960 dt
+= wxDateSpan::Days(diff
);
1962 else // count from the end of the month
1964 // get the last day of the month
1965 dt
.SetToLastMonthDay(month
, year
);
1968 WeekDay wdayLast
= dt
.GetWeekDay();
1970 // go to the last weekday of the month
1971 int diff
= wdayLast
- weekday
;
1975 // and rewind n-1 weeks from there
1978 dt
-= wxDateSpan::Days(diff
);
1981 // check that it is still in the same month
1982 if ( dt
.GetMonth() == month
)
1990 // no such day in this month
1996 wxDateTime::wxDateTime_t
GetDayOfYearFromTm(const wxDateTime::Tm
& tm
)
1998 return (wxDateTime::wxDateTime_t
)(gs_cumulatedDays
[wxDateTime::IsLeapYear(tm
.year
)][tm
.mon
] + tm
.mday
);
2001 wxDateTime::wxDateTime_t
wxDateTime::GetDayOfYear(const TimeZone
& tz
) const
2003 return GetDayOfYearFromTm(GetTm(tz
));
2006 wxDateTime::wxDateTime_t
2007 wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags
, const TimeZone
& tz
) const
2009 if ( flags
== Default_First
)
2011 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
2015 wxDateTime_t nDayInYear
= GetDayOfYearFromTm(tm
);
2017 int wdTarget
= GetWeekDay(tz
);
2018 int wdYearStart
= wxDateTime(1, Jan
, GetYear()).GetWeekDay();
2020 if ( flags
== Sunday_First
)
2022 // FIXME: First week is not calculated correctly.
2023 week
= (nDayInYear
- wdTarget
+ 7) / 7;
2024 if ( wdYearStart
== Wed
|| wdYearStart
== Thu
)
2027 else // week starts with monday
2029 // adjust the weekdays to non-US style.
2030 wdYearStart
= ConvertWeekDayToMondayBase(wdYearStart
);
2031 wdTarget
= ConvertWeekDayToMondayBase(wdTarget
);
2033 // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html:
2035 // Week 01 of a year is per definition the first week that has the
2036 // Thursday in this year, which is equivalent to the week that
2037 // contains the fourth day of January. In other words, the first
2038 // week of a new year is the week that has the majority of its
2039 // days in the new year. Week 01 might also contain days from the
2040 // previous year and the week before week 01 of a year is the last
2041 // week (52 or 53) of the previous year even if it contains days
2042 // from the new year. A week starts with Monday (day 1) and ends
2043 // with Sunday (day 7).
2046 // if Jan 1 is Thursday or less, it is in the first week of this year
2047 if ( wdYearStart
< 4 )
2049 // count the number of entire weeks between Jan 1 and this date
2050 week
= (nDayInYear
+ wdYearStart
+ 6 - wdTarget
)/7;
2052 // be careful to check for overflow in the next year
2053 if ( week
== 53 && tm
.mday
- wdTarget
> 28 )
2056 else // Jan 1 is in the last week of the previous year
2058 // check if we happen to be at the last week of previous year:
2059 if ( tm
.mon
== Jan
&& tm
.mday
< 8 - wdYearStart
)
2060 week
= wxDateTime(31, Dec
, GetYear()-1).GetWeekOfYear();
2062 week
= (nDayInYear
+ wdYearStart
- 1 - wdTarget
)/7;
2066 return (wxDateTime::wxDateTime_t
)week
;
2069 wxDateTime::wxDateTime_t
wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags
,
2070 const TimeZone
& tz
) const
2073 wxDateTime dtMonthStart
= wxDateTime(1, tm
.mon
, tm
.year
);
2074 int nWeek
= GetWeekOfYear(flags
) - dtMonthStart
.GetWeekOfYear(flags
) + 1;
2077 // this may happen for January when Jan, 1 is the last week of the
2079 nWeek
+= IsLeapYear(tm
.year
- 1) ? 53 : 52;
2082 return (wxDateTime::wxDateTime_t
)nWeek
;
2085 wxDateTime
& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday
)
2087 int year
= GetYear();
2088 wxDATETIME_CHECK( (0 < yday
) && (yday
<= GetNumberOfDays(year
)),
2089 _T("invalid year day") );
2091 bool isLeap
= IsLeapYear(year
);
2092 for ( Month mon
= Jan
; mon
< Inv_Month
; wxNextMonth(mon
) )
2094 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
2095 // don't need it neither - because of the CHECK above we know that
2096 // yday lies in December then
2097 if ( (mon
== Dec
) || (yday
<= gs_cumulatedDays
[isLeap
][mon
+ 1]) )
2099 Set((wxDateTime::wxDateTime_t
)(yday
- gs_cumulatedDays
[isLeap
][mon
]), mon
, year
);
2108 // ----------------------------------------------------------------------------
2109 // Julian day number conversion and related stuff
2110 // ----------------------------------------------------------------------------
2112 double wxDateTime::GetJulianDayNumber() const
2114 return m_time
.ToDouble() / MILLISECONDS_PER_DAY
+ EPOCH_JDN
+ 0.5;
2117 double wxDateTime::GetRataDie() const
2119 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
2120 return GetJulianDayNumber() - 1721119.5 - 306;
2123 // ----------------------------------------------------------------------------
2124 // timezone and DST stuff
2125 // ----------------------------------------------------------------------------
2127 int wxDateTime::IsDST(wxDateTime::Country country
) const
2129 wxCHECK_MSG( country
== Country_Default
, -1,
2130 _T("country support not implemented") );
2132 // use the C RTL for the dates in the standard range
2133 time_t timet
= GetTicks();
2134 if ( timet
!= (time_t)-1 )
2137 tm
*tm
= wxLocaltime_r(&timet
, &tmstruct
);
2139 wxCHECK_MSG( tm
, -1, _T("wxLocaltime_r() failed") );
2141 return tm
->tm_isdst
;
2145 int year
= GetYear();
2147 if ( !IsDSTApplicable(year
, country
) )
2149 // no DST time in this year in this country
2153 return IsBetween(GetBeginDST(year
, country
), GetEndDST(year
, country
));
2157 wxDateTime
& wxDateTime::MakeTimezone(const TimeZone
& tz
, bool noDST
)
2159 long secDiff
= GetTimeZone() + tz
.GetOffset();
2161 // we need to know whether DST is or not in effect for this date unless
2162 // the test disabled by the caller
2163 if ( !noDST
&& (IsDST() == 1) )
2165 // FIXME we assume that the DST is always shifted by 1 hour
2169 return Add(wxTimeSpan::Seconds(secDiff
));
2172 wxDateTime
& wxDateTime::MakeFromTimezone(const TimeZone
& tz
, bool noDST
)
2174 long secDiff
= GetTimeZone() + tz
.GetOffset();
2176 // we need to know whether DST is or not in effect for this date unless
2177 // the test disabled by the caller
2178 if ( !noDST
&& (IsDST() == 1) )
2180 // FIXME we assume that the DST is always shifted by 1 hour
2184 return Subtract(wxTimeSpan::Seconds(secDiff
));
2187 // ----------------------------------------------------------------------------
2188 // wxDateTime to/from text representations
2189 // ----------------------------------------------------------------------------
2191 wxString
wxDateTime::Format(const wxChar
*format
, const TimeZone
& tz
) const
2193 wxCHECK_MSG( format
, wxEmptyString
, _T("NULL format in wxDateTime::Format") );
2195 // we have to use our own implementation if the date is out of range of
2196 // strftime() or if we use non standard specificators
2197 time_t time
= GetTicks();
2198 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
2203 if ( tz
.GetOffset() == -GetTimeZone() )
2205 // we are working with local time
2206 tm
= wxLocaltime_r(&time
, &tmstruct
);
2208 // should never happen
2209 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxLocaltime_r() failed") );
2213 time
+= (int)tz
.GetOffset();
2215 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
2216 int time2
= (int) time
;
2222 tm
= wxGmtime_r(&time
, &tmstruct
);
2224 // should never happen
2225 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxGmtime_r() failed") );
2229 tm
= (struct tm
*)NULL
;
2233 //Windows CE doesn't support strftime or wcsftime, so we use the generic implementation
2236 return CallStrftime(format
, tm
);
2239 //else: use generic code below
2242 // we only parse ANSI C format specifications here, no POSIX 2
2243 // complications, no GNU extensions but we do add support for a "%l" format
2244 // specifier allowing to get the number of milliseconds
2247 // used for calls to strftime() when we only deal with time
2248 struct tm tmTimeOnly
;
2249 tmTimeOnly
.tm_hour
= tm
.hour
;
2250 tmTimeOnly
.tm_min
= tm
.min
;
2251 tmTimeOnly
.tm_sec
= tm
.sec
;
2252 tmTimeOnly
.tm_wday
= 0;
2253 tmTimeOnly
.tm_yday
= 0;
2254 tmTimeOnly
.tm_mday
= 1; // any date will do
2255 tmTimeOnly
.tm_mon
= 0;
2256 tmTimeOnly
.tm_year
= 76;
2257 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
2259 wxString tmp
, res
, fmt
;
2260 for ( const wxChar
*p
= format
; *p
; p
++ )
2262 if ( *p
!= _T('%') )
2270 // set the default format
2273 case _T('Y'): // year has 4 digits
2277 case _T('j'): // day of year has 3 digits
2278 case _T('l'): // milliseconds have 3 digits
2282 case _T('w'): // week day as number has only one
2287 // it's either another valid format specifier in which case
2288 // the format is "%02d" (for all the rest) or we have the
2289 // field width preceding the format in which case it will
2290 // override the default format anyhow
2294 bool restart
= true;
2299 // start of the format specification
2302 case _T('a'): // a weekday name
2304 // second parameter should be true for abbreviated names
2305 res
+= GetWeekDayName(tm
.GetWeekDay(),
2306 *p
== _T('a') ? Name_Abbr
: Name_Full
);
2309 case _T('b'): // a month name
2311 res
+= GetMonthName(tm
.mon
,
2312 *p
== _T('b') ? Name_Abbr
: Name_Full
);
2315 case _T('c'): // locale default date and time representation
2316 case _T('x'): // locale default date representation
2319 // the problem: there is no way to know what do these format
2320 // specifications correspond to for the current locale.
2322 // the solution: use a hack and still use strftime(): first
2323 // find the YEAR which is a year in the strftime() range (1970
2324 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
2325 // of the real year. Then make a copy of the format and
2326 // replace all occurrences of YEAR in it with some unique
2327 // string not appearing anywhere else in it, then use
2328 // strftime() to format the date in year YEAR and then replace
2329 // YEAR back by the real year and the unique replacement
2330 // string back with YEAR. Notice that "all occurrences of YEAR"
2331 // means all occurrences of 4 digit as well as 2 digit form!
2333 // the bugs: we assume that neither of %c nor %x contains any
2334 // fields which may change between the YEAR and real year. For
2335 // example, the week number (%U, %W) and the day number (%j)
2336 // will change if one of these years is leap and the other one
2339 // find the YEAR: normally, for any year X, Jan 1 or the
2340 // year X + 28 is the same weekday as Jan 1 of X (because
2341 // the weekday advances by 1 for each normal X and by 2
2342 // for each leap X, hence by 5 every 4 years or by 35
2343 // which is 0 mod 7 every 28 years) but this rule breaks
2344 // down if there are years between X and Y which are
2345 // divisible by 4 but not leap (i.e. divisible by 100 but
2346 // not 400), hence the correction.
2348 int yearReal
= GetYear(tz
);
2349 int mod28
= yearReal
% 28;
2351 // be careful to not go too far - we risk to leave the
2356 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
2360 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
2363 int nCentury
= year
/ 100,
2364 nCenturyReal
= yearReal
/ 100;
2366 // need to adjust for the years divisble by 400 which are
2367 // not leap but are counted like leap ones if we just take
2368 // the number of centuries in between for nLostWeekDays
2369 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
2370 (nCentury
/ 4 - nCenturyReal
/ 4);
2372 // we have to gain back the "lost" weekdays: note that the
2373 // effect of this loop is to not do anything to
2374 // nLostWeekDays (which we won't use any more), but to
2375 // (indirectly) set the year correctly
2376 while ( (nLostWeekDays
% 7) != 0 )
2378 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
2381 // at any rate, we couldn't go further than 1988 + 9 + 28!
2382 wxASSERT_MSG( year
< 2030,
2383 _T("logic error in wxDateTime::Format") );
2385 wxString strYear
, strYear2
;
2386 strYear
.Printf(_T("%d"), year
);
2387 strYear2
.Printf(_T("%d"), year
% 100);
2389 // find two strings not occurring in format (this is surely
2390 // not the optimal way of doing it... improvements welcome!)
2391 wxString fmt2
= format
;
2392 wxString replacement
= (wxChar
)-1;
2393 while ( fmt2
.Find(replacement
) != wxNOT_FOUND
)
2395 replacement
<< (wxChar
)-1;
2398 wxString replacement2
= (wxChar
)-2;
2399 while ( fmt2
.Find(replacement
) != wxNOT_FOUND
)
2401 replacement
<< (wxChar
)-2;
2404 // replace all occurrences of year with it
2405 bool wasReplaced
= fmt2
.Replace(strYear
, replacement
) > 0;
2407 wasReplaced
= fmt2
.Replace(strYear2
, replacement2
) > 0;
2409 // use strftime() to format the same date but in supported
2412 // NB: we assume that strftime() doesn't check for the
2413 // date validity and will happily format the date
2414 // corresponding to Feb 29 of a non leap year (which
2415 // may happen if yearReal was leap and year is not)
2416 struct tm tmAdjusted
;
2418 tmAdjusted
.tm_hour
= tm
.hour
;
2419 tmAdjusted
.tm_min
= tm
.min
;
2420 tmAdjusted
.tm_sec
= tm
.sec
;
2421 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
2422 tmAdjusted
.tm_yday
= GetDayOfYear();
2423 tmAdjusted
.tm_mday
= tm
.mday
;
2424 tmAdjusted
.tm_mon
= tm
.mon
;
2425 tmAdjusted
.tm_year
= year
- 1900;
2426 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
2427 wxString str
= CallStrftime(*p
== _T('c') ? _T("%c")
2431 // now replace the occurrence of 1999 with the real year
2432 wxString strYearReal
, strYearReal2
;
2433 strYearReal
.Printf(_T("%04d"), yearReal
);
2434 strYearReal2
.Printf(_T("%02d"), yearReal
% 100);
2435 str
.Replace(strYear
, strYearReal
);
2436 str
.Replace(strYear2
, strYearReal2
);
2438 // and replace back all occurrences of replacement string
2441 str
.Replace(replacement2
, strYear2
);
2442 str
.Replace(replacement
, strYear
);
2448 //Use "%m/%d/%y %H:%M:%S" format instead
2449 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
2450 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
2454 case _T('d'): // day of a month (01-31)
2455 res
+= wxString::Format(fmt
, tm
.mday
);
2458 case _T('H'): // hour in 24h format (00-23)
2459 res
+= wxString::Format(fmt
, tm
.hour
);
2462 case _T('I'): // hour in 12h format (01-12)
2464 // 24h -> 12h, 0h -> 12h too
2465 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
2466 : tm
.hour
? tm
.hour
: 12;
2467 res
+= wxString::Format(fmt
, hour12
);
2471 case _T('j'): // day of the year
2472 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
2475 case _T('l'): // milliseconds (NOT STANDARD)
2476 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
2479 case _T('m'): // month as a number (01-12)
2480 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
2483 case _T('M'): // minute as a decimal number (00-59)
2484 res
+= wxString::Format(fmt
, tm
.min
);
2487 case _T('p'): // AM or PM string
2489 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
2491 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
2495 case _T('S'): // second as a decimal number (00-61)
2496 res
+= wxString::Format(fmt
, tm
.sec
);
2499 case _T('U'): // week number in the year (Sunday 1st week day)
2500 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
2503 case _T('W'): // week number in the year (Monday 1st week day)
2504 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
2507 case _T('w'): // weekday as a number (0-6), Sunday = 0
2508 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
2511 // case _T('x'): -- handled with "%c"
2513 case _T('X'): // locale default time representation
2514 // just use strftime() to format the time for us
2516 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
2518 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
2522 case _T('y'): // year without century (00-99)
2523 res
+= wxString::Format(fmt
, tm
.year
% 100);
2526 case _T('Y'): // year with century
2527 res
+= wxString::Format(fmt
, tm
.year
);
2530 case _T('Z'): // timezone name
2532 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
2537 // is it the format width?
2539 while ( *p
== _T('-') || *p
== _T('+') ||
2540 *p
== _T(' ') || wxIsdigit(*p
) )
2547 // we've only got the flags and width so far in fmt
2548 fmt
.Prepend(_T('%'));
2549 fmt
.Append(_T('d'));
2556 // no, it wasn't the width
2557 wxFAIL_MSG(_T("unknown format specificator"));
2559 // fall through and just copy it nevertheless
2561 case _T('%'): // a percent sign
2565 case 0: // the end of string
2566 wxFAIL_MSG(_T("missing format at the end of string"));
2568 // just put the '%' which was the last char in format
2578 // this function parses a string in (strict) RFC 822 format: see the section 5
2579 // of the RFC for the detailed description, but briefly it's something of the
2580 // form "Sat, 18 Dec 1999 00:48:30 +0100"
2582 // this function is "strict" by design - it must reject anything except true
2583 // RFC822 time specs.
2585 // TODO a great candidate for using reg exps
2586 const wxChar
*wxDateTime::ParseRfc822Date(const wxChar
* date
)
2588 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
2590 const wxChar
*p
= date
;
2591 const wxChar
*comma
= wxStrchr(p
, _T(','));
2594 // the part before comma is the weekday
2596 // skip it for now - we don't use but might check that it really
2597 // corresponds to the specfied date
2600 if ( *p
!= _T(' ') )
2602 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
2604 return (wxChar
*)NULL
;
2610 // the following 1 or 2 digits are the day number
2611 if ( !wxIsdigit(*p
) )
2613 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
2615 return (wxChar
*)NULL
;
2618 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - _T('0'));
2619 if ( wxIsdigit(*p
) )
2622 day
= (wxDateTime_t
)(day
+ (*p
++ - _T('0')));
2625 if ( *p
++ != _T(' ') )
2627 return (wxChar
*)NULL
;
2630 // the following 3 letters specify the month
2631 wxString
monName(p
, 3);
2633 if ( monName
== _T("Jan") )
2635 else if ( monName
== _T("Feb") )
2637 else if ( monName
== _T("Mar") )
2639 else if ( monName
== _T("Apr") )
2641 else if ( monName
== _T("May") )
2643 else if ( monName
== _T("Jun") )
2645 else if ( monName
== _T("Jul") )
2647 else if ( monName
== _T("Aug") )
2649 else if ( monName
== _T("Sep") )
2651 else if ( monName
== _T("Oct") )
2653 else if ( monName
== _T("Nov") )
2655 else if ( monName
== _T("Dec") )
2659 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str());
2661 return (wxChar
*)NULL
;
2666 if ( *p
++ != _T(' ') )
2668 return (wxChar
*)NULL
;
2672 if ( !wxIsdigit(*p
) )
2675 return (wxChar
*)NULL
;
2678 int year
= *p
++ - _T('0');
2680 if ( !wxIsdigit(*p
) )
2682 // should have at least 2 digits in the year
2683 return (wxChar
*)NULL
;
2687 year
+= *p
++ - _T('0');
2689 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
2690 if ( wxIsdigit(*p
) )
2693 year
+= *p
++ - _T('0');
2695 if ( !wxIsdigit(*p
) )
2697 // no 3 digit years please
2698 return (wxChar
*)NULL
;
2702 year
+= *p
++ - _T('0');
2705 if ( *p
++ != _T(' ') )
2707 return (wxChar
*)NULL
;
2710 // time is in the format hh:mm:ss and seconds are optional
2711 if ( !wxIsdigit(*p
) )
2713 return (wxChar
*)NULL
;
2716 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - _T('0'));
2718 if ( !wxIsdigit(*p
) )
2720 return (wxChar
*)NULL
;
2724 hour
= (wxDateTime_t
)(hour
+ (*p
++ - _T('0')));
2726 if ( *p
++ != _T(':') )
2728 return (wxChar
*)NULL
;
2731 if ( !wxIsdigit(*p
) )
2733 return (wxChar
*)NULL
;
2736 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - _T('0'));
2738 if ( !wxIsdigit(*p
) )
2740 return (wxChar
*)NULL
;
2744 min
= (wxDateTime_t
)(min
+ *p
++ - _T('0'));
2746 wxDateTime_t sec
= 0;
2747 if ( *p
++ == _T(':') )
2749 if ( !wxIsdigit(*p
) )
2751 return (wxChar
*)NULL
;
2754 sec
= (wxDateTime_t
)(*p
++ - _T('0'));
2756 if ( !wxIsdigit(*p
) )
2758 return (wxChar
*)NULL
;
2762 sec
= (wxDateTime_t
)(sec
+ *p
++ - _T('0'));
2765 if ( *p
++ != _T(' ') )
2767 return (wxChar
*)NULL
;
2770 // and now the interesting part: the timezone
2771 int offset
wxDUMMY_INITIALIZE(0);
2772 if ( *p
== _T('-') || *p
== _T('+') )
2774 // the explicit offset given: it has the form of hhmm
2775 bool plus
= *p
++ == _T('+');
2777 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2779 return (wxChar
*)NULL
;
2783 offset
= MIN_PER_HOUR
*(10*(*p
- _T('0')) + (*(p
+ 1) - _T('0')));
2787 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2789 return (wxChar
*)NULL
;
2793 offset
+= 10*(*p
- _T('0')) + (*(p
+ 1) - _T('0'));
2804 // the symbolic timezone given: may be either military timezone or one
2805 // of standard abbreviations
2808 // military: Z = UTC, J unused, A = -1, ..., Y = +12
2809 static const int offsets
[26] =
2811 //A B C D E F G H I J K L M
2812 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
2813 //N O P R Q S T U V W Z Y Z
2814 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
2817 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
2819 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
);
2821 return (wxChar
*)NULL
;
2824 offset
= offsets
[*p
++ - _T('A')];
2830 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
2832 else if ( tz
== _T("AST") )
2833 offset
= AST
- GMT0
;
2834 else if ( tz
== _T("ADT") )
2835 offset
= ADT
- GMT0
;
2836 else if ( tz
== _T("EST") )
2837 offset
= EST
- GMT0
;
2838 else if ( tz
== _T("EDT") )
2839 offset
= EDT
- GMT0
;
2840 else if ( tz
== _T("CST") )
2841 offset
= CST
- GMT0
;
2842 else if ( tz
== _T("CDT") )
2843 offset
= CDT
- GMT0
;
2844 else if ( tz
== _T("MST") )
2845 offset
= MST
- GMT0
;
2846 else if ( tz
== _T("MDT") )
2847 offset
= MDT
- GMT0
;
2848 else if ( tz
== _T("PST") )
2849 offset
= PST
- GMT0
;
2850 else if ( tz
== _T("PDT") )
2851 offset
= PDT
- GMT0
;
2854 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
);
2856 return (wxChar
*)NULL
;
2863 offset
*= MIN_PER_HOUR
;
2866 // the spec was correct, construct the date from the values we found
2867 Set(day
, mon
, year
, hour
, min
, sec
);
2868 MakeFromTimezone(TimeZone((wxDateTime_t
)(offset
*SEC_PER_MIN
)));
2875 // returns the string containing strftime() format used for short dates in the
2876 // current locale or an empty string
2877 static wxString
GetLocaleDateFormat()
2881 // there is no setlocale() under Windows CE, so just always query the
2884 if ( strcmp(setlocale(LC_ALL
, NULL
), "C") != 0 )
2887 // The locale was programatically set to non-C. We assume that this was
2888 // done using wxLocale, in which case thread's current locale is also
2889 // set to correct LCID value and we can use GetLocaleInfo to determine
2890 // the correct formatting string:
2892 LCID lcid
= LOCALE_USER_DEFAULT
;
2894 LCID lcid
= GetThreadLocale();
2896 // according to MSDN 80 chars is max allowed for short date format
2898 if ( ::GetLocaleInfo(lcid
, LOCALE_SSHORTDATE
, fmt
, WXSIZEOF(fmt
)) )
2900 wxChar chLast
= _T('\0');
2901 size_t lastCount
= 0;
2902 for ( const wxChar
*p
= fmt
; /* NUL handled inside */; p
++ )
2912 // these characters come in groups, start counting them
2922 // first deal with any special characters we have had
2928 switch ( lastCount
)
2932 // these two are the same as we
2933 // don't distinguish between 1 and
2934 // 2 digits for days
2947 wxFAIL_MSG( _T("too many 'd's") );
2952 switch ( lastCount
)
2956 // as for 'd' and 'dd' above
2969 wxFAIL_MSG( _T("too many 'M's") );
2974 switch ( lastCount
)
2986 wxFAIL_MSG( _T("wrong number of 'y's") );
2991 // strftime() doesn't have era string,
2992 // ignore this format
2993 wxASSERT_MSG( lastCount
<= 2,
2994 _T("too many 'g's") );
2998 wxFAIL_MSG( _T("unreachable") );
3005 // not a special character so must be just a separator,
3007 if ( *p
!= _T('\0') )
3009 if ( *p
== _T('%') )
3011 // this one needs to be escaped
3019 if ( *p
== _T('\0') )
3023 //else: GetLocaleInfo() failed, leave fmtDate value unchanged and
3024 // try our luck with the default formats
3026 //else: default C locale, default formats should work
3031 #endif // __WINDOWS__
3033 const wxChar
*wxDateTime::ParseFormat(const wxChar
*date
,
3034 const wxChar
*format
,
3035 const wxDateTime
& dateDef
)
3037 wxCHECK_MSG( date
&& format
, (wxChar
*)NULL
,
3038 _T("NULL pointer in wxDateTime::ParseFormat()") );
3043 // what fields have we found?
3044 bool haveWDay
= false,
3053 bool hourIsIn12hFormat
= false, // or in 24h one?
3054 isPM
= false; // AM by default
3056 // and the value of the items we have (init them to get rid of warnings)
3057 wxDateTime_t sec
= 0,
3060 WeekDay wday
= Inv_WeekDay
;
3061 wxDateTime_t yday
= 0,
3063 wxDateTime::Month mon
= Inv_Month
;
3066 const wxChar
*input
= date
;
3067 for ( const wxChar
*fmt
= format
; *fmt
; fmt
++ )
3069 if ( *fmt
!= _T('%') )
3071 if ( wxIsspace(*fmt
) )
3073 // a white space in the format string matches 0 or more white
3074 // spaces in the input
3075 while ( wxIsspace(*input
) )
3082 // any other character (not whitespace, not '%') must be
3083 // matched by itself in the input
3084 if ( *input
++ != *fmt
)
3087 return (wxChar
*)NULL
;
3091 // done with this format char
3095 // start of a format specification
3097 // parse the optional width
3099 while ( wxIsdigit(*++fmt
) )
3102 width
+= *fmt
- _T('0');
3105 // the default widths for the various fields
3110 case _T('Y'): // year has 4 digits
3114 case _T('j'): // day of year has 3 digits
3115 case _T('l'): // milliseconds have 3 digits
3119 case _T('w'): // week day as number has only one
3124 // default for all other fields
3129 // then the format itself
3132 case _T('a'): // a weekday name
3135 int flag
= *fmt
== _T('a') ? Name_Abbr
: Name_Full
;
3136 wday
= GetWeekDayFromName(GetAlphaToken(input
), flag
);
3137 if ( wday
== Inv_WeekDay
)
3140 return (wxChar
*)NULL
;
3146 case _T('b'): // a month name
3149 int flag
= *fmt
== _T('b') ? Name_Abbr
: Name_Full
;
3150 mon
= GetMonthFromName(GetAlphaToken(input
), flag
);
3151 if ( mon
== Inv_Month
)
3154 return (wxChar
*)NULL
;
3160 case _T('c'): // locale default date and time representation
3164 // this is the format which corresponds to ctime() output
3165 // and strptime("%c") should parse it, so try it first
3166 static const wxChar
*fmtCtime
= _T("%a %b %d %H:%M:%S %Y");
3168 const wxChar
*result
= dt
.ParseFormat(input
, fmtCtime
);
3171 result
= dt
.ParseFormat(input
, _T("%x %X"));
3176 result
= dt
.ParseFormat(input
, _T("%X %x"));
3181 // we've tried everything and still no match
3182 return (wxChar
*)NULL
;
3187 haveDay
= haveMon
= haveYear
=
3188 haveHour
= haveMin
= haveSec
= true;
3202 case _T('d'): // day of a month (01-31)
3203 if ( !GetNumericToken(width
, input
, &num
) ||
3204 (num
> 31) || (num
< 1) )
3207 return (wxChar
*)NULL
;
3210 // we can't check whether the day range is correct yet, will
3211 // do it later - assume ok for now
3213 mday
= (wxDateTime_t
)num
;
3216 case _T('H'): // hour in 24h format (00-23)
3217 if ( !GetNumericToken(width
, input
, &num
) || (num
> 23) )
3220 return (wxChar
*)NULL
;
3224 hour
= (wxDateTime_t
)num
;
3227 case _T('I'): // hour in 12h format (01-12)
3228 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3231 return (wxChar
*)NULL
;
3235 hourIsIn12hFormat
= true;
3236 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
3239 case _T('j'): // day of the year
3240 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 366) )
3243 return (wxChar
*)NULL
;
3247 yday
= (wxDateTime_t
)num
;
3250 case _T('m'): // month as a number (01-12)
3251 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3254 return (wxChar
*)NULL
;
3258 mon
= (Month
)(num
- 1);
3261 case _T('M'): // minute as a decimal number (00-59)
3262 if ( !GetNumericToken(width
, input
, &num
) || (num
> 59) )
3265 return (wxChar
*)NULL
;
3269 min
= (wxDateTime_t
)num
;
3272 case _T('p'): // AM or PM string
3274 wxString am
, pm
, token
= GetAlphaToken(input
);
3276 GetAmPmStrings(&am
, &pm
);
3277 if (am
.empty() && pm
.empty())
3278 return (wxChar
*)NULL
; // no am/pm strings defined
3279 if ( token
.CmpNoCase(pm
) == 0 )
3283 else if ( token
.CmpNoCase(am
) != 0 )
3286 return (wxChar
*)NULL
;
3291 case _T('r'): // time as %I:%M:%S %p
3294 input
= dt
.ParseFormat(input
, _T("%I:%M:%S %p"));
3298 return (wxChar
*)NULL
;
3301 haveHour
= haveMin
= haveSec
= true;
3310 case _T('R'): // time as %H:%M
3313 input
= dt
.ParseFormat(input
, _T("%H:%M"));
3317 return (wxChar
*)NULL
;
3320 haveHour
= haveMin
= true;
3328 case _T('S'): // second as a decimal number (00-61)
3329 if ( !GetNumericToken(width
, input
, &num
) || (num
> 61) )
3332 return (wxChar
*)NULL
;
3336 sec
= (wxDateTime_t
)num
;
3339 case _T('T'): // time as %H:%M:%S
3342 input
= dt
.ParseFormat(input
, _T("%H:%M:%S"));
3346 return (wxChar
*)NULL
;
3349 haveHour
= haveMin
= haveSec
= true;
3358 case _T('w'): // weekday as a number (0-6), Sunday = 0
3359 if ( !GetNumericToken(width
, input
, &num
) || (wday
> 6) )
3362 return (wxChar
*)NULL
;
3366 wday
= (WeekDay
)num
;
3369 case _T('x'): // locale default date representation
3370 #ifdef HAVE_STRPTIME
3371 // try using strptime() -- it may fail even if the input is
3372 // correct but the date is out of range, so we will fall back
3373 // to our generic code anyhow
3377 const wxChar
*result
= CallStrptime(input
, "%x", &tm
);
3382 haveDay
= haveMon
= haveYear
= true;
3384 year
= 1900 + tm
.tm_year
;
3385 mon
= (Month
)tm
.tm_mon
;
3391 #endif // HAVE_STRPTIME
3399 // The above doesn't work for all locales, try to query
3400 // Windows for the right way of formatting the date:
3401 fmtDate
= GetLocaleDateFormat();
3402 if ( fmtDate
.empty() )
3405 if ( IsWestEuropeanCountry(GetCountry()) ||
3406 GetCountry() == Russia
)
3408 fmtDate
= _T("%d/%m/%y");
3409 fmtDateAlt
= _T("%m/%d/%y");
3413 fmtDate
= _T("%m/%d/%y");
3414 fmtDateAlt
= _T("%d/%m/%y");
3418 const wxChar
*result
= dt
.ParseFormat(input
, fmtDate
);
3420 if ( !result
&& !fmtDateAlt
.empty() )
3422 // ok, be nice and try another one
3423 result
= dt
.ParseFormat(input
, fmtDateAlt
);
3429 return (wxChar
*)NULL
;
3434 haveDay
= haveMon
= haveYear
= true;
3445 case _T('X'): // locale default time representation
3446 #ifdef HAVE_STRPTIME
3448 // use strptime() to do it for us (FIXME !Unicode friendly)
3450 input
= CallStrptime(input
, "%X", &tm
);
3453 return (wxChar
*)NULL
;
3456 haveHour
= haveMin
= haveSec
= true;
3462 #else // !HAVE_STRPTIME
3463 // TODO under Win32 we can query the LOCALE_ITIME system
3464 // setting which says whether the default time format is
3467 // try to parse what follows as "%H:%M:%S" and, if this
3468 // fails, as "%I:%M:%S %p" - this should catch the most
3472 const wxChar
*result
= dt
.ParseFormat(input
, _T("%T"));
3475 result
= dt
.ParseFormat(input
, _T("%r"));
3481 return (wxChar
*)NULL
;
3484 haveHour
= haveMin
= haveSec
= true;
3493 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
3496 case _T('y'): // year without century (00-99)
3497 if ( !GetNumericToken(width
, input
, &num
) || (num
> 99) )
3500 return (wxChar
*)NULL
;
3505 // TODO should have an option for roll over date instead of
3506 // hard coding it here
3507 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
3510 case _T('Y'): // year with century
3511 if ( !GetNumericToken(width
, input
, &num
) )
3514 return (wxChar
*)NULL
;
3518 year
= (wxDateTime_t
)num
;
3521 case _T('Z'): // timezone name
3522 wxFAIL_MSG(_T("TODO"));
3525 case _T('%'): // a percent sign
3526 if ( *input
++ != _T('%') )
3529 return (wxChar
*)NULL
;
3533 case 0: // the end of string
3534 wxFAIL_MSG(_T("unexpected format end"));
3538 default: // not a known format spec
3539 return (wxChar
*)NULL
;
3543 // format matched, try to construct a date from what we have now
3545 if ( dateDef
.IsValid() )
3547 // take this date as default
3548 tmDef
= dateDef
.GetTm();
3550 else if ( IsValid() )
3552 // if this date is valid, don't change it
3557 // no default and this date is invalid - fall back to Today()
3558 tmDef
= Today().GetTm();
3569 // TODO we don't check here that the values are consistent, if both year
3570 // day and month/day were found, we just ignore the year day and we
3571 // also always ignore the week day
3572 if ( haveMon
&& haveDay
)
3574 if ( mday
> GetNumOfDaysInMonth(tm
.year
, mon
) )
3576 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
3578 return (wxChar
*)NULL
;
3584 else if ( haveYDay
)
3586 if ( yday
> GetNumberOfDays(tm
.year
) )
3588 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
3590 return (wxChar
*)NULL
;
3593 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
3600 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
3602 // translate to 24hour format
3605 //else: either already in 24h format or no translation needed
3625 // finally check that the week day is consistent -- if we had it
3626 if ( haveWDay
&& GetWeekDay() != wday
)
3628 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
3636 const wxChar
*wxDateTime::ParseDateTime(const wxChar
*date
)
3638 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3640 // Set to current day and hour, so strings like '14:00' becomes today at
3641 // 14, not some other random date
3642 wxDateTime dtDate
= wxDateTime::Today();
3643 wxDateTime dtTime
= wxDateTime::Today();
3645 const wxChar
* pchTime
;
3647 // Try to parse the beginning of the string as a date
3648 const wxChar
* pchDate
= dtDate
.ParseDate(date
);
3650 // We got a date in the beginning, see if there is a time specified after the date
3653 // Skip spaces, as the ParseTime() function fails on spaces
3654 while ( wxIsspace(*pchDate
) )
3657 pchTime
= dtTime
.ParseTime(pchDate
);
3659 else // no date in the beginning
3661 // check and see if we have a time followed by a date
3662 pchTime
= dtTime
.ParseTime(date
);
3665 while ( wxIsspace(*pchTime
) )
3668 pchDate
= dtDate
.ParseDate(pchTime
);
3672 // If we have a date specified, set our own data to the same date
3673 if ( !pchDate
|| !pchTime
)
3676 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
3677 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
3678 dtTime
.GetMillisecond());
3680 // Return endpoint of scan
3681 return pchDate
> pchTime
? pchDate
: pchTime
;
3684 const wxChar
*wxDateTime::ParseDate(const wxChar
*date
)
3686 // this is a simplified version of ParseDateTime() which understands only
3687 // "today" (for wxDate compatibility) and digits only otherwise (and not
3688 // all esoteric constructions ParseDateTime() knows about)
3690 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3692 const wxChar
*p
= date
;
3693 while ( wxIsspace(*p
) )
3696 // some special cases
3700 int dayDiffFromToday
;
3703 { wxTRANSLATE("today"), 0 },
3704 { wxTRANSLATE("yesterday"), -1 },
3705 { wxTRANSLATE("tomorrow"), 1 },
3708 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
3710 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
3711 size_t len
= dateStr
.length();
3712 if ( wxStrlen(p
) >= len
)
3714 wxString
str(p
, len
);
3715 if ( str
.CmpNoCase(dateStr
) == 0 )
3717 // nothing can follow this, so stop here
3720 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
3722 if ( dayDiffFromToday
)
3724 *this += wxDateSpan::Days(dayDiffFromToday
);
3732 // We try to guess what we have here: for each new (numeric) token, we
3733 // determine if it can be a month, day or a year. Of course, there is an
3734 // ambiguity as some numbers may be days as well as months, so we also
3735 // have the ability to back track.
3738 bool haveDay
= false, // the months day?
3739 haveWDay
= false, // the day of week?
3740 haveMon
= false, // the month?
3741 haveYear
= false; // the year?
3743 // and the value of the items we have (init them to get rid of warnings)
3744 WeekDay wday
= Inv_WeekDay
;
3745 wxDateTime_t day
= 0;
3746 wxDateTime::Month mon
= Inv_Month
;
3749 // tokenize the string
3751 static const wxChar
*dateDelimiters
= _T(".,/-\t\r\n ");
3752 wxStringTokenizer
tok(p
, dateDelimiters
);
3753 while ( tok
.HasMoreTokens() )
3755 wxString token
= tok
.GetNextToken();
3761 if ( token
.ToULong(&val
) )
3763 // guess what this number is
3769 if ( !haveMon
&& val
> 0 && val
<= 12 )
3771 // assume it is month
3774 else // not the month
3778 // this can only be the year
3781 else // may be either day or year
3783 wxDateTime_t max_days
= (wxDateTime_t
)(
3785 ? GetNumOfDaysInMonth(haveYear
? year
: Inv_Year
, mon
)
3790 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
3795 else // yes, suppose it's the day
3809 year
= (wxDateTime_t
)val
;
3818 day
= (wxDateTime_t
)val
;
3824 mon
= (Month
)(val
- 1);
3827 else // not a number
3829 // be careful not to overwrite the current mon value
3830 Month mon2
= GetMonthFromName(token
, Name_Full
| Name_Abbr
);
3831 if ( mon2
!= Inv_Month
)
3836 // but we already have a month - maybe we guessed wrong?
3839 // no need to check in month range as always < 12, but
3840 // the days are counted from 1 unlike the months
3841 day
= (wxDateTime_t
)(mon
+ 1);
3846 // could possible be the year (doesn't the year come
3847 // before the month in the japanese format?) (FIXME)
3856 else // not a valid month name
3858 wday
= GetWeekDayFromName(token
, Name_Full
| Name_Abbr
);
3859 if ( wday
!= Inv_WeekDay
)
3869 else // not a valid weekday name
3872 static const wxChar
*ordinals
[] =
3874 wxTRANSLATE("first"),
3875 wxTRANSLATE("second"),
3876 wxTRANSLATE("third"),
3877 wxTRANSLATE("fourth"),
3878 wxTRANSLATE("fifth"),
3879 wxTRANSLATE("sixth"),
3880 wxTRANSLATE("seventh"),
3881 wxTRANSLATE("eighth"),
3882 wxTRANSLATE("ninth"),
3883 wxTRANSLATE("tenth"),
3884 wxTRANSLATE("eleventh"),
3885 wxTRANSLATE("twelfth"),
3886 wxTRANSLATE("thirteenth"),
3887 wxTRANSLATE("fourteenth"),
3888 wxTRANSLATE("fifteenth"),
3889 wxTRANSLATE("sixteenth"),
3890 wxTRANSLATE("seventeenth"),
3891 wxTRANSLATE("eighteenth"),
3892 wxTRANSLATE("nineteenth"),
3893 wxTRANSLATE("twentieth"),
3894 // that's enough - otherwise we'd have problems with
3895 // composite (or not) ordinals
3899 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
3901 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
3907 if ( n
== WXSIZEOF(ordinals
) )
3909 // stop here - something unknown
3916 // don't try anything here (as in case of numeric day
3917 // above) - the symbolic day spec should always
3918 // precede the month/year
3924 day
= (wxDateTime_t
)(n
+ 1);
3929 nPosCur
= tok
.GetPosition();
3932 // either no more tokens or the scan was stopped by something we couldn't
3933 // parse - in any case, see if we can construct a date from what we have
3934 if ( !haveDay
&& !haveWDay
)
3936 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
3938 return (wxChar
*)NULL
;
3941 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
3942 !(haveDay
&& haveMon
&& haveYear
) )
3944 // without adjectives (which we don't support here) the week day only
3945 // makes sense completely separately or with the full date
3946 // specification (what would "Wed 1999" mean?)
3947 return (wxChar
*)NULL
;
3950 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
3952 // may be we have month and day instead of day and year?
3953 if ( haveDay
&& !haveMon
)
3957 // exchange day and month
3958 mon
= (wxDateTime::Month
)(day
- 1);
3960 // we're in the current year then
3961 if ( (year
> 0) && (year
<= (int)GetNumOfDaysInMonth(Inv_Year
, mon
)) )
3963 day
= (wxDateTime_t
)year
;
3968 //else: no, can't exchange, leave haveMon == false
3974 // if we give the year, month and day must be given too
3975 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
3977 return (wxChar
*)NULL
;
3983 mon
= GetCurrentMonth();
3988 year
= GetCurrentYear();
3993 Set(day
, mon
, year
);
3997 // check that it is really the same
3998 if ( GetWeekDay() != wday
)
4000 // inconsistency detected
4001 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
4003 return (wxChar
*)NULL
;
4011 SetToWeekDayInSameWeek(wday
);
4014 // return the pointer to the first unparsed char
4016 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
4018 // if we couldn't parse the token after the delimiter, put back the
4019 // delimiter as well
4026 const wxChar
*wxDateTime::ParseTime(const wxChar
*time
)
4028 wxCHECK_MSG( time
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
4030 // first try some extra things
4037 { wxTRANSLATE("noon"), 12 },
4038 { wxTRANSLATE("midnight"), 00 },
4042 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
4044 wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
4045 size_t len
= timeString
.length();
4046 if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 )
4048 // casts required by DigitalMars
4049 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
4055 // try all time formats we may think about in the order from longest to
4058 // 12hour with AM/PM?
4059 const wxChar
*result
= ParseFormat(time
, _T("%I:%M:%S %p"));
4063 // normally, it's the same, but why not try it?
4064 result
= ParseFormat(time
, _T("%H:%M:%S"));
4069 // 12hour with AM/PM but without seconds?
4070 result
= ParseFormat(time
, _T("%I:%M %p"));
4076 result
= ParseFormat(time
, _T("%H:%M"));
4081 // just the hour and AM/PM?
4082 result
= ParseFormat(time
, _T("%I %p"));
4088 result
= ParseFormat(time
, _T("%H"));
4093 // parse the standard format: normally it is one of the formats above
4094 // but it may be set to something completely different by the user
4095 result
= ParseFormat(time
, _T("%X"));
4098 // TODO: parse timezones
4103 // ----------------------------------------------------------------------------
4104 // Workdays and holidays support
4105 // ----------------------------------------------------------------------------
4107 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
4109 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
4112 // ============================================================================
4114 // ============================================================================
4116 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
4119 return ds1
.Multiply(n
);
4122 // ============================================================================
4124 // ============================================================================
4126 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
4128 return wxTimeSpan(ts
).Multiply(n
);
4131 // this enum is only used in wxTimeSpan::Format() below but we can't declare
4132 // it locally to the method as it provokes an internal compiler error in egcs
4133 // 2.91.60 when building with -O2
4144 // not all strftime(3) format specifiers make sense here because, for example,
4145 // a time span doesn't have a year nor a timezone
4147 // Here are the ones which are supported (all of them are supported by strftime
4149 // %H hour in 24 hour format
4150 // %M minute (00 - 59)
4151 // %S second (00 - 59)
4154 // Also, for MFC CTimeSpan compatibility, we support
4155 // %D number of days
4157 // And, to be better than MFC :-), we also have
4158 // %E number of wEeks
4159 // %l milliseconds (000 - 999)
4160 wxString
wxTimeSpan::Format(const wxChar
*format
) const
4162 wxCHECK_MSG( format
, wxEmptyString
, _T("NULL format in wxTimeSpan::Format") );
4165 str
.Alloc(wxStrlen(format
));
4167 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
4169 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
4170 // question is what should ts.Format("%S") do? The code here returns "3273"
4171 // in this case (i.e. the total number of seconds, not just seconds % 60)
4172 // because, for me, this call means "give me entire time interval in
4173 // seconds" and not "give me the seconds part of the time interval"
4175 // If we agree that it should behave like this, it is clear that the
4176 // interpretation of each format specifier depends on the presence of the
4177 // other format specs in the string: if there was "%H" before "%M", we
4178 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
4180 // we remember the most important unit found so far
4181 TimeSpanPart partBiggest
= Part_MSec
;
4183 for ( const wxChar
*pch
= format
; *pch
; pch
++ )
4187 if ( ch
== _T('%') )
4189 // the start of the format specification of the printf() below
4190 wxString fmtPrefix
= _T('%');
4195 ch
= *++pch
; // get the format spec char
4199 wxFAIL_MSG( _T("invalid format character") );
4205 // skip the part below switch
4210 if ( partBiggest
< Part_Day
)
4216 partBiggest
= Part_Day
;
4221 partBiggest
= Part_Week
;
4227 if ( partBiggest
< Part_Hour
)
4233 partBiggest
= Part_Hour
;
4236 fmtPrefix
+= _T("02");
4240 n
= GetMilliseconds().ToLong();
4241 if ( partBiggest
< Part_MSec
)
4245 //else: no need to reset partBiggest to Part_MSec, it is
4246 // the least significant one anyhow
4248 fmtPrefix
+= _T("03");
4253 if ( partBiggest
< Part_Min
)
4259 partBiggest
= Part_Min
;
4262 fmtPrefix
+= _T("02");
4266 n
= GetSeconds().ToLong();
4267 if ( partBiggest
< Part_Sec
)
4273 partBiggest
= Part_Sec
;
4276 fmtPrefix
+= _T("02");
4280 str
+= wxString::Format(fmtPrefix
+ _T("ld"), n
);
4284 // normal character, just copy
4292 // ============================================================================
4293 // wxDateTimeHolidayAuthority and related classes
4294 // ============================================================================
4296 #include "wx/arrimpl.cpp"
4298 WX_DEFINE_OBJARRAY(wxDateTimeArray
)
4300 static int wxCMPFUNC_CONV
4301 wxDateTimeCompareFunc(wxDateTime
**first
, wxDateTime
**second
)
4303 wxDateTime dt1
= **first
,
4306 return dt1
== dt2
? 0 : dt1
< dt2
? -1 : +1;
4309 // ----------------------------------------------------------------------------
4310 // wxDateTimeHolidayAuthority
4311 // ----------------------------------------------------------------------------
4313 wxHolidayAuthoritiesArray
wxDateTimeHolidayAuthority::ms_authorities
;
4316 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime
& dt
)
4318 size_t count
= ms_authorities
.size();
4319 for ( size_t n
= 0; n
< count
; n
++ )
4321 if ( ms_authorities
[n
]->DoIsHoliday(dt
) )
4332 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime
& dtStart
,
4333 const wxDateTime
& dtEnd
,
4334 wxDateTimeArray
& holidays
)
4336 wxDateTimeArray hol
;
4340 const size_t countAuth
= ms_authorities
.size();
4341 for ( size_t nAuth
= 0; nAuth
< countAuth
; nAuth
++ )
4343 ms_authorities
[nAuth
]->DoGetHolidaysInRange(dtStart
, dtEnd
, hol
);
4345 WX_APPEND_ARRAY(holidays
, hol
);
4348 holidays
.Sort(wxDateTimeCompareFunc
);
4350 return holidays
.size();
4354 void wxDateTimeHolidayAuthority::ClearAllAuthorities()
4356 WX_CLEAR_ARRAY(ms_authorities
);
4360 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority
*auth
)
4362 ms_authorities
.push_back(auth
);
4365 wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
4367 // required here for Darwin
4370 // ----------------------------------------------------------------------------
4371 // wxDateTimeWorkDays
4372 // ----------------------------------------------------------------------------
4374 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime
& dt
) const
4376 wxDateTime::WeekDay wd
= dt
.GetWeekDay();
4378 return (wd
== wxDateTime::Sun
) || (wd
== wxDateTime::Sat
);
4381 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime
& dtStart
,
4382 const wxDateTime
& dtEnd
,
4383 wxDateTimeArray
& holidays
) const
4385 if ( dtStart
> dtEnd
)
4387 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
4394 // instead of checking all days, start with the first Sat after dtStart and
4395 // end with the last Sun before dtEnd
4396 wxDateTime dtSatFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sat
),
4397 dtSatLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sat
),
4398 dtSunFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sun
),
4399 dtSunLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sun
),
4402 for ( dt
= dtSatFirst
; dt
<= dtSatLast
; dt
+= wxDateSpan::Week() )
4407 for ( dt
= dtSunFirst
; dt
<= dtSunLast
; dt
+= wxDateSpan::Week() )
4412 return holidays
.GetCount();
4415 // ============================================================================
4416 // other helper functions
4417 // ============================================================================
4419 // ----------------------------------------------------------------------------
4420 // iteration helpers: can be used to write a for loop over enum variable like
4422 // for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
4423 // ----------------------------------------------------------------------------
4425 WXDLLIMPEXP_BASE
void wxNextMonth(wxDateTime::Month
& m
)
4427 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4429 // no wrapping or the for loop above would never end!
4430 m
= (wxDateTime::Month
)(m
+ 1);
4433 WXDLLIMPEXP_BASE
void wxPrevMonth(wxDateTime::Month
& m
)
4435 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4437 m
= m
== wxDateTime::Jan
? wxDateTime::Inv_Month
4438 : (wxDateTime::Month
)(m
- 1);
4441 WXDLLIMPEXP_BASE
void wxNextWDay(wxDateTime::WeekDay
& wd
)
4443 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4445 // no wrapping or the for loop above would never end!
4446 wd
= (wxDateTime::WeekDay
)(wd
+ 1);
4449 WXDLLIMPEXP_BASE
void wxPrevWDay(wxDateTime::WeekDay
& wd
)
4451 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4453 wd
= wd
== wxDateTime::Sun
? wxDateTime::Inv_WeekDay
4454 : (wxDateTime::WeekDay
)(wd
- 1);
4457 #endif // wxUSE_DATETIME