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"
62 // ============================================================================
63 // implementation of wxDateTime
64 // ============================================================================
66 // ----------------------------------------------------------------------------
67 // helpers shared between datetime.cpp and datetimefmt.cpp
68 // ----------------------------------------------------------------------------
70 extern void InitTm(struct tm
& tm
);
72 extern wxString
CallStrftime(const wxString
& format
, const tm
* tm
);
74 // ----------------------------------------------------------------------------
75 // constants (see also datetime.cpp)
76 // ----------------------------------------------------------------------------
78 static const int DAYS_PER_WEEK
= 7;
80 static const int HOURS_PER_DAY
= 24;
82 static const int SEC_PER_MIN
= 60;
84 static const int MIN_PER_HOUR
= 60;
86 // ----------------------------------------------------------------------------
88 // ----------------------------------------------------------------------------
93 // all the functions below taking non-const wxString::const_iterator p advance
94 // it until the end of the match
96 // scans all digits (but no more than len) and returns the resulting number
97 bool GetNumericToken(size_t len
,
98 wxString::const_iterator
& p
,
99 const wxString::const_iterator
& end
,
100 unsigned long *number
)
104 while ( p
!= end
&& wxIsdigit(*p
) )
108 if ( len
&& ++n
> len
)
112 return !s
.empty() && s
.ToULong(number
);
115 // scans all alphabetic characters and returns the resulting string
117 GetAlphaToken(wxString::const_iterator
& p
,
118 const wxString::const_iterator
& end
)
121 while ( p
!= end
&& wxIsalpha(*p
) )
131 DateLang_English
= 1,
135 // return the month if the string is a month name or Inv_Month otherwise
137 // flags can contain wxDateTime::Name_Abbr/Name_Full or both of them and lang
138 // can be either DateLang_Local (default) to interpret string as a localized
139 // month name or DateLang_English to parse it as a standard English name or
140 // their combination to interpret it in any way
142 GetMonthFromName(wxString::const_iterator
& p
,
143 const wxString::const_iterator
& end
,
147 const wxString::const_iterator pOrig
= p
;
148 const wxString name
= GetAlphaToken(p
, end
);
150 return wxDateTime::Inv_Month
;
152 wxDateTime::Month mon
;
153 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
155 // case-insensitive comparison either one of or with both abbreviated
157 if ( flags
& wxDateTime::Name_Full
)
159 if ( lang
& DateLang_English
)
161 if ( name
.CmpNoCase(wxDateTime::GetEnglishMonthName(mon
,
162 wxDateTime::Name_Full
)) == 0 )
166 if ( lang
& DateLang_Local
)
168 if ( name
.CmpNoCase(wxDateTime::GetMonthName(mon
,
169 wxDateTime::Name_Full
)) == 0 )
174 if ( flags
& wxDateTime::Name_Abbr
)
176 if ( lang
& DateLang_English
)
178 if ( name
.CmpNoCase(wxDateTime::GetEnglishMonthName(mon
,
179 wxDateTime::Name_Abbr
)) == 0 )
183 if ( lang
& DateLang_Local
)
185 // some locales (e.g. French one) use periods for the
186 // abbreviated month names but it's never part of name so
187 // compare it specially
188 wxString nameAbbr
= wxDateTime::GetMonthName(mon
,
189 wxDateTime::Name_Abbr
);
190 const bool hasPeriod
= *nameAbbr
.rbegin() == '.';
192 nameAbbr
.erase(nameAbbr
.end() - 1);
194 if ( name
.CmpNoCase(nameAbbr
) == 0 )
198 // skip trailing period if it was part of the match
201 else // no match as no matching period
211 if ( mon
== wxDateTime::Inv_Month
)
217 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
219 // flags and lang parameters have the same meaning as for GetMonthFromName()
222 GetWeekDayFromName(wxString::const_iterator
& p
,
223 const wxString::const_iterator
& end
,
226 const wxString::const_iterator pOrig
= p
;
227 const wxString name
= GetAlphaToken(p
, end
);
229 return wxDateTime::Inv_WeekDay
;
231 wxDateTime::WeekDay wd
;
232 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
234 if ( flags
& wxDateTime::Name_Full
)
236 if ( lang
& DateLang_English
)
238 if ( name
.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd
,
239 wxDateTime::Name_Full
)) == 0 )
243 if ( lang
& DateLang_Local
)
245 if ( name
.CmpNoCase(wxDateTime::GetWeekDayName(wd
,
246 wxDateTime::Name_Full
)) == 0 )
251 if ( flags
& wxDateTime::Name_Abbr
)
253 if ( lang
& DateLang_English
)
255 if ( name
.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd
,
256 wxDateTime::Name_Abbr
)) == 0 )
260 if ( lang
& DateLang_Local
)
262 if ( name
.CmpNoCase(wxDateTime::GetWeekDayName(wd
,
263 wxDateTime::Name_Abbr
)) == 0 )
269 if ( wd
== wxDateTime::Inv_WeekDay
)
275 // parses string starting at given iterator using the specified format and,
276 // optionally, a fall back format (and optionally another one... but it stops
279 // if unsuccessful, returns invalid wxDateTime without changing p; otherwise
280 // advance p to the end of the match and returns wxDateTime containing the
281 // results of the parsing
283 ParseFormatAt(wxString::const_iterator
& p
,
284 const wxString::const_iterator
& end
,
286 // FIXME-VC6: using wxString() instead of wxEmptyString in the
287 // line below results in error C2062: type 'class
288 // wxString (__cdecl *)(void)' unexpected
289 const wxString
& fmtAlt
= wxEmptyString
)
291 const wxString
str(p
, end
);
292 wxString::const_iterator endParse
;
294 if ( dt
.ParseFormat(str
, fmt
, &endParse
) ||
295 (!fmtAlt
.empty() && dt
.ParseFormat(str
, fmtAlt
, &endParse
)) )
297 p
+= endParse
- str
.begin();
299 //else: all formats failed
304 } // anonymous namespace
306 // ----------------------------------------------------------------------------
307 // wxDateTime to/from text representations
308 // ----------------------------------------------------------------------------
310 wxString
wxDateTime::Format(const wxString
& formatp
, const TimeZone
& tz
) const
312 wxCHECK_MSG( !formatp
.empty(), wxEmptyString
,
313 wxT("NULL format in wxDateTime::Format") );
315 wxString format
= formatp
;
317 format
.Replace("%c",wxLocale::GetInfo(wxLOCALE_DATE_TIME_FMT
));
318 format
.Replace("%x",wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT
));
319 format
.Replace("%X",wxLocale::GetInfo(wxLOCALE_TIME_FMT
));
321 // we have to use our own implementation if the date is out of range of
322 // strftime() or if we use non standard specifiers
323 #ifdef wxHAS_STRFTIME
324 time_t time
= GetTicks();
326 if ( (time
!= (time_t)-1) && !wxStrstr(format
, wxT("%l")) )
331 if ( tz
.GetOffset() == -wxGetTimeZone() )
333 // we are working with local time
334 tm
= wxLocaltime_r(&time
, &tmstruct
);
336 // should never happen
337 wxCHECK_MSG( tm
, wxEmptyString
, wxT("wxLocaltime_r() failed") );
341 time
+= (int)tz
.GetOffset();
343 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
344 int time2
= (int) time
;
350 tm
= wxGmtime_r(&time
, &tmstruct
);
352 // should never happen
353 wxCHECK_MSG( tm
, wxEmptyString
, wxT("wxGmtime_r() failed") );
357 tm
= (struct tm
*)NULL
;
363 return CallStrftime(format
, tm
);
366 //else: use generic code below
367 #endif // wxHAS_STRFTIME
369 // we only parse ANSI C format specifications here, no POSIX 2
370 // complications, no GNU extensions but we do add support for a "%l" format
371 // specifier allowing to get the number of milliseconds
374 // used for calls to strftime() when we only deal with time
375 struct tm tmTimeOnly
;
376 memset(&tmTimeOnly
, 0, sizeof(tmTimeOnly
));
377 tmTimeOnly
.tm_hour
= tm
.hour
;
378 tmTimeOnly
.tm_min
= tm
.min
;
379 tmTimeOnly
.tm_sec
= tm
.sec
;
380 tmTimeOnly
.tm_mday
= 1; // any date will do, use 1976-01-01
381 tmTimeOnly
.tm_mon
= 0;
382 tmTimeOnly
.tm_year
= 76;
383 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
385 wxString tmp
, res
, fmt
;
386 for ( wxString::const_iterator p
= format
.begin(); p
!= format
.end(); ++p
)
388 if ( *p
!= wxT('%') )
396 // set the default format
397 switch ( (*++p
).GetValue() )
399 case wxT('Y'): // year has 4 digits
403 case wxT('j'): // day of year has 3 digits
404 case wxT('l'): // milliseconds have 3 digits
408 case wxT('w'): // week day as number has only one
413 // it's either another valid format specifier in which case
414 // the format is "%02d" (for all the rest) or we have the
415 // field width preceding the format in which case it will
416 // override the default format anyhow
425 // start of the format specification
426 switch ( (*p
).GetValue() )
428 case wxT('a'): // a weekday name
430 // second parameter should be true for abbreviated names
431 res
+= GetWeekDayName(tm
.GetWeekDay(),
432 *p
== wxT('a') ? Name_Abbr
: Name_Full
);
435 case wxT('b'): // a month name
437 res
+= GetMonthName(tm
.mon
,
438 *p
== wxT('b') ? Name_Abbr
: Name_Full
);
441 case wxT('c'): // locale default date and time representation
442 case wxT('x'): // locale default date representation
443 #ifdef wxHAS_STRFTIME
445 // the problem: there is no way to know what do these format
446 // specifications correspond to for the current locale.
448 // the solution: use a hack and still use strftime(): first
449 // find the YEAR which is a year in the strftime() range (1970
450 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
451 // of the real year. Then make a copy of the format and
452 // replace all occurrences of YEAR in it with some unique
453 // string not appearing anywhere else in it, then use
454 // strftime() to format the date in year YEAR and then replace
455 // YEAR back by the real year and the unique replacement
456 // string back with YEAR. Notice that "all occurrences of YEAR"
457 // means all occurrences of 4 digit as well as 2 digit form!
459 // the bugs: we assume that neither of %c nor %x contains any
460 // fields which may change between the YEAR and real year. For
461 // example, the week number (%U, %W) and the day number (%j)
462 // will change if one of these years is leap and the other one
465 // find the YEAR: normally, for any year X, Jan 1 of the
466 // year X + 28 is the same weekday as Jan 1 of X (because
467 // the weekday advances by 1 for each normal X and by 2
468 // for each leap X, hence by 5 every 4 years or by 35
469 // which is 0 mod 7 every 28 years) but this rule breaks
470 // down if there are years between X and Y which are
471 // divisible by 4 but not leap (i.e. divisible by 100 but
472 // not 400), hence the correction.
474 int yearReal
= GetYear(tz
);
475 int mod28
= yearReal
% 28;
477 // be careful to not go too far - we risk to leave the
482 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
486 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
489 int nCentury
= year
/ 100,
490 nCenturyReal
= yearReal
/ 100;
492 // need to adjust for the years divisble by 400 which are
493 // not leap but are counted like leap ones if we just take
494 // the number of centuries in between for nLostWeekDays
495 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
496 (nCentury
/ 4 - nCenturyReal
/ 4);
498 // we have to gain back the "lost" weekdays: note that the
499 // effect of this loop is to not do anything to
500 // nLostWeekDays (which we won't use any more), but to
501 // (indirectly) set the year correctly
502 while ( (nLostWeekDays
% 7) != 0 )
504 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
507 // finally move the year below 2000 so that the 2-digit
508 // year number can never match the month or day of the
509 // month when we do the replacements below
513 wxASSERT_MSG( year
>= 1970 && year
< 2000,
514 wxT("logic error in wxDateTime::Format") );
517 // use strftime() to format the same date but in supported
520 // NB: we assume that strftime() doesn't check for the
521 // date validity and will happily format the date
522 // corresponding to Feb 29 of a non leap year (which
523 // may happen if yearReal was leap and year is not)
524 struct tm tmAdjusted
;
526 tmAdjusted
.tm_hour
= tm
.hour
;
527 tmAdjusted
.tm_min
= tm
.min
;
528 tmAdjusted
.tm_sec
= tm
.sec
;
529 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
530 tmAdjusted
.tm_yday
= GetDayOfYear();
531 tmAdjusted
.tm_mday
= tm
.mday
;
532 tmAdjusted
.tm_mon
= tm
.mon
;
533 tmAdjusted
.tm_year
= year
- 1900;
534 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
535 wxString str
= CallStrftime(*p
== wxT('c') ? wxT("%c")
539 // now replace the replacement year with the real year:
540 // notice that we have to replace the 4 digit year with
541 // a unique string not appearing in strftime() output
542 // first to prevent the 2 digit year from matching any
543 // substring of the 4 digit year (but any day, month,
544 // hours or minutes components should be safe because
545 // they are never in 70-99 range)
546 wxString
replacement("|");
547 while ( str
.find(replacement
) != wxString::npos
)
550 str
.Replace(wxString::Format("%d", year
),
552 str
.Replace(wxString::Format("%d", year
% 100),
553 wxString::Format("%d", yearReal
% 100));
554 str
.Replace(replacement
,
555 wxString::Format("%d", yearReal
));
559 #else // !wxHAS_STRFTIME
560 // Use "%m/%d/%y %H:%M:%S" format instead
561 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
562 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
563 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
566 case wxT('d'): // day of a month (01-31)
567 res
+= wxString::Format(fmt
, tm
.mday
);
570 case wxT('H'): // hour in 24h format (00-23)
571 res
+= wxString::Format(fmt
, tm
.hour
);
574 case wxT('I'): // hour in 12h format (01-12)
576 // 24h -> 12h, 0h -> 12h too
577 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
578 : tm
.hour
? tm
.hour
: 12;
579 res
+= wxString::Format(fmt
, hour12
);
583 case wxT('j'): // day of the year
584 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
587 case wxT('l'): // milliseconds (NOT STANDARD)
588 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
591 case wxT('m'): // month as a number (01-12)
592 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
595 case wxT('M'): // minute as a decimal number (00-59)
596 res
+= wxString::Format(fmt
, tm
.min
);
599 case wxT('p'): // AM or PM string
600 #ifdef wxHAS_STRFTIME
601 res
+= CallStrftime(wxT("%p"), &tmTimeOnly
);
602 #else // !wxHAS_STRFTIME
603 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
604 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
607 case wxT('S'): // second as a decimal number (00-61)
608 res
+= wxString::Format(fmt
, tm
.sec
);
611 case wxT('U'): // week number in the year (Sunday 1st week day)
612 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
615 case wxT('W'): // week number in the year (Monday 1st week day)
616 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
619 case wxT('w'): // weekday as a number (0-6), Sunday = 0
620 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
623 // case wxT('x'): -- handled with "%c"
625 case wxT('X'): // locale default time representation
626 // just use strftime() to format the time for us
627 #ifdef wxHAS_STRFTIME
628 res
+= CallStrftime(wxT("%X"), &tmTimeOnly
);
629 #else // !wxHAS_STRFTIME
630 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
631 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
634 case wxT('y'): // year without century (00-99)
635 res
+= wxString::Format(fmt
, tm
.year
% 100);
638 case wxT('Y'): // year with century
639 res
+= wxString::Format(fmt
, tm
.year
);
642 case wxT('Z'): // timezone name
643 #ifdef wxHAS_STRFTIME
644 res
+= CallStrftime(wxT("%Z"), &tmTimeOnly
);
649 // is it the format width?
651 *p
== wxT('-') || *p
== wxT('+') ||
652 *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 specifier"));
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 if ( wxStrchr(".,/-\t\r\n ", *p
) )
1633 // modify copy of the iterator as we're not sure if the next token is
1634 // still part of the date at all
1635 wxString::const_iterator pCopy
= p
;
1637 // we can have either alphabetic or numeric token, start by testing if
1640 if ( GetNumericToken(10 /* max length */, pCopy
, pEnd
, &val
) )
1642 // guess what this number is
1648 if ( !haveMon
&& val
> 0 && val
<= 12 )
1650 // assume it is month
1653 else // not the month
1657 // this can only be the year
1660 else // may be either day or year
1662 // use a leap year if we don't have the year yet to allow
1663 // dates like 2/29/1976 which would be rejected otherwise
1664 wxDateTime_t max_days
= (wxDateTime_t
)(
1666 ? GetNumberOfDays(mon
, haveYear
? year
: 1976)
1671 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
1676 else // yes, suppose it's the day
1690 year
= (wxDateTime_t
)val
;
1699 day
= (wxDateTime_t
)val
;
1704 monWasNumeric
= true;
1706 mon
= (Month
)(val
- 1);
1709 else // not a number
1711 // be careful not to overwrite the current mon value
1712 Month mon2
= GetMonthFromName
1715 Name_Full
| Name_Abbr
,
1716 DateLang_Local
| DateLang_English
1718 if ( mon2
!= Inv_Month
)
1723 // but we already have a month - maybe we guessed wrong
1724 // when we had interpreted that numeric value as a month
1725 // and it was the day number instead?
1726 if ( haveDay
|| !monWasNumeric
)
1729 // assume we did and change our mind: reinterpret the month
1730 // value as a day (notice that there is no need to check
1731 // that it is valid as month values are always < 12, but
1732 // the days are counted from 1 unlike the months)
1733 day
= (wxDateTime_t
)(mon
+ 1);
1741 else // not a valid month name
1743 WeekDay wday2
= GetWeekDayFromName
1746 Name_Full
| Name_Abbr
,
1747 DateLang_Local
| DateLang_English
1749 if ( wday2
!= Inv_WeekDay
)
1759 else // not a valid weekday name
1762 static const char *const ordinals
[] =
1764 wxTRANSLATE("first"),
1765 wxTRANSLATE("second"),
1766 wxTRANSLATE("third"),
1767 wxTRANSLATE("fourth"),
1768 wxTRANSLATE("fifth"),
1769 wxTRANSLATE("sixth"),
1770 wxTRANSLATE("seventh"),
1771 wxTRANSLATE("eighth"),
1772 wxTRANSLATE("ninth"),
1773 wxTRANSLATE("tenth"),
1774 wxTRANSLATE("eleventh"),
1775 wxTRANSLATE("twelfth"),
1776 wxTRANSLATE("thirteenth"),
1777 wxTRANSLATE("fourteenth"),
1778 wxTRANSLATE("fifteenth"),
1779 wxTRANSLATE("sixteenth"),
1780 wxTRANSLATE("seventeenth"),
1781 wxTRANSLATE("eighteenth"),
1782 wxTRANSLATE("nineteenth"),
1783 wxTRANSLATE("twentieth"),
1784 // that's enough - otherwise we'd have problems with
1785 // composite (or not) ordinals
1789 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
1791 const wxString ord
= wxGetTranslation(ordinals
[n
]);
1792 const size_t len
= ord
.length();
1793 if ( date
.compare(p
- pBegin
, len
, ord
) == 0 )
1800 if ( n
== WXSIZEOF(ordinals
) )
1802 // stop here - something unknown
1809 // don't try anything here (as in case of numeric day
1810 // above) - the symbolic day spec should always
1811 // precede the month/year
1817 day
= (wxDateTime_t
)(n
+ 1);
1822 // advance iterator past a successfully parsed token
1826 // either no more tokens or the scan was stopped by something we couldn't
1827 // parse - in any case, see if we can construct a date from what we have
1828 if ( !haveDay
&& !haveWDay
)
1831 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
1832 !(haveDay
&& haveMon
&& haveYear
) )
1834 // without adjectives (which we don't support here) the week day only
1835 // makes sense completely separately or with the full date
1836 // specification (what would "Wed 1999" mean?)
1840 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
1842 // may be we have month and day instead of day and year?
1843 if ( haveDay
&& !haveMon
)
1847 // exchange day and month
1848 mon
= (wxDateTime::Month
)(day
- 1);
1850 // we're in the current year then
1851 if ( (year
> 0) && (year
<= (int)GetNumberOfDays(mon
, Inv_Year
)) )
1853 day
= (wxDateTime_t
)year
;
1858 //else: no, can't exchange, leave haveMon == false
1868 mon
= GetCurrentMonth();
1873 year
= GetCurrentYear();
1878 // normally we check the day above but the check is optimistic in case
1879 // we find the day before its month/year so we have to redo it now
1880 if ( day
> GetNumberOfDays(mon
, year
) )
1883 Set(day
, mon
, year
);
1887 // check that it is really the same
1888 if ( GetWeekDay() != wday
)
1896 SetToWeekDayInSameWeek(wday
);
1905 wxDateTime::ParseTime(const wxString
& time
, wxString::const_iterator
*end
)
1907 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1909 // first try some extra things
1916 { wxTRANSLATE("noon"), 12 },
1917 { wxTRANSLATE("midnight"), 00 },
1921 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
1923 const wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
1924 if ( timeString
.CmpNoCase(wxString(time
, timeString
.length())) == 0 )
1926 // casts required by DigitalMars
1927 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
1930 *end
= time
.begin() + timeString
.length();
1936 // try all time formats we may think about in the order from longest to
1938 static const char *const timeFormats
[] =
1940 "%I:%M:%S %p", // 12hour with AM/PM
1941 "%H:%M:%S", // could be the same or 24 hour one so try it too
1942 "%I:%M %p", // 12hour with AM/PM but without seconds
1943 "%H:%M", // and a possibly 24 hour version without seconds
1944 "%X", // possibly something from above or maybe something
1945 // completely different -- try it last
1947 // TODO: parse timezones
1950 for ( size_t nFmt
= 0; nFmt
< WXSIZEOF(timeFormats
); nFmt
++ )
1952 if ( ParseFormat(time
, timeFormats
[nFmt
], end
) )
1959 // ----------------------------------------------------------------------------
1960 // Workdays and holidays support
1961 // ----------------------------------------------------------------------------
1963 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
1965 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
1968 // ============================================================================
1970 // ============================================================================
1972 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
1975 return ds1
.Multiply(n
);
1978 // ============================================================================
1980 // ============================================================================
1982 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
1984 return wxTimeSpan(ts
).Multiply(n
);
1987 // this enum is only used in wxTimeSpan::Format() below but we can't declare
1988 // it locally to the method as it provokes an internal compiler error in egcs
1989 // 2.91.60 when building with -O2
2000 // not all strftime(3) format specifiers make sense here because, for example,
2001 // a time span doesn't have a year nor a timezone
2003 // Here are the ones which are supported (all of them are supported by strftime
2005 // %H hour in 24 hour format
2006 // %M minute (00 - 59)
2007 // %S second (00 - 59)
2010 // Also, for MFC CTimeSpan compatibility, we support
2011 // %D number of days
2013 // And, to be better than MFC :-), we also have
2014 // %E number of wEeks
2015 // %l milliseconds (000 - 999)
2016 wxString
wxTimeSpan::Format(const wxString
& format
) const
2018 // we deal with only positive time spans here and just add the sign in
2019 // front for the negative ones
2022 wxString
str(Negate().Format(format
));
2026 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
2027 wxT("NULL format in wxTimeSpan::Format") );
2030 str
.Alloc(format
.length());
2032 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
2034 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
2035 // question is what should ts.Format("%S") do? The code here returns "3273"
2036 // in this case (i.e. the total number of seconds, not just seconds % 60)
2037 // because, for me, this call means "give me entire time interval in
2038 // seconds" and not "give me the seconds part of the time interval"
2040 // If we agree that it should behave like this, it is clear that the
2041 // interpretation of each format specifier depends on the presence of the
2042 // other format specs in the string: if there was "%H" before "%M", we
2043 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
2045 // we remember the most important unit found so far
2046 TimeSpanPart partBiggest
= Part_MSec
;
2048 for ( wxString::const_iterator pch
= format
.begin(); pch
!= format
.end(); ++pch
)
2052 if ( ch
== wxT('%') )
2054 // the start of the format specification of the printf() below
2055 wxString
fmtPrefix(wxT('%'));
2060 // the number of digits for the format string, 0 if unused
2061 unsigned digits
= 0;
2063 ch
= *++pch
; // get the format spec char
2067 wxFAIL_MSG( wxT("invalid format character") );
2073 // skip the part below switch
2078 if ( partBiggest
< Part_Day
)
2084 partBiggest
= Part_Day
;
2089 partBiggest
= Part_Week
;
2095 if ( partBiggest
< Part_Hour
)
2101 partBiggest
= Part_Hour
;
2108 n
= GetMilliseconds().ToLong();
2109 if ( partBiggest
< Part_MSec
)
2113 //else: no need to reset partBiggest to Part_MSec, it is
2114 // the least significant one anyhow
2121 if ( partBiggest
< Part_Min
)
2127 partBiggest
= Part_Min
;
2134 n
= GetSeconds().ToLong();
2135 if ( partBiggest
< Part_Sec
)
2141 partBiggest
= Part_Sec
;
2150 fmtPrefix
<< wxT("0") << digits
;
2153 str
+= wxString::Format(fmtPrefix
+ wxT("ld"), n
);
2157 // normal character, just copy
2165 #endif // wxUSE_DATETIME