1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/datetimefmt.cpp
3 // Purpose: wxDateTime formatting & parsing code
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 // ============================================================================
21 // ============================================================================
23 // ----------------------------------------------------------------------------
25 // ----------------------------------------------------------------------------
27 // For compilers that support precompilation, includes "wx.h".
28 #include "wx/wxprec.h"
34 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
38 #include "wx/msw/wrapwin.h"
40 #include "wx/string.h"
43 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
44 #include "wx/module.h"
48 #include "wx/thread.h"
59 #include "wx/datetime.h"
61 // ============================================================================
62 // implementation of wxDateTime
63 // ============================================================================
65 // ----------------------------------------------------------------------------
66 // helpers shared between datetime.cpp and datetimefmt.cpp
67 // ----------------------------------------------------------------------------
69 extern void InitTm(struct tm
& tm
);
71 extern int GetTimeZone();
73 extern wxString
CallStrftime(const wxString
& format
, const tm
* tm
);
75 // ----------------------------------------------------------------------------
76 // constants (see also datetime.cpp)
77 // ----------------------------------------------------------------------------
79 static const int DAYS_PER_WEEK
= 7;
81 static const int HOURS_PER_DAY
= 24;
83 static const int SEC_PER_MIN
= 60;
85 static const int MIN_PER_HOUR
= 60;
87 // ----------------------------------------------------------------------------
89 // ----------------------------------------------------------------------------
94 // all the functions below taking non-const wxString::const_iterator p advance
95 // it until the end of the match
97 // scans all digits (but no more than len) and returns the resulting number
98 bool GetNumericToken(size_t len
,
99 wxString::const_iterator
& p
,
100 const wxString::const_iterator
& end
,
101 unsigned long *number
)
105 while ( p
!= end
&& wxIsdigit(*p
) )
109 if ( len
&& ++n
> len
)
113 return !s
.empty() && s
.ToULong(number
);
116 // scans all alphabetic characters and returns the resulting string
118 GetAlphaToken(wxString::const_iterator
& p
,
119 const wxString::const_iterator
& end
)
122 while ( p
!= end
&& wxIsalpha(*p
) )
132 DateLang_English
= 1,
136 // return the month if the string is a month name or Inv_Month otherwise
138 // flags can contain wxDateTime::Name_Abbr/Name_Full or both of them and lang
139 // can be either DateLang_Local (default) to interpret string as a localized
140 // month name or DateLang_English to parse it as a standard English name or
141 // their combination to interpret it in any way
143 GetMonthFromName(wxString::const_iterator
& p
,
144 const wxString::const_iterator
& end
,
148 const wxString::const_iterator pOrig
= p
;
149 const wxString name
= GetAlphaToken(p
, end
);
151 return wxDateTime::Inv_Month
;
153 wxDateTime::Month mon
;
154 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
156 // case-insensitive comparison either one of or with both abbreviated
158 if ( flags
& wxDateTime::Name_Full
)
160 if ( lang
& DateLang_English
)
162 if ( name
.CmpNoCase(wxDateTime::GetEnglishMonthName(mon
,
163 wxDateTime::Name_Full
)) == 0 )
167 if ( lang
& DateLang_Local
)
169 if ( name
.CmpNoCase(wxDateTime::GetMonthName(mon
,
170 wxDateTime::Name_Full
)) == 0 )
175 if ( flags
& wxDateTime::Name_Abbr
)
177 if ( lang
& DateLang_English
)
179 if ( name
.CmpNoCase(wxDateTime::GetEnglishMonthName(mon
,
180 wxDateTime::Name_Abbr
)) == 0 )
184 if ( lang
& DateLang_Local
)
186 // some locales (e.g. French one) use periods for the
187 // abbreviated month names but it's never part of name so
188 // compare it specially
189 wxString nameAbbr
= wxDateTime::GetMonthName(mon
,
190 wxDateTime::Name_Abbr
);
191 const bool hasPeriod
= *nameAbbr
.rbegin() == '.';
193 nameAbbr
.erase(nameAbbr
.end() - 1);
195 if ( name
.CmpNoCase(nameAbbr
) == 0 )
199 // skip trailing period if it was part of the match
202 else // no match as no matching period
212 if ( mon
== wxDateTime::Inv_Month
)
218 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
220 // flags and lang parameters have the same meaning as for GetMonthFromName()
223 GetWeekDayFromName(wxString::const_iterator
& p
,
224 const wxString::const_iterator
& end
,
227 const wxString::const_iterator pOrig
= p
;
228 const wxString name
= GetAlphaToken(p
, end
);
230 return wxDateTime::Inv_WeekDay
;
232 wxDateTime::WeekDay wd
;
233 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
235 if ( flags
& wxDateTime::Name_Full
)
237 if ( lang
& DateLang_English
)
239 if ( name
.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd
,
240 wxDateTime::Name_Full
)) == 0 )
244 if ( lang
& DateLang_Local
)
246 if ( name
.CmpNoCase(wxDateTime::GetWeekDayName(wd
,
247 wxDateTime::Name_Full
)) == 0 )
252 if ( flags
& wxDateTime::Name_Abbr
)
254 if ( lang
& DateLang_English
)
256 if ( name
.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd
,
257 wxDateTime::Name_Abbr
)) == 0 )
261 if ( lang
& DateLang_Local
)
263 if ( name
.CmpNoCase(wxDateTime::GetWeekDayName(wd
,
264 wxDateTime::Name_Abbr
)) == 0 )
270 if ( wd
== wxDateTime::Inv_WeekDay
)
276 // parses string starting at given iterator using the specified format and,
277 // optionally, a fall back format (and optionally another one... but it stops
280 // if unsuccessful, returns invalid wxDateTime without changing p; otherwise
281 // advance p to the end of the match and returns wxDateTime containing the
282 // results of the parsing
284 ParseFormatAt(wxString::const_iterator
& p
,
285 const wxString::const_iterator
& end
,
287 // FIXME-VC6: using wxString() instead of wxEmptyString in the
288 // line below results in error C2062: type 'class
289 // wxString (__cdecl *)(void)' unexpected
290 const wxString
& fmtAlt
= wxEmptyString
)
292 const wxString
str(p
, end
);
293 wxString::const_iterator endParse
;
295 if ( dt
.ParseFormat(str
, fmt
, &endParse
) ||
296 (!fmtAlt
.empty() && dt
.ParseFormat(str
, fmtAlt
, &endParse
)) )
298 p
+= endParse
- str
.begin();
300 //else: all formats failed
305 } // anonymous namespace
307 // ----------------------------------------------------------------------------
308 // wxDateTime to/from text representations
309 // ----------------------------------------------------------------------------
311 wxString
wxDateTime::Format(const wxString
& formatp
, const TimeZone
& tz
) const
313 wxCHECK_MSG( !formatp
.empty(), wxEmptyString
,
314 wxT("NULL format in wxDateTime::Format") );
316 wxString format
= formatp
;
318 format
.Replace("%c",wxLocale::GetInfo(wxLOCALE_DATE_TIME_FMT
));
319 format
.Replace("%x",wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT
));
320 format
.Replace("%X",wxLocale::GetInfo(wxLOCALE_TIME_FMT
));
322 // we have to use our own implementation if the date is out of range of
323 // strftime() or if we use non standard specificators
324 #ifdef wxHAS_STRFTIME
325 time_t time
= GetTicks();
327 if ( (time
!= (time_t)-1) && !wxStrstr(format
, wxT("%l")) )
332 if ( tz
.GetOffset() == -GetTimeZone() )
334 // we are working with local time
335 tm
= wxLocaltime_r(&time
, &tmstruct
);
337 // should never happen
338 wxCHECK_MSG( tm
, wxEmptyString
, wxT("wxLocaltime_r() failed") );
342 time
+= (int)tz
.GetOffset();
344 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
345 int time2
= (int) time
;
351 tm
= wxGmtime_r(&time
, &tmstruct
);
353 // should never happen
354 wxCHECK_MSG( tm
, wxEmptyString
, wxT("wxGmtime_r() failed") );
358 tm
= (struct tm
*)NULL
;
364 return CallStrftime(format
, tm
);
367 //else: use generic code below
368 #endif // wxHAS_STRFTIME
370 // we only parse ANSI C format specifications here, no POSIX 2
371 // complications, no GNU extensions but we do add support for a "%l" format
372 // specifier allowing to get the number of milliseconds
375 // used for calls to strftime() when we only deal with time
376 struct tm tmTimeOnly
;
377 memset(&tmTimeOnly
, 0, sizeof(tmTimeOnly
));
378 tmTimeOnly
.tm_hour
= tm
.hour
;
379 tmTimeOnly
.tm_min
= tm
.min
;
380 tmTimeOnly
.tm_sec
= tm
.sec
;
381 tmTimeOnly
.tm_mday
= 1; // any date will do, use 1976-01-01
382 tmTimeOnly
.tm_mon
= 0;
383 tmTimeOnly
.tm_year
= 76;
384 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
386 wxString tmp
, res
, fmt
;
387 for ( wxString::const_iterator p
= format
.begin(); p
!= format
.end(); ++p
)
389 if ( *p
!= wxT('%') )
397 // set the default format
398 switch ( (*++p
).GetValue() )
400 case wxT('Y'): // year has 4 digits
404 case wxT('j'): // day of year has 3 digits
405 case wxT('l'): // milliseconds have 3 digits
409 case wxT('w'): // week day as number has only one
414 // it's either another valid format specifier in which case
415 // the format is "%02d" (for all the rest) or we have the
416 // field width preceding the format in which case it will
417 // override the default format anyhow
426 // start of the format specification
427 switch ( (*p
).GetValue() )
429 case wxT('a'): // a weekday name
431 // second parameter should be true for abbreviated names
432 res
+= GetWeekDayName(tm
.GetWeekDay(),
433 *p
== wxT('a') ? Name_Abbr
: Name_Full
);
436 case wxT('b'): // a month name
438 res
+= GetMonthName(tm
.mon
,
439 *p
== wxT('b') ? Name_Abbr
: Name_Full
);
442 case wxT('c'): // locale default date and time representation
443 case wxT('x'): // locale default date representation
444 #ifdef wxHAS_STRFTIME
446 // the problem: there is no way to know what do these format
447 // specifications correspond to for the current locale.
449 // the solution: use a hack and still use strftime(): first
450 // find the YEAR which is a year in the strftime() range (1970
451 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
452 // of the real year. Then make a copy of the format and
453 // replace all occurrences of YEAR in it with some unique
454 // string not appearing anywhere else in it, then use
455 // strftime() to format the date in year YEAR and then replace
456 // YEAR back by the real year and the unique replacement
457 // string back with YEAR. Notice that "all occurrences of YEAR"
458 // means all occurrences of 4 digit as well as 2 digit form!
460 // the bugs: we assume that neither of %c nor %x contains any
461 // fields which may change between the YEAR and real year. For
462 // example, the week number (%U, %W) and the day number (%j)
463 // will change if one of these years is leap and the other one
466 // find the YEAR: normally, for any year X, Jan 1 of the
467 // year X + 28 is the same weekday as Jan 1 of X (because
468 // the weekday advances by 1 for each normal X and by 2
469 // for each leap X, hence by 5 every 4 years or by 35
470 // which is 0 mod 7 every 28 years) but this rule breaks
471 // down if there are years between X and Y which are
472 // divisible by 4 but not leap (i.e. divisible by 100 but
473 // not 400), hence the correction.
475 int yearReal
= GetYear(tz
);
476 int mod28
= yearReal
% 28;
478 // be careful to not go too far - we risk to leave the
483 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
487 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
490 int nCentury
= year
/ 100,
491 nCenturyReal
= yearReal
/ 100;
493 // need to adjust for the years divisble by 400 which are
494 // not leap but are counted like leap ones if we just take
495 // the number of centuries in between for nLostWeekDays
496 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
497 (nCentury
/ 4 - nCenturyReal
/ 4);
499 // we have to gain back the "lost" weekdays: note that the
500 // effect of this loop is to not do anything to
501 // nLostWeekDays (which we won't use any more), but to
502 // (indirectly) set the year correctly
503 while ( (nLostWeekDays
% 7) != 0 )
505 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
508 // finally move the year below 2000 so that the 2-digit
509 // year number can never match the month or day of the
510 // month when we do the replacements below
514 wxASSERT_MSG( year
>= 1970 && year
< 2000,
515 wxT("logic error in wxDateTime::Format") );
518 // use strftime() to format the same date but in supported
521 // NB: we assume that strftime() doesn't check for the
522 // date validity and will happily format the date
523 // corresponding to Feb 29 of a non leap year (which
524 // may happen if yearReal was leap and year is not)
525 struct tm tmAdjusted
;
527 tmAdjusted
.tm_hour
= tm
.hour
;
528 tmAdjusted
.tm_min
= tm
.min
;
529 tmAdjusted
.tm_sec
= tm
.sec
;
530 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
531 tmAdjusted
.tm_yday
= GetDayOfYear();
532 tmAdjusted
.tm_mday
= tm
.mday
;
533 tmAdjusted
.tm_mon
= tm
.mon
;
534 tmAdjusted
.tm_year
= year
- 1900;
535 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
536 wxString str
= CallStrftime(*p
== wxT('c') ? wxT("%c")
540 // now replace the replacement year with the real year:
541 // notice that we have to replace the 4 digit year with
542 // a unique string not appearing in strftime() output
543 // first to prevent the 2 digit year from matching any
544 // substring of the 4 digit year (but any day, month,
545 // hours or minutes components should be safe because
546 // they are never in 70-99 range)
547 wxString
replacement("|");
548 while ( str
.find(replacement
) != wxString::npos
)
551 str
.Replace(wxString::Format("%d", year
),
553 str
.Replace(wxString::Format("%d", year
% 100),
554 wxString::Format("%d", yearReal
% 100));
555 str
.Replace(replacement
,
556 wxString::Format("%d", yearReal
));
560 #else // !wxHAS_STRFTIME
561 // Use "%m/%d/%y %H:%M:%S" format instead
562 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
563 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
564 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
567 case wxT('d'): // day of a month (01-31)
568 res
+= wxString::Format(fmt
, tm
.mday
);
571 case wxT('H'): // hour in 24h format (00-23)
572 res
+= wxString::Format(fmt
, tm
.hour
);
575 case wxT('I'): // hour in 12h format (01-12)
577 // 24h -> 12h, 0h -> 12h too
578 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
579 : tm
.hour
? tm
.hour
: 12;
580 res
+= wxString::Format(fmt
, hour12
);
584 case wxT('j'): // day of the year
585 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
588 case wxT('l'): // milliseconds (NOT STANDARD)
589 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
592 case wxT('m'): // month as a number (01-12)
593 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
596 case wxT('M'): // minute as a decimal number (00-59)
597 res
+= wxString::Format(fmt
, tm
.min
);
600 case wxT('p'): // AM or PM string
601 #ifdef wxHAS_STRFTIME
602 res
+= CallStrftime(wxT("%p"), &tmTimeOnly
);
603 #else // !wxHAS_STRFTIME
604 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
605 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
608 case wxT('S'): // second as a decimal number (00-61)
609 res
+= wxString::Format(fmt
, tm
.sec
);
612 case wxT('U'): // week number in the year (Sunday 1st week day)
613 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
616 case wxT('W'): // week number in the year (Monday 1st week day)
617 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
620 case wxT('w'): // weekday as a number (0-6), Sunday = 0
621 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
624 // case wxT('x'): -- handled with "%c"
626 case wxT('X'): // locale default time representation
627 // just use strftime() to format the time for us
628 #ifdef wxHAS_STRFTIME
629 res
+= CallStrftime(wxT("%X"), &tmTimeOnly
);
630 #else // !wxHAS_STRFTIME
631 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
632 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
635 case wxT('y'): // year without century (00-99)
636 res
+= wxString::Format(fmt
, tm
.year
% 100);
639 case wxT('Y'): // year with century
640 res
+= wxString::Format(fmt
, tm
.year
);
643 case wxT('Z'): // timezone name
644 #ifdef wxHAS_STRFTIME
645 res
+= CallStrftime(wxT("%Z"), &tmTimeOnly
);
650 // is it the format width?
652 while ( *p
== wxT('-') || *p
== wxT('+') ||
653 *p
== wxT(' ') || wxIsdigit(*p
) )
660 // we've only got the flags and width so far in fmt
661 fmt
.Prepend(wxT('%'));
662 fmt
.Append(wxT('d'));
669 // no, it wasn't the width
670 wxFAIL_MSG(wxT("unknown format specificator"));
672 // fall through and just copy it nevertheless
674 case wxT('%'): // a percent sign
678 case 0: // the end of string
679 wxFAIL_MSG(wxT("missing format at the end of string"));
681 // just put the '%' which was the last char in format
691 // this function parses a string in (strict) RFC 822 format: see the section 5
692 // of the RFC for the detailed description, but briefly it's something of the
693 // form "Sat, 18 Dec 1999 00:48:30 +0100"
695 // this function is "strict" by design - it must reject anything except true
696 // RFC822 time specs.
698 wxDateTime::ParseRfc822Date(const wxString
& date
, wxString::const_iterator
*end
)
700 const wxString::const_iterator pEnd
= date
.end();
701 wxString::const_iterator p
= date
.begin();
704 const wxDateTime::WeekDay
705 wd
= GetWeekDayFromName(p
, pEnd
, Name_Abbr
, DateLang_English
);
706 if ( wd
== Inv_WeekDay
)
708 //else: ignore week day for now, we could also check that it really
709 // corresponds to the specified date
711 // 2. separating comma
712 if ( *p
++ != ',' || *p
++ != ' ' )
716 if ( !wxIsdigit(*p
) )
719 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - '0');
723 day
= (wxDateTime_t
)(day
+ (*p
++ - '0'));
730 const Month mon
= GetMonthFromName(p
, pEnd
, Name_Abbr
, DateLang_English
);
731 if ( mon
== Inv_Month
)
738 if ( !wxIsdigit(*p
) )
741 int year
= *p
++ - '0';
742 if ( !wxIsdigit(*p
) ) // should have at least 2 digits in the year
748 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
754 if ( !wxIsdigit(*p
) )
756 // no 3 digit years please
767 // 6. time in hh:mm:ss format with seconds being optional
768 if ( !wxIsdigit(*p
) )
771 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - '0');
773 if ( !wxIsdigit(*p
) )
777 hour
= (wxDateTime_t
)(hour
+ (*p
++ - '0'));
782 if ( !wxIsdigit(*p
) )
785 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - '0');
787 if ( !wxIsdigit(*p
) )
791 min
+= (wxDateTime_t
)(*p
++ - '0');
793 wxDateTime_t sec
= 0;
797 if ( !wxIsdigit(*p
) )
800 sec
= (wxDateTime_t
)(*p
++ - '0');
802 if ( !wxIsdigit(*p
) )
806 sec
+= (wxDateTime_t
)(*p
++ - '0');
812 // 7. now the interesting part: the timezone
813 int offset
= 0; // just to suppress warnings
814 if ( *p
== '-' || *p
== '+' )
816 // the explicit offset given: it has the form of hhmm
817 bool plus
= *p
++ == '+';
819 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
824 offset
= MIN_PER_HOUR
*(10*(*p
- '0') + (*(p
+ 1) - '0'));
828 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
832 offset
+= 10*(*p
- '0') + (*(p
+ 1) - '0');
841 // the symbolic timezone given: may be either military timezone or one
842 // of standard abbreviations
845 // military: Z = UTC, J unused, A = -1, ..., Y = +12
846 static const int offsets
[26] =
848 //A B C D E F G H I J K L M
849 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
850 //N O P R Q S T U V W Z Y Z
851 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
854 if ( *p
< wxT('A') || *p
> wxT('Z') || *p
== wxT('J') )
857 offset
= offsets
[*p
++ - 'A'];
862 const wxString
tz(p
, date
.end());
863 if ( tz
== wxT("UT") || tz
== wxT("UTC") || tz
== wxT("GMT") )
865 else if ( tz
== wxT("AST") )
867 else if ( tz
== wxT("ADT") )
869 else if ( tz
== wxT("EST") )
871 else if ( tz
== wxT("EDT") )
873 else if ( tz
== wxT("CST") )
875 else if ( tz
== wxT("CDT") )
877 else if ( tz
== wxT("MST") )
879 else if ( tz
== wxT("MDT") )
881 else if ( tz
== wxT("PST") )
883 else if ( tz
== wxT("PDT") )
892 offset
*= MIN_PER_HOUR
;
896 // the spec was correct, construct the date from the values we found
897 Set(day
, mon
, year
, hour
, min
, sec
);
898 MakeFromTimezone(TimeZone::Make(offset
*SEC_PER_MIN
));
907 wxDateTime::ParseFormat(const wxString
& date
,
908 const wxString
& format
,
909 const wxDateTime
& dateDef
,
910 wxString::const_iterator
*endParse
)
912 wxCHECK_MSG( !format
.empty(), false, "format can't be empty" );
913 wxCHECK_MSG( endParse
, false, "end iterator pointer must be specified" );
918 // what fields have we found?
919 bool haveWDay
= false,
929 bool hourIsIn12hFormat
= false, // or in 24h one?
930 isPM
= false; // AM by default
932 // and the value of the items we have (init them to get rid of warnings)
933 wxDateTime_t msec
= 0,
937 WeekDay wday
= Inv_WeekDay
;
938 wxDateTime_t yday
= 0,
940 wxDateTime::Month mon
= Inv_Month
;
943 wxString::const_iterator input
= date
.begin();
944 const wxString::const_iterator end
= date
.end();
945 for ( wxString::const_iterator fmt
= format
.begin(); fmt
!= format
.end(); ++fmt
)
947 if ( *fmt
!= wxT('%') )
949 if ( wxIsspace(*fmt
) )
951 // a white space in the format string matches 0 or more white
952 // spaces in the input
953 while ( input
!= end
&& wxIsspace(*input
) )
960 // any other character (not whitespace, not '%') must be
961 // matched by itself in the input
962 if ( input
== end
|| *input
++ != *fmt
)
969 // done with this format char
973 // start of a format specification
975 // parse the optional width
977 while ( wxIsdigit(*++fmt
) )
983 // the default widths for the various fields
986 switch ( (*fmt
).GetValue() )
988 case wxT('Y'): // year has 4 digits
992 case wxT('j'): // day of year has 3 digits
993 case wxT('l'): // milliseconds have 3 digits
997 case wxT('w'): // week day as number has only one
1002 // default for all other fields
1007 // then the format itself
1008 switch ( (*fmt
).GetValue() )
1010 case wxT('a'): // a weekday name
1013 wday
= GetWeekDayFromName
1016 *fmt
== 'a' ? Name_Abbr
: Name_Full
,
1019 if ( wday
== Inv_WeekDay
)
1028 case wxT('b'): // a month name
1031 mon
= GetMonthFromName
1034 *fmt
== 'b' ? Name_Abbr
: Name_Full
,
1037 if ( mon
== Inv_Month
)
1046 case wxT('c'): // locale default date and time representation
1052 fmtDateTime
= wxLocale::GetInfo(wxLOCALE_DATE_TIME_FMT
);
1053 if ( !fmtDateTime
.empty() )
1054 dt
= ParseFormatAt(input
, end
, fmtDateTime
);
1055 #endif // wxUSE_INTL
1056 if ( !dt
.IsValid() )
1058 // also try the format which corresponds to ctime()
1059 // output (i.e. the "C" locale default)
1060 dt
= ParseFormatAt(input
, end
, wxS("%a %b %d %H:%M:%S %Y"));
1063 if ( !dt
.IsValid() )
1065 // and finally also the two generic date/time formats
1066 dt
= ParseFormatAt(input
, end
, wxS("%x %X"), wxS("%X %x"));
1069 if ( !dt
.IsValid() )
1072 const Tm tm
= dt
.GetTm();
1082 haveDay
= haveMon
= haveYear
=
1083 haveHour
= haveMin
= haveSec
= true;
1087 case wxT('d'): // day of a month (01-31)
1088 case 'e': // day of a month (1-31) (GNU extension)
1089 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1090 (num
> 31) || (num
< 1) )
1096 // we can't check whether the day range is correct yet, will
1097 // do it later - assume ok for now
1099 mday
= (wxDateTime_t
)num
;
1102 case wxT('H'): // hour in 24h format (00-23)
1103 if ( !GetNumericToken(width
, input
, end
, &num
) || (num
> 23) )
1110 hour
= (wxDateTime_t
)num
;
1113 case wxT('I'): // hour in 12h format (01-12)
1114 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1115 !num
|| (num
> 12) )
1122 hourIsIn12hFormat
= true;
1123 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
1126 case wxT('j'): // day of the year
1127 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1128 !num
|| (num
> 366) )
1135 yday
= (wxDateTime_t
)num
;
1138 case wxT('l'): // milliseconds (0-999)
1139 if ( !GetNumericToken(width
, input
, end
, &num
) )
1143 msec
= (wxDateTime_t
)num
;
1146 case wxT('m'): // month as a number (01-12)
1147 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1148 !num
|| (num
> 12) )
1155 mon
= (Month
)(num
- 1);
1158 case wxT('M'): // minute as a decimal number (00-59)
1159 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1167 min
= (wxDateTime_t
)num
;
1170 case wxT('p'): // AM or PM string
1173 GetAmPmStrings(&am
, &pm
);
1175 // we can never match %p in locales which don't use AM/PM
1176 if ( am
.empty() || pm
.empty() )
1179 const size_t pos
= input
- date
.begin();
1180 if ( date
.compare(pos
, pm
.length(), pm
) == 0 )
1183 input
+= pm
.length();
1185 else if ( date
.compare(pos
, am
.length(), am
) == 0 )
1187 input
+= am
.length();
1196 case wxT('r'): // time as %I:%M:%S %p
1199 if ( !dt
.ParseFormat(wxString(input
, end
),
1200 wxS("%I:%M:%S %p"), &input
) )
1203 haveHour
= haveMin
= haveSec
= true;
1205 const Tm tm
= dt
.GetTm();
1212 case wxT('R'): // time as %H:%M
1215 dt
= ParseFormatAt(input
, end
, wxS("%H:%M"));
1216 if ( !dt
.IsValid() )
1222 const Tm tm
= dt
.GetTm();
1228 case wxT('S'): // second as a decimal number (00-61)
1229 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1237 sec
= (wxDateTime_t
)num
;
1240 case wxT('T'): // time as %H:%M:%S
1243 dt
= ParseFormatAt(input
, end
, wxS("%H:%M:%S"));
1244 if ( !dt
.IsValid() )
1251 const Tm tm
= dt
.GetTm();
1258 case wxT('w'): // weekday as a number (0-6), Sunday = 0
1259 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1267 wday
= (WeekDay
)num
;
1270 case wxT('x'): // locale default date representation
1274 fmtDate
= wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT
),
1275 fmtDateAlt
= wxLocale::GetInfo(wxLOCALE_LONG_DATE_FMT
);
1276 #else // !wxUSE_INTL
1277 wxString fmtDate
, fmtDateAlt
;
1278 #endif // wxUSE_INTL/!wxUSE_INTL
1279 if ( fmtDate
.empty() )
1281 if ( IsWestEuropeanCountry(GetCountry()) ||
1282 GetCountry() == Russia
)
1284 fmtDate
= wxS("%d/%m/%Y");
1285 fmtDateAlt
= wxS("%m/%d/%Y");
1289 fmtDate
= wxS("%m/%d/%Y");
1290 fmtDateAlt
= wxS("%d/%m/%Y");
1295 dt
= ParseFormatAt(input
, end
, fmtDate
, fmtDateAlt
);
1297 if ( !dt
.IsValid() )
1299 // try with short years too
1300 fmtDate
.Replace("%Y","%y");
1301 fmtDateAlt
.Replace("%Y","%y");
1302 dt
= ParseFormatAt(input
, end
, fmtDate
, fmtDateAlt
);
1304 if ( !dt
.IsValid() )
1308 const Tm tm
= dt
.GetTm();
1321 case wxT('X'): // locale default time representation
1324 wxString fmtTime
= wxLocale::GetInfo(wxLOCALE_TIME_FMT
),
1326 #else // !wxUSE_INTL
1327 wxString fmtTime
, fmtTimeAlt
;
1328 #endif // wxUSE_INTL/!wxUSE_INTL
1329 if ( fmtTime
.empty() )
1331 // try to parse what follows as "%H:%M:%S" and, if this
1332 // fails, as "%I:%M:%S %p" - this should catch the most
1339 dt
= ParseFormatAt(input
, end
, fmtTime
, fmtTimeAlt
);
1340 if ( !dt
.IsValid() )
1347 const Tm tm
= dt
.GetTm();
1354 case wxT('y'): // year without century (00-99)
1355 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1364 // TODO should have an option for roll over date instead of
1365 // hard coding it here
1366 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
1369 case wxT('Y'): // year with century
1370 if ( !GetNumericToken(width
, input
, end
, &num
) )
1377 year
= (wxDateTime_t
)num
;
1380 case wxT('Z'): // timezone name
1381 // FIXME: currently we just ignore everything that looks like a
1383 GetAlphaToken(input
, end
);
1386 case wxT('%'): // a percent sign
1387 if ( *input
++ != wxT('%') )
1394 case 0: // the end of string
1395 wxFAIL_MSG(wxT("unexpected format end"));
1399 default: // not a known format spec
1404 // format matched, try to construct a date from what we have now
1406 if ( dateDef
.IsValid() )
1408 // take this date as default
1409 tmDef
= dateDef
.GetTm();
1411 else if ( IsValid() )
1413 // if this date is valid, don't change it
1418 // no default and this date is invalid - fall back to Today()
1419 tmDef
= Today().GetTm();
1435 // TODO we don't check here that the values are consistent, if both year
1436 // day and month/day were found, we just ignore the year day and we
1437 // also always ignore the week day
1440 if ( mday
> GetNumberOfDays(tm
.mon
, tm
.year
) )
1445 else if ( haveYDay
)
1447 if ( yday
> GetNumberOfDays(tm
.year
) )
1450 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
1457 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
1459 // translate to 24hour format
1462 //else: either already in 24h format or no translation needed
1485 // finally check that the week day is consistent -- if we had it
1486 if ( haveWDay
&& GetWeekDay() != wday
)
1495 wxDateTime::ParseDateTime(const wxString
& date
, wxString::const_iterator
*end
)
1497 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1503 wxString::const_iterator
1508 // If we got a date in the beginning, see if there is a time specified
1510 if ( dtDate
.ParseDate(date
, &endDate
) )
1512 // Skip spaces, as the ParseTime() function fails on spaces
1513 while ( endDate
!= date
.end() && wxIsspace(*endDate
) )
1516 const wxString
timestr(endDate
, date
.end());
1517 if ( !dtTime
.ParseTime(timestr
, &endTime
) )
1520 endBoth
= endDate
+ (endTime
- timestr
.begin());
1522 else // no date in the beginning
1524 // check if we have a time followed by a date
1525 if ( !dtTime
.ParseTime(date
, &endTime
) )
1528 while ( endTime
!= date
.end() && wxIsspace(*endTime
) )
1531 const wxString
datestr(endTime
, date
.end());
1532 if ( !dtDate
.ParseDate(datestr
, &endDate
) )
1535 endBoth
= endTime
+ (endDate
- datestr
.begin());
1538 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
1539 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
1540 dtTime
.GetMillisecond());
1548 wxDateTime::ParseDate(const wxString
& date
, wxString::const_iterator
*end
)
1550 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1552 // this is a simplified version of ParseDateTime() which understands only
1553 // "today" (for wxDate compatibility) and digits only otherwise (and not
1554 // all esoteric constructions ParseDateTime() knows about)
1556 const wxString::const_iterator pBegin
= date
.begin();
1557 const wxString::const_iterator pEnd
= date
.end();
1559 wxString::const_iterator p
= pBegin
;
1560 while ( p
!= pEnd
&& wxIsspace(*p
) )
1563 // some special cases
1567 int dayDiffFromToday
;
1570 { wxTRANSLATE("today"), 0 },
1571 { wxTRANSLATE("yesterday"), -1 },
1572 { wxTRANSLATE("tomorrow"), 1 },
1575 const size_t lenRest
= pEnd
- p
;
1576 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
1578 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
1579 size_t len
= dateStr
.length();
1581 if ( len
> lenRest
)
1584 const wxString::const_iterator pEnd
= p
+ len
;
1585 if ( wxString(p
, pEnd
).CmpNoCase(dateStr
) == 0 )
1587 // nothing can follow this, so stop here
1591 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
1593 if ( dayDiffFromToday
)
1595 *this += wxDateSpan::Days(dayDiffFromToday
);
1604 // We try to guess what we have here: for each new (numeric) token, we
1605 // determine if it can be a month, day or a year. Of course, there is an
1606 // ambiguity as some numbers may be days as well as months, so we also
1607 // have the ability to back track.
1610 bool haveDay
= false, // the months day?
1611 haveWDay
= false, // the day of week?
1612 haveMon
= false, // the month?
1613 haveYear
= false; // the year?
1615 bool monWasNumeric
= false; // was month specified as a number?
1617 // and the value of the items we have (init them to get rid of warnings)
1618 WeekDay wday
= Inv_WeekDay
;
1619 wxDateTime_t day
= 0;
1620 wxDateTime::Month mon
= Inv_Month
;
1623 // tokenize the string
1626 // skip white space and date delimiters
1627 while ( wxStrchr(".,/-\t\r\n ", *p
) )
1632 // modify copy of the iterator as we're not sure if the next token is
1633 // still part of the date at all
1634 wxString::const_iterator pCopy
= p
;
1636 // we can have either alphabetic or numeric token, start by testing if
1639 if ( GetNumericToken(10 /* max length */, pCopy
, pEnd
, &val
) )
1641 // guess what this number is
1647 if ( !haveMon
&& val
> 0 && val
<= 12 )
1649 // assume it is month
1652 else // not the month
1656 // this can only be the year
1659 else // may be either day or year
1661 // use a leap year if we don't have the year yet to allow
1662 // dates like 2/29/1976 which would be rejected otherwise
1663 wxDateTime_t max_days
= (wxDateTime_t
)(
1665 ? GetNumberOfDays(mon
, haveYear
? year
: 1976)
1670 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
1675 else // yes, suppose it's the day
1689 year
= (wxDateTime_t
)val
;
1698 day
= (wxDateTime_t
)val
;
1703 monWasNumeric
= true;
1705 mon
= (Month
)(val
- 1);
1708 else // not a number
1710 // be careful not to overwrite the current mon value
1711 Month mon2
= GetMonthFromName
1714 Name_Full
| Name_Abbr
,
1715 DateLang_Local
| DateLang_English
1717 if ( mon2
!= Inv_Month
)
1722 // but we already have a month - maybe we guessed wrong
1723 // when we had interpreted that numeric value as a month
1724 // and it was the day number instead?
1725 if ( haveDay
|| !monWasNumeric
)
1728 // assume we did and change our mind: reinterpret the month
1729 // value as a day (notice that there is no need to check
1730 // that it is valid as month values are always < 12, but
1731 // the days are counted from 1 unlike the months)
1732 day
= (wxDateTime_t
)(mon
+ 1);
1740 else // not a valid month name
1742 WeekDay wday2
= GetWeekDayFromName
1745 Name_Full
| Name_Abbr
,
1746 DateLang_Local
| DateLang_English
1748 if ( wday2
!= Inv_WeekDay
)
1758 else // not a valid weekday name
1761 static const char *const ordinals
[] =
1763 wxTRANSLATE("first"),
1764 wxTRANSLATE("second"),
1765 wxTRANSLATE("third"),
1766 wxTRANSLATE("fourth"),
1767 wxTRANSLATE("fifth"),
1768 wxTRANSLATE("sixth"),
1769 wxTRANSLATE("seventh"),
1770 wxTRANSLATE("eighth"),
1771 wxTRANSLATE("ninth"),
1772 wxTRANSLATE("tenth"),
1773 wxTRANSLATE("eleventh"),
1774 wxTRANSLATE("twelfth"),
1775 wxTRANSLATE("thirteenth"),
1776 wxTRANSLATE("fourteenth"),
1777 wxTRANSLATE("fifteenth"),
1778 wxTRANSLATE("sixteenth"),
1779 wxTRANSLATE("seventeenth"),
1780 wxTRANSLATE("eighteenth"),
1781 wxTRANSLATE("nineteenth"),
1782 wxTRANSLATE("twentieth"),
1783 // that's enough - otherwise we'd have problems with
1784 // composite (or not) ordinals
1788 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
1790 const wxString ord
= wxGetTranslation(ordinals
[n
]);
1791 const size_t len
= ord
.length();
1792 if ( date
.compare(p
- pBegin
, len
, ord
) == 0 )
1799 if ( n
== WXSIZEOF(ordinals
) )
1801 // stop here - something unknown
1808 // don't try anything here (as in case of numeric day
1809 // above) - the symbolic day spec should always
1810 // precede the month/year
1816 day
= (wxDateTime_t
)(n
+ 1);
1821 // advance iterator past a successfully parsed token
1825 // either no more tokens or the scan was stopped by something we couldn't
1826 // parse - in any case, see if we can construct a date from what we have
1827 if ( !haveDay
&& !haveWDay
)
1830 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
1831 !(haveDay
&& haveMon
&& haveYear
) )
1833 // without adjectives (which we don't support here) the week day only
1834 // makes sense completely separately or with the full date
1835 // specification (what would "Wed 1999" mean?)
1839 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
1841 // may be we have month and day instead of day and year?
1842 if ( haveDay
&& !haveMon
)
1846 // exchange day and month
1847 mon
= (wxDateTime::Month
)(day
- 1);
1849 // we're in the current year then
1850 if ( (year
> 0) && (year
<= (int)GetNumberOfDays(mon
, Inv_Year
)) )
1852 day
= (wxDateTime_t
)year
;
1857 //else: no, can't exchange, leave haveMon == false
1867 mon
= GetCurrentMonth();
1872 year
= GetCurrentYear();
1877 // normally we check the day above but the check is optimistic in case
1878 // we find the day before its month/year so we have to redo it now
1879 if ( day
> GetNumberOfDays(mon
, year
) )
1882 Set(day
, mon
, year
);
1886 // check that it is really the same
1887 if ( GetWeekDay() != wday
)
1895 SetToWeekDayInSameWeek(wday
);
1904 wxDateTime::ParseTime(const wxString
& time
, wxString::const_iterator
*end
)
1906 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1908 // first try some extra things
1915 { wxTRANSLATE("noon"), 12 },
1916 { wxTRANSLATE("midnight"), 00 },
1920 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
1922 const wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
1923 if ( timeString
.CmpNoCase(wxString(time
, timeString
.length())) == 0 )
1925 // casts required by DigitalMars
1926 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
1929 *end
= time
.begin() + timeString
.length();
1935 // try all time formats we may think about in the order from longest to
1937 static const char *const timeFormats
[] =
1939 "%I:%M:%S %p", // 12hour with AM/PM
1940 "%H:%M:%S", // could be the same or 24 hour one so try it too
1941 "%I:%M %p", // 12hour with AM/PM but without seconds
1942 "%H:%M", // and a possibly 24 hour version without seconds
1943 "%X", // possibly something from above or maybe something
1944 // completely different -- try it last
1946 // TODO: parse timezones
1949 for ( size_t nFmt
= 0; nFmt
< WXSIZEOF(timeFormats
); nFmt
++ )
1951 if ( ParseFormat(time
, timeFormats
[nFmt
], end
) )
1958 // ----------------------------------------------------------------------------
1959 // Workdays and holidays support
1960 // ----------------------------------------------------------------------------
1962 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
1964 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
1967 // ============================================================================
1969 // ============================================================================
1971 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
1974 return ds1
.Multiply(n
);
1977 // ============================================================================
1979 // ============================================================================
1981 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
1983 return wxTimeSpan(ts
).Multiply(n
);
1986 // this enum is only used in wxTimeSpan::Format() below but we can't declare
1987 // it locally to the method as it provokes an internal compiler error in egcs
1988 // 2.91.60 when building with -O2
1999 // not all strftime(3) format specifiers make sense here because, for example,
2000 // a time span doesn't have a year nor a timezone
2002 // Here are the ones which are supported (all of them are supported by strftime
2004 // %H hour in 24 hour format
2005 // %M minute (00 - 59)
2006 // %S second (00 - 59)
2009 // Also, for MFC CTimeSpan compatibility, we support
2010 // %D number of days
2012 // And, to be better than MFC :-), we also have
2013 // %E number of wEeks
2014 // %l milliseconds (000 - 999)
2015 wxString
wxTimeSpan::Format(const wxString
& format
) const
2017 // we deal with only positive time spans here and just add the sign in
2018 // front for the negative ones
2021 wxString
str(Negate().Format(format
));
2025 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
2026 wxT("NULL format in wxTimeSpan::Format") );
2029 str
.Alloc(format
.length());
2031 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
2033 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
2034 // question is what should ts.Format("%S") do? The code here returns "3273"
2035 // in this case (i.e. the total number of seconds, not just seconds % 60)
2036 // because, for me, this call means "give me entire time interval in
2037 // seconds" and not "give me the seconds part of the time interval"
2039 // If we agree that it should behave like this, it is clear that the
2040 // interpretation of each format specifier depends on the presence of the
2041 // other format specs in the string: if there was "%H" before "%M", we
2042 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
2044 // we remember the most important unit found so far
2045 TimeSpanPart partBiggest
= Part_MSec
;
2047 for ( wxString::const_iterator pch
= format
.begin(); pch
!= format
.end(); ++pch
)
2051 if ( ch
== wxT('%') )
2053 // the start of the format specification of the printf() below
2054 wxString
fmtPrefix(wxT('%'));
2059 // the number of digits for the format string, 0 if unused
2060 unsigned digits
= 0;
2062 ch
= *++pch
; // get the format spec char
2066 wxFAIL_MSG( wxT("invalid format character") );
2072 // skip the part below switch
2077 if ( partBiggest
< Part_Day
)
2083 partBiggest
= Part_Day
;
2088 partBiggest
= Part_Week
;
2094 if ( partBiggest
< Part_Hour
)
2100 partBiggest
= Part_Hour
;
2107 n
= GetMilliseconds().ToLong();
2108 if ( partBiggest
< Part_MSec
)
2112 //else: no need to reset partBiggest to Part_MSec, it is
2113 // the least significant one anyhow
2120 if ( partBiggest
< Part_Min
)
2126 partBiggest
= Part_Min
;
2133 n
= GetSeconds().ToLong();
2134 if ( partBiggest
< Part_Sec
)
2140 partBiggest
= Part_Sec
;
2149 fmtPrefix
<< wxT("0") << digits
;
2152 str
+= wxString::Format(fmtPrefix
+ wxT("ld"), n
);
2156 // normal character, just copy
2164 #endif // wxUSE_DATETIME