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 ///////////////////////////////////////////////////////////////////////////////
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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
60 #pragma implementation "datetime.h"
63 // For compilers that support precompilation, includes "wx.h".
64 #include "wx/wxprec.h"
70 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
73 #include "wx/string.h"
78 #include "wx/thread.h"
79 #include "wx/tokenzr.h"
80 #include "wx/module.h"
85 #include "wx/msw/wrapwin.h"
92 #include "wx/datetime.h"
93 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
95 const long wxDateTime::TIME_T_FACTOR
= 1000l;
97 #if wxUSE_EXTENDED_RTTI
99 template<> void wxStringReadValue(const wxString
&s
, wxDateTime
&data
)
101 data
.ParseFormat(s
,wxT("%Y-%m-%d %H:%M:%S")) ;
104 template<> void wxStringWriteValue(wxString
&s
, const wxDateTime
&data
)
106 s
= data
.Format(wxT("%Y-%m-%d %H:%M:%S")) ;
109 wxCUSTOM_TYPE_INFO(wxDateTime
, wxToStringConverter
<wxDateTime
> , wxFromStringConverter
<wxDateTime
>)
114 // ----------------------------------------------------------------------------
115 // conditional compilation
116 // ----------------------------------------------------------------------------
118 #if defined(HAVE_STRPTIME) && defined(__GLIBC__) && \
119 ((__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0))
120 // glibc 2.0.7 strptime() is broken - the following snippet causes it to
121 // crash (instead of just failing):
123 // strncpy(buf, "Tue Dec 21 20:25:40 1999", 128);
124 // strptime(buf, "%x", &tm);
128 #endif // broken strptime()
130 #if defined(HAVE_STRPTIME) && defined(__DARWIN__) && defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
131 // configure detects strptime as linkable because it's in the OS X
132 // System library but MSL headers don't declare it.
133 char *strptime(const char *, const char *, struct tm
*);
136 #if defined(__MWERKS__) && wxUSE_UNICODE
140 #if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM)
141 #if defined(__WXPALMOS__)
142 #define WX_GMTOFF_IN_TM
143 #elif defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
144 #define WX_TIMEZONE _timezone
145 #elif defined(__MWERKS__)
146 long wxmw_timezone
= 28800;
147 #define WX_TIMEZONE wxmw_timezone
148 #elif defined(__DJGPP__) || defined(__WINE__)
149 #include <sys/timeb.h>
151 static long wxGetTimeZone()
153 static long timezone
= MAXLONG
; // invalid timezone
154 if (timezone
== MAXLONG
)
158 timezone
= tb
.timezone
;
162 #define WX_TIMEZONE wxGetTimeZone()
163 #elif defined(__DARWIN__)
164 #define WX_GMTOFF_IN_TM
165 #else // unknown platform - try timezone
166 #define WX_TIMEZONE timezone
168 #endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
170 // ----------------------------------------------------------------------------
172 // ----------------------------------------------------------------------------
174 // debugging helper: just a convenient replacement of wxCHECK()
175 #define wxDATETIME_CHECK(expr, msg) \
179 *this = wxInvalidDateTime; \
183 // ----------------------------------------------------------------------------
185 // ----------------------------------------------------------------------------
187 class wxDateTimeHolidaysModule
: public wxModule
190 virtual bool OnInit()
192 wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays
);
197 virtual void OnExit()
199 wxDateTimeHolidayAuthority::ClearAllAuthorities();
200 wxDateTimeHolidayAuthority::ms_authorities
.clear();
204 DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule
)
207 IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule
, wxModule
)
209 // ----------------------------------------------------------------------------
211 // ----------------------------------------------------------------------------
214 static const int MONTHS_IN_YEAR
= 12;
216 static const int SEC_PER_MIN
= 60;
218 static const int MIN_PER_HOUR
= 60;
220 static const int HOURS_PER_DAY
= 24;
222 static const long SECONDS_PER_DAY
= 86400l;
224 static const int DAYS_PER_WEEK
= 7;
226 static const long MILLISECONDS_PER_DAY
= 86400000l;
228 // this is the integral part of JDN of the midnight of Jan 1, 1970
229 // (i.e. JDN(Jan 1, 1970) = 2440587.5)
230 static const long EPOCH_JDN
= 2440587l;
232 // the date of JDN -0.5 (as we don't work with fractional parts, this is the
233 // reference date for us) is Nov 24, 4714BC
234 static const int JDN_0_YEAR
= -4713;
235 static const int JDN_0_MONTH
= wxDateTime::Nov
;
236 static const int JDN_0_DAY
= 24;
238 // the constants used for JDN calculations
239 static const long JDN_OFFSET
= 32046l;
240 static const long DAYS_PER_5_MONTHS
= 153l;
241 static const long DAYS_PER_4_YEARS
= 1461l;
242 static const long DAYS_PER_400_YEARS
= 146097l;
244 // this array contains the cumulated number of days in all previous months for
245 // normal and leap years
246 static const wxDateTime::wxDateTime_t gs_cumulatedDays
[2][MONTHS_IN_YEAR
] =
248 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
249 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
252 // ----------------------------------------------------------------------------
254 // ----------------------------------------------------------------------------
256 const wxChar
* wxDefaultDateTimeFormat
= wxT("%c");
257 const wxChar
* wxDefaultTimeSpanFormat
= wxT("%H:%M:%S");
259 // in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
260 // indicate an invalid wxDateTime object
261 const wxDateTime wxDefaultDateTime
;
263 wxDateTime::Country
wxDateTime::ms_country
= wxDateTime::Country_Unknown
;
265 // ----------------------------------------------------------------------------
267 // ----------------------------------------------------------------------------
269 // debugger helper: shows what the date really is
271 extern const wxChar
*wxDumpDate(const wxDateTime
* dt
)
273 static wxChar buf
[128];
275 wxStrcpy(buf
, dt
->Format(_T("%Y-%m-%d (%a) %H:%M:%S")));
281 // get the number of days in the given month of the given year
283 wxDateTime::wxDateTime_t
GetNumOfDaysInMonth(int year
, wxDateTime::Month month
)
285 // the number of days in month in Julian/Gregorian calendar: the first line
286 // is for normal years, the second one is for the leap ones
287 static wxDateTime::wxDateTime_t daysInMonth
[2][MONTHS_IN_YEAR
] =
289 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
290 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
293 return daysInMonth
[wxDateTime::IsLeapYear(year
)][month
];
296 // returns the time zone in the C sense, i.e. the difference UTC - local
298 static int GetTimeZone()
300 #ifdef WX_GMTOFF_IN_TM
301 // set to true when the timezone is set
302 static bool s_timezoneSet
= false;
303 static long gmtoffset
= LONG_MAX
; // invalid timezone
305 // ensure that the timezone variable is set by calling localtime
306 if ( !s_timezoneSet
)
308 // just call localtime() instead of figuring out whether this system
309 // supports tzset(), _tzset() or something else
314 s_timezoneSet
= true;
316 // note that GMT offset is the opposite of time zone and so to return
317 // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM
318 // cases we have to negate it
319 gmtoffset
= -tm
->tm_gmtoff
;
322 return (int)gmtoffset
;
323 #else // !WX_GMTOFF_IN_TM
324 return (int)WX_TIMEZONE
;
325 #endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
328 // return the integral part of the JDN for the midnight of the given date (to
329 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
330 // noon of the previous day)
331 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day
,
332 wxDateTime::Month mon
,
335 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
337 // check the date validity
339 (year
> JDN_0_YEAR
) ||
340 ((year
== JDN_0_YEAR
) && (mon
> JDN_0_MONTH
)) ||
341 ((year
== JDN_0_YEAR
) && (mon
== JDN_0_MONTH
) && (day
>= JDN_0_DAY
)),
342 _T("date out of range - can't convert to JDN")
345 // make the year positive to avoid problems with negative numbers division
348 // months are counted from March here
350 if ( mon
>= wxDateTime::Mar
)
360 // now we can simply add all the contributions together
361 return ((year
/ 100) * DAYS_PER_400_YEARS
) / 4
362 + ((year
% 100) * DAYS_PER_4_YEARS
) / 4
363 + (month
* DAYS_PER_5_MONTHS
+ 2) / 5
369 // this function is a wrapper around strftime(3) adding error checking
370 static wxString
CallStrftime(const wxChar
*format
, const tm
* tm
)
373 if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) )
375 // buffer is too small?
376 wxFAIL_MSG(_T("strftime() failed"));
379 return wxString(buf
);
385 // glibc2 doesn't define this in the headers unless _XOPEN_SOURCE is defined
386 // which, unfortunately, wreaks havoc elsewhere
387 #if defined(__GLIBC__) && (__GLIBC__ == 2)
388 extern "C" char *strptime(const char *, const char *, struct tm
*);
391 // Unicode-friendly strptime() wrapper
392 static const wxChar
*
393 CallStrptime(const wxChar
*input
, const char *fmt
, tm
*tm
)
395 // the problem here is that strptime() returns pointer into the string we
396 // passed to it while we're really interested in the pointer into the
397 // original, Unicode, string so we try to transform the pointer back
399 wxCharBuffer
inputMB(wxConvertWX2MB(input
));
401 const char * const inputMB
= input
;
402 #endif // Unicode/Ascii
404 const char *result
= strptime(inputMB
, fmt
, tm
);
409 // FIXME: this is wrong in presence of surrogates &c
410 return input
+ (result
- inputMB
.data());
413 #endif // Unicode/Ascii
416 #endif // HAVE_STRPTIME
418 // if year and/or month have invalid values, replace them with the current ones
419 static void ReplaceDefaultYearMonthWithCurrent(int *year
,
420 wxDateTime::Month
*month
)
422 struct tm
*tmNow
= NULL
;
424 if ( *year
== wxDateTime::Inv_Year
)
426 tmNow
= wxDateTime::GetTmNow();
428 *year
= 1900 + tmNow
->tm_year
;
431 if ( *month
== wxDateTime::Inv_Month
)
434 tmNow
= wxDateTime::GetTmNow();
436 *month
= (wxDateTime::Month
)tmNow
->tm_mon
;
440 // fll the struct tm with default values
441 static void InitTm(struct tm
& tm
)
443 // struct tm may have etxra fields (undocumented and with unportable
444 // names) which, nevertheless, must be set to 0
445 memset(&tm
, 0, sizeof(struct tm
));
447 tm
.tm_mday
= 1; // mday 0 is invalid
448 tm
.tm_year
= 76; // any valid year
449 tm
.tm_isdst
= -1; // auto determine
455 // return the month if the string is a month name or Inv_Month otherwise
456 static wxDateTime::Month
GetMonthFromName(const wxString
& name
, int flags
)
458 wxDateTime::Month mon
;
459 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
461 // case-insensitive comparison either one of or with both abbreviated
463 if ( flags
& wxDateTime::Name_Full
)
465 if ( name
.CmpNoCase(wxDateTime::
466 GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 )
472 if ( flags
& wxDateTime::Name_Abbr
)
474 if ( name
.CmpNoCase(wxDateTime::
475 GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 )
485 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
486 static wxDateTime::WeekDay
GetWeekDayFromName(const wxString
& name
, int flags
)
488 wxDateTime::WeekDay wd
;
489 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
491 // case-insensitive comparison either one of or with both abbreviated
493 if ( flags
& wxDateTime::Name_Full
)
495 if ( name
.CmpNoCase(wxDateTime::
496 GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 )
502 if ( flags
& wxDateTime::Name_Abbr
)
504 if ( name
.CmpNoCase(wxDateTime::
505 GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 )
515 // scans all digits (but no more than len) and returns the resulting number
516 static bool GetNumericToken(size_t len
, const wxChar
*& p
, unsigned long *number
)
520 while ( wxIsdigit(*p
) )
524 if ( len
&& ++n
> len
)
528 return !s
.empty() && s
.ToULong(number
);
531 // scans all alphabetic characters and returns the resulting string
532 static wxString
GetAlphaToken(const wxChar
*& p
)
535 while ( wxIsalpha(*p
) )
543 // ============================================================================
544 // implementation of wxDateTime
545 // ============================================================================
547 // ----------------------------------------------------------------------------
549 // ----------------------------------------------------------------------------
553 year
= (wxDateTime_t
)wxDateTime::Inv_Year
;
554 mon
= wxDateTime::Inv_Month
;
556 hour
= min
= sec
= msec
= 0;
557 wday
= wxDateTime::Inv_WeekDay
;
560 wxDateTime::Tm::Tm(const struct tm
& tm
, const TimeZone
& tz
)
564 sec
= (wxDateTime::wxDateTime_t
)tm
.tm_sec
;
565 min
= (wxDateTime::wxDateTime_t
)tm
.tm_min
;
566 hour
= (wxDateTime::wxDateTime_t
)tm
.tm_hour
;
567 mday
= (wxDateTime::wxDateTime_t
)tm
.tm_mday
;
568 mon
= (wxDateTime::Month
)tm
.tm_mon
;
569 year
= 1900 + tm
.tm_year
;
570 wday
= (wxDateTime::wxDateTime_t
)tm
.tm_wday
;
571 yday
= (wxDateTime::wxDateTime_t
)tm
.tm_yday
;
574 bool wxDateTime::Tm::IsValid() const
576 // we allow for the leap seconds, although we don't use them (yet)
577 return (year
!= wxDateTime::Inv_Year
) && (mon
!= wxDateTime::Inv_Month
) &&
578 (mday
<= GetNumOfDaysInMonth(year
, mon
)) &&
579 (hour
< 24) && (min
< 60) && (sec
< 62) && (msec
< 1000);
582 void wxDateTime::Tm::ComputeWeekDay()
584 // compute the week day from day/month/year: we use the dumbest algorithm
585 // possible: just compute our JDN and then use the (simple to derive)
586 // formula: weekday = (JDN + 1.5) % 7
587 wday
= (wxDateTime::wxDateTime_t
)((wxDateTime::WeekDay
)(GetTruncatedJDN(mday
, mon
, year
) + 2) % 7);
590 void wxDateTime::Tm::AddMonths(int monDiff
)
592 // normalize the months field
593 while ( monDiff
< -mon
)
597 monDiff
+= MONTHS_IN_YEAR
;
600 while ( monDiff
+ mon
>= MONTHS_IN_YEAR
)
604 monDiff
-= MONTHS_IN_YEAR
;
607 mon
= (wxDateTime::Month
)(mon
+ monDiff
);
609 wxASSERT_MSG( mon
>= 0 && mon
< MONTHS_IN_YEAR
, _T("logic error") );
611 // NB: we don't check here that the resulting date is valid, this function
612 // is private and the caller must check it if needed
615 void wxDateTime::Tm::AddDays(int dayDiff
)
617 // normalize the days field
618 while ( dayDiff
+ mday
< 1 )
622 dayDiff
+= GetNumOfDaysInMonth(year
, mon
);
625 mday
= (wxDateTime::wxDateTime_t
)( mday
+ dayDiff
);
626 while ( mday
> GetNumOfDaysInMonth(year
, mon
) )
628 mday
-= GetNumOfDaysInMonth(year
, mon
);
633 wxASSERT_MSG( mday
> 0 && mday
<= GetNumOfDaysInMonth(year
, mon
),
637 // ----------------------------------------------------------------------------
639 // ----------------------------------------------------------------------------
641 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz
)
645 case wxDateTime::Local
:
646 // get the offset from C RTL: it returns the difference GMT-local
647 // while we want to have the offset _from_ GMT, hence the '-'
648 m_offset
= -GetTimeZone();
651 case wxDateTime::GMT_12
:
652 case wxDateTime::GMT_11
:
653 case wxDateTime::GMT_10
:
654 case wxDateTime::GMT_9
:
655 case wxDateTime::GMT_8
:
656 case wxDateTime::GMT_7
:
657 case wxDateTime::GMT_6
:
658 case wxDateTime::GMT_5
:
659 case wxDateTime::GMT_4
:
660 case wxDateTime::GMT_3
:
661 case wxDateTime::GMT_2
:
662 case wxDateTime::GMT_1
:
663 m_offset
= -3600*(wxDateTime::GMT0
- tz
);
666 case wxDateTime::GMT0
:
667 case wxDateTime::GMT1
:
668 case wxDateTime::GMT2
:
669 case wxDateTime::GMT3
:
670 case wxDateTime::GMT4
:
671 case wxDateTime::GMT5
:
672 case wxDateTime::GMT6
:
673 case wxDateTime::GMT7
:
674 case wxDateTime::GMT8
:
675 case wxDateTime::GMT9
:
676 case wxDateTime::GMT10
:
677 case wxDateTime::GMT11
:
678 case wxDateTime::GMT12
:
679 m_offset
= 3600*(tz
- wxDateTime::GMT0
);
682 case wxDateTime::A_CST
:
683 // Central Standard Time in use in Australia = UTC + 9.5
684 m_offset
= 60l*(9*60 + 30);
688 wxFAIL_MSG( _T("unknown time zone") );
692 // ----------------------------------------------------------------------------
694 // ----------------------------------------------------------------------------
697 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
)
699 if ( year
== Inv_Year
)
700 year
= GetCurrentYear();
702 if ( cal
== Gregorian
)
704 // in Gregorian calendar leap years are those divisible by 4 except
705 // those divisible by 100 unless they're also divisible by 400
706 // (in some countries, like Russia and Greece, additional corrections
707 // exist, but they won't manifest themselves until 2700)
708 return (year
% 4 == 0) && ((year
% 100 != 0) || (year
% 400 == 0));
710 else if ( cal
== Julian
)
712 // in Julian calendar the rule is simpler
713 return year
% 4 == 0;
717 wxFAIL_MSG(_T("unknown calendar"));
724 int wxDateTime::GetCentury(int year
)
726 return year
> 0 ? year
/ 100 : year
/ 100 - 1;
730 int wxDateTime::ConvertYearToBC(int year
)
733 return year
> 0 ? year
: year
- 1;
737 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
)
742 return Now().GetYear();
745 wxFAIL_MSG(_T("TODO"));
749 wxFAIL_MSG(_T("unsupported calendar"));
757 wxDateTime::Month
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
)
762 return Now().GetMonth();
765 wxFAIL_MSG(_T("TODO"));
769 wxFAIL_MSG(_T("unsupported calendar"));
777 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(int year
, Calendar cal
)
779 if ( year
== Inv_Year
)
781 // take the current year if none given
782 year
= GetCurrentYear();
789 return IsLeapYear(year
) ? 366 : 365;
792 wxFAIL_MSG(_T("unsupported calendar"));
800 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(wxDateTime::Month month
,
802 wxDateTime::Calendar cal
)
804 wxCHECK_MSG( month
< MONTHS_IN_YEAR
, 0, _T("invalid month") );
806 if ( cal
== Gregorian
|| cal
== Julian
)
808 if ( year
== Inv_Year
)
810 // take the current year if none given
811 year
= GetCurrentYear();
814 return GetNumOfDaysInMonth(year
, month
);
818 wxFAIL_MSG(_T("unsupported calendar"));
825 wxString
wxDateTime::GetMonthName(wxDateTime::Month month
,
826 wxDateTime::NameFlags flags
)
828 wxCHECK_MSG( month
!= Inv_Month
, wxEmptyString
, _T("invalid month") );
830 // notice that we must set all the fields to avoid confusing libc (GNU one
831 // gets confused to a crash if we don't do this)
836 return CallStrftime(flags
== Name_Abbr
? _T("%b") : _T("%B"), &tm
);
842 ret
= (flags
== Name_Abbr
? wxT("Jan"): wxT("January"));
845 ret
= (flags
== Name_Abbr
? wxT("Feb"): wxT("Febuary"));
848 ret
= (flags
== Name_Abbr
? wxT("Mar"): wxT("March"));
851 ret
= (flags
== Name_Abbr
? wxT("Apr"): wxT("April"));
854 ret
= (flags
== Name_Abbr
? wxT("May"): wxT("May"));
857 ret
= (flags
== Name_Abbr
? wxT("Jun"): wxT("June"));
860 ret
= (flags
== Name_Abbr
? wxT("Jul"): wxT("July"));
863 ret
= (flags
== Name_Abbr
? wxT("Aug"): wxT("August"));
866 ret
= (flags
== Name_Abbr
? wxT("Sep"): wxT("September"));
869 ret
= (flags
== Name_Abbr
? wxT("Oct"): wxT("October"));
872 ret
= (flags
== Name_Abbr
? wxT("Nov"): wxT("November"));
875 ret
= (flags
== Name_Abbr
? wxT("Dec"): wxT("December"));
883 wxString
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
,
884 wxDateTime::NameFlags flags
)
886 wxCHECK_MSG( wday
!= Inv_WeekDay
, wxEmptyString
, _T("invalid weekday") );
888 // take some arbitrary Sunday (but notice that the day should be such that
889 // after adding wday to it below we still have a valid date, e.g. don't
897 // and offset it by the number of days needed to get the correct wday
900 // call mktime() to normalize it...
903 // ... and call strftime()
904 return CallStrftime(flags
== Name_Abbr
? _T("%a") : _T("%A"), &tm
);
910 ret
= (flags
== Name_Abbr
? wxT("Sun") : wxT("Sunday"));
913 ret
= (flags
== Name_Abbr
? wxT("Mon") : wxT("Monday"));
916 ret
= (flags
== Name_Abbr
? wxT("Tue") : wxT("Tuesday"));
919 ret
= (flags
== Name_Abbr
? wxT("Wed") : wxT("Wednesday"));
922 ret
= (flags
== Name_Abbr
? wxT("Thu") : wxT("Thursday"));
925 ret
= (flags
== Name_Abbr
? wxT("Fri") : wxT("Friday"));
928 ret
= (flags
== Name_Abbr
? wxT("Sat") : wxT("Saturday"));
937 void wxDateTime::GetAmPmStrings(wxString
*am
, wxString
*pm
)
942 // @Note: Do not call 'CallStrftime' here! CallStrftime checks the return code
943 // and causes an assertion failed if the buffer is to small (which is good) - OR -
944 // if strftime does not return anything because the format string is invalid - OR -
945 // if there are no 'am' / 'pm' tokens defined for the current locale (which is not good).
946 // wxDateTime::ParseTime will try several different formats to parse the time.
947 // As a result, GetAmPmStrings might get called, even if the current locale
948 // does not define any 'am' / 'pm' tokens. In this case, wxStrftime would
949 // assert, even though it is a perfectly legal use.
952 if (wxStrftime(buffer
, sizeof(buffer
)/sizeof(wxChar
), _T("%p"), &tm
) > 0)
953 *am
= wxString(buffer
);
960 if (wxStrftime(buffer
, sizeof(buffer
)/sizeof(wxChar
), _T("%p"), &tm
) > 0)
961 *pm
= wxString(buffer
);
967 // ----------------------------------------------------------------------------
968 // Country stuff: date calculations depend on the country (DST, work days,
969 // ...), so we need to know which rules to follow.
970 // ----------------------------------------------------------------------------
973 wxDateTime::Country
wxDateTime::GetCountry()
975 // TODO use LOCALE_ICOUNTRY setting under Win32
977 if ( ms_country
== Country_Unknown
)
979 // try to guess from the time zone name
980 time_t t
= time(NULL
);
981 struct tm
*tm
= localtime(&t
);
983 wxString tz
= CallStrftime(_T("%Z"), tm
);
984 if ( tz
== _T("WET") || tz
== _T("WEST") )
988 else if ( tz
== _T("CET") || tz
== _T("CEST") )
990 ms_country
= Country_EEC
;
992 else if ( tz
== _T("MSK") || tz
== _T("MSD") )
996 else if ( tz
== _T("AST") || tz
== _T("ADT") ||
997 tz
== _T("EST") || tz
== _T("EDT") ||
998 tz
== _T("CST") || tz
== _T("CDT") ||
999 tz
== _T("MST") || tz
== _T("MDT") ||
1000 tz
== _T("PST") || tz
== _T("PDT") )
1006 // well, choose a default one
1018 void wxDateTime::SetCountry(wxDateTime::Country country
)
1020 ms_country
= country
;
1024 bool wxDateTime::IsWestEuropeanCountry(Country country
)
1026 if ( country
== Country_Default
)
1028 country
= GetCountry();
1031 return (Country_WesternEurope_Start
<= country
) &&
1032 (country
<= Country_WesternEurope_End
);
1035 // ----------------------------------------------------------------------------
1036 // DST calculations: we use 3 different rules for the West European countries,
1037 // USA and for the rest of the world. This is undoubtedly false for many
1038 // countries, but I lack the necessary info (and the time to gather it),
1039 // please add the other rules here!
1040 // ----------------------------------------------------------------------------
1043 bool wxDateTime::IsDSTApplicable(int year
, Country country
)
1045 if ( year
== Inv_Year
)
1047 // take the current year if none given
1048 year
= GetCurrentYear();
1051 if ( country
== Country_Default
)
1053 country
= GetCountry();
1060 // DST was first observed in the US and UK during WWI, reused
1061 // during WWII and used again since 1966
1062 return year
>= 1966 ||
1063 (year
>= 1942 && year
<= 1945) ||
1064 (year
== 1918 || year
== 1919);
1067 // assume that it started after WWII
1073 wxDateTime
wxDateTime::GetBeginDST(int year
, Country country
)
1075 if ( year
== Inv_Year
)
1077 // take the current year if none given
1078 year
= GetCurrentYear();
1081 if ( country
== Country_Default
)
1083 country
= GetCountry();
1086 if ( !IsDSTApplicable(year
, country
) )
1088 return wxInvalidDateTime
;
1093 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1095 // DST begins at 1 a.m. GMT on the last Sunday of March
1096 if ( !dt
.SetToLastWeekDay(Sun
, Mar
, year
) )
1099 wxFAIL_MSG( _T("no last Sunday in March?") );
1102 dt
+= wxTimeSpan::Hours(1);
1104 // disable DST tests because it could result in an infinite recursion!
1107 else switch ( country
)
1114 // don't know for sure - assume it was in effect all year
1119 dt
.Set(1, Jan
, year
);
1123 // DST was installed Feb 2, 1942 by the Congress
1124 dt
.Set(2, Feb
, year
);
1127 // Oil embargo changed the DST period in the US
1129 dt
.Set(6, Jan
, 1974);
1133 dt
.Set(23, Feb
, 1975);
1137 // before 1986, DST begun on the last Sunday of April, but
1138 // in 1986 Reagan changed it to begin at 2 a.m. of the
1139 // first Sunday in April
1142 if ( !dt
.SetToLastWeekDay(Sun
, Apr
, year
) )
1145 wxFAIL_MSG( _T("no first Sunday in April?") );
1150 if ( !dt
.SetToWeekDay(Sun
, 1, Apr
, year
) )
1153 wxFAIL_MSG( _T("no first Sunday in April?") );
1157 dt
+= wxTimeSpan::Hours(2);
1159 // TODO what about timezone??
1165 // assume Mar 30 as the start of the DST for the rest of the world
1166 // - totally bogus, of course
1167 dt
.Set(30, Mar
, year
);
1174 wxDateTime
wxDateTime::GetEndDST(int year
, Country country
)
1176 if ( year
== Inv_Year
)
1178 // take the current year if none given
1179 year
= GetCurrentYear();
1182 if ( country
== Country_Default
)
1184 country
= GetCountry();
1187 if ( !IsDSTApplicable(year
, country
) )
1189 return wxInvalidDateTime
;
1194 if ( IsWestEuropeanCountry(country
) || (country
== Russia
) )
1196 // DST ends at 1 a.m. GMT on the last Sunday of October
1197 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1199 // weirder and weirder...
1200 wxFAIL_MSG( _T("no last Sunday in October?") );
1203 dt
+= wxTimeSpan::Hours(1);
1205 // disable DST tests because it could result in an infinite recursion!
1208 else switch ( country
)
1215 // don't know for sure - assume it was in effect all year
1219 dt
.Set(31, Dec
, year
);
1223 // the time was reset after the end of the WWII
1224 dt
.Set(30, Sep
, year
);
1228 // DST ends at 2 a.m. on the last Sunday of October
1229 if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) )
1231 // weirder and weirder...
1232 wxFAIL_MSG( _T("no last Sunday in October?") );
1235 dt
+= wxTimeSpan::Hours(2);
1237 // TODO what about timezone??
1242 // assume October 26th as the end of the DST - totally bogus too
1243 dt
.Set(26, Oct
, year
);
1249 // ----------------------------------------------------------------------------
1250 // constructors and assignment operators
1251 // ----------------------------------------------------------------------------
1253 // return the current time with ms precision
1254 /* static */ wxDateTime
wxDateTime::UNow()
1256 return wxDateTime(wxGetLocalTimeMillis());
1259 // the values in the tm structure contain the local time
1260 wxDateTime
& wxDateTime::Set(const struct tm
& tm
)
1263 time_t timet
= mktime(&tm2
);
1265 if ( timet
== (time_t)-1 )
1267 // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
1268 // less than timezone - try to make it work for this case
1269 if ( tm2
.tm_year
== 70 && tm2
.tm_mon
== 0 && tm2
.tm_mday
== 1 )
1271 return Set((time_t)(
1273 tm2
.tm_hour
* MIN_PER_HOUR
* SEC_PER_MIN
+
1274 tm2
.tm_min
* SEC_PER_MIN
+
1278 wxFAIL_MSG( _T("mktime() failed") );
1280 *this = wxInvalidDateTime
;
1290 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
,
1291 wxDateTime_t minute
,
1292 wxDateTime_t second
,
1293 wxDateTime_t millisec
)
1295 // we allow seconds to be 61 to account for the leap seconds, even if we
1296 // don't use them really
1297 wxDATETIME_CHECK( hour
< 24 &&
1301 _T("Invalid time in wxDateTime::Set()") );
1303 // get the current date from system
1304 struct tm
*tm
= GetTmNow();
1306 wxDATETIME_CHECK( tm
, _T("localtime() failed") );
1308 // make a copy so it isn't clobbered by the call to mktime() below
1313 tm1
.tm_min
= minute
;
1314 tm1
.tm_sec
= second
;
1316 // and the DST in case it changes on this date
1319 if ( tm2
.tm_isdst
!= tm1
.tm_isdst
)
1320 tm1
.tm_isdst
= tm2
.tm_isdst
;
1324 // and finally adjust milliseconds
1325 return SetMillisecond(millisec
);
1328 wxDateTime
& wxDateTime::Set(wxDateTime_t day
,
1332 wxDateTime_t minute
,
1333 wxDateTime_t second
,
1334 wxDateTime_t millisec
)
1336 wxDATETIME_CHECK( hour
< 24 &&
1340 _T("Invalid time in wxDateTime::Set()") );
1342 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1344 wxDATETIME_CHECK( (0 < day
) && (day
<= GetNumberOfDays(month
, year
)),
1345 _T("Invalid date in wxDateTime::Set()") );
1347 // the range of time_t type (inclusive)
1348 static const int yearMinInRange
= 1970;
1349 static const int yearMaxInRange
= 2037;
1351 // test only the year instead of testing for the exact end of the Unix
1352 // time_t range - it doesn't bring anything to do more precise checks
1353 if ( year
>= yearMinInRange
&& year
<= yearMaxInRange
)
1355 // use the standard library version if the date is in range - this is
1356 // probably more efficient than our code
1358 tm
.tm_year
= year
- 1900;
1364 tm
.tm_isdst
= -1; // mktime() will guess it
1368 // and finally adjust milliseconds
1370 SetMillisecond(millisec
);
1376 // do time calculations ourselves: we want to calculate the number of
1377 // milliseconds between the given date and the epoch
1379 // get the JDN for the midnight of this day
1380 m_time
= GetTruncatedJDN(day
, month
, year
);
1381 m_time
-= EPOCH_JDN
;
1382 m_time
*= SECONDS_PER_DAY
* TIME_T_FACTOR
;
1384 // JDN corresponds to GMT, we take localtime
1385 Add(wxTimeSpan(hour
, minute
, second
+ GetTimeZone(), millisec
));
1391 wxDateTime
& wxDateTime::Set(double jdn
)
1393 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1395 jdn
-= EPOCH_JDN
+ 0.5;
1397 jdn
*= MILLISECONDS_PER_DAY
;
1401 // JDNs always suppose an UTC date, so bring it back to local time zone
1402 // (also see GetJulianDayNumber() implementation)
1403 long tzDiff
= GetTimeZone();
1406 // FIXME: again, we suppose that DST is always one hour
1410 m_time
+= tzDiff
*1000; // tzDiff is in seconds
1415 wxDateTime
& wxDateTime::ResetTime()
1419 if ( tm
.hour
|| tm
.min
|| tm
.sec
|| tm
.msec
)
1432 // ----------------------------------------------------------------------------
1433 // DOS Date and Time Format functions
1434 // ----------------------------------------------------------------------------
1435 // the dos date and time value is an unsigned 32 bit value in the format:
1436 // YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
1438 // Y = year offset from 1980 (0-127)
1440 // D = day of month (1-31)
1442 // m = minute (0-59)
1443 // s = bisecond (0-29) each bisecond indicates two seconds
1444 // ----------------------------------------------------------------------------
1446 wxDateTime
& wxDateTime::SetFromDOS(unsigned long ddt
)
1451 long year
= ddt
& 0xFE000000;
1456 long month
= ddt
& 0x1E00000;
1461 long day
= ddt
& 0x1F0000;
1465 long hour
= ddt
& 0xF800;
1469 long minute
= ddt
& 0x7E0;
1473 long second
= ddt
& 0x1F;
1474 tm
.tm_sec
= second
* 2;
1476 return Set(mktime(&tm
));
1479 unsigned long wxDateTime::GetAsDOS() const
1482 time_t ticks
= GetTicks();
1483 struct tm
*tm
= localtime(&ticks
);
1485 long year
= tm
->tm_year
;
1489 long month
= tm
->tm_mon
;
1493 long day
= tm
->tm_mday
;
1496 long hour
= tm
->tm_hour
;
1499 long minute
= tm
->tm_min
;
1502 long second
= tm
->tm_sec
;
1505 ddt
= year
| month
| day
| hour
| minute
| second
;
1509 // ----------------------------------------------------------------------------
1510 // time_t <-> broken down time conversions
1511 // ----------------------------------------------------------------------------
1513 wxDateTime::Tm
wxDateTime::GetTm(const TimeZone
& tz
) const
1515 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1517 time_t time
= GetTicks();
1518 if ( time
!= (time_t)-1 )
1520 // use C RTL functions
1522 if ( tz
.GetOffset() == -GetTimeZone() )
1524 // we are working with local time
1525 tm
= localtime(&time
);
1527 // should never happen
1528 wxCHECK_MSG( tm
, Tm(), _T("localtime() failed") );
1532 time
+= (time_t)tz
.GetOffset();
1533 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
1534 int time2
= (int) time
;
1542 // should never happen
1543 wxCHECK_MSG( tm
, Tm(), _T("gmtime() failed") );
1547 tm
= (struct tm
*)NULL
;
1553 // adjust the milliseconds
1555 long timeOnly
= (m_time
% MILLISECONDS_PER_DAY
).ToLong();
1556 tm2
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1559 //else: use generic code below
1562 // remember the time and do the calculations with the date only - this
1563 // eliminates rounding errors of the floating point arithmetics
1565 wxLongLong timeMidnight
= m_time
+ tz
.GetOffset() * 1000;
1567 long timeOnly
= (timeMidnight
% MILLISECONDS_PER_DAY
).ToLong();
1569 // we want to always have positive time and timeMidnight to be really
1570 // the midnight before it
1573 timeOnly
= MILLISECONDS_PER_DAY
+ timeOnly
;
1576 timeMidnight
-= timeOnly
;
1578 // calculate the Gregorian date from JDN for the midnight of our date:
1579 // this will yield day, month (in 1..12 range) and year
1581 // actually, this is the JDN for the noon of the previous day
1582 long jdn
= (timeMidnight
/ MILLISECONDS_PER_DAY
).ToLong() + EPOCH_JDN
;
1584 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1586 wxASSERT_MSG( jdn
> -2, _T("JDN out of range") );
1588 // calculate the century
1589 long temp
= (jdn
+ JDN_OFFSET
) * 4 - 1;
1590 long century
= temp
/ DAYS_PER_400_YEARS
;
1592 // then the year and day of year (1 <= dayOfYear <= 366)
1593 temp
= ((temp
% DAYS_PER_400_YEARS
) / 4) * 4 + 3;
1594 long year
= (century
* 100) + (temp
/ DAYS_PER_4_YEARS
);
1595 long dayOfYear
= (temp
% DAYS_PER_4_YEARS
) / 4 + 1;
1597 // and finally the month and day of the month
1598 temp
= dayOfYear
* 5 - 3;
1599 long month
= temp
/ DAYS_PER_5_MONTHS
;
1600 long day
= (temp
% DAYS_PER_5_MONTHS
) / 5 + 1;
1602 // month is counted from March - convert to normal
1613 // year is offset by 4800
1616 // check that the algorithm gave us something reasonable
1617 wxASSERT_MSG( (0 < month
) && (month
<= 12), _T("invalid month") );
1618 wxASSERT_MSG( (1 <= day
) && (day
< 32), _T("invalid day") );
1620 // construct Tm from these values
1622 tm
.year
= (int)year
;
1623 tm
.mon
= (Month
)(month
- 1); // algorithm yields 1 for January, not 0
1624 tm
.mday
= (wxDateTime_t
)day
;
1625 tm
.msec
= (wxDateTime_t
)(timeOnly
% 1000);
1626 timeOnly
-= tm
.msec
;
1627 timeOnly
/= 1000; // now we have time in seconds
1629 tm
.sec
= (wxDateTime_t
)(timeOnly
% 60);
1631 timeOnly
/= 60; // now we have time in minutes
1633 tm
.min
= (wxDateTime_t
)(timeOnly
% 60);
1636 tm
.hour
= (wxDateTime_t
)(timeOnly
/ 60);
1641 wxDateTime
& wxDateTime::SetYear(int year
)
1643 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1652 wxDateTime
& wxDateTime::SetMonth(Month month
)
1654 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1663 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
)
1665 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1674 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
)
1676 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1685 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
)
1687 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1696 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
)
1698 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1707 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
)
1709 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1711 // we don't need to use GetTm() for this one
1712 m_time
-= m_time
% 1000l;
1713 m_time
+= millisecond
;
1718 // ----------------------------------------------------------------------------
1719 // wxDateTime arithmetics
1720 // ----------------------------------------------------------------------------
1722 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
)
1726 tm
.year
+= diff
.GetYears();
1727 tm
.AddMonths(diff
.GetMonths());
1729 // check that the resulting date is valid
1730 if ( tm
.mday
> GetNumOfDaysInMonth(tm
.year
, tm
.mon
) )
1732 // We suppose that when adding one month to Jan 31 we want to get Feb
1733 // 28 (or 29), i.e. adding a month to the last day of the month should
1734 // give the last day of the next month which is quite logical.
1736 // Unfortunately, there is no logic way to understand what should
1737 // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1738 // We make it Feb 28 (last day too), but it is highly questionable.
1739 tm
.mday
= GetNumOfDaysInMonth(tm
.year
, tm
.mon
);
1742 tm
.AddDays(diff
.GetTotalDays());
1746 wxASSERT_MSG( IsSameTime(tm
),
1747 _T("Add(wxDateSpan) shouldn't modify time") );
1752 // ----------------------------------------------------------------------------
1753 // Weekday and monthday stuff
1754 // ----------------------------------------------------------------------------
1756 // convert Sun, Mon, ..., Sat into 6, 0, ..., 5
1757 static inline int ConvertWeekDayToMondayBase(int wd
)
1759 return wd
== wxDateTime::Sun
? 6 : wd
- 1;
1764 wxDateTime::SetToWeekOfYear(int year
, wxDateTime_t numWeek
, WeekDay wd
)
1766 wxASSERT_MSG( numWeek
> 0,
1767 _T("invalid week number: weeks are counted from 1") );
1769 // Jan 4 always lies in the 1st week of the year
1770 wxDateTime
dt(4, Jan
, year
);
1771 dt
.SetToWeekDayInSameWeek(wd
);
1772 dt
+= wxDateSpan::Weeks(numWeek
- 1);
1777 // use a separate function to avoid warnings about using deprecated
1778 // SetToTheWeek in GetWeek below
1780 SetToTheWeek(int year
,
1781 wxDateTime::wxDateTime_t numWeek
,
1782 wxDateTime::WeekDay weekday
,
1783 wxDateTime::WeekFlags flags
)
1785 // Jan 4 always lies in the 1st week of the year
1786 wxDateTime
dt(4, wxDateTime::Jan
, year
);
1787 dt
.SetToWeekDayInSameWeek(weekday
, flags
);
1788 dt
+= wxDateSpan::Weeks(numWeek
- 1);
1793 bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek
,
1797 int year
= GetYear();
1798 *this = ::SetToTheWeek(year
, numWeek
, weekday
, flags
);
1799 if ( GetYear() != year
)
1801 // oops... numWeek was too big
1808 wxDateTime
wxDateTime::GetWeek(wxDateTime_t numWeek
,
1810 WeekFlags flags
) const
1812 return ::SetToTheWeek(GetYear(), numWeek
, weekday
, flags
);
1815 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
,
1818 // take the current month/year if none specified
1819 if ( year
== Inv_Year
)
1821 if ( month
== Inv_Month
)
1824 return Set(GetNumOfDaysInMonth(year
, month
), month
, year
);
1827 wxDateTime
& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday
, WeekFlags flags
)
1829 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1831 int wdayDst
= weekday
,
1832 wdayThis
= GetWeekDay();
1833 if ( wdayDst
== wdayThis
)
1839 if ( flags
== Default_First
)
1841 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
1844 // the logic below based on comparing weekday and wdayThis works if Sun (0)
1845 // is the first day in the week, but breaks down for Monday_First case so
1846 // we adjust the week days in this case
1847 if ( flags
== Monday_First
)
1849 if ( wdayThis
== Sun
)
1851 if ( wdayDst
== Sun
)
1854 //else: Sunday_First, nothing to do
1856 // go forward or back in time to the day we want
1857 if ( wdayDst
< wdayThis
)
1859 return Subtract(wxDateSpan::Days(wdayThis
- wdayDst
));
1861 else // weekday > wdayThis
1863 return Add(wxDateSpan::Days(wdayDst
- wdayThis
));
1867 wxDateTime
& wxDateTime::SetToNextWeekDay(WeekDay weekday
)
1869 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1872 WeekDay wdayThis
= GetWeekDay();
1873 if ( weekday
== wdayThis
)
1878 else if ( weekday
< wdayThis
)
1880 // need to advance a week
1881 diff
= 7 - (wdayThis
- weekday
);
1883 else // weekday > wdayThis
1885 diff
= weekday
- wdayThis
;
1888 return Add(wxDateSpan::Days(diff
));
1891 wxDateTime
& wxDateTime::SetToPrevWeekDay(WeekDay weekday
)
1893 wxDATETIME_CHECK( weekday
!= Inv_WeekDay
, _T("invalid weekday") );
1896 WeekDay wdayThis
= GetWeekDay();
1897 if ( weekday
== wdayThis
)
1902 else if ( weekday
> wdayThis
)
1904 // need to go to previous week
1905 diff
= 7 - (weekday
- wdayThis
);
1907 else // weekday < wdayThis
1909 diff
= wdayThis
- weekday
;
1912 return Subtract(wxDateSpan::Days(diff
));
1915 bool wxDateTime::SetToWeekDay(WeekDay weekday
,
1920 wxCHECK_MSG( weekday
!= Inv_WeekDay
, false, _T("invalid weekday") );
1922 // we don't check explicitly that -5 <= n <= 5 because we will return false
1923 // anyhow in such case - but may be should still give an assert for it?
1925 // take the current month/year if none specified
1926 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
1930 // TODO this probably could be optimised somehow...
1934 // get the first day of the month
1935 dt
.Set(1, month
, year
);
1938 WeekDay wdayFirst
= dt
.GetWeekDay();
1940 // go to the first weekday of the month
1941 int diff
= weekday
- wdayFirst
;
1945 // add advance n-1 weeks more
1948 dt
+= wxDateSpan::Days(diff
);
1950 else // count from the end of the month
1952 // get the last day of the month
1953 dt
.SetToLastMonthDay(month
, year
);
1956 WeekDay wdayLast
= dt
.GetWeekDay();
1958 // go to the last weekday of the month
1959 int diff
= wdayLast
- weekday
;
1963 // and rewind n-1 weeks from there
1966 dt
-= wxDateSpan::Days(diff
);
1969 // check that it is still in the same month
1970 if ( dt
.GetMonth() == month
)
1978 // no such day in this month
1984 wxDateTime::wxDateTime_t
GetDayOfYearFromTm(const wxDateTime::Tm
& tm
)
1986 return (wxDateTime::wxDateTime_t
)(gs_cumulatedDays
[wxDateTime::IsLeapYear(tm
.year
)][tm
.mon
] + tm
.mday
);
1989 wxDateTime::wxDateTime_t
wxDateTime::GetDayOfYear(const TimeZone
& tz
) const
1991 return GetDayOfYearFromTm(GetTm(tz
));
1994 wxDateTime::wxDateTime_t
1995 wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags
, const TimeZone
& tz
) const
1997 if ( flags
== Default_First
)
1999 flags
= GetCountry() == USA
? Sunday_First
: Monday_First
;
2003 wxDateTime_t nDayInYear
= GetDayOfYearFromTm(tm
);
2005 int wdTarget
= GetWeekDay(tz
);
2006 int wdYearStart
= wxDateTime(1, Jan
, GetYear()).GetWeekDay();
2008 if ( flags
== Sunday_First
)
2010 // FIXME: First week is not calculated correctly.
2011 week
= (nDayInYear
- wdTarget
+ 7) / 7;
2012 if ( wdYearStart
== Wed
|| wdYearStart
== Thu
)
2015 else // week starts with monday
2017 // adjust the weekdays to non-US style.
2018 wdYearStart
= ConvertWeekDayToMondayBase(wdYearStart
);
2019 wdTarget
= ConvertWeekDayToMondayBase(wdTarget
);
2021 // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html:
2023 // Week 01 of a year is per definition the first week that has the
2024 // Thursday in this year, which is equivalent to the week that
2025 // contains the fourth day of January. In other words, the first
2026 // week of a new year is the week that has the majority of its
2027 // days in the new year. Week 01 might also contain days from the
2028 // previous year and the week before week 01 of a year is the last
2029 // week (52 or 53) of the previous year even if it contains days
2030 // from the new year. A week starts with Monday (day 1) and ends
2031 // with Sunday (day 7).
2034 // if Jan 1 is Thursday or less, it is in the first week of this year
2035 if ( wdYearStart
< 4 )
2037 // count the number of entire weeks between Jan 1 and this date
2038 week
= (nDayInYear
+ wdYearStart
+ 6 - wdTarget
)/7;
2040 // be careful to check for overflow in the next year
2041 if ( week
== 53 && tm
.mday
- wdTarget
> 28 )
2044 else // Jan 1 is in the last week of the previous year
2046 // check if we happen to be at the last week of previous year:
2047 if ( tm
.mon
== Jan
&& tm
.mday
< 8 - wdYearStart
)
2048 week
= wxDateTime(31, Dec
, GetYear()-1).GetWeekOfYear();
2050 week
= (nDayInYear
+ wdYearStart
- 1 - wdTarget
)/7;
2054 return (wxDateTime::wxDateTime_t
)week
;
2057 wxDateTime::wxDateTime_t
wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags
,
2058 const TimeZone
& tz
) const
2061 wxDateTime dtMonthStart
= wxDateTime(1, tm
.mon
, tm
.year
);
2062 int nWeek
= GetWeekOfYear(flags
) - dtMonthStart
.GetWeekOfYear(flags
) + 1;
2065 // this may happen for January when Jan, 1 is the last week of the
2067 nWeek
+= IsLeapYear(tm
.year
- 1) ? 53 : 52;
2070 return (wxDateTime::wxDateTime_t
)nWeek
;
2073 wxDateTime
& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday
)
2075 int year
= GetYear();
2076 wxDATETIME_CHECK( (0 < yday
) && (yday
<= GetNumberOfDays(year
)),
2077 _T("invalid year day") );
2079 bool isLeap
= IsLeapYear(year
);
2080 for ( Month mon
= Jan
; mon
< Inv_Month
; wxNextMonth(mon
) )
2082 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
2083 // don't need it neither - because of the CHECK above we know that
2084 // yday lies in December then
2085 if ( (mon
== Dec
) || (yday
<= gs_cumulatedDays
[isLeap
][mon
+ 1]) )
2087 Set((wxDateTime::wxDateTime_t
)(yday
- gs_cumulatedDays
[isLeap
][mon
]), mon
, year
);
2096 // ----------------------------------------------------------------------------
2097 // Julian day number conversion and related stuff
2098 // ----------------------------------------------------------------------------
2100 double wxDateTime::GetJulianDayNumber() const
2102 // JDN are always expressed for the UTC dates
2103 Tm
tm(ToTimezone(UTC
).GetTm(UTC
));
2105 double result
= GetTruncatedJDN(tm
.mday
, tm
.mon
, tm
.year
);
2107 // add the part GetTruncatedJDN() neglected
2110 // and now add the time: 86400 sec = 1 JDN
2111 return result
+ ((double)(60*(60*tm
.hour
+ tm
.min
) + tm
.sec
)) / 86400;
2114 double wxDateTime::GetRataDie() const
2116 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
2117 return GetJulianDayNumber() - 1721119.5 - 306;
2120 // ----------------------------------------------------------------------------
2121 // timezone and DST stuff
2122 // ----------------------------------------------------------------------------
2124 int wxDateTime::IsDST(wxDateTime::Country country
) const
2126 wxCHECK_MSG( country
== Country_Default
, -1,
2127 _T("country support not implemented") );
2129 // use the C RTL for the dates in the standard range
2130 time_t timet
= GetTicks();
2131 if ( timet
!= (time_t)-1 )
2133 tm
*tm
= localtime(&timet
);
2135 wxCHECK_MSG( tm
, -1, _T("localtime() failed") );
2137 return tm
->tm_isdst
;
2141 int year
= GetYear();
2143 if ( !IsDSTApplicable(year
, country
) )
2145 // no DST time in this year in this country
2149 return IsBetween(GetBeginDST(year
, country
), GetEndDST(year
, country
));
2153 wxDateTime
& wxDateTime::MakeTimezone(const TimeZone
& tz
, bool noDST
)
2155 long secDiff
= GetTimeZone() + tz
.GetOffset();
2157 // we need to know whether DST is or not in effect for this date unless
2158 // the test disabled by the caller
2159 if ( !noDST
&& (IsDST() == 1) )
2161 // FIXME we assume that the DST is always shifted by 1 hour
2165 return Subtract(wxTimeSpan::Seconds(secDiff
));
2168 // ----------------------------------------------------------------------------
2169 // wxDateTime to/from text representations
2170 // ----------------------------------------------------------------------------
2172 wxString
wxDateTime::Format(const wxChar
*format
, const TimeZone
& tz
) const
2174 wxCHECK_MSG( format
, wxEmptyString
, _T("NULL format in wxDateTime::Format") );
2176 // we have to use our own implementation if the date is out of range of
2177 // strftime() or if we use non standard specificators
2178 time_t time
= GetTicks();
2179 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
2183 if ( tz
.GetOffset() == -GetTimeZone() )
2185 // we are working with local time
2186 tm
= localtime(&time
);
2188 // should never happen
2189 wxCHECK_MSG( tm
, wxEmptyString
, _T("localtime() failed") );
2193 time
+= (int)tz
.GetOffset();
2195 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
2196 int time2
= (int) time
;
2204 // should never happen
2205 wxCHECK_MSG( tm
, wxEmptyString
, _T("gmtime() failed") );
2209 tm
= (struct tm
*)NULL
;
2213 //Windows CE doesn't support strftime or wcsftime, so we use the generic implementation
2216 return CallStrftime(format
, tm
);
2219 //else: use generic code below
2222 // we only parse ANSI C format specifications here, no POSIX 2
2223 // complications, no GNU extensions but we do add support for a "%l" format
2224 // specifier allowing to get the number of milliseconds
2227 // used for calls to strftime() when we only deal with time
2228 struct tm tmTimeOnly
;
2229 tmTimeOnly
.tm_hour
= tm
.hour
;
2230 tmTimeOnly
.tm_min
= tm
.min
;
2231 tmTimeOnly
.tm_sec
= tm
.sec
;
2232 tmTimeOnly
.tm_wday
= 0;
2233 tmTimeOnly
.tm_yday
= 0;
2234 tmTimeOnly
.tm_mday
= 1; // any date will do
2235 tmTimeOnly
.tm_mon
= 0;
2236 tmTimeOnly
.tm_year
= 76;
2237 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
2239 wxString tmp
, res
, fmt
;
2240 for ( const wxChar
*p
= format
; *p
; p
++ )
2242 if ( *p
!= _T('%') )
2250 // set the default format
2253 case _T('Y'): // year has 4 digits
2257 case _T('j'): // day of year has 3 digits
2258 case _T('l'): // milliseconds have 3 digits
2262 case _T('w'): // week day as number has only one
2267 // it's either another valid format specifier in which case
2268 // the format is "%02d" (for all the rest) or we have the
2269 // field width preceding the format in which case it will
2270 // override the default format anyhow
2274 bool restart
= true;
2279 // start of the format specification
2282 case _T('a'): // a weekday name
2284 // second parameter should be true for abbreviated names
2285 res
+= GetWeekDayName(tm
.GetWeekDay(),
2286 *p
== _T('a') ? Name_Abbr
: Name_Full
);
2289 case _T('b'): // a month name
2291 res
+= GetMonthName(tm
.mon
,
2292 *p
== _T('b') ? Name_Abbr
: Name_Full
);
2295 case _T('c'): // locale default date and time representation
2296 case _T('x'): // locale default date representation
2299 // the problem: there is no way to know what do these format
2300 // specifications correspond to for the current locale.
2302 // the solution: use a hack and still use strftime(): first
2303 // find the YEAR which is a year in the strftime() range (1970
2304 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
2305 // of the real year. Then make a copy of the format and
2306 // replace all occurences of YEAR in it with some unique
2307 // string not appearing anywhere else in it, then use
2308 // strftime() to format the date in year YEAR and then replace
2309 // YEAR back by the real year and the unique replacement
2310 // string back with YEAR. Notice that "all occurences of YEAR"
2311 // means all occurences of 4 digit as well as 2 digit form!
2313 // the bugs: we assume that neither of %c nor %x contains any
2314 // fields which may change between the YEAR and real year. For
2315 // example, the week number (%U, %W) and the day number (%j)
2316 // will change if one of these years is leap and the other one
2319 // find the YEAR: normally, for any year X, Jan 1 or the
2320 // year X + 28 is the same weekday as Jan 1 of X (because
2321 // the weekday advances by 1 for each normal X and by 2
2322 // for each leap X, hence by 5 every 4 years or by 35
2323 // which is 0 mod 7 every 28 years) but this rule breaks
2324 // down if there are years between X and Y which are
2325 // divisible by 4 but not leap (i.e. divisible by 100 but
2326 // not 400), hence the correction.
2328 int yearReal
= GetYear(tz
);
2329 int mod28
= yearReal
% 28;
2331 // be careful to not go too far - we risk to leave the
2336 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
2340 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
2343 int nCentury
= year
/ 100,
2344 nCenturyReal
= yearReal
/ 100;
2346 // need to adjust for the years divisble by 400 which are
2347 // not leap but are counted like leap ones if we just take
2348 // the number of centuries in between for nLostWeekDays
2349 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
2350 (nCentury
/ 4 - nCenturyReal
/ 4);
2352 // we have to gain back the "lost" weekdays: note that the
2353 // effect of this loop is to not do anything to
2354 // nLostWeekDays (which we won't use any more), but to
2355 // (indirectly) set the year correctly
2356 while ( (nLostWeekDays
% 7) != 0 )
2358 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
2361 // at any rate, we couldn't go further than 1988 + 9 + 28!
2362 wxASSERT_MSG( year
< 2030,
2363 _T("logic error in wxDateTime::Format") );
2365 wxString strYear
, strYear2
;
2366 strYear
.Printf(_T("%d"), year
);
2367 strYear2
.Printf(_T("%d"), year
% 100);
2369 // find two strings not occuring in format (this is surely
2370 // not optimal way of doing it... improvements welcome!)
2371 wxString fmt
= format
;
2372 wxString replacement
= (wxChar
)-1;
2373 while ( fmt
.Find(replacement
) != wxNOT_FOUND
)
2375 replacement
<< (wxChar
)-1;
2378 wxString replacement2
= (wxChar
)-2;
2379 while ( fmt
.Find(replacement
) != wxNOT_FOUND
)
2381 replacement
<< (wxChar
)-2;
2384 // replace all occurences of year with it
2385 bool wasReplaced
= fmt
.Replace(strYear
, replacement
) > 0;
2387 wasReplaced
= fmt
.Replace(strYear2
, replacement2
) > 0;
2389 // use strftime() to format the same date but in supported
2392 // NB: we assume that strftime() doesn't check for the
2393 // date validity and will happily format the date
2394 // corresponding to Feb 29 of a non leap year (which
2395 // may happen if yearReal was leap and year is not)
2396 struct tm tmAdjusted
;
2398 tmAdjusted
.tm_hour
= tm
.hour
;
2399 tmAdjusted
.tm_min
= tm
.min
;
2400 tmAdjusted
.tm_sec
= tm
.sec
;
2401 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
2402 tmAdjusted
.tm_yday
= GetDayOfYear();
2403 tmAdjusted
.tm_mday
= tm
.mday
;
2404 tmAdjusted
.tm_mon
= tm
.mon
;
2405 tmAdjusted
.tm_year
= year
- 1900;
2406 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
2407 wxString str
= CallStrftime(*p
== _T('c') ? _T("%c")
2411 // now replace the occurence of 1999 with the real year
2412 wxString strYearReal
, strYearReal2
;
2413 strYearReal
.Printf(_T("%04d"), yearReal
);
2414 strYearReal2
.Printf(_T("%02d"), yearReal
% 100);
2415 str
.Replace(strYear
, strYearReal
);
2416 str
.Replace(strYear2
, strYearReal2
);
2418 // and replace back all occurences of replacement string
2421 str
.Replace(replacement2
, strYear2
);
2422 str
.Replace(replacement
, strYear
);
2428 //Use "%m/%d/%y %H:%M:%S" format instead
2429 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
2430 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
2434 case _T('d'): // day of a month (01-31)
2435 res
+= wxString::Format(fmt
, tm
.mday
);
2438 case _T('H'): // hour in 24h format (00-23)
2439 res
+= wxString::Format(fmt
, tm
.hour
);
2442 case _T('I'): // hour in 12h format (01-12)
2444 // 24h -> 12h, 0h -> 12h too
2445 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
2446 : tm
.hour
? tm
.hour
: 12;
2447 res
+= wxString::Format(fmt
, hour12
);
2451 case _T('j'): // day of the year
2452 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
2455 case _T('l'): // milliseconds (NOT STANDARD)
2456 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
2459 case _T('m'): // month as a number (01-12)
2460 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
2463 case _T('M'): // minute as a decimal number (00-59)
2464 res
+= wxString::Format(fmt
, tm
.min
);
2467 case _T('p'): // AM or PM string
2469 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
2471 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
2475 case _T('S'): // second as a decimal number (00-61)
2476 res
+= wxString::Format(fmt
, tm
.sec
);
2479 case _T('U'): // week number in the year (Sunday 1st week day)
2480 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
2483 case _T('W'): // week number in the year (Monday 1st week day)
2484 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
2487 case _T('w'): // weekday as a number (0-6), Sunday = 0
2488 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
2491 // case _T('x'): -- handled with "%c"
2493 case _T('X'): // locale default time representation
2494 // just use strftime() to format the time for us
2496 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
2498 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
2502 case _T('y'): // year without century (00-99)
2503 res
+= wxString::Format(fmt
, tm
.year
% 100);
2506 case _T('Y'): // year with century
2507 res
+= wxString::Format(fmt
, tm
.year
);
2510 case _T('Z'): // timezone name
2512 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
2517 // is it the format width?
2519 while ( *p
== _T('-') || *p
== _T('+') ||
2520 *p
== _T(' ') || wxIsdigit(*p
) )
2527 // we've only got the flags and width so far in fmt
2528 fmt
.Prepend(_T('%'));
2529 fmt
.Append(_T('d'));
2536 // no, it wasn't the width
2537 wxFAIL_MSG(_T("unknown format specificator"));
2539 // fall through and just copy it nevertheless
2541 case _T('%'): // a percent sign
2545 case 0: // the end of string
2546 wxFAIL_MSG(_T("missing format at the end of string"));
2548 // just put the '%' which was the last char in format
2558 // this function parses a string in (strict) RFC 822 format: see the section 5
2559 // of the RFC for the detailed description, but briefly it's something of the
2560 // form "Sat, 18 Dec 1999 00:48:30 +0100"
2562 // this function is "strict" by design - it must reject anything except true
2563 // RFC822 time specs.
2565 // TODO a great candidate for using reg exps
2566 const wxChar
*wxDateTime::ParseRfc822Date(const wxChar
* date
)
2568 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
2570 const wxChar
*p
= date
;
2571 const wxChar
*comma
= wxStrchr(p
, _T(','));
2574 // the part before comma is the weekday
2576 // skip it for now - we don't use but might check that it really
2577 // corresponds to the specfied date
2580 if ( *p
!= _T(' ') )
2582 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
2584 return (wxChar
*)NULL
;
2590 // the following 1 or 2 digits are the day number
2591 if ( !wxIsdigit(*p
) )
2593 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
2595 return (wxChar
*)NULL
;
2598 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - _T('0'));
2599 if ( wxIsdigit(*p
) )
2602 day
= (wxDateTime_t
)(day
+ (*p
++ - _T('0')));
2605 if ( *p
++ != _T(' ') )
2607 return (wxChar
*)NULL
;
2610 // the following 3 letters specify the month
2611 wxString
monName(p
, 3);
2613 if ( monName
== _T("Jan") )
2615 else if ( monName
== _T("Feb") )
2617 else if ( monName
== _T("Mar") )
2619 else if ( monName
== _T("Apr") )
2621 else if ( monName
== _T("May") )
2623 else if ( monName
== _T("Jun") )
2625 else if ( monName
== _T("Jul") )
2627 else if ( monName
== _T("Aug") )
2629 else if ( monName
== _T("Sep") )
2631 else if ( monName
== _T("Oct") )
2633 else if ( monName
== _T("Nov") )
2635 else if ( monName
== _T("Dec") )
2639 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str());
2641 return (wxChar
*)NULL
;
2646 if ( *p
++ != _T(' ') )
2648 return (wxChar
*)NULL
;
2652 if ( !wxIsdigit(*p
) )
2655 return (wxChar
*)NULL
;
2658 int year
= *p
++ - _T('0');
2660 if ( !wxIsdigit(*p
) )
2662 // should have at least 2 digits in the year
2663 return (wxChar
*)NULL
;
2667 year
+= *p
++ - _T('0');
2669 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
2670 if ( wxIsdigit(*p
) )
2673 year
+= *p
++ - _T('0');
2675 if ( !wxIsdigit(*p
) )
2677 // no 3 digit years please
2678 return (wxChar
*)NULL
;
2682 year
+= *p
++ - _T('0');
2685 if ( *p
++ != _T(' ') )
2687 return (wxChar
*)NULL
;
2690 // time is in the format hh:mm:ss and seconds are optional
2691 if ( !wxIsdigit(*p
) )
2693 return (wxChar
*)NULL
;
2696 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - _T('0'));
2698 if ( !wxIsdigit(*p
) )
2700 return (wxChar
*)NULL
;
2704 hour
= (wxDateTime_t
)(hour
+ (*p
++ - _T('0')));
2706 if ( *p
++ != _T(':') )
2708 return (wxChar
*)NULL
;
2711 if ( !wxIsdigit(*p
) )
2713 return (wxChar
*)NULL
;
2716 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - _T('0'));
2718 if ( !wxIsdigit(*p
) )
2720 return (wxChar
*)NULL
;
2724 min
= (wxDateTime_t
)(min
+ *p
++ - _T('0'));
2726 wxDateTime_t sec
= 0;
2727 if ( *p
++ == _T(':') )
2729 if ( !wxIsdigit(*p
) )
2731 return (wxChar
*)NULL
;
2734 sec
= (wxDateTime_t
)(*p
++ - _T('0'));
2736 if ( !wxIsdigit(*p
) )
2738 return (wxChar
*)NULL
;
2742 sec
= (wxDateTime_t
)(sec
+ *p
++ - _T('0'));
2745 if ( *p
++ != _T(' ') )
2747 return (wxChar
*)NULL
;
2750 // and now the interesting part: the timezone
2751 int offset
wxDUMMY_INITIALIZE(0);
2752 if ( *p
== _T('-') || *p
== _T('+') )
2754 // the explicit offset given: it has the form of hhmm
2755 bool plus
= *p
++ == _T('+');
2757 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2759 return (wxChar
*)NULL
;
2763 offset
= 60*(10*(*p
- _T('0')) + (*(p
+ 1) - _T('0')));
2767 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
2769 return (wxChar
*)NULL
;
2773 offset
+= 10*(*p
- _T('0')) + (*(p
+ 1) - _T('0'));
2784 // the symbolic timezone given: may be either military timezone or one
2785 // of standard abbreviations
2788 // military: Z = UTC, J unused, A = -1, ..., Y = +12
2789 static const int offsets
[26] =
2791 //A B C D E F G H I J K L M
2792 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
2793 //N O P R Q S T U V W Z Y Z
2794 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
2797 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
2799 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
);
2801 return (wxChar
*)NULL
;
2804 offset
= offsets
[*p
++ - _T('A')];
2810 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
2812 else if ( tz
== _T("AST") )
2813 offset
= AST
- GMT0
;
2814 else if ( tz
== _T("ADT") )
2815 offset
= ADT
- GMT0
;
2816 else if ( tz
== _T("EST") )
2817 offset
= EST
- GMT0
;
2818 else if ( tz
== _T("EDT") )
2819 offset
= EDT
- GMT0
;
2820 else if ( tz
== _T("CST") )
2821 offset
= CST
- GMT0
;
2822 else if ( tz
== _T("CDT") )
2823 offset
= CDT
- GMT0
;
2824 else if ( tz
== _T("MST") )
2825 offset
= MST
- GMT0
;
2826 else if ( tz
== _T("MDT") )
2827 offset
= MDT
- GMT0
;
2828 else if ( tz
== _T("PST") )
2829 offset
= PST
- GMT0
;
2830 else if ( tz
== _T("PDT") )
2831 offset
= PDT
- GMT0
;
2834 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
);
2836 return (wxChar
*)NULL
;
2846 // the spec was correct
2847 Set(day
, mon
, year
, hour
, min
, sec
);
2848 MakeTimezone((wxDateTime_t
)(60*offset
));
2855 // Get's current locale's date formatting string and stores it in fmt if
2856 // the locale is set; otherwise or in case of failure, leaves fmt unchanged
2857 static void GetLocaleDateFormat(wxString
*fmt
)
2859 // there is no setlocale() under Windows CE, so just always query the
2862 if ( strcmp(setlocale(LC_ALL
, NULL
), "C") != 0 )
2865 // The locale was programatically set to non-C. We assume that this was
2866 // done using wxLocale, in which case thread's current locale is also
2867 // set to correct LCID value and we can use GetLocaleInfo to determine
2868 // the correct formatting string:
2870 LCID lcid
= LOCALE_USER_DEFAULT
;
2872 LCID lcid
= GetThreadLocale();
2874 wxChar delim
[5]; // fields deliminer, 4 chars max
2875 if ( GetLocaleInfo(lcid
, LOCALE_SDATE
, delim
, 5) )
2877 wxChar centurybuf
[2]; // use %y or %Y, 1 char max
2878 wxChar century
= 'y';
2879 if ( GetLocaleInfo(lcid
, LOCALE_ICENTURY
, centurybuf
, 2) )
2881 if ( centurybuf
[0] == _T('1') )
2883 // else 'y' as above
2886 wxChar order
[2]; // order code, 1 char max
2887 if ( GetLocaleInfo(lcid
, LOCALE_IDATE
, order
, 2) )
2889 if ( order
[0] == _T('0') ) // M-D-Y
2891 *fmt
= wxString::Format(_T("%%m%s%%d%s%%%c"),
2892 delim
, delim
, century
);
2894 else if ( order
[0] == _T('1') ) // D-M-Y
2896 *fmt
= wxString::Format(_T("%%d%s%%m%s%%%c"),
2897 delim
, delim
, century
);
2899 else if ( order
[0] == _T('2') ) // Y-M-D
2901 *fmt
= wxString::Format(_T("%%%c%s%%m%s%%d"),
2902 century
, delim
, delim
);
2906 wxFAIL_MSG(_T("unexpected GetLocaleInfo return value"));
2910 // if we failed, leave fmtDate value unchanged and
2911 // try our luck with the default set above
2915 #endif // __WINDOWS__
2917 const wxChar
*wxDateTime::ParseFormat(const wxChar
*date
,
2918 const wxChar
*format
,
2919 const wxDateTime
& dateDef
)
2921 wxCHECK_MSG( date
&& format
, (wxChar
*)NULL
,
2922 _T("NULL pointer in wxDateTime::ParseFormat()") );
2927 // what fields have we found?
2928 bool haveWDay
= false,
2937 bool hourIsIn12hFormat
= false, // or in 24h one?
2938 isPM
= false; // AM by default
2940 // and the value of the items we have (init them to get rid of warnings)
2941 wxDateTime_t sec
= 0,
2944 WeekDay wday
= Inv_WeekDay
;
2945 wxDateTime_t yday
= 0,
2947 wxDateTime::Month mon
= Inv_Month
;
2950 const wxChar
*input
= date
;
2951 for ( const wxChar
*fmt
= format
; *fmt
; fmt
++ )
2953 if ( *fmt
!= _T('%') )
2955 if ( wxIsspace(*fmt
) )
2957 // a white space in the format string matches 0 or more white
2958 // spaces in the input
2959 while ( wxIsspace(*input
) )
2966 // any other character (not whitespace, not '%') must be
2967 // matched by itself in the input
2968 if ( *input
++ != *fmt
)
2971 return (wxChar
*)NULL
;
2975 // done with this format char
2979 // start of a format specification
2981 // parse the optional width
2983 while ( wxIsdigit(*++fmt
) )
2986 width
+= *fmt
- _T('0');
2989 // the default widths for the various fields
2994 case _T('Y'): // year has 4 digits
2998 case _T('j'): // day of year has 3 digits
2999 case _T('l'): // milliseconds have 3 digits
3003 case _T('w'): // week day as number has only one
3008 // default for all other fields
3013 // then the format itself
3016 case _T('a'): // a weekday name
3019 int flag
= *fmt
== _T('a') ? Name_Abbr
: Name_Full
;
3020 wday
= GetWeekDayFromName(GetAlphaToken(input
), flag
);
3021 if ( wday
== Inv_WeekDay
)
3024 return (wxChar
*)NULL
;
3030 case _T('b'): // a month name
3033 int flag
= *fmt
== _T('b') ? Name_Abbr
: Name_Full
;
3034 mon
= GetMonthFromName(GetAlphaToken(input
), flag
);
3035 if ( mon
== Inv_Month
)
3038 return (wxChar
*)NULL
;
3044 case _T('c'): // locale default date and time representation
3048 // this is the format which corresponds to ctime() output
3049 // and strptime("%c") should parse it, so try it first
3050 static const wxChar
*fmtCtime
= _T("%a %b %d %H:%M:%S %Y");
3052 const wxChar
*result
= dt
.ParseFormat(input
, fmtCtime
);
3055 result
= dt
.ParseFormat(input
, _T("%x %X"));
3060 result
= dt
.ParseFormat(input
, _T("%X %x"));
3065 // we've tried everything and still no match
3066 return (wxChar
*)NULL
;
3071 haveDay
= haveMon
= haveYear
=
3072 haveHour
= haveMin
= haveSec
= true;
3086 case _T('d'): // day of a month (01-31)
3087 if ( !GetNumericToken(width
, input
, &num
) ||
3088 (num
> 31) || (num
< 1) )
3091 return (wxChar
*)NULL
;
3094 // we can't check whether the day range is correct yet, will
3095 // do it later - assume ok for now
3097 mday
= (wxDateTime_t
)num
;
3100 case _T('H'): // hour in 24h format (00-23)
3101 if ( !GetNumericToken(width
, input
, &num
) || (num
> 23) )
3104 return (wxChar
*)NULL
;
3108 hour
= (wxDateTime_t
)num
;
3111 case _T('I'): // hour in 12h format (01-12)
3112 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3115 return (wxChar
*)NULL
;
3119 hourIsIn12hFormat
= true;
3120 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
3123 case _T('j'): // day of the year
3124 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 366) )
3127 return (wxChar
*)NULL
;
3131 yday
= (wxDateTime_t
)num
;
3134 case _T('m'): // month as a number (01-12)
3135 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
3138 return (wxChar
*)NULL
;
3142 mon
= (Month
)(num
- 1);
3145 case _T('M'): // minute as a decimal number (00-59)
3146 if ( !GetNumericToken(width
, input
, &num
) || (num
> 59) )
3149 return (wxChar
*)NULL
;
3153 min
= (wxDateTime_t
)num
;
3156 case _T('p'): // AM or PM string
3158 wxString am
, pm
, token
= GetAlphaToken(input
);
3160 GetAmPmStrings(&am
, &pm
);
3161 if (am
.empty() && pm
.empty())
3162 return (wxChar
*)NULL
; // no am/pm strings defined
3163 if ( token
.CmpNoCase(pm
) == 0 )
3167 else if ( token
.CmpNoCase(am
) != 0 )
3170 return (wxChar
*)NULL
;
3175 case _T('r'): // time as %I:%M:%S %p
3178 input
= dt
.ParseFormat(input
, _T("%I:%M:%S %p"));
3182 return (wxChar
*)NULL
;
3185 haveHour
= haveMin
= haveSec
= true;
3194 case _T('R'): // time as %H:%M
3197 input
= dt
.ParseFormat(input
, _T("%H:%M"));
3201 return (wxChar
*)NULL
;
3204 haveHour
= haveMin
= true;
3212 case _T('S'): // second as a decimal number (00-61)
3213 if ( !GetNumericToken(width
, input
, &num
) || (num
> 61) )
3216 return (wxChar
*)NULL
;
3220 sec
= (wxDateTime_t
)num
;
3223 case _T('T'): // time as %H:%M:%S
3226 input
= dt
.ParseFormat(input
, _T("%H:%M:%S"));
3230 return (wxChar
*)NULL
;
3233 haveHour
= haveMin
= haveSec
= true;
3242 case _T('w'): // weekday as a number (0-6), Sunday = 0
3243 if ( !GetNumericToken(width
, input
, &num
) || (wday
> 6) )
3246 return (wxChar
*)NULL
;
3250 wday
= (WeekDay
)num
;
3253 case _T('x'): // locale default date representation
3254 #ifdef HAVE_STRPTIME
3255 // try using strptime() -- it may fail even if the input is
3256 // correct but the date is out of range, so we will fall back
3257 // to our generic code anyhow
3261 const wxChar
*result
= CallStrptime(input
, "%x", &tm
);
3266 haveDay
= haveMon
= haveYear
= true;
3268 year
= 1900 + tm
.tm_year
;
3269 mon
= (Month
)tm
.tm_mon
;
3275 #endif // HAVE_STRPTIME
3280 wxString fmtDate
, fmtDateAlt
;
3282 if ( IsWestEuropeanCountry(GetCountry()) ||
3283 GetCountry() == Russia
)
3285 fmtDate
= _T("%d/%m/%y");
3286 fmtDateAlt
= _T("%m/%d/%y");
3290 fmtDate
= _T("%m/%d/%y");
3291 fmtDateAlt
= _T("%d/%m/%y");
3295 // The above doesn't work for all locales, try to query
3296 // Windows for the right way of formatting the date:
3297 GetLocaleDateFormat(&fmtDate
);
3300 const wxChar
*result
= dt
.ParseFormat(input
, fmtDate
);
3304 // ok, be nice and try another one
3305 result
= dt
.ParseFormat(input
, fmtDateAlt
);
3311 return (wxChar
*)NULL
;
3316 haveDay
= haveMon
= haveYear
= true;
3327 case _T('X'): // locale default time representation
3328 #ifdef HAVE_STRPTIME
3330 // use strptime() to do it for us (FIXME !Unicode friendly)
3332 input
= CallStrptime(input
, "%X", &tm
);
3335 return (wxChar
*)NULL
;
3338 haveHour
= haveMin
= haveSec
= true;
3344 #else // !HAVE_STRPTIME
3345 // TODO under Win32 we can query the LOCALE_ITIME system
3346 // setting which says whether the default time format is
3349 // try to parse what follows as "%H:%M:%S" and, if this
3350 // fails, as "%I:%M:%S %p" - this should catch the most
3354 const wxChar
*result
= dt
.ParseFormat(input
, _T("%T"));
3357 result
= dt
.ParseFormat(input
, _T("%r"));
3363 return (wxChar
*)NULL
;
3366 haveHour
= haveMin
= haveSec
= true;
3375 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
3378 case _T('y'): // year without century (00-99)
3379 if ( !GetNumericToken(width
, input
, &num
) || (num
> 99) )
3382 return (wxChar
*)NULL
;
3387 // TODO should have an option for roll over date instead of
3388 // hard coding it here
3389 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
3392 case _T('Y'): // year with century
3393 if ( !GetNumericToken(width
, input
, &num
) )
3396 return (wxChar
*)NULL
;
3400 year
= (wxDateTime_t
)num
;
3403 case _T('Z'): // timezone name
3404 wxFAIL_MSG(_T("TODO"));
3407 case _T('%'): // a percent sign
3408 if ( *input
++ != _T('%') )
3411 return (wxChar
*)NULL
;
3415 case 0: // the end of string
3416 wxFAIL_MSG(_T("unexpected format end"));
3420 default: // not a known format spec
3421 return (wxChar
*)NULL
;
3425 // format matched, try to construct a date from what we have now
3427 if ( dateDef
.IsValid() )
3429 // take this date as default
3430 tmDef
= dateDef
.GetTm();
3432 else if ( IsValid() )
3434 // if this date is valid, don't change it
3439 // no default and this date is invalid - fall back to Today()
3440 tmDef
= Today().GetTm();
3451 // TODO we don't check here that the values are consistent, if both year
3452 // day and month/day were found, we just ignore the year day and we
3453 // also always ignore the week day
3454 if ( haveMon
&& haveDay
)
3456 if ( mday
> GetNumOfDaysInMonth(tm
.year
, mon
) )
3458 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
3460 return (wxChar
*)NULL
;
3466 else if ( haveYDay
)
3468 if ( yday
> GetNumberOfDays(tm
.year
) )
3470 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
3472 return (wxChar
*)NULL
;
3475 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
3482 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
3484 // translate to 24hour format
3487 //else: either already in 24h format or no translation needed
3507 // finally check that the week day is consistent -- if we had it
3508 if ( haveWDay
&& GetWeekDay() != wday
)
3510 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
3518 const wxChar
*wxDateTime::ParseDateTime(const wxChar
*date
)
3520 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3522 // Set to current day and hour, so strings like '14:00' becomes today at
3523 // 14, not some other random date
3524 wxDateTime dtDate
= wxDateTime::Today();
3525 wxDateTime dtTime
= wxDateTime::Today();
3527 const wxChar
* pchTime
;
3529 // Try to parse the beginning of the string as a date
3530 const wxChar
* pchDate
= dtDate
.ParseDate(date
);
3532 // We got a date in the beginning, see if there is a time specified after the date
3535 // Skip spaces, as the ParseTime() function fails on spaces
3536 while ( wxIsspace(*pchDate
) )
3539 pchTime
= dtTime
.ParseTime(pchDate
);
3541 else // no date in the beginning
3543 // check and see if we have a time followed by a date
3544 pchTime
= dtTime
.ParseTime(date
);
3547 while ( wxIsspace(*pchTime
) )
3550 pchDate
= dtDate
.ParseDate(pchTime
);
3554 // If we have a date specified, set our own data to the same date
3555 if ( !pchDate
|| !pchTime
)
3558 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
3559 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
3560 dtTime
.GetMillisecond());
3562 // Return endpoint of scan
3563 return pchDate
> pchTime
? pchDate
: pchTime
;
3566 const wxChar
*wxDateTime::ParseDate(const wxChar
*date
)
3568 // this is a simplified version of ParseDateTime() which understands only
3569 // "today" (for wxDate compatibility) and digits only otherwise (and not
3570 // all esoteric constructions ParseDateTime() knows about)
3572 wxCHECK_MSG( date
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3574 const wxChar
*p
= date
;
3575 while ( wxIsspace(*p
) )
3578 // some special cases
3582 int dayDiffFromToday
;
3585 { wxTRANSLATE("today"), 0 },
3586 { wxTRANSLATE("yesterday"), -1 },
3587 { wxTRANSLATE("tomorrow"), 1 },
3590 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
3592 wxString date
= wxGetTranslation(literalDates
[n
].str
);
3593 size_t len
= date
.length();
3594 if ( wxStrlen(p
) >= len
)
3596 wxString
str(p
, len
);
3597 if ( str
.CmpNoCase(date
) == 0 )
3599 // nothing can follow this, so stop here
3602 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
3604 if ( dayDiffFromToday
)
3606 *this += wxDateSpan::Days(dayDiffFromToday
);
3614 // We try to guess what we have here: for each new (numeric) token, we
3615 // determine if it can be a month, day or a year. Of course, there is an
3616 // ambiguity as some numbers may be days as well as months, so we also
3617 // have the ability to back track.
3620 bool haveDay
= false, // the months day?
3621 haveWDay
= false, // the day of week?
3622 haveMon
= false, // the month?
3623 haveYear
= false; // the year?
3625 // and the value of the items we have (init them to get rid of warnings)
3626 WeekDay wday
= Inv_WeekDay
;
3627 wxDateTime_t day
= 0;
3628 wxDateTime::Month mon
= Inv_Month
;
3631 // tokenize the string
3633 static const wxChar
*dateDelimiters
= _T(".,/-\t\r\n ");
3634 wxStringTokenizer
tok(p
, dateDelimiters
);
3635 while ( tok
.HasMoreTokens() )
3637 wxString token
= tok
.GetNextToken();
3643 if ( token
.ToULong(&val
) )
3645 // guess what this number is
3651 if ( !haveMon
&& val
> 0 && val
<= 12 )
3653 // assume it is month
3656 else // not the month
3660 // this can only be the year
3663 else // may be either day or year
3665 wxDateTime_t max_days
= (wxDateTime_t
)(
3667 ? GetNumOfDaysInMonth(haveYear
? year
: Inv_Year
, mon
)
3672 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
3677 else // yes, suppose it's the day
3691 year
= (wxDateTime_t
)val
;
3700 day
= (wxDateTime_t
)val
;
3706 mon
= (Month
)(val
- 1);
3709 else // not a number
3711 // be careful not to overwrite the current mon value
3712 Month mon2
= GetMonthFromName(token
, Name_Full
| Name_Abbr
);
3713 if ( mon2
!= Inv_Month
)
3718 // but we already have a month - maybe we guessed wrong?
3721 // no need to check in month range as always < 12, but
3722 // the days are counted from 1 unlike the months
3723 day
= (wxDateTime_t
)(mon
+ 1);
3728 // could possible be the year (doesn't the year come
3729 // before the month in the japanese format?) (FIXME)
3738 else // not a valid month name
3740 wday
= GetWeekDayFromName(token
, Name_Full
| Name_Abbr
);
3741 if ( wday
!= Inv_WeekDay
)
3751 else // not a valid weekday name
3754 static const wxChar
*ordinals
[] =
3756 wxTRANSLATE("first"),
3757 wxTRANSLATE("second"),
3758 wxTRANSLATE("third"),
3759 wxTRANSLATE("fourth"),
3760 wxTRANSLATE("fifth"),
3761 wxTRANSLATE("sixth"),
3762 wxTRANSLATE("seventh"),
3763 wxTRANSLATE("eighth"),
3764 wxTRANSLATE("ninth"),
3765 wxTRANSLATE("tenth"),
3766 wxTRANSLATE("eleventh"),
3767 wxTRANSLATE("twelfth"),
3768 wxTRANSLATE("thirteenth"),
3769 wxTRANSLATE("fourteenth"),
3770 wxTRANSLATE("fifteenth"),
3771 wxTRANSLATE("sixteenth"),
3772 wxTRANSLATE("seventeenth"),
3773 wxTRANSLATE("eighteenth"),
3774 wxTRANSLATE("nineteenth"),
3775 wxTRANSLATE("twentieth"),
3776 // that's enough - otherwise we'd have problems with
3777 // composite (or not) ordinals
3781 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
3783 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
3789 if ( n
== WXSIZEOF(ordinals
) )
3791 // stop here - something unknown
3798 // don't try anything here (as in case of numeric day
3799 // above) - the symbolic day spec should always
3800 // precede the month/year
3806 day
= (wxDateTime_t
)(n
+ 1);
3811 nPosCur
= tok
.GetPosition();
3814 // either no more tokens or the scan was stopped by something we couldn't
3815 // parse - in any case, see if we can construct a date from what we have
3816 if ( !haveDay
&& !haveWDay
)
3818 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
3820 return (wxChar
*)NULL
;
3823 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
3824 !(haveDay
&& haveMon
&& haveYear
) )
3826 // without adjectives (which we don't support here) the week day only
3827 // makes sense completely separately or with the full date
3828 // specification (what would "Wed 1999" mean?)
3829 return (wxChar
*)NULL
;
3832 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
3834 // may be we have month and day instead of day and year?
3835 if ( haveDay
&& !haveMon
)
3839 // exchange day and month
3840 mon
= (wxDateTime::Month
)(day
- 1);
3842 // we're in the current year then
3843 if ( (year
> 0) && (year
<= (int)GetNumOfDaysInMonth(Inv_Year
, mon
)) )
3845 day
= (wxDateTime_t
)year
;
3850 //else: no, can't exchange, leave haveMon == false
3856 // if we give the year, month and day must be given too
3857 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
3859 return (wxChar
*)NULL
;
3865 mon
= GetCurrentMonth();
3870 year
= GetCurrentYear();
3875 Set(day
, mon
, year
);
3879 // check that it is really the same
3880 if ( GetWeekDay() != wday
)
3882 // inconsistency detected
3883 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
3885 return (wxChar
*)NULL
;
3893 SetToWeekDayInSameWeek(wday
);
3896 // return the pointer to the first unparsed char
3898 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
3900 // if we couldn't parse the token after the delimiter, put back the
3901 // delimiter as well
3908 const wxChar
*wxDateTime::ParseTime(const wxChar
*time
)
3910 wxCHECK_MSG( time
, (wxChar
*)NULL
, _T("NULL pointer in wxDateTime::Parse") );
3912 // first try some extra things
3919 { wxTRANSLATE("noon"), 12 },
3920 { wxTRANSLATE("midnight"), 00 },
3924 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
3926 wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
3927 size_t len
= timeString
.length();
3928 if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 )
3930 // casts required by DigitalMars
3931 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
3937 // try all time formats we may think about in the order from longest to
3940 // 12hour with AM/PM?
3941 const wxChar
*result
= ParseFormat(time
, _T("%I:%M:%S %p"));
3945 // normally, it's the same, but why not try it?
3946 result
= ParseFormat(time
, _T("%H:%M:%S"));
3951 // 12hour with AM/PM but without seconds?
3952 result
= ParseFormat(time
, _T("%I:%M %p"));
3958 result
= ParseFormat(time
, _T("%H:%M"));
3963 // just the hour and AM/PM?
3964 result
= ParseFormat(time
, _T("%I %p"));
3970 result
= ParseFormat(time
, _T("%H"));
3975 // parse the standard format: normally it is one of the formats above
3976 // but it may be set to something completely different by the user
3977 result
= ParseFormat(time
, _T("%X"));
3980 // TODO: parse timezones
3985 // ----------------------------------------------------------------------------
3986 // Workdays and holidays support
3987 // ----------------------------------------------------------------------------
3989 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
3991 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
3994 // ============================================================================
3996 // ============================================================================
3998 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
4001 return ds1
.Multiply(n
);
4004 // ============================================================================
4006 // ============================================================================
4008 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
4010 return wxTimeSpan(ts
).Multiply(n
);
4013 // this enum is only used in wxTimeSpan::Format() below but we can't declare
4014 // it locally to the method as it provokes an internal compiler error in egcs
4015 // 2.91.60 when building with -O2
4026 // not all strftime(3) format specifiers make sense here because, for example,
4027 // a time span doesn't have a year nor a timezone
4029 // Here are the ones which are supported (all of them are supported by strftime
4031 // %H hour in 24 hour format
4032 // %M minute (00 - 59)
4033 // %S second (00 - 59)
4036 // Also, for MFC CTimeSpan compatibility, we support
4037 // %D number of days
4039 // And, to be better than MFC :-), we also have
4040 // %E number of wEeks
4041 // %l milliseconds (000 - 999)
4042 wxString
wxTimeSpan::Format(const wxChar
*format
) const
4044 wxCHECK_MSG( format
, wxEmptyString
, _T("NULL format in wxTimeSpan::Format") );
4047 str
.Alloc(wxStrlen(format
));
4049 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
4051 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
4052 // question is what should ts.Format("%S") do? The code here returns "3273"
4053 // in this case (i.e. the total number of seconds, not just seconds % 60)
4054 // because, for me, this call means "give me entire time interval in
4055 // seconds" and not "give me the seconds part of the time interval"
4057 // If we agree that it should behave like this, it is clear that the
4058 // interpretation of each format specifier depends on the presence of the
4059 // other format specs in the string: if there was "%H" before "%M", we
4060 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
4062 // we remember the most important unit found so far
4063 TimeSpanPart partBiggest
= Part_MSec
;
4065 for ( const wxChar
*pch
= format
; *pch
; pch
++ )
4069 if ( ch
== _T('%') )
4071 // the start of the format specification of the printf() below
4072 wxString fmtPrefix
= _T('%');
4077 ch
= *++pch
; // get the format spec char
4081 wxFAIL_MSG( _T("invalid format character") );
4087 // skip the part below switch
4092 if ( partBiggest
< Part_Day
)
4098 partBiggest
= Part_Day
;
4103 partBiggest
= Part_Week
;
4109 if ( partBiggest
< Part_Hour
)
4115 partBiggest
= Part_Hour
;
4118 fmtPrefix
+= _T("02");
4122 n
= GetMilliseconds().ToLong();
4123 if ( partBiggest
< Part_MSec
)
4127 //else: no need to reset partBiggest to Part_MSec, it is
4128 // the least significant one anyhow
4130 fmtPrefix
+= _T("03");
4135 if ( partBiggest
< Part_Min
)
4141 partBiggest
= Part_Min
;
4144 fmtPrefix
+= _T("02");
4148 n
= GetSeconds().ToLong();
4149 if ( partBiggest
< Part_Sec
)
4155 partBiggest
= Part_Sec
;
4158 fmtPrefix
+= _T("02");
4162 str
+= wxString::Format(fmtPrefix
+ _T("ld"), n
);
4166 // normal character, just copy
4174 // ============================================================================
4175 // wxDateTimeHolidayAuthority and related classes
4176 // ============================================================================
4178 #include "wx/arrimpl.cpp"
4180 WX_DEFINE_OBJARRAY(wxDateTimeArray
);
4182 static int wxCMPFUNC_CONV
4183 wxDateTimeCompareFunc(wxDateTime
**first
, wxDateTime
**second
)
4185 wxDateTime dt1
= **first
,
4188 return dt1
== dt2
? 0 : dt1
< dt2
? -1 : +1;
4191 // ----------------------------------------------------------------------------
4192 // wxDateTimeHolidayAuthority
4193 // ----------------------------------------------------------------------------
4195 wxHolidayAuthoritiesArray
wxDateTimeHolidayAuthority::ms_authorities
;
4198 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime
& dt
)
4200 size_t count
= ms_authorities
.size();
4201 for ( size_t n
= 0; n
< count
; n
++ )
4203 if ( ms_authorities
[n
]->DoIsHoliday(dt
) )
4214 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime
& dtStart
,
4215 const wxDateTime
& dtEnd
,
4216 wxDateTimeArray
& holidays
)
4218 wxDateTimeArray hol
;
4222 size_t count
= ms_authorities
.size();
4223 for ( size_t nAuth
= 0; nAuth
< count
; nAuth
++ )
4225 ms_authorities
[nAuth
]->DoGetHolidaysInRange(dtStart
, dtEnd
, hol
);
4227 WX_APPEND_ARRAY(holidays
, hol
);
4230 holidays
.Sort(wxDateTimeCompareFunc
);
4232 return holidays
.size();
4236 void wxDateTimeHolidayAuthority::ClearAllAuthorities()
4238 WX_CLEAR_ARRAY(ms_authorities
);
4242 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority
*auth
)
4244 ms_authorities
.push_back(auth
);
4247 wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
4249 // required here for Darwin
4252 // ----------------------------------------------------------------------------
4253 // wxDateTimeWorkDays
4254 // ----------------------------------------------------------------------------
4256 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime
& dt
) const
4258 wxDateTime::WeekDay wd
= dt
.GetWeekDay();
4260 return (wd
== wxDateTime::Sun
) || (wd
== wxDateTime::Sat
);
4263 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime
& dtStart
,
4264 const wxDateTime
& dtEnd
,
4265 wxDateTimeArray
& holidays
) const
4267 if ( dtStart
> dtEnd
)
4269 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
4276 // instead of checking all days, start with the first Sat after dtStart and
4277 // end with the last Sun before dtEnd
4278 wxDateTime dtSatFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sat
),
4279 dtSatLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sat
),
4280 dtSunFirst
= dtStart
.GetNextWeekDay(wxDateTime::Sun
),
4281 dtSunLast
= dtEnd
.GetPrevWeekDay(wxDateTime::Sun
),
4284 for ( dt
= dtSatFirst
; dt
<= dtSatLast
; dt
+= wxDateSpan::Week() )
4289 for ( dt
= dtSunFirst
; dt
<= dtSunLast
; dt
+= wxDateSpan::Week() )
4294 return holidays
.GetCount();
4297 // ============================================================================
4298 // other helper functions
4299 // ============================================================================
4301 // ----------------------------------------------------------------------------
4302 // iteration helpers: can be used to write a for loop over enum variable like
4304 // for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
4305 // ----------------------------------------------------------------------------
4307 WXDLLIMPEXP_BASE
void wxNextMonth(wxDateTime::Month
& m
)
4309 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4311 // no wrapping or the for loop above would never end!
4312 m
= (wxDateTime::Month
)(m
+ 1);
4315 WXDLLIMPEXP_BASE
void wxPrevMonth(wxDateTime::Month
& m
)
4317 wxASSERT_MSG( m
< wxDateTime::Inv_Month
, _T("invalid month") );
4319 m
= m
== wxDateTime::Jan
? wxDateTime::Inv_Month
4320 : (wxDateTime::Month
)(m
- 1);
4323 WXDLLIMPEXP_BASE
void wxNextWDay(wxDateTime::WeekDay
& wd
)
4325 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4327 // no wrapping or the for loop above would never end!
4328 wd
= (wxDateTime::WeekDay
)(wd
+ 1);
4331 WXDLLIMPEXP_BASE
void wxPrevWDay(wxDateTime::WeekDay
& wd
)
4333 wxASSERT_MSG( wd
< wxDateTime::Inv_WeekDay
, _T("invalid week day") );
4335 wd
= wd
== wxDateTime::Sun
? wxDateTime::Inv_WeekDay
4336 : (wxDateTime::WeekDay
)(wd
- 1);
4339 #endif // wxUSE_DATETIME