1 ///////////////////////////////////////////////////////////////////////////////
3 // Purpose: implementation of time/date related classes
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // parts of code taken from sndcal library by Scott E. Lee:
11 // Copyright 1993-1995, Scott E. Lee, all rights reserved.
12 // Permission granted to use, copy, modify, distribute and sell
13 // so long as the above copyright and this permission statement
14 // are retained in all copies.
16 // Licence: wxWindows licence
17 ///////////////////////////////////////////////////////////////////////////////
20 * Implementation notes:
22 * 1. the time is stored as a 64bit integer containing the signed number of
23 * milliseconds since Jan 1. 1970 (the Unix Epoch) - so it is always
26 * 2. the range is thus something about 580 million years, but due to current
27 * algorithms limitations, only dates from Nov 24, 4714BC are handled
29 * 3. standard ANSI C functions are used to do time calculations whenever
30 * possible, i.e. when the date is in the range Jan 1, 1970 to 2038
32 * 4. otherwise, the calculations are done by converting the date to/from JDN
33 * first (the range limitation mentioned above comes from here: the
34 * algorithm used by Scott E. Lee's code only works for positive JDNs, more
37 * 5. the object constructed for the given DD-MM-YYYY HH:MM:SS corresponds to
38 * this moment in local time and may be converted to the object
39 * corresponding to the same date/time in another time zone by using
42 * 6. the conversions to the current (or any other) timezone are done when the
43 * internal time representation is converted to the broken-down one in
47 // ============================================================================
49 // ============================================================================
51 // ----------------------------------------------------------------------------
53 // ----------------------------------------------------------------------------
55 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
56 #pragma implementation "datetime.h"
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"
80 #include "wx/datetime.h"
81 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
83 const long wxDateTime::TIME_T_FACTOR
= 1000l;
85 #if wxUSE_EXTENDED_RTTI
87 template<> void wxStringReadValue(const wxString
&s
, wxDateTime
&data
)
89 data
.ParseFormat(s
,wxT("%Y-%m-%d %H:%M:%S")) ;
92 template<> void wxStringWriteValue(wxString
&s
, const wxDateTime
&data
)
94 s
= data
.Format(wxT("%Y-%m-%d %H:%M:%S")) ;
97 wxCUSTOM_TYPE_INFO(wxDateTime
, wxToStringConverter
<wxDateTime
> , wxFromStringConverter
<wxDateTime
>)
102 // ----------------------------------------------------------------------------
103 // conditional compilation
104 // ----------------------------------------------------------------------------
106 #if defined(HAVE_STRPTIME) && defined(__GLIBC__) && \
107 ((__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0))
108 // glibc 2.0.7 strptime() is broken - the following snippet causes it to
109 // crash (instead of just failing):
111 // strncpy(buf, "Tue Dec 21 20:25:40 1999", 128);
112 // strptime(buf, "%x", &tm);
116 #endif // broken strptime()
118 #if defined(__MWERKS__) && wxUSE_UNICODE
122 #if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM)
123 #if defined(__WXPALMOS__)
124 #define WX_GMTOFF_IN_TM
125 #elif defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
126 #define WX_TIMEZONE _timezone
127 #elif defined(__MWERKS__)
128 long wxmw_timezone
= 28800;
129 #define WX_TIMEZONE wxmw_timezone
130 #elif defined(__DJGPP__) || defined(__WINE__)
131 #include <sys/timeb.h>
133 static long wxGetTimeZone()
135 static long timezone
= MAXLONG
; // invalid timezone
136 if (timezone
== MAXLONG
)
140 timezone
= tb
.timezone
;
144 #define WX_TIMEZONE wxGetTimeZone()
145 #elif defined(__DARWIN__)
146 #define WX_GMTOFF_IN_TM
147 #else // unknown platform - try timezone
148 #define WX_TIMEZONE timezone
150 #endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
152 // ----------------------------------------------------------------------------
154 // ----------------------------------------------------------------------------
156 // debugging helper: just a convenient replacement of wxCHECK()
157 #define wxDATETIME_CHECK(expr, msg) \
161 *this = wxInvalidDateTime; \
165 // ----------------------------------------------------------------------------
167 // ----------------------------------------------------------------------------
169 class wxDateTimeHolidaysModule
: public wxModule
172 virtual bool OnInit()
174 wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays
);
179 virtual void OnExit()
181 wxDateTimeHolidayAuthority::ClearAllAuthorities();
182 wxDateTimeHolidayAuthority::ms_authorities
.clear();
186 DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule
)
189 IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule
, wxModule
)
191 // ----------------------------------------------------------------------------
193 // ----------------------------------------------------------------------------
196 static const int MONTHS_IN_YEAR
= 12;
198 static const int SEC_PER_MIN
= 60;
200 static const int MIN_PER_HOUR
= 60;
202 static const int HOURS_PER_DAY
= 24;
204 static const long SECONDS_PER_DAY
= 86400l;
206 static const int DAYS_PER_WEEK
= 7;
208 static const long MILLISECONDS_PER_DAY
= 86400000l;
210 // this is the integral part of JDN of the midnight of Jan 1, 1970
211 // (i.e. JDN(Jan 1, 1970) = 2440587.5)
212 static const long EPOCH_JDN
= 2440587l;
214 // the date of JDN -0.5 (as we don't work with fractional parts, this is the
215 // reference date for us) is Nov 24, 4714BC
216 static const int JDN_0_YEAR
= -4713;
217 static const int JDN_0_MONTH
= wxDateTime::Nov
;
218 static const int JDN_0_DAY
= 24;
220 // the constants used for JDN calculations
221 static const long JDN_OFFSET
= 32046l;
222 static const long DAYS_PER_5_MONTHS
= 153l;
223 static const long DAYS_PER_4_YEARS
= 1461l;
224 static const long DAYS_PER_400_YEARS
= 146097l;
226 // this array contains the cumulated number of days in all previous months for
227 // normal and leap years
228 static const wxDateTime::wxDateTime_t gs_cumulatedDays
[2][MONTHS_IN_YEAR
] =
230 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
231 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
234 // ----------------------------------------------------------------------------
236 // ----------------------------------------------------------------------------
238 const wxChar
* wxDefaultDateTimeFormat
= wxT("%c");
239 const wxChar
* wxDefaultTimeSpanFormat
= wxT("%H:%M:%S");
241 // in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
242 // indicate an invalid wxDateTime object
243 const wxDateTime wxDefaultDateTime
;
245 wxDateTime::Country
wxDateTime::ms_country
= wxDateTime::Country_Unknown
;
247 // ----------------------------------------------------------------------------
249 // ----------------------------------------------------------------------------
251 // debugger helper: shows what the date really is
253 extern const wxChar
*wxDumpDate(const wxDateTime
* dt
)
255 static wxChar buf
[128];
257 wxStrcpy(buf
, dt
->Format(_T("%Y-%m-%d (%a) %H:%M:%S")));
263 // get the number of days in the given month of the given year
265 wxDateTime::wxDateTime_t
GetNumOfDaysInMonth(int year
, wxDateTime::Month month
)
267 // the number of days in month in Julian/Gregorian calendar: the first line
268 // is for normal years, the second one is for the leap ones
269 static wxDateTime::wxDateTime_t daysInMonth
[2][MONTHS_IN_YEAR
] =
271 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
272 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
275 return daysInMonth
[wxDateTime::IsLeapYear(year
)][month
];
278 // returns the time zone in the C sense, i.e. the difference UTC - local
280 static int GetTimeZone()
282 #ifdef WX_GMTOFF_IN_TM
283 // set to true when the timezone is set
284 static bool s_timezoneSet
= false;
285 static long gmtoffset
= LONG_MAX
; // invalid timezone
287 // ensure that the timezone variable is set by calling localtime
288 if ( !s_timezoneSet
)
290 // just call localtime() instead of figuring out whether this system
291 // supports tzset(), _tzset() or something else
296 s_timezoneSet
= true;
298 // note that GMT offset is the opposite of time zone and so to return
299 // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM
300 // cases we have to negate it
301 gmtoffset
= -tm
->tm_gmtoff
;
304 return (int)gmtoffset
;
305 #else // !WX_GMTOFF_IN_TM
306 return (int)WX_TIMEZONE
;
307 #endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
310 // return the integral part of the JDN for the midnight of the given date (to
311 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
312 // noon of the previous day)
313 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day
,
314 wxDateTime::Month mon
,
317 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
319 // check the date validity
321 (year
> JDN_0_YEAR
) ||
322 ((year
== JDN_0_YEAR
) && (mon
> JDN_0_MONTH
)) ||
323 ((year
== JDN_0_YEAR
) && (mon
== JDN_0_MONTH
) && (day
>= JDN_0_DAY
)),
324 _T("date out of range - can't convert to JDN")
327 // make the year positive to avoid problems with negative numbers division
330 // months are counted from March here
332 if ( mon
>= wxDateTime::Mar
)
342 // now we can simply add all the contributions together
343 return ((year
/ 100) * DAYS_PER_400_YEARS
) / 4
344 + ((year
% 100) * DAYS_PER_4_YEARS
) / 4
345 + (month
* DAYS_PER_5_MONTHS
+ 2) / 5
350 // this function is a wrapper around strftime(3) adding error checking
351 static wxString
CallStrftime(const wxChar
*format
, const tm
* tm
)
354 if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) )
356 // buffer is too small?
357 wxFAIL_MSG(_T("strftime() failed"));
360 return wxString(buf
);
365 // glibc2 doesn't define this in the headers unless _XOPEN_SOURCE is defined
366 // which, unfortunately, wreaks havoc elsewhere
367 #if defined(__GLIBC__) && (__GLIBC__ == 2)
368 extern "C" char *strptime(const char *, const char *, struct tm
*);
371 // Unicode-friendly strptime() wrapper
372 static const wxChar
*
373 CallStrptime(const wxChar
*input
, const char *fmt
, tm
*tm
)
375 // the problem here is that strptime() returns pointer into the string we
376 // passed to it while we're really interested in the pointer into the
377 // original, Unicode, string so we try to transform the pointer back
379 wxCharBuffer
inputMB(wxConvertWX2MB(input
));
381 const char * const inputMB
= input
;
382 #endif // Unicode/Ascii
384 const char *result
= strptime(inputMB
, fmt
, tm
);
389 // FIXME: this is wrong in presence of surrogates &c
390 return input
+ (result
- inputMB
.data());
393 #endif // Unicode/Ascii
396 #endif // HAVE_STRPTIME
398 // if year and/or month have invalid values, replace them with the current ones
399 static void ReplaceDefaultYearMonthWithCurrent(int *year
,
400 wxDateTime::Month
*month
)
402 struct tm
*tmNow
= NULL
;
404 if ( *year
== wxDateTime::Inv_Year
)
406 tmNow
= wxDateTime::GetTmNow();
408 *year
= 1900 + tmNow
->tm_year
;
411 if ( *month
== wxDateTime::Inv_Month
)
414 tmNow
= wxDateTime::GetTmNow();
416 *month
= (wxDateTime::Month
)tmNow
->tm_mon
;
420 // fll the struct tm with default values
421 static void InitTm(struct tm
& tm
)
423 // struct tm may have etxra fields (undocumented and with unportable
424 // names) which, nevertheless, must be set to 0
425 memset(&tm
, 0, sizeof(struct tm
));
427 tm
.tm_mday
= 1; // mday 0 is invalid
428 tm
.tm_year
= 76; // any valid year
429 tm
.tm_isdst
= -1; // auto determine
435 // return the month if the string is a month name or Inv_Month otherwise
436 static wxDateTime::Month
GetMonthFromName(const wxString
& name
, int flags
)
438 wxDateTime::Month mon
;
439 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
441 // case-insensitive comparison either one of or with both abbreviated
443 if ( flags
& wxDateTime::Name_Full
)
445 if ( name
.CmpNoCase(wxDateTime::
446 GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 )
452 if ( flags
& wxDateTime::Name_Abbr
)
454 if ( name
.CmpNoCase(wxDateTime::
455 GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 )
465 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
466 static wxDateTime::WeekDay
GetWeekDayFromName(const wxString
& name
, int flags
)
468 wxDateTime::WeekDay wd
;
469 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
471 // case-insensitive comparison either one of or with both abbreviated
473 if ( flags
& wxDateTime::Name_Full
)
475 if ( name
.CmpNoCase(wxDateTime::
476 GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 )
482 if ( flags
& wxDateTime::Name_Abbr
)
484 if ( name
.CmpNoCase(wxDateTime::
485 GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 )
495 // scans all digits (but no more than len) and returns the resulting number
496 static bool GetNumericToken(size_t len
, const wxChar
*& p
, unsigned long *number
)
500 while ( wxIsdigit(*p
) )
504 if ( len
&& ++n
> len
)
508 return !s
.empty() && s
.ToULong(number
);
511 // scans all alphabetic characters and returns the resulting string
512 static wxString
GetAlphaToken(const wxChar
*& p
)
515 while ( wxIsalpha(*p
) )
523 // ============================================================================
524 // implementation of wxDateTime
525 // ============================================================================
527 // ----------------------------------------------------------------------------
529 // ----------------------------------------------------------------------------
533 year
= (wxDateTime_t
)wxDateTime::Inv_Year
;
534 mon
= wxDateTime::Inv_Month
;
536 hour
= min
= sec
= msec
= 0;
537 wday
= wxDateTime::Inv_WeekDay
;
540 wxDateTime::Tm::Tm(const struct tm
& tm
, const TimeZone
& tz
)
544 sec
= (wxDateTime::wxDateTime_t
)tm
.tm_sec
;
545 min
= (wxDateTime::wxDateTime_t
)tm
.tm_min
;
546 hour
= (wxDateTime::wxDateTime_t
)tm
.tm_hour
;
547 mday
= (wxDateTime::wxDateTime_t
)tm
.tm_mday
;
548 mon
= (wxDateTime::Month
)tm
.tm_mon
;
549 year
= 1900 + tm
.tm_year
;
550 wday
= (wxDateTime::wxDateTime_t
)tm
.tm_wday
;
551 yday
= (wxDateTime::wxDateTime_t
)tm
.tm_yday
;
554 bool wxDateTime::Tm::IsValid() const
556 // we allow for the leap seconds, although we don't use them (yet)
557 return (year
!= wxDateTime::Inv_Year
) && (mon
!= wxDateTime::Inv_Month
) &&
558 (mday
<= GetNumOfDaysInMonth(year
, mon
)) &&
559 (hour
< 24) && (min
< 60) && (sec
< 62) && (msec
< 1000);
562 void wxDateTime::Tm::ComputeWeekDay()
564 // compute the week day from day/month/year: we use the dumbest algorithm
565 // possible: just compute our JDN and then use the (simple to derive)
566 // formula: weekday = (JDN + 1.5) % 7
567 wday
= (wxDateTime::wxDateTime_t
)((wxDateTime::WeekDay
)(GetTruncatedJDN(mday
, mon
, year
) + 2) % 7);
570 void wxDateTime::Tm::AddMonths(int monDiff
)
572 // normalize the months field
573 while ( monDiff
< -mon
)
577 monDiff
+= MONTHS_IN_YEAR
;
580 while ( monDiff
+ mon
>= MONTHS_IN_YEAR
)
584 monDiff
-= MONTHS_IN_YEAR
;
587 mon
= (wxDateTime::Month
)(mon
+ monDiff
);
589 wxASSERT_MSG( mon
>= 0 && mon
< MONTHS_IN_YEAR
, _T("logic error") );
591 // NB: we don't check here that the resulting date is valid, this function
592 // is private and the caller must check it if needed
595 void wxDateTime::Tm::AddDays(int dayDiff
)
597 // normalize the days field
598 while ( dayDiff
+ mday
< 1 )
602 dayDiff
+= GetNumOfDaysInMonth(year
, mon
);
605 mday
= (wxDateTime::wxDateTime_t
)( mday
+ dayDiff
);
606 while ( mday
> GetNumOfDaysInMonth(year
, mon
) )
608 mday
-= GetNumOfDaysInMonth(year
, mon
);
613 wxASSERT_MSG( mday
> 0 && mday
<= GetNumOfDaysInMonth(year
, mon
),
617 // ----------------------------------------------------------------------------
619 // ----------------------------------------------------------------------------
621 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz
)
625 case wxDateTime::Local
:
626 // get the offset from C RTL: it returns the difference GMT-local
627 // while we want to have the offset _from_ GMT, hence the '-'
628 m_offset
= -GetTimeZone();
631 case wxDateTime::GMT_12
:
632 case wxDateTime::GMT_11
:
633 case wxDateTime::GMT_10
:
634 case wxDateTime::GMT_9
:
635 case wxDateTime::GMT_8
:
636 case wxDateTime::GMT_7
:
637 case wxDateTime::GMT_6
:
638 case wxDateTime::GMT_5
:
639 case wxDateTime::GMT_4
:
640 case wxDateTime::GMT_3
:
641 case wxDateTime::GMT_2
:
642 case wxDateTime::GMT_1
:
643 m_offset
= -3600*(wxDateTime::GMT0
- tz
);
646 case wxDateTime::GMT0
:
647 case wxDateTime::GMT1
:
648 case wxDateTime::GMT2
:
649 case wxDateTime::GMT3
:
650 case wxDateTime::GMT4
:
651 case wxDateTime::GMT5
:
652 case wxDateTime::GMT6
:
653 case wxDateTime::GMT7
:
654 case wxDateTime::GMT8
:
655 case wxDateTime::GMT9
:
656 case wxDateTime::GMT10
:
657 case wxDateTime::GMT11
:
658 case wxDateTime::GMT12
:
659 m_offset
= 3600*(tz
- wxDateTime::GMT0
);
662 case wxDateTime::A_CST
:
663 // Central Standard Time in use in Australia = UTC + 9.5
664 m_offset
= 60l*(9*60 + 30);
668 wxFAIL_MSG( _T("unknown time zone") );
672 // ----------------------------------------------------------------------------
674 // ----------------------------------------------------------------------------
677 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
)
679 if ( year
== Inv_Year
)
680 year
= GetCurrentYear();
682 if ( cal
== Gregorian
)
684 // in Gregorian calendar leap years are those divisible by 4 except
685 // those divisible by 100 unless they're also divisible by 400
686 // (in some countries, like Russia and Greece, additional corrections
687 // exist, but they won't manifest themselves until 2700)
688 return (year
% 4 == 0) && ((year
% 100 != 0) || (year
% 400 == 0));
690 else if ( cal
== Julian
)
692 // in Julian calendar the rule is simpler
693 return year
% 4 == 0;
697 wxFAIL_MSG(_T("unknown calendar"));
704 int wxDateTime::GetCentury(int year
)
706 return year
> 0 ? year
/ 100 : year
/ 100 - 1;
710 int wxDateTime::ConvertYearToBC(int year
)
713 return year
> 0 ? year
: year
- 1;
717 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
)
722 return Now().GetYear();
725 wxFAIL_MSG(_T("TODO"));
729 wxFAIL_MSG(_T("unsupported calendar"));
737 wxDateTime::Month
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
)
742 return Now().GetMonth();
745 wxFAIL_MSG(_T("TODO"));
749 wxFAIL_MSG(_T("unsupported calendar"));
757 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(int year
, Calendar cal
)
759 if ( year
== Inv_Year
)
761 // take the current year if none given
762 year
= GetCurrentYear();
769 return IsLeapYear(year
) ? 366 : 365;
772 wxFAIL_MSG(_T("unsupported calendar"));
780 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(wxDateTime::Month month
,
782 wxDateTime::Calendar cal
)
784 wxCHECK_MSG( month
< MONTHS_IN_YEAR
, 0, _T("invalid month") );
786 if ( cal
== Gregorian
|| cal
== Julian
)
788 if ( year
== Inv_Year
)
790 // take the current year if none given
791 year
= GetCurrentYear();
794 return GetNumOfDaysInMonth(year
, month
);
798 wxFAIL_MSG(_T("unsupported calendar"));
805 wxString
wxDateTime::GetMonthName(wxDateTime::Month month
,
806 wxDateTime::NameFlags flags
)
808 wxCHECK_MSG( month
!= Inv_Month
, wxEmptyString
, _T("invalid month") );
810 // notice that we must set all the fields to avoid confusing libc (GNU one
811 // gets confused to a crash if we don't do this)
816 return CallStrftime(flags
== Name_Abbr
? _T("%b") : _T("%B"), &tm
);
822 ret
= (flags
== Name_Abbr
? wxT("Jan"): wxT("January"));
825 ret
= (flags
== Name_Abbr
? wxT("Feb"): wxT("Febuary"));
828 ret
= (flags
== Name_Abbr
? wxT("Mar"): wxT("March"));
831 ret
= (flags
== Name_Abbr
? wxT("Apr"): wxT("April"));
834 ret
= (flags
== Name_Abbr
? wxT("May"): wxT("May"));
837 ret
= (flags
== Name_Abbr
? wxT("Jun"): wxT("June"));
840 ret
= (flags
== Name_Abbr
? wxT("Jul"): wxT("July"));
843 ret
= (flags
== Name_Abbr
? wxT("Aug"): wxT("August"));
846 ret
= (flags
== Name_Abbr
? wxT("Sep"): wxT("September"));
849 ret
= (flags
== Name_Abbr
? wxT("Oct"): wxT("October"));
852 ret
= (flags
== Name_Abbr
? wxT("Nov"): wxT("November"));
855 ret
= (flags
== Name_Abbr
? wxT("Dec"): wxT("December"));
863 wxString
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
,
864 wxDateTime::NameFlags flags
)
866 wxCHECK_MSG( wday
!= Inv_WeekDay
, wxEmptyString
, _T("invalid weekday") );
868 // take some arbitrary Sunday (but notice that the day should be such that
869 // after adding wday to it below we still have a valid date, e.g. don't
877 // and offset it by the number of days needed to get the correct wday
880 // call mktime() to normalize it...
883 // ... and call strftime()
884 return CallStrftime(flags
== Name_Abbr
? _T("%a") : _T("%A"), &tm
);
890 ret
= (flags
== Name_Abbr
? wxT("Sun") : wxT("Sunday"));
893 ret
= (flags
== Name_Abbr
? wxT("Mon") : wxT("Monday"));
896 ret
= (flags
== Name_Abbr
? wxT("Tue") : wxT("Tuesday"));
899 ret
= (flags
== Name_Abbr
? wxT("Wed") : wxT("Wednesday"));
902 ret
= (flags
== Name_Abbr
? wxT("Thu") : wxT("Thursday"));
905 ret
= (flags
== Name_Abbr
? wxT("Fri") : wxT("Friday"));
908 ret
= (flags
== Name_Abbr
? wxT("Sat") : wxT("Saturday"));
917 void wxDateTime::GetAmPmStrings(wxString
*am
, wxString
*pm
)
922 // @Note: Do not call 'CallStrftime' here! CallStrftime checks the return code
923 // and causes an assertion failed if the buffer is to small (which is good) - OR -
924 // if strftime does not return anything because the format string is invalid - OR -
925 // if there are no 'am' / 'pm' tokens defined for the current locale (which is not good).
926 // wxDateTime::ParseTime will try several different formats to parse the time.
927 // As a result, GetAmPmStrings might get called, even if the current locale
928 // does not define any 'am' / 'pm' tokens. In this case, wxStrftime would
929 // assert, even though it is a perfectly legal use.
932 if (wxStrftime(buffer
, sizeof buffer
, _T("%p"), &tm
) > 0)
933 *am
= wxString(buffer
);
940 if (wxStrftime(buffer
, sizeof buffer
, _T("%p"), &tm
) > 0)
941 *pm
= wxString(buffer
);
947 // ----------------------------------------------------------------------------
948 // Country stuff: date calculations depend on the country (DST, work days,
949 // ...), so we need to know which rules to follow.
950 // ----------------------------------------------------------------------------
953 wxDateTime::Country
wxDateTime::GetCountry()
955 // TODO use LOCALE_ICOUNTRY setting under Win32
957 if ( ms_country
== Country_Unknown
)
959 // try to guess from the time zone name
960 time_t t
= time(NULL
);
961 struct tm
*tm
= localtime(&t
);
963 wxString tz
= CallStrftime(_T("%Z"), tm
);
964 if ( tz
== _T("WET") || tz
== _T("WEST") )
968 else if ( tz
== _T("CET") || tz
== _T("CEST") )
970 ms_country
= Country_EEC
;
972 else if ( tz
== _T("MSK") || tz
== _T("MSD") )
976 else if ( tz
== _T("AST") || tz
== _T("ADT") ||
977 tz
== _T("EST") || tz
== _T("EDT") ||
978 tz
== _T("CST") || tz
== _T("CDT") ||
979 tz
== _T("MST") || tz
== _T("MDT") ||
980 tz
== _T("PST") || tz
== _T("PDT") )
986 // well, choose a default one
998 void wxDateTime::SetCountry(wxDateTime::Country country
)
1000 ms_country
= country
;
1004 bool wxDateTime::IsWestEuropeanCountry(Country country
)
1006 if ( country
== Country_Default
)
1008 country
= GetCountry();
1011 return (Country_WesternEurope_Start
<= country
) &&
1012 (country
<= Country_WesternEurope_End
);
1015 // ----------------------------------------------------------------------------
1016 // DST calculations: we use 3 different rules for the West European countries,
1017 // USA and for the rest of the world. This is undoubtedly false for many
1018 // countries, but I lack the necessary info (and the time to gather it),
1019 // please add the other rules here!
1020 // ----------------------------------------------------------------------------
1023 bool wxDateTime::IsDSTApplicable(int year
, Country country
)
1025 if ( year
== Inv_Year
)
1027 // take the current year if none given
1028 year
= GetCurrentYear();
1031 if ( country
== Country_Default
)
1033 country
= GetCountry();
1040 // DST was first observed in the US and UK during WWI, reused
1041 // during WWII and used again since 1966
1042 return year
>= 1966 ||
1043 (year
>= 1942 && year
<= 1945) ||
1044 (year
== 1918 || year
== 1919);
1047 // assume that it started after WWII
1053 wxDateTime
wxDateTime::GetBeginDST(int year
, Country country
)
1055 if ( year
== Inv_Year
)
1057 // take the current year if none given
1058 year
= GetCurrentYear();
1061 if ( country
== Country_Default
)
1063 country
= GetCountry();
1066 if ( !IsDSTApplicable(year
, country
) )
1068 return wxInvalidDateTime
;
1073 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1075 // DST begins at 1 a.m. GMT on the last Sunday of March
1076 if ( !dt
.SetToLastWeekDay(Sun
, Mar
, year
) )
1079 wxFAIL_MSG( _T("no last Sunday in March?") );
1082 dt
+= wxTimeSpan::Hours(1);
1084 // disable DST tests because it could result in an infinite recursion!
1087 else switch ( country
)
1094 // don't know for sure - assume it was in effect all year
1099 dt
.Set(1, Jan
, year
);
1103 // DST was installed Feb 2, 1942 by the Congress
1104 dt
.Set(2, Feb
, year
);
1107 // Oil embargo changed the DST period in the US
1109 dt
.Set(6, Jan
, 1974);
1113 dt
.Set(23, Feb
, 1975);
1117 // before 1986, DST begun on the last Sunday of April, but
1118 // in 1986 Reagan changed it to begin at 2 a.m. of the
1119 // first Sunday in April
1122 if ( !dt
.SetToLastWeekDay(Sun
, Apr
, year
) )
1125 wxFAIL_MSG( _T("no first Sunday in April?") );
1130 if ( !dt
.SetToWeekDay(Sun
, 1, Apr
, year
) )
1133 wxFAIL_MSG( _T("no first Sunday in April?") );
1137 dt
+= wxTimeSpan::Hours(2);
1139 // TODO what about timezone??
1145 // assume Mar 30 as the start of the DST for the rest of the world
1146 // - totally bogus, of course
1147 dt
.Set(30, Mar
, year
);
1154 wxDateTime
wxDateTime::GetEndDST(int year
, Country country
)
1156 if ( year
== Inv_Year
)
1158 // take the current year if none given
1159 year
= GetCurrentYear();
1162 if ( country
== Country_Default
)
1164 country
= GetCountry();
1167 if ( !IsDSTApplicable(year
, country
) )
1169 return wxInvalidDateTime
;
1174 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1176 // DST ends at 1 a.m. GMT on the last Sunday of October
1177 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1179 // weirder and weirder...
1180 wxFAIL_MSG( _T("no last Sunday in October?") );
1183 dt
+= wxTimeSpan::Hours(1);
1185 // disable DST tests because it could result in an infinite recursion!
1188 else switch ( country
)
1195 // don't know for sure - assume it was in effect all year
1199 dt
.Set(31, Dec
, year
);
1203 // the time was reset after the end of the WWII
1204 dt
.Set(30, Sep
, year
);
1208 // DST ends at 2 a.m. on the last Sunday of October
1209 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1211 // weirder and weirder...
1212 wxFAIL_MSG( _T("no last Sunday in October?") );
1215 dt
+= wxTimeSpan::Hours(2);
1217 // TODO what about timezone??
1222 // assume October 26th as the end of the DST - totally bogus too
1223 dt
.Set(26, Oct
, year
);
1229 // ----------------------------------------------------------------------------
1230 // constructors and assignment operators
1231 // ----------------------------------------------------------------------------
1233 // return the current time with ms precision
1234 /* static */ wxDateTime
wxDateTime::UNow()
1236 return wxDateTime(wxGetLocalTimeMillis());
1239 // the values in the tm structure contain the local time
1240 wxDateTime
& wxDateTime::Set(const struct tm
& tm
)
1243 time_t timet
= mktime(&tm2
);
1245 if ( timet
== (time_t)-1 )
1247 // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
1248 // less than timezone - try to make it work for this case
1249 if ( tm2
.tm_year
== 70 && tm2
.tm_mon
== 0 && tm2
.tm_mday
== 1 )
1251 return Set((time_t)(
1253 tm2
.tm_hour
* MIN_PER_HOUR
* SEC_PER_MIN
+
1254 tm2
.tm_min
* SEC_PER_MIN
+
1258 wxFAIL_MSG( _T("mktime() failed") );
1260 *this = wxInvalidDateTime
;
1270 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
,
1271 wxDateTime_t minute
,
1272 wxDateTime_t second
,
1273 wxDateTime_t millisec
)
1275 // we allow seconds to be 61 to account for the leap seconds, even if we
1276 // don't use them really
1277 wxDATETIME_CHECK( hour
< 24 &&
1281 _T("Invalid time in wxDateTime::Set()") );
1283 // get the current date from system
1284 struct tm
*tm
= GetTmNow();
1286 wxDATETIME_CHECK( tm
, _T("localtime() failed") );
1290 tm
->tm_min
= minute
;
1291 tm
->tm_sec
= second
;
1293 // and the DST in case it changes on this date
1296 if ( tm2
.tm_isdst
!= tm
->tm_isdst
)
1297 tm
->tm_isdst
= tm2
.tm_isdst
;
1301 // and finally adjust milliseconds
1302 return SetMillisecond(millisec
);
1305 wxDateTime
& wxDateTime::Set(wxDateTime_t day
,
1309 wxDateTime_t minute
,
1310 wxDateTime_t second
,
1311 wxDateTime_t millisec
)
1313 wxDATETIME_CHECK( hour
< 24 &&
1317 _T("Invalid time in wxDateTime::Set()") );
1319 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1321 wxDATETIME_CHECK( (0 < day
) && (day
<= GetNumberOfDays(month
, year
)),
1322 _T("Invalid date in wxDateTime::Set()") );
1324 // the range of time_t type (inclusive)
1325 static const int yearMinInRange
= 1970;
1326 static const int yearMaxInRange
= 2037;
1328 // test only the year instead of testing for the exact end of the Unix
1329 // time_t range - it doesn't bring anything to do more precise checks
1330 if ( year
>= yearMinInRange
&& year
<= yearMaxInRange
)
1332 // use the standard library version if the date is in range - this is
1333 // probably more efficient than our code
1335 tm
.tm_year
= year
- 1900;
1341 tm
.tm_isdst
= -1; // mktime() will guess it
1345 // and finally adjust milliseconds
1347 SetMillisecond(millisec
);
1353 // do time calculations ourselves: we want to calculate the number of
1354 // milliseconds between the given date and the epoch
1356 // get the JDN for the midnight of this day
1357 m_time
= GetTruncatedJDN(day
, month
, year
);
1358 m_time
-= EPOCH_JDN
;
1359 m_time
*= SECONDS_PER_DAY
* TIME_T_FACTOR
;
1361 // JDN corresponds to GMT, we take localtime
1362 Add(wxTimeSpan(hour
, minute
, second
+ GetTimeZone(), millisec
));
1368 wxDateTime
& wxDateTime::Set(double jdn
)
1370 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1372 jdn
-= EPOCH_JDN
+ 0.5;
1374 jdn
*= MILLISECONDS_PER_DAY
;
1376 // JDNs always suppose an UTC date, so bring it back to local time zone
1377 // (also see GetJulianDayNumber() implementation)
1378 long tzDiff
= GetTimeZone();
1381 // FIXME: again, we suppose that DST is always one hour
1385 jdn
+= tzDiff
*1000; // tzDiff is in seconds
1392 wxDateTime
& wxDateTime::ResetTime()
1396 if ( tm
.hour
|| tm
.min
|| tm
.sec
|| tm
.msec
)
1409 // ----------------------------------------------------------------------------
1410 // DOS Date and Time Format functions
1411 // ----------------------------------------------------------------------------
1412 // the dos date and time value is an unsigned 32 bit value in the format:
1413 // YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
1415 // Y = year offset from 1980 (0-127)
1417 // D = day of month (1-31)
1419 // m = minute (0-59)
1420 // s = bisecond (0-29) each bisecond indicates two seconds
1421 // ----------------------------------------------------------------------------
1423 wxDateTime
& wxDateTime::SetFromDOS(unsigned long ddt
)
1428 long year
= ddt
& 0xFE000000;
1433 long month
= ddt
& 0x1E00000;
1438 long day
= ddt
& 0x1F0000;
1442 long hour
= ddt
& 0xF800;
1446 long minute
= ddt
& 0x7E0;
1450 long second
= ddt
& 0x1F;
1451 tm
.tm_sec
= second
* 2;
1453 return Set(mktime(&tm
));
1456 unsigned long wxDateTime::GetAsDOS() const
1459 time_t ticks
= GetTicks();
1460 struct tm
*tm
= localtime(&ticks
);
1462 long year
= tm
->tm_year
;
1466 long month
= tm
->tm_mon
;
1470 long day
= tm
->tm_mday
;
1473 long hour
= tm
->tm_hour
;
1476 long minute
= tm
->tm_min
;
1479 long second
= tm
->tm_sec
;
1482 ddt
= year
| month
| day
| hour
| minute
| second
;
1486 // ----------------------------------------------------------------------------
1487 // time_t <-> broken down time conversions
1488 // ----------------------------------------------------------------------------
1490 wxDateTime::Tm
wxDateTime::GetTm(const TimeZone
& tz
) const
1492 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1494 time_t time
= GetTicks();
1495 if ( time
!= (time_t)-1 )
1497 // use C RTL functions
1499 if ( tz
.GetOffset() == -GetTimeZone() )
1501 // we are working with local time
1502 tm
= localtime(&time
);
1504 // should never happen
1505 wxCHECK_MSG( tm
, Tm(), _T("localtime() failed") );
1509 time
+= (time_t)tz
.GetOffset();
1510 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
1511 int time2
= (int) time
;
1519 // should never happen
1520 wxCHECK_MSG( tm
, Tm(), _T("gmtime() failed") );
1524 tm
= (struct tm
*)NULL
;
1530 // adjust the milliseconds
1532 long timeOnly
= (m_time
% MILLISECONDS_PER_DAY
).ToLong();
1533 tm2
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1536 //else: use generic code below
1539 // remember the time and do the calculations with the date only - this
1540 // eliminates rounding errors of the floating point arithmetics
1542 wxLongLong timeMidnight
= m_time
+ tz
.GetOffset() * 1000;
1544 long timeOnly
= (timeMidnight
% MILLISECONDS_PER_DAY
).ToLong();
1546 // we want to always have positive time and timeMidnight to be really
1547 // the midnight before it
1550 timeOnly
= MILLISECONDS_PER_DAY
+ timeOnly
;
1553 timeMidnight
-= timeOnly
;
1555 // calculate the Gregorian date from JDN for the midnight of our date:
1556 // this will yield day, month (in 1..12 range) and year
1558 // actually, this is the JDN for the noon of the previous day
1559 long jdn
= (timeMidnight
/ MILLISECONDS_PER_DAY
).ToLong() + EPOCH_JDN
;
1561 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1563 wxASSERT_MSG( jdn
> -2, _T("JDN out of range") );
1565 // calculate the century
1566 long temp
= (jdn
+ JDN_OFFSET
) * 4 - 1;
1567 long century
= temp
/ DAYS_PER_400_YEARS
;
1569 // then the year and day of year (1 <= dayOfYear <= 366)
1570 temp
= ((temp
% DAYS_PER_400_YEARS
) / 4) * 4 + 3;
1571 long year
= (century
* 100) + (temp
/ DAYS_PER_4_YEARS
);
1572 long dayOfYear
= (temp
% DAYS_PER_4_YEARS
) / 4 + 1;
1574 // and finally the month and day of the month
1575 temp
= dayOfYear
* 5 - 3;
1576 long month
= temp
/ DAYS_PER_5_MONTHS
;
1577 long day
= (temp
% DAYS_PER_5_MONTHS
) / 5 + 1;
1579 // month is counted from March - convert to normal
1590 // year is offset by 4800
1593 // check that the algorithm gave us something reasonable
1594 wxASSERT_MSG( (0 < month
) && (month
<= 12), _T("invalid month") );
1595 wxASSERT_MSG( (1 <= day
) && (day
< 32), _T("invalid day") );
1597 // construct Tm from these values
1599 tm
.year
= (int)year
;
1600 tm
.mon
= (Month
)(month
- 1); // algorithm yields 1 for January, not 0
1601 tm
.mday
= (wxDateTime_t
)day
;
1602 tm
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1603 timeOnly
-= tm
.msec
;
1604 timeOnly
/= 1000; // now we have time in seconds
1606 tm
.sec
= (wxDateTime_t
)(timeOnly
% 60);
1608 timeOnly
/= 60; // now we have time in minutes
1610 tm
.min
= (wxDateTime_t
)(timeOnly
% 60);
1613 tm
.hour
= (wxDateTime_t
)(timeOnly
/ 60);
1618 wxDateTime
& wxDateTime::SetYear(int year
)
1620 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1629 wxDateTime
& wxDateTime::SetMonth(Month month
)
1631 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1640 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
)
1642 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1651 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
)
1653 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1662 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
)
1664 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1673 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
)
1675 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1684 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
)
1686 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1688 // we don't need to use GetTm() for this one
1689 m_time
-= m_time
% 1000l;
1690 m_time
+= millisecond
;
1695 // ----------------------------------------------------------------------------
1696 // wxDateTime arithmetics
1697 // ----------------------------------------------------------------------------
1699 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
)
1703 tm
.year
+= diff
.GetYears();
1704 tm
.AddMonths(diff
.GetMonths());
1706 // check that the resulting date is valid
1707 if ( tm
.mday
> GetNumOfDaysInMonth(tm
.year
, tm
.mon
) )
1709 // We suppose that when adding one month to Jan 31 we want to get Feb
1710 // 28 (or 29), i.e. adding a month to the last day of the month should
1711 // give the last day of the next month which is quite logical.
1713 // Unfortunately, there is no logic way to understand what should
1714 // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1715 // We make it Feb 28 (last day too), but it is highly questionable.
1716 tm
.mday
= GetNumOfDaysInMonth(tm
.year
, tm
.mon
);
1719 tm
.AddDays(diff
.GetTotalDays());
1723 wxASSERT_MSG( IsSameTime(tm
),
1724 _T("Add(wxDateSpan) shouldn't modify time") );
1729 // ----------------------------------------------------------------------------
1730 // Weekday and monthday stuff
1731 // ----------------------------------------------------------------------------
1733 // convert Sun, Mon, ..., Sat into 6, 0, ..., 5
1734 static inline int ConvertWeekDayToMondayBase(int wd
)
1736 return wd
== wxDateTime::Sun
? 6 : wd
- 1;
1741 wxDateTime::SetToWeekOfYear(int year
, wxDateTime_t numWeek
, WeekDay wd
)
1743 wxASSERT_MSG( numWeek
> 0,
1744 _T("invalid week number: weeks are counted from 1") );
1746 // Jan 4 always lies in the 1st week of the year
1747 wxDateTime
dt(4, Jan
, year
);
1748 dt
.SetToWeekDayInSameWeek(wd
);
1749 dt
+= wxDateSpan::Weeks(numWeek
- 1);
1754 // use a separate function to avoid warnings about using deprecated
1755 // SetToTheWeek in GetWeek below
1757 SetToTheWeek(int year
,
1758 wxDateTime::wxDateTime_t numWeek
,
1759 wxDateTime::WeekDay weekday
,
1760 wxDateTime::WeekFlags flags
)
1762 // Jan 4 always lies in the 1st week of the year
1763 wxDateTime
dt(4, wxDateTime::Jan
, year
);
1764 dt
.SetToWeekDayInSameWeek(weekday
, flags
);
1765 dt
+= wxDateSpan::Weeks(numWeek
- 1);
1770 bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek
,
1774 int year
= GetYear();
1775 *this = ::SetToTheWeek(year
, numWeek
, weekday
, flags
);
1776 if ( GetYear() != year
)
1778 // oops... numWeek was too big
1785 wxDateTime
wxDateTime::GetWeek(wxDateTime_t numWeek
,
1787 WeekFlags flags
) const
1789 return ::SetToTheWeek(GetYear(), numWeek
, weekday
, flags
);
1792 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
,
1795 // take the current month/year if none specified
1796 if ( year
== Inv_Year
)
1798 if ( month
== Inv_Month
)
1801 return Set(GetNumOfDaysInMonth(year
, month
), month
, year
);
1804 wxDateTime
& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday
, WeekFlags flags
)
1806 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1808 int wdayThis
= GetWeekDay();
1809 if ( weekday
== wdayThis
)
1815 if ( flags
== Default_First
)
1817 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
1820 // the logic below based on comparing weekday and wdayThis works if Sun (0)
1821 // is the first day in the week, but breaks down for Monday_First case so
1822 // we adjust the week days in this case
1823 if( flags
== Monday_First
)
1825 if ( wdayThis
== Sun
)
1828 //else: Sunday_First, nothing to do
1830 // go forward or back in time to the day we want
1831 if ( weekday
< wdayThis
)
1833 return Subtract(wxDateSpan::Days(wdayThis
- weekday
));
1835 else // weekday > wdayThis
1837 return Add(wxDateSpan::Days(weekday
- wdayThis
));
1841 wxDateTime
& wxDateTime::SetToNextWeekDay(WeekDay weekday
)
1843 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1846 WeekDay wdayThis
= GetWeekDay();
1847 if ( weekday
== wdayThis
)
1852 else if ( weekday
< wdayThis
)
1854 // need to advance a week
1855 diff
= 7 - (wdayThis
- weekday
);
1857 else // weekday > wdayThis
1859 diff
= weekday
- wdayThis
;
1862 return Add(wxDateSpan::Days(diff
));
1865 wxDateTime
& wxDateTime::SetToPrevWeekDay(WeekDay weekday
)
1867 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1870 WeekDay wdayThis
= GetWeekDay();
1871 if ( weekday
== wdayThis
)
1876 else if ( weekday
> wdayThis
)
1878 // need to go to previous week
1879 diff
= 7 - (weekday
- wdayThis
);
1881 else // weekday < wdayThis
1883 diff
= wdayThis
- weekday
;
1886 return Subtract(wxDateSpan::Days(diff
));
1889 bool wxDateTime::SetToWeekDay(WeekDay weekday
,
1894 wxCHECK_MSG( weekday
!= Inv_WeekDay
, false, _T("invalid weekday") );
1896 // we don't check explicitly that -5 <= n <= 5 because we will return false
1897 // anyhow in such case - but may be should still give an assert for it?
1899 // take the current month/year if none specified
1900 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1904 // TODO this probably could be optimised somehow...
1908 // get the first day of the month
1909 dt
.Set(1, month
, year
);
1912 WeekDay wdayFirst
= dt
.GetWeekDay();
1914 // go to the first weekday of the month
1915 int diff
= weekday
- wdayFirst
;
1919 // add advance n-1 weeks more
1922 dt
+= wxDateSpan::Days(diff
);
1924 else // count from the end of the month
1926 // get the last day of the month
1927 dt
.SetToLastMonthDay(month
, year
);
1930 WeekDay wdayLast
= dt
.GetWeekDay();
1932 // go to the last weekday of the month
1933 int diff
= wdayLast
- weekday
;
1937 // and rewind n-1 weeks from there
1940 dt
-= wxDateSpan::Days(diff
);
1943 // check that it is still in the same month
1944 if ( dt
.GetMonth() == month
)
1952 // no such day in this month
1958 wxDateTime::wxDateTime_t
GetDayOfYearFromTm(const wxDateTime::Tm
& tm
)
1960 return (wxDateTime::wxDateTime_t
)(gs_cumulatedDays
[wxDateTime::IsLeapYear(tm
.year
)][tm
.mon
] + tm
.mday
);
1963 wxDateTime::wxDateTime_t
wxDateTime::GetDayOfYear(const TimeZone
& tz
) const
1965 return GetDayOfYearFromTm(GetTm(tz
));
1968 wxDateTime::wxDateTime_t
1969 wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags
, const TimeZone
& tz
) const
1971 if ( flags
== Default_First
)
1973 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
1977 wxDateTime_t nDayInYear
= GetDayOfYearFromTm(tm
);
1979 int wdTarget
= GetWeekDay(tz
);
1980 int wdYearStart
= wxDateTime(1, Jan
, GetYear()).GetWeekDay();
1982 if ( flags
== Sunday_First
)
1984 // FIXME: First week is not calculated correctly.
1985 week
= (nDayInYear
- wdTarget
+ 7) / 7;
1986 if ( wdYearStart
== Wed
|| wdYearStart
== Thu
)
1989 else // week starts with monday
1991 // adjust the weekdays to non-US style.
1992 wdYearStart
= ConvertWeekDayToMondayBase(wdYearStart
);
1993 wdTarget
= ConvertWeekDayToMondayBase(wdTarget
);
1995 // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html:
1997 // Week 01 of a year is per definition the first week that has the
1998 // Thursday in this year, which is equivalent to the week that
1999 // contains the fourth day of January. In other words, the first
2000 // week of a new year is the week that has the majority of its
2001 // days in the new year. Week 01 might also contain days from the
2002 // previous year and the week before week 01 of a year is the last
2003 // week (52 or 53) of the previous year even if it contains days
2004 // from the new year. A week starts with Monday (day 1) and ends
2005 // with Sunday (day 7).
2008 // if Jan 1 is Thursday or less, it is in the first week of this year
2009 if ( wdYearStart
< 4 )
2011 // count the number of entire weeks between Jan 1 and this date
2012 week
= (nDayInYear
+ wdYearStart
+ 6 - wdTarget
)/7;
2014 // be careful to check for overflow in the next year
2015 if ( week
== 53 && tm
.mday
- wdTarget
> 28 )
2018 else // Jan 1 is in the last week of the previous year
2020 // check if we happen to be at the last week of previous year:
2021 if ( tm
.mon
== Jan
&& tm
.mday
< 8 - wdYearStart
)
2022 week
= wxDateTime(31, Dec
, GetYear()-1).GetWeekOfYear();
2024 week
= (nDayInYear
+ wdYearStart
- 1 - wdTarget
)/7;
2028 return (wxDateTime::wxDateTime_t
)week
;
2031 wxDateTime::wxDateTime_t
wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags
,
2032 const TimeZone
& tz
) const
2035 wxDateTime dtMonthStart
= wxDateTime(1, tm
.mon
, tm
.year
);
2036 int nWeek
= GetWeekOfYear(flags
) - dtMonthStart
.GetWeekOfYear(flags
) + 1;
2039 // this may happen for January when Jan, 1 is the last week of the
2041 nWeek
+= IsLeapYear(tm
.year
- 1) ? 53 : 52;
2044 return (wxDateTime::wxDateTime_t
)nWeek
;
2047 wxDateTime
& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday
)
2049 int year
= GetYear();
2050 wxDATETIME_CHECK( (0 < yday
) && (yday
<= GetNumberOfDays(year
)),
2051 _T("invalid year day") );
2053 bool isLeap
= IsLeapYear(year
);
2054 for ( Month mon
= Jan
; mon
< Inv_Month
; wxNextMonth(mon
) )
2056 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
2057 // don't need it neither - because of the CHECK above we know that
2058 // yday lies in December then
2059 if ( (mon
== Dec
) || (yday
<= gs_cumulatedDays
[isLeap
][mon
+ 1]) )
2061 Set((wxDateTime::wxDateTime_t
)(yday
- gs_cumulatedDays
[isLeap
][mon
]), mon
, year
);
2070 // ----------------------------------------------------------------------------
2071 // Julian day number conversion and related stuff
2072 // ----------------------------------------------------------------------------
2074 double wxDateTime::GetJulianDayNumber() const
2076 // JDN are always expressed for the UTC dates
2077 Tm
tm(ToTimezone(UTC
).GetTm(UTC
));
2079 double result
= GetTruncatedJDN(tm
.mday
, tm
.mon
, tm
.year
);
2081 // add the part GetTruncatedJDN() neglected
2084 // and now add the time: 86400 sec = 1 JDN
2085 return result
+ ((double)(60*(60*tm
.hour
+ tm
.min
) + tm
.sec
)) / 86400;
2088 double wxDateTime::GetRataDie() const
2090 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
2091 return GetJulianDayNumber() - 1721119.5 - 306;
2094 // ----------------------------------------------------------------------------
2095 // timezone and DST stuff
2096 // ----------------------------------------------------------------------------
2098 int wxDateTime::IsDST(wxDateTime::Country country
) const
2100 wxCHECK_MSG( country
== Country_Default
, -1,
2101 _T("country support not implemented") );
2103 // use the C RTL for the dates in the standard range
2104 time_t timet
= GetTicks();
2105 if ( timet
!= (time_t)-1 )
2107 tm
*tm
= localtime(&timet
);
2109 wxCHECK_MSG( tm
, -1, _T("localtime() failed") );
2111 return tm
->tm_isdst
;
2115 int year
= GetYear();
2117 if ( !IsDSTApplicable(year
, country
) )
2119 // no DST time in this year in this country
2123 return IsBetween(GetBeginDST(year
, country
), GetEndDST(year
, country
));
2127 wxDateTime
& wxDateTime::MakeTimezone(const TimeZone
& tz
, bool noDST
)
2129 long secDiff
= GetTimeZone() + tz
.GetOffset();
2131 // we need to know whether DST is or not in effect for this date unless
2132 // the test disabled by the caller
2133 if ( !noDST
&& (IsDST() == 1) )
2135 // FIXME we assume that the DST is always shifted by 1 hour
2139 return Subtract(wxTimeSpan::Seconds(secDiff
));
2142 // ----------------------------------------------------------------------------
2143 // wxDateTime to/from text representations
2144 // ----------------------------------------------------------------------------
2146 wxString
wxDateTime::Format(const wxChar
*format
, const TimeZone
& tz
) const
2148 wxCHECK_MSG( format
, wxEmptyString
, _T("NULL format in wxDateTime::Format") );
2150 // we have to use our own implementation if the date is out of range of
2151 // strftime() or if we use non standard specificators
2152 time_t time
= GetTicks();
2153 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
2157 if ( tz
.GetOffset() == -GetTimeZone() )
2159 // we are working with local time
2160 tm
= localtime(&time
);
2162 // should never happen
2163 wxCHECK_MSG( tm
, wxEmptyString
, _T("localtime() failed") );
2167 time
+= (int)tz
.GetOffset();
2169 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
2170 int time2
= (int) time
;
2178 // should never happen
2179 wxCHECK_MSG( tm
, wxEmptyString
, _T("gmtime() failed") );
2183 tm
= (struct tm
*)NULL
;
2187 //Windows CE doesn't support strftime or wcsftime, so we use the generic implementation
2190 return CallStrftime(format
, tm
);
2193 //else: use generic code below
2196 // we only parse ANSI C format specifications here, no POSIX 2
2197 // complications, no GNU extensions but we do add support for a "%l" format
2198 // specifier allowing to get the number of milliseconds
2201 // used for calls to strftime() when we only deal with time
2202 struct tm tmTimeOnly
;
2203 tmTimeOnly
.tm_hour
= tm
.hour
;
2204 tmTimeOnly
.tm_min
= tm
.min
;
2205 tmTimeOnly
.tm_sec
= tm
.sec
;
2206 tmTimeOnly
.tm_wday
= 0;
2207 tmTimeOnly
.tm_yday
= 0;
2208 tmTimeOnly
.tm_mday
= 1; // any date will do
2209 tmTimeOnly
.tm_mon
= 0;
2210 tmTimeOnly
.tm_year
= 76;
2211 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
2213 wxString tmp
, res
, fmt
;
2214 for ( const wxChar
*p
= format
; *p
; p
++ )
2216 if ( *p
!= _T('%') )
2224 // set the default format
2227 case _T('Y'): // year has 4 digits
2231 case _T('j'): // day of year has 3 digits
2232 case _T('l'): // milliseconds have 3 digits
2236 case _T('w'): // week day as number has only one
2241 // it's either another valid format specifier in which case
2242 // the format is "%02d" (for all the rest) or we have the
2243 // field width preceding the format in which case it will
2244 // override the default format anyhow
2248 bool restart
= true;
2253 // start of the format specification
2256 case _T('a'): // a weekday name
2258 // second parameter should be true for abbreviated names
2259 res
+= GetWeekDayName(tm
.GetWeekDay(),
2260 *p
== _T('a') ? Name_Abbr
: Name_Full
);
2263 case _T('b'): // a month name
2265 res
+= GetMonthName(tm
.mon
,
2266 *p
== _T('b') ? Name_Abbr
: Name_Full
);
2269 case _T('c'): // locale default date and time representation
2270 case _T('x'): // locale default date representation
2273 // the problem: there is no way to know what do these format
2274 // specifications correspond to for the current locale.
2276 // the solution: use a hack and still use strftime(): first
2277 // find the YEAR which is a year in the strftime() range (1970
2278 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
2279 // of the real year. Then make a copy of the format and
2280 // replace all occurences of YEAR in it with some unique
2281 // string not appearing anywhere else in it, then use
2282 // strftime() to format the date in year YEAR and then replace
2283 // YEAR back by the real year and the unique replacement
2284 // string back with YEAR. Notice that "all occurences of YEAR"
2285 // means all occurences of 4 digit as well as 2 digit form!
2287 // the bugs: we assume that neither of %c nor %x contains any
2288 // fields which may change between the YEAR and real year. For
2289 // example, the week number (%U, %W) and the day number (%j)
2290 // will change if one of these years is leap and the other one
2293 // find the YEAR: normally, for any year X, Jan 1 or the
2294 // year X + 28 is the same weekday as Jan 1 of X (because
2295 // the weekday advances by 1 for each normal X and by 2
2296 // for each leap X, hence by 5 every 4 years or by 35
2297 // which is 0 mod 7 every 28 years) but this rule breaks
2298 // down if there are years between X and Y which are
2299 // divisible by 4 but not leap (i.e. divisible by 100 but
2300 // not 400), hence the correction.
2302 int yearReal
= GetYear(tz
);
2303 int mod28
= yearReal
% 28;
2305 // be careful to not go too far - we risk to leave the
2310 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
2314 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
2317 int nCentury
= year
/ 100,
2318 nCenturyReal
= yearReal
/ 100;
2320 // need to adjust for the years divisble by 400 which are
2321 // not leap but are counted like leap ones if we just take
2322 // the number of centuries in between for nLostWeekDays
2323 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
2324 (nCentury
/ 4 - nCenturyReal
/ 4);
2326 // we have to gain back the "lost" weekdays: note that the
2327 // effect of this loop is to not do anything to
2328 // nLostWeekDays (which we won't use any more), but to
2329 // (indirectly) set the year correctly
2330 while ( (nLostWeekDays
% 7) != 0 )
2332 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
2335 // at any rate, we couldn't go further than 1988 + 9 + 28!
2336 wxASSERT_MSG( year
< 2030,
2337 _T("logic error in wxDateTime::Format") );
2339 wxString strYear
, strYear2
;
2340 strYear
.Printf(_T("%d"), year
);
2341 strYear2
.Printf(_T("%d"), year
% 100);
2343 // find two strings not occuring in format (this is surely
2344 // not optimal way of doing it... improvements welcome!)
2345 wxString fmt
= format
;
2346 wxString replacement
= (wxChar
)-1;
2347 while ( fmt
.Find(replacement
) != wxNOT_FOUND
)
2349 replacement
<< (wxChar
)-1;
2352 wxString replacement2
= (wxChar
)-2;
2353 while ( fmt
.Find(replacement
) != wxNOT_FOUND
)
2355 replacement
<< (wxChar
)-2;
2358 // replace all occurences of year with it
2359 bool wasReplaced
= fmt
.Replace(strYear
, replacement
) > 0;
2361 wasReplaced
= fmt
.Replace(strYear2
, replacement2
) > 0;
2363 // use strftime() to format the same date but in supported
2366 // NB: we assume that strftime() doesn't check for the
2367 // date validity and will happily format the date
2368 // corresponding to Feb 29 of a non leap year (which
2369 // may happen if yearReal was leap and year is not)
2370 struct tm tmAdjusted
;
2372 tmAdjusted
.tm_hour
= tm
.hour
;
2373 tmAdjusted
.tm_min
= tm
.min
;
2374 tmAdjusted
.tm_sec
= tm
.sec
;
2375 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
2376 tmAdjusted
.tm_yday
= GetDayOfYear();
2377 tmAdjusted
.tm_mday
= tm
.mday
;
2378 tmAdjusted
.tm_mon
= tm
.mon
;
2379 tmAdjusted
.tm_year
= year
- 1900;
2380 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
2381 wxString str
= CallStrftime(*p
== _T('c') ? _T("%c")
2385 // now replace the occurence of 1999 with the real year
2386 wxString strYearReal
, strYearReal2
;
2387 strYearReal
.Printf(_T("%04d"), yearReal
);
2388 strYearReal2
.Printf(_T("%02d"), yearReal
% 100);
2389 str
.Replace(strYear
, strYearReal
);
2390 str
.Replace(strYear2
, strYearReal2
);
2392 // and replace back all occurences of replacement string
2395 str
.Replace(replacement2
, strYear2
);
2396 str
.Replace(replacement
, strYear
);
2402 //Use "%m/%d/%y %H:%M:%S" format instead
2403 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
2404 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
2408 case _T('d'): // day of a month (01-31)
2409 res
+= wxString::Format(fmt
, tm
.mday
);
2412 case _T('H'): // hour in 24h format (00-23)
2413 res
+= wxString::Format(fmt
, tm
.hour
);
2416 case _T('I'): // hour in 12h format (01-12)
2418 // 24h -> 12h, 0h -> 12h too
2419 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
2420 : tm
.hour
? tm
.hour
: 12;
2421 res
+= wxString::Format(fmt
, hour12
);
2425 case _T('j'): // day of the year
2426 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
2429 case _T('l'): // milliseconds (NOT STANDARD)
2430 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
2433 case _T('m'): // month as a number (01-12)
2434 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
2437 case _T('M'): // minute as a decimal number (00-59)
2438 res
+= wxString::Format(fmt
, tm
.min
);
2441 case _T('p'): // AM or PM string
2443 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
2445 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
2449 case _T('S'): // second as a decimal number (00-61)
2450 res
+= wxString::Format(fmt
, tm
.sec
);
2453 case _T('U'): // week number in the year (Sunday 1st week day)
2454 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
2457 case _T('W'): // week number in the year (Monday 1st week day)
2458 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
2461 case _T('w'): // weekday as a number (0-6), Sunday = 0
2462 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
2465 // case _T('x'): -- handled with "%c"
2467 case _T('X'): // locale default time representation
2468 // just use strftime() to format the time for us
2470 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
2472 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
2476 case _T('y'): // year without century (00-99)
2477 res
+= wxString::Format(fmt
, tm
.year
% 100);
2480 case _T('Y'): // year with century
2481 res
+= wxString::Format(fmt
, tm
.year
);
2484 case _T('Z'): // timezone name
2486 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
2491 // is it the format width?
2493 while ( *p
== _T('-') || *p
== _T('+') ||
2494 *p
== _T(' ') || wxIsdigit(*p
) )
2501 // we've only got the flags and width so far in fmt
2502 fmt
.Prepend(_T('%'));
2503 fmt
.Append(_T('d'));
2510 // no, it wasn't the width
2511 wxFAIL_MSG(_T("unknown format specificator"));
2513 // fall through and just copy it nevertheless
2515 case _T('%'): // a percent sign
2519 case 0: // the end of string
2520 wxFAIL_MSG(_T("missing format at the end of string"));
2522 // just put the '%' which was the last char in format
2532 // this function parses a string in (strict) RFC 822 format: see the section 5
2533 // of the RFC for the detailed description, but briefly it's something of the
2534 // form "Sat, 18 Dec 1999 00:48:30 +0100"
2536 // this function is "strict" by design - it must reject anything except true
2537 // RFC822 time specs.
2539 // TODO a great candidate for using reg exps
2540 const wxChar
*wxDateTime::ParseRfc822Date(const wxChar
* date
)
2542 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
2544 const wxChar
*p
= date
;
2545 const wxChar
*comma
= wxStrchr(p
, _T(','));
2548 // the part before comma is the weekday
2550 // skip it for now - we don't use but might check that it really
2551 // corresponds to the specfied date
2554 if ( *p
!= _T(' ') )
2556 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
2558 return (wxChar
*)NULL
;
2564 // the following 1 or 2 digits are the day number
2565 if ( !wxIsdigit(*p
) )
2567 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
2569 return (wxChar
*)NULL
;
2572 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - _T('0'));
2573 if ( wxIsdigit(*p
) )
2576 day
= (wxDateTime_t
)(day
+ (*p
++ - _T('0')));
2579 if ( *p
++ != _T(' ') )
2581 return (wxChar
*)NULL
;
2584 // the following 3 letters specify the month
2585 wxString
monName(p
, 3);
2587 if ( monName
== _T("Jan") )
2589 else if ( monName
== _T("Feb") )
2591 else if ( monName
== _T("Mar") )
2593 else if ( monName
== _T("Apr") )
2595 else if ( monName
== _T("May") )
2597 else if ( monName
== _T("Jun") )
2599 else if ( monName
== _T("Jul") )
2601 else if ( monName
== _T("Aug") )
2603 else if ( monName
== _T("Sep") )
2605 else if ( monName
== _T("Oct") )
2607 else if ( monName
== _T("Nov") )
2609 else if ( monName
== _T("Dec") )
2613 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str());
2615 return (wxChar
*)NULL
;
2620 if ( *p
++ != _T(' ') )
2622 return (wxChar
*)NULL
;
2626 if ( !wxIsdigit(*p
) )
2629 return (wxChar
*)NULL
;
2632 int year
= *p
++ - _T('0');
2634 if ( !wxIsdigit(*p
) )
2636 // should have at least 2 digits in the year
2637 return (wxChar
*)NULL
;
2641 year
+= *p
++ - _T('0');
2643 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
2644 if ( wxIsdigit(*p
) )
2647 year
+= *p
++ - _T('0');
2649 if ( !wxIsdigit(*p
) )
2651 // no 3 digit years please
2652 return (wxChar
*)NULL
;
2656 year
+= *p
++ - _T('0');
2659 if ( *p
++ != _T(' ') )
2661 return (wxChar
*)NULL
;
2664 // time is in the format hh:mm:ss and seconds are optional
2665 if ( !wxIsdigit(*p
) )
2667 return (wxChar
*)NULL
;
2670 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - _T('0'));
2672 if ( !wxIsdigit(*p
) )
2674 return (wxChar
*)NULL
;
2678 hour
= (wxDateTime_t
)(hour
+ (*p
++ - _T('0')));
2680 if ( *p
++ != _T(':') )
2682 return (wxChar
*)NULL
;
2685 if ( !wxIsdigit(*p
) )
2687 return (wxChar
*)NULL
;
2690 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - _T('0'));
2692 if ( !wxIsdigit(*p
) )
2694 return (wxChar
*)NULL
;
2698 min
= (wxDateTime_t
)(min
+ *p
++ - _T('0'));
2700 wxDateTime_t sec
= 0;
2701 if ( *p
++ == _T(':') )
2703 if ( !wxIsdigit(*p
) )
2705 return (wxChar
*)NULL
;
2708 sec
= (wxDateTime_t
)(*p
++ - _T('0'));
2710 if ( !wxIsdigit(*p
) )
2712 return (wxChar
*)NULL
;
2716 sec
= (wxDateTime_t
)(sec
+ *p
++ - _T('0'));
2719 if ( *p
++ != _T(' ') )
2721 return (wxChar
*)NULL
;
2724 // and now the interesting part: the timezone
2726 if ( *p
== _T('-') || *p
== _T('+') )
2728 // the explicit offset given: it has the form of hhmm
2729 bool plus
= *p
++ == _T('+');
2731 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2733 return (wxChar
*)NULL
;
2737 offset
= 60*(10*(*p
- _T('0')) + (*(p
+ 1) - _T('0')));
2741 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2743 return (wxChar
*)NULL
;
2747 offset
+= 10*(*p
- _T('0')) + (*(p
+ 1) - _T('0'));
2758 // the symbolic timezone given: may be either military timezone or one
2759 // of standard abbreviations
2762 // military: Z = UTC, J unused, A = -1, ..., Y = +12
2763 static const int offsets
[26] =
2765 //A B C D E F G H I J K L M
2766 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
2767 //N O P R Q S T U V W Z Y Z
2768 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
2771 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
2773 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
);
2775 return (wxChar
*)NULL
;
2778 offset
= offsets
[*p
++ - _T('A')];
2784 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
2786 else if ( tz
== _T("AST") )
2787 offset
= AST
- GMT0
;
2788 else if ( tz
== _T("ADT") )
2789 offset
= ADT
- GMT0
;
2790 else if ( tz
== _T("EST") )
2791 offset
= EST
- GMT0
;
2792 else if ( tz
== _T("EDT") )
2793 offset
= EDT
- GMT0
;
2794 else if ( tz
== _T("CST") )
2795 offset
= CST
- GMT0
;
2796 else if ( tz
== _T("CDT") )
2797 offset
= CDT
- GMT0
;
2798 else if ( tz
== _T("MST") )
2799 offset
= MST
- GMT0
;
2800 else if ( tz
== _T("MDT") )
2801 offset
= MDT
- GMT0
;
2802 else if ( tz
== _T("PST") )
2803 offset
= PST
- GMT0
;
2804 else if ( tz
== _T("PDT") )
2805 offset
= PDT
- GMT0
;
2808 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
);
2810 return (wxChar
*)NULL
;
2820 // the spec was correct
2821 Set(day
, mon
, year
, hour
, min
, sec
);
2822 MakeTimezone((wxDateTime_t
)(60*offset
));
2827 const wxChar
*wxDateTime::ParseFormat(const wxChar
*date
,
2828 const wxChar
*format
,
2829 const wxDateTime
& dateDef
)
2831 wxCHECK_MSG( date
&& format
, (wxChar
*)NULL
,
2832 _T("NULL pointer in wxDateTime::ParseFormat()") );
2837 // what fields have we found?
2838 bool haveWDay
= false,
2847 bool hourIsIn12hFormat
= false, // or in 24h one?
2848 isPM
= false; // AM by default
2850 // and the value of the items we have (init them to get rid of warnings)
2851 wxDateTime_t sec
= 0,
2854 WeekDay wday
= Inv_WeekDay
;
2855 wxDateTime_t yday
= 0,
2857 wxDateTime::Month mon
= Inv_Month
;
2860 const wxChar
*input
= date
;
2861 for ( const wxChar
*fmt
= format
; *fmt
; fmt
++ )
2863 if ( *fmt
!= _T('%') )
2865 if ( wxIsspace(*fmt
) )
2867 // a white space in the format string matches 0 or more white
2868 // spaces in the input
2869 while ( wxIsspace(*input
) )
2876 // any other character (not whitespace, not '%') must be
2877 // matched by itself in the input
2878 if ( *input
++ != *fmt
)
2881 return (wxChar
*)NULL
;
2885 // done with this format char
2889 // start of a format specification
2891 // parse the optional width
2893 while ( wxIsdigit(*++fmt
) )
2896 width
+= *fmt
- _T('0');
2899 // the default widths for the various fields
2904 case _T('Y'): // year has 4 digits
2908 case _T('j'): // day of year has 3 digits
2909 case _T('l'): // milliseconds have 3 digits
2913 case _T('w'): // week day as number has only one
2918 // default for all other fields
2923 // then the format itself
2926 case _T('a'): // a weekday name
2929 int flag
= *fmt
== _T('a') ? Name_Abbr
: Name_Full
;
2930 wday
= GetWeekDayFromName(GetAlphaToken(input
), flag
);
2931 if ( wday
== Inv_WeekDay
)
2934 return (wxChar
*)NULL
;
2940 case _T('b'): // a month name
2943 int flag
= *fmt
== _T('b') ? Name_Abbr
: Name_Full
;
2944 mon
= GetMonthFromName(GetAlphaToken(input
), flag
);
2945 if ( mon
== Inv_Month
)
2948 return (wxChar
*)NULL
;
2954 case _T('c'): // locale default date and time representation
2958 // this is the format which corresponds to ctime() output
2959 // and strptime("%c") should parse it, so try it first
2960 static const wxChar
*fmtCtime
= _T("%a %b %d %H:%M:%S %Y");
2962 const wxChar
*result
= dt
.ParseFormat(input
, fmtCtime
);
2965 result
= dt
.ParseFormat(input
, _T("%x %X"));
2970 result
= dt
.ParseFormat(input
, _T("%X %x"));
2975 // we've tried everything and still no match
2976 return (wxChar
*)NULL
;
2981 haveDay
= haveMon
= haveYear
=
2982 haveHour
= haveMin
= haveSec
= true;
2996 case _T('d'): // day of a month (01-31)
2997 if ( !GetNumericToken(width
, input
, &num
) ||
2998 (num
> 31) || (num
< 1) )
3001 return (wxChar
*)NULL
;
3004 // we can't check whether the day range is correct yet, will
3005 // do it later - assume ok for now
3007 mday
= (wxDateTime_t
)num
;
3010 case _T('H'): // hour in 24h format (00-23)
3011 if ( !GetNumericToken(width
, input
, &num
) || (num
> 23) )
3014 return (wxChar
*)NULL
;
3018 hour
= (wxDateTime_t
)num
;
3021 case _T('I'): // hour in 12h format (01-12)
3022 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3025 return (wxChar
*)NULL
;
3029 hourIsIn12hFormat
= true;
3030 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
3033 case _T('j'): // day of the year
3034 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 366) )
3037 return (wxChar
*)NULL
;
3041 yday
= (wxDateTime_t
)num
;
3044 case _T('m'): // month as a number (01-12)
3045 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3048 return (wxChar
*)NULL
;
3052 mon
= (Month
)(num
- 1);
3055 case _T('M'): // minute as a decimal number (00-59)
3056 if ( !GetNumericToken(width
, input
, &num
) || (num
> 59) )
3059 return (wxChar
*)NULL
;
3063 min
= (wxDateTime_t
)num
;
3066 case _T('p'): // AM or PM string
3068 wxString am
, pm
, token
= GetAlphaToken(input
);
3070 GetAmPmStrings(&am
, &pm
);
3071 if (am
.empty() && pm
.empty())
3072 return (wxChar
*)NULL
; // no am/pm strings defined
3073 if ( token
.CmpNoCase(pm
) == 0 )
3077 else if ( token
.CmpNoCase(am
) != 0 )
3080 return (wxChar
*)NULL
;
3085 case _T('r'): // time as %I:%M:%S %p
3088 input
= dt
.ParseFormat(input
, _T("%I:%M:%S %p"));
3092 return (wxChar
*)NULL
;
3095 haveHour
= haveMin
= haveSec
= true;
3104 case _T('R'): // time as %H:%M
3107 input
= dt
.ParseFormat(input
, _T("%H:%M"));
3111 return (wxChar
*)NULL
;
3114 haveHour
= haveMin
= true;
3121 case _T('S'): // second as a decimal number (00-61)
3122 if ( !GetNumericToken(width
, input
, &num
) || (num
> 61) )
3125 return (wxChar
*)NULL
;
3129 sec
= (wxDateTime_t
)num
;
3132 case _T('T'): // time as %H:%M:%S
3135 input
= dt
.ParseFormat(input
, _T("%H:%M:%S"));
3139 return (wxChar
*)NULL
;
3142 haveHour
= haveMin
= haveSec
= true;
3151 case _T('w'): // weekday as a number (0-6), Sunday = 0
3152 if ( !GetNumericToken(width
, input
, &num
) || (wday
> 6) )
3155 return (wxChar
*)NULL
;
3159 wday
= (WeekDay
)num
;
3162 case _T('x'): // locale default date representation
3163 #ifdef HAVE_STRPTIME
3164 // try using strptime() -- it may fail even if the input is
3165 // correct but the date is out of range, so we will fall back
3166 // to our generic code anyhow
3170 const wxChar
*result
= CallStrptime(input
, "%x", &tm
);
3175 haveDay
= haveMon
= haveYear
= true;
3177 year
= 1900 + tm
.tm_year
;
3178 mon
= (Month
)tm
.tm_mon
;
3184 #endif // HAVE_STRPTIME
3186 // TODO query the LOCALE_IDATE setting under Win32
3190 wxString fmtDate
, fmtDateAlt
;
3191 if ( IsWestEuropeanCountry(GetCountry()) ||
3192 GetCountry() == Russia
)
3194 fmtDate
= _T("%d/%m/%y");
3195 fmtDateAlt
= _T("%m/%d/%y");
3199 fmtDate
= _T("%m/%d/%y");
3200 fmtDateAlt
= _T("%d/%m/%y");
3203 const wxChar
*result
= dt
.ParseFormat(input
, fmtDate
);
3207 // ok, be nice and try another one
3208 result
= dt
.ParseFormat(input
, fmtDateAlt
);
3214 return (wxChar
*)NULL
;
3219 haveDay
= haveMon
= haveYear
= true;
3230 case _T('X'): // locale default time representation
3231 #ifdef HAVE_STRPTIME
3233 // use strptime() to do it for us (FIXME !Unicode friendly)
3235 input
= CallStrptime(input
, "%X", &tm
);
3238 return (wxChar
*)NULL
;
3241 haveHour
= haveMin
= haveSec
= true;
3247 #else // !HAVE_STRPTIME
3248 // TODO under Win32 we can query the LOCALE_ITIME system
3249 // setting which says whether the default time format is
3252 // try to parse what follows as "%H:%M:%S" and, if this
3253 // fails, as "%I:%M:%S %p" - this should catch the most
3257 const wxChar
*result
= dt
.ParseFormat(input
, _T("%T"));
3260 result
= dt
.ParseFormat(input
, _T("%r"));
3266 return (wxChar
*)NULL
;
3269 haveHour
= haveMin
= haveSec
= true;
3278 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
3281 case _T('y'): // year without century (00-99)
3282 if ( !GetNumericToken(width
, input
, &num
) || (num
> 99) )
3285 return (wxChar
*)NULL
;
3290 // TODO should have an option for roll over date instead of
3291 // hard coding it here
3292 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
3295 case _T('Y'): // year with century
3296 if ( !GetNumericToken(width
, input
, &num
) )
3299 return (wxChar
*)NULL
;
3303 year
= (wxDateTime_t
)num
;
3306 case _T('Z'): // timezone name
3307 wxFAIL_MSG(_T("TODO"));
3310 case _T('%'): // a percent sign
3311 if ( *input
++ != _T('%') )
3314 return (wxChar
*)NULL
;
3318 case 0: // the end of string
3319 wxFAIL_MSG(_T("unexpected format end"));
3323 default: // not a known format spec
3324 return (wxChar
*)NULL
;
3328 // format matched, try to construct a date from what we have now
3330 if ( dateDef
.IsValid() )
3332 // take this date as default
3333 tmDef
= dateDef
.GetTm();
3335 else if ( IsValid() )
3337 // if this date is valid, don't change it
3342 // no default and this date is invalid - fall back to Today()
3343 tmDef
= Today().GetTm();
3354 // TODO we don't check here that the values are consistent, if both year
3355 // day and month/day were found, we just ignore the year day and we
3356 // also always ignore the week day
3357 if ( haveMon
&& haveDay
)
3359 if ( mday
> GetNumOfDaysInMonth(tm
.year
, mon
) )
3361 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
3363 return (wxChar
*)NULL
;
3369 else if ( haveYDay
)
3371 if ( yday
> GetNumberOfDays(tm
.year
) )
3373 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
3375 return (wxChar
*)NULL
;
3378 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
3385 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
3387 // translate to 24hour format
3390 //else: either already in 24h format or no translation needed
3410 // finally check that the week day is consistent -- if we had it
3411 if ( haveWDay
&& GetWeekDay() != wday
)
3413 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
3421 const wxChar
*wxDateTime::ParseDateTime(const wxChar
*date
)
3423 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3425 // Set to current day and hour, so strings like '14:00' becomes today at
3426 // 14, not some other random date
3427 wxDateTime dtDate
= wxDateTime::Today();
3428 wxDateTime dtTime
= wxDateTime::Today();
3430 const wxChar
* pchTime
;
3432 // Try to parse the beginning of the string as a date
3433 const wxChar
* pchDate
= dtDate
.ParseDate(date
);
3435 // We got a date in the beginning, see if there is a time specified after the date
3438 // Skip spaces, as the ParseTime() function fails on spaces
3439 while ( wxIsspace(*pchDate
) )
3442 pchTime
= dtTime
.ParseTime(pchDate
);
3444 else // no date in the beginning
3446 // check and see if we have a time followed by a date
3447 pchTime
= dtTime
.ParseTime(date
);
3450 while ( wxIsspace(*pchTime
) )
3453 pchDate
= dtDate
.ParseDate(pchTime
);
3457 // If we have a date specified, set our own data to the same date
3458 if ( !pchDate
|| !pchTime
)
3461 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
3462 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
3463 dtTime
.GetMillisecond());
3465 // Return endpoint of scan
3466 return pchDate
> pchTime
? pchDate
: pchTime
;
3469 const wxChar
*wxDateTime::ParseDate(const wxChar
*date
)
3471 // this is a simplified version of ParseDateTime() which understands only
3472 // "today" (for wxDate compatibility) and digits only otherwise (and not
3473 // all esoteric constructions ParseDateTime() knows about)
3475 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3477 const wxChar
*p
= date
;
3478 while ( wxIsspace(*p
) )
3481 // some special cases
3485 int dayDiffFromToday
;
3488 { wxTRANSLATE("today"), 0 },
3489 { wxTRANSLATE("yesterday"), -1 },
3490 { wxTRANSLATE("tomorrow"), 1 },
3493 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
3495 wxString date
= wxGetTranslation(literalDates
[n
].str
);
3496 size_t len
= date
.length();
3497 if ( wxStrlen(p
) >= len
)
3499 wxString
str(p
, len
);
3500 if ( str
.CmpNoCase(date
) == 0 )
3502 // nothing can follow this, so stop here
3505 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
3507 if ( dayDiffFromToday
)
3509 *this += wxDateSpan::Days(dayDiffFromToday
);
3517 // We try to guess what we have here: for each new (numeric) token, we
3518 // determine if it can be a month, day or a year. Of course, there is an
3519 // ambiguity as some numbers may be days as well as months, so we also
3520 // have the ability to back track.
3523 bool haveDay
= false, // the months day?
3524 haveWDay
= false, // the day of week?
3525 haveMon
= false, // the month?
3526 haveYear
= false; // the year?
3528 // and the value of the items we have (init them to get rid of warnings)
3529 WeekDay wday
= Inv_WeekDay
;
3530 wxDateTime_t day
= 0;
3531 wxDateTime::Month mon
= Inv_Month
;
3534 // tokenize the string
3536 static const wxChar
*dateDelimiters
= _T(".,/-\t\r\n ");
3537 wxStringTokenizer
tok(p
, dateDelimiters
);
3538 while ( tok
.HasMoreTokens() )
3540 wxString token
= tok
.GetNextToken();
3546 if ( token
.ToULong(&val
) )
3548 // guess what this number is
3554 if ( !haveMon
&& val
> 0 && val
<= 12 )
3556 // assume it is month
3559 else // not the month
3563 // this can only be the year
3566 else // may be either day or year
3568 wxDateTime_t max_days
= (wxDateTime_t
)(
3570 ? GetNumOfDaysInMonth(haveYear
? year
: Inv_Year
, mon
)
3575 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
3580 else // yes, suppose it's the day
3594 year
= (wxDateTime_t
)val
;
3603 day
= (wxDateTime_t
)val
;
3609 mon
= (Month
)(val
- 1);
3612 else // not a number
3614 // be careful not to overwrite the current mon value
3615 Month mon2
= GetMonthFromName(token
, Name_Full
| Name_Abbr
);
3616 if ( mon2
!= Inv_Month
)
3621 // but we already have a month - maybe we guessed wrong?
3624 // no need to check in month range as always < 12, but
3625 // the days are counted from 1 unlike the months
3626 day
= (wxDateTime_t
)(mon
+ 1);
3631 // could possible be the year (doesn't the year come
3632 // before the month in the japanese format?) (FIXME)
3641 else // not a valid month name
3643 wday
= GetWeekDayFromName(token
, Name_Full
| Name_Abbr
);
3644 if ( wday
!= Inv_WeekDay
)
3654 else // not a valid weekday name
3657 static const wxChar
*ordinals
[] =
3659 wxTRANSLATE("first"),
3660 wxTRANSLATE("second"),
3661 wxTRANSLATE("third"),
3662 wxTRANSLATE("fourth"),
3663 wxTRANSLATE("fifth"),
3664 wxTRANSLATE("sixth"),
3665 wxTRANSLATE("seventh"),
3666 wxTRANSLATE("eighth"),
3667 wxTRANSLATE("ninth"),
3668 wxTRANSLATE("tenth"),
3669 wxTRANSLATE("eleventh"),
3670 wxTRANSLATE("twelfth"),
3671 wxTRANSLATE("thirteenth"),
3672 wxTRANSLATE("fourteenth"),
3673 wxTRANSLATE("fifteenth"),
3674 wxTRANSLATE("sixteenth"),
3675 wxTRANSLATE("seventeenth"),
3676 wxTRANSLATE("eighteenth"),
3677 wxTRANSLATE("nineteenth"),
3678 wxTRANSLATE("twentieth"),
3679 // that's enough - otherwise we'd have problems with
3680 // composite (or not) ordinals
3684 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
3686 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
3692 if ( n
== WXSIZEOF(ordinals
) )
3694 // stop here - something unknown
3701 // don't try anything here (as in case of numeric day
3702 // above) - the symbolic day spec should always
3703 // precede the month/year
3709 day
= (wxDateTime_t
)(n
+ 1);
3714 nPosCur
= tok
.GetPosition();
3717 // either no more tokens or the scan was stopped by something we couldn't
3718 // parse - in any case, see if we can construct a date from what we have
3719 if ( !haveDay
&& !haveWDay
)
3721 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
3723 return (wxChar
*)NULL
;
3726 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
3727 !(haveDay
&& haveMon
&& haveYear
) )
3729 // without adjectives (which we don't support here) the week day only
3730 // makes sense completely separately or with the full date
3731 // specification (what would "Wed 1999" mean?)
3732 return (wxChar
*)NULL
;
3735 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
3737 // may be we have month and day instead of day and year?
3738 if ( haveDay
&& !haveMon
)
3742 // exchange day and month
3743 mon
= (wxDateTime::Month
)(day
- 1);
3745 // we're in the current year then
3746 if ( (year
> 0) && (year
<= (int)GetNumOfDaysInMonth(Inv_Year
, mon
)) )
3748 day
= (wxDateTime_t
)year
;
3753 //else: no, can't exchange, leave haveMon == false
3759 // if we give the year, month and day must be given too
3760 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
3762 return (wxChar
*)NULL
;
3768 mon
= GetCurrentMonth();
3773 year
= GetCurrentYear();
3778 Set(day
, mon
, year
);
3782 // check that it is really the same
3783 if ( GetWeekDay() != wday
)
3785 // inconsistency detected
3786 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
3788 return (wxChar
*)NULL
;
3796 SetToWeekDayInSameWeek(wday
);
3799 // return the pointer to the first unparsed char
3801 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
3803 // if we couldn't parse the token after the delimiter, put back the
3804 // delimiter as well
3811 const wxChar
*wxDateTime::ParseTime(const wxChar
*time
)
3813 wxCHECK_MSG( time
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3815 // first try some extra things
3822 { wxTRANSLATE("noon"), 12 },
3823 { wxTRANSLATE("midnight"), 00 },
3827 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
3829 wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
3830 size_t len
= timeString
.length();
3831 if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 )
3833 // casts required by DigitalMars
3834 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
3840 // try all time formats we may think about in the order from longest to
3843 // 12hour with AM/PM?
3844 const wxChar
*result
= ParseFormat(time
, _T("%I:%M:%S %p"));
3848 // normally, it's the same, but why not try it?
3849 result
= ParseFormat(time
, _T("%H:%M:%S"));
3854 // 12hour with AM/PM but without seconds?
3855 result
= ParseFormat(time
, _T("%I:%M %p"));
3861 result
= ParseFormat(time
, _T("%H:%M"));
3866 // just the hour and AM/PM?
3867 result
= ParseFormat(time
, _T("%I %p"));
3873 result
= ParseFormat(time
, _T("%H"));
3878 // parse the standard format: normally it is one of the formats above
3879 // but it may be set to something completely different by the user
3880 result
= ParseFormat(time
, _T("%X"));
3883 // TODO: parse timezones
3888 // ----------------------------------------------------------------------------
3889 // Workdays and holidays support
3890 // ----------------------------------------------------------------------------
3892 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
3894 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
3897 // ============================================================================
3899 // ============================================================================
3901 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
3904 return ds1
.Multiply(n
);
3907 // ============================================================================
3909 // ============================================================================
3911 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
3913 return wxTimeSpan(ts
).Multiply(n
);
3916 // this enum is only used in wxTimeSpan::Format() below but we can't declare
3917 // it locally to the method as it provokes an internal compiler error in egcs
3918 // 2.91.60 when building with -O2
3929 // not all strftime(3) format specifiers make sense here because, for example,
3930 // a time span doesn't have a year nor a timezone
3932 // Here are the ones which are supported (all of them are supported by strftime
3934 // %H hour in 24 hour format
3935 // %M minute (00 - 59)
3936 // %S second (00 - 59)
3939 // Also, for MFC CTimeSpan compatibility, we support
3940 // %D number of days
3942 // And, to be better than MFC :-), we also have
3943 // %E number of wEeks
3944 // %l milliseconds (000 - 999)
3945 wxString
wxTimeSpan::Format(const wxChar
*format
) const
3947 wxCHECK_MSG( format
, wxEmptyString
, _T("NULL format in wxTimeSpan::Format") );
3950 str
.Alloc(wxStrlen(format
));
3952 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
3954 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
3955 // question is what should ts.Format("%S") do? The code here returns "3273"
3956 // in this case (i.e. the total number of seconds, not just seconds % 60)
3957 // because, for me, this call means "give me entire time interval in
3958 // seconds" and not "give me the seconds part of the time interval"
3960 // If we agree that it should behave like this, it is clear that the
3961 // interpretation of each format specifier depends on the presence of the
3962 // other format specs in the string: if there was "%H" before "%M", we
3963 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
3965 // we remember the most important unit found so far
3966 TimeSpanPart partBiggest
= Part_MSec
;
3968 for ( const wxChar
*pch
= format
; *pch
; pch
++ )
3972 if ( ch
== _T('%') )
3974 // the start of the format specification of the printf() below
3975 wxString fmtPrefix
= _T('%');
3980 ch
= *++pch
; // get the format spec char
3984 wxFAIL_MSG( _T("invalid format character") );
3990 // skip the part below switch
3995 if ( partBiggest
< Part_Day
)
4001 partBiggest
= Part_Day
;
4006 partBiggest
= Part_Week
;
4012 if ( partBiggest
< Part_Hour
)
4018 partBiggest
= Part_Hour
;
4021 fmtPrefix
+= _T("02");
4025 n
= GetMilliseconds().ToLong();
4026 if ( partBiggest
< Part_MSec
)
4030 //else: no need to reset partBiggest to Part_MSec, it is
4031 // the least significant one anyhow
4033 fmtPrefix
+= _T("03");
4038 if ( partBiggest
< Part_Min
)
4044 partBiggest
= Part_Min
;
4047 fmtPrefix
+= _T("02");
4051 n
= GetSeconds().ToLong();
4052 if ( partBiggest
< Part_Sec
)
4058 partBiggest
= Part_Sec
;
4061 fmtPrefix
+= _T("02");
4065 str
+= wxString::Format(fmtPrefix
+ _T("ld"), n
);
4069 // normal character, just copy
4077 // ============================================================================
4078 // wxDateTimeHolidayAuthority and related classes
4079 // ============================================================================
4081 #include "wx/arrimpl.cpp"
4083 WX_DEFINE_OBJARRAY(wxDateTimeArray
);
4085 static int wxCMPFUNC_CONV
4086 wxDateTimeCompareFunc(wxDateTime
**first
, wxDateTime
**second
)
4088 wxDateTime dt1
= **first
,
4091 return dt1
== dt2
? 0 : dt1
< dt2
? -1 : +1;
4094 // ----------------------------------------------------------------------------
4095 // wxDateTimeHolidayAuthority
4096 // ----------------------------------------------------------------------------
4098 wxHolidayAuthoritiesArray
wxDateTimeHolidayAuthority::ms_authorities
;
4101 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime
& dt
)
4103 size_t count
= ms_authorities
.size();
4104 for ( size_t n
= 0; n
< count
; n
++ )
4106 if ( ms_authorities
[n
]->DoIsHoliday(dt
) )
4117 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime
& dtStart
,
4118 const wxDateTime
& dtEnd
,
4119 wxDateTimeArray
& holidays
)
4121 wxDateTimeArray hol
;
4125 size_t count
= ms_authorities
.size();
4126 for ( size_t nAuth
= 0; nAuth
< count
; nAuth
++ )
4128 ms_authorities
[nAuth
]->DoGetHolidaysInRange(dtStart
, dtEnd
, hol
);
4130 WX_APPEND_ARRAY(holidays
, hol
);
4133 holidays
.Sort(wxDateTimeCompareFunc
);
4135 return holidays
.size();
4139 void wxDateTimeHolidayAuthority::ClearAllAuthorities()
4141 WX_CLEAR_ARRAY(ms_authorities
);
4145 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority
*auth
)
4147 ms_authorities
.push_back(auth
);
4150 wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
4152 // required here for Darwin
4155 // ----------------------------------------------------------------------------
4156 // wxDateTimeWorkDays
4157 // ----------------------------------------------------------------------------
4159 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime
& dt
) const
4161 wxDateTime::WeekDay wd
= dt
.GetWeekDay();
4163 return (wd
== wxDateTime::Sun
) || (wd
== wxDateTime::Sat
);
4166 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime
& dtStart
,
4167 const wxDateTime
& dtEnd
,
4168 wxDateTimeArray
& holidays
) const
4170 if ( dtStart
> dtEnd
)
4172 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
4179 // instead of checking all days, start with the first Sat after dtStart and
4180 // end with the last Sun before dtEnd
4181 wxDateTime dtSatFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sat
),
4182 dtSatLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sat
),
4183 dtSunFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sun
),
4184 dtSunLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sun
),
4187 for ( dt
= dtSatFirst
; dt
<= dtSatLast
; dt
+= wxDateSpan::Week() )
4192 for ( dt
= dtSunFirst
; dt
<= dtSunLast
; dt
+= wxDateSpan::Week() )
4197 return holidays
.GetCount();
4200 // ============================================================================
4201 // other helper functions
4202 // ============================================================================
4204 // ----------------------------------------------------------------------------
4205 // iteration helpers: can be used to write a for loop over enum variable like
4207 // for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
4208 // ----------------------------------------------------------------------------
4210 WXDLLIMPEXP_BASE
void wxNextMonth(wxDateTime::Month
& m
)
4212 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4214 // no wrapping or the for loop above would never end!
4215 m
= (wxDateTime::Month
)(m
+ 1);
4218 WXDLLIMPEXP_BASE
void wxPrevMonth(wxDateTime::Month
& m
)
4220 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4222 m
= m
== wxDateTime::Jan
? wxDateTime::Inv_Month
4223 : (wxDateTime::Month
)(m
- 1);
4226 WXDLLIMPEXP_BASE
void wxNextWDay(wxDateTime::WeekDay
& wd
)
4228 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4230 // no wrapping or the for loop above would never end!
4231 wd
= (wxDateTime::WeekDay
)(wd
+ 1);
4234 WXDLLIMPEXP_BASE
void wxPrevWDay(wxDateTime::WeekDay
& wd
)
4236 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4238 wd
= wd
== wxDateTime::Sun
? wxDateTime::Inv_WeekDay
4239 : (wxDateTime::WeekDay
)(wd
- 1);
4242 #endif // wxUSE_DATETIME