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 tmTimeOnly
.tm_hour
= tm
.hour
;
378 tmTimeOnly
.tm_min
= tm
.min
;
379 tmTimeOnly
.tm_sec
= tm
.sec
;
380 tmTimeOnly
.tm_wday
= 0;
381 tmTimeOnly
.tm_yday
= 0;
382 tmTimeOnly
.tm_mday
= 1; // any date will do
383 tmTimeOnly
.tm_mon
= 0;
384 tmTimeOnly
.tm_year
= 76;
385 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
387 wxString tmp
, res
, fmt
;
388 for ( wxString::const_iterator p
= format
.begin(); p
!= format
.end(); ++p
)
390 if ( *p
!= wxT('%') )
398 // set the default format
399 switch ( (*++p
).GetValue() )
401 case wxT('Y'): // year has 4 digits
405 case wxT('j'): // day of year has 3 digits
406 case wxT('l'): // milliseconds have 3 digits
410 case wxT('w'): // week day as number has only one
415 // it's either another valid format specifier in which case
416 // the format is "%02d" (for all the rest) or we have the
417 // field width preceding the format in which case it will
418 // override the default format anyhow
427 // start of the format specification
428 switch ( (*p
).GetValue() )
430 case wxT('a'): // a weekday name
432 // second parameter should be true for abbreviated names
433 res
+= GetWeekDayName(tm
.GetWeekDay(),
434 *p
== wxT('a') ? Name_Abbr
: Name_Full
);
437 case wxT('b'): // a month name
439 res
+= GetMonthName(tm
.mon
,
440 *p
== wxT('b') ? Name_Abbr
: Name_Full
);
443 case wxT('c'): // locale default date and time representation
444 case wxT('x'): // locale default date representation
445 #ifdef wxHAS_STRFTIME
447 // the problem: there is no way to know what do these format
448 // specifications correspond to for the current locale.
450 // the solution: use a hack and still use strftime(): first
451 // find the YEAR which is a year in the strftime() range (1970
452 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
453 // of the real year. Then make a copy of the format and
454 // replace all occurrences of YEAR in it with some unique
455 // string not appearing anywhere else in it, then use
456 // strftime() to format the date in year YEAR and then replace
457 // YEAR back by the real year and the unique replacement
458 // string back with YEAR. Notice that "all occurrences of YEAR"
459 // means all occurrences of 4 digit as well as 2 digit form!
461 // the bugs: we assume that neither of %c nor %x contains any
462 // fields which may change between the YEAR and real year. For
463 // example, the week number (%U, %W) and the day number (%j)
464 // will change if one of these years is leap and the other one
467 // find the YEAR: normally, for any year X, Jan 1 of the
468 // year X + 28 is the same weekday as Jan 1 of X (because
469 // the weekday advances by 1 for each normal X and by 2
470 // for each leap X, hence by 5 every 4 years or by 35
471 // which is 0 mod 7 every 28 years) but this rule breaks
472 // down if there are years between X and Y which are
473 // divisible by 4 but not leap (i.e. divisible by 100 but
474 // not 400), hence the correction.
476 int yearReal
= GetYear(tz
);
477 int mod28
= yearReal
% 28;
479 // be careful to not go too far - we risk to leave the
484 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
488 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
491 int nCentury
= year
/ 100,
492 nCenturyReal
= yearReal
/ 100;
494 // need to adjust for the years divisble by 400 which are
495 // not leap but are counted like leap ones if we just take
496 // the number of centuries in between for nLostWeekDays
497 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
498 (nCentury
/ 4 - nCenturyReal
/ 4);
500 // we have to gain back the "lost" weekdays: note that the
501 // effect of this loop is to not do anything to
502 // nLostWeekDays (which we won't use any more), but to
503 // (indirectly) set the year correctly
504 while ( (nLostWeekDays
% 7) != 0 )
506 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
509 // finally move the year below 2000 so that the 2-digit
510 // year number can never match the month or day of the
511 // month when we do the replacements below
515 wxASSERT_MSG( year
>= 1970 && year
< 2000,
516 wxT("logic error in wxDateTime::Format") );
519 // use strftime() to format the same date but in supported
522 // NB: we assume that strftime() doesn't check for the
523 // date validity and will happily format the date
524 // corresponding to Feb 29 of a non leap year (which
525 // may happen if yearReal was leap and year is not)
526 struct tm tmAdjusted
;
528 tmAdjusted
.tm_hour
= tm
.hour
;
529 tmAdjusted
.tm_min
= tm
.min
;
530 tmAdjusted
.tm_sec
= tm
.sec
;
531 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
532 tmAdjusted
.tm_yday
= GetDayOfYear();
533 tmAdjusted
.tm_mday
= tm
.mday
;
534 tmAdjusted
.tm_mon
= tm
.mon
;
535 tmAdjusted
.tm_year
= year
- 1900;
536 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
537 wxString str
= CallStrftime(*p
== wxT('c') ? wxT("%c")
541 // now replace the replacement year with the real year:
542 // notice that we have to replace the 4 digit year with
543 // a unique string not appearing in strftime() output
544 // first to prevent the 2 digit year from matching any
545 // substring of the 4 digit year (but any day, month,
546 // hours or minutes components should be safe because
547 // they are never in 70-99 range)
548 wxString
replacement("|");
549 while ( str
.find(replacement
) != wxString::npos
)
552 str
.Replace(wxString::Format("%d", year
),
554 str
.Replace(wxString::Format("%d", year
% 100),
555 wxString::Format("%d", yearReal
% 100));
556 str
.Replace(replacement
,
557 wxString::Format("%d", yearReal
));
561 #else // !wxHAS_STRFTIME
562 // Use "%m/%d/%y %H:%M:%S" format instead
563 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
564 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
565 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
568 case wxT('d'): // day of a month (01-31)
569 res
+= wxString::Format(fmt
, tm
.mday
);
572 case wxT('H'): // hour in 24h format (00-23)
573 res
+= wxString::Format(fmt
, tm
.hour
);
576 case wxT('I'): // hour in 12h format (01-12)
578 // 24h -> 12h, 0h -> 12h too
579 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
580 : tm
.hour
? tm
.hour
: 12;
581 res
+= wxString::Format(fmt
, hour12
);
585 case wxT('j'): // day of the year
586 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
589 case wxT('l'): // milliseconds (NOT STANDARD)
590 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
593 case wxT('m'): // month as a number (01-12)
594 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
597 case wxT('M'): // minute as a decimal number (00-59)
598 res
+= wxString::Format(fmt
, tm
.min
);
601 case wxT('p'): // AM or PM string
602 #ifdef wxHAS_STRFTIME
603 res
+= CallStrftime(wxT("%p"), &tmTimeOnly
);
604 #else // !wxHAS_STRFTIME
605 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
606 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
609 case wxT('S'): // second as a decimal number (00-61)
610 res
+= wxString::Format(fmt
, tm
.sec
);
613 case wxT('U'): // week number in the year (Sunday 1st week day)
614 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
617 case wxT('W'): // week number in the year (Monday 1st week day)
618 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
621 case wxT('w'): // weekday as a number (0-6), Sunday = 0
622 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
625 // case wxT('x'): -- handled with "%c"
627 case wxT('X'): // locale default time representation
628 // just use strftime() to format the time for us
629 #ifdef wxHAS_STRFTIME
630 res
+= CallStrftime(wxT("%X"), &tmTimeOnly
);
631 #else // !wxHAS_STRFTIME
632 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
633 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
636 case wxT('y'): // year without century (00-99)
637 res
+= wxString::Format(fmt
, tm
.year
% 100);
640 case wxT('Y'): // year with century
641 res
+= wxString::Format(fmt
, tm
.year
);
644 case wxT('Z'): // timezone name
645 #ifdef wxHAS_STRFTIME
646 res
+= CallStrftime(wxT("%Z"), &tmTimeOnly
);
651 // is it the format width?
653 while ( *p
== wxT('-') || *p
== wxT('+') ||
654 *p
== wxT(' ') || wxIsdigit(*p
) )
661 // we've only got the flags and width so far in fmt
662 fmt
.Prepend(wxT('%'));
663 fmt
.Append(wxT('d'));
670 // no, it wasn't the width
671 wxFAIL_MSG(wxT("unknown format specificator"));
673 // fall through and just copy it nevertheless
675 case wxT('%'): // a percent sign
679 case 0: // the end of string
680 wxFAIL_MSG(wxT("missing format at the end of string"));
682 // just put the '%' which was the last char in format
692 // this function parses a string in (strict) RFC 822 format: see the section 5
693 // of the RFC for the detailed description, but briefly it's something of the
694 // form "Sat, 18 Dec 1999 00:48:30 +0100"
696 // this function is "strict" by design - it must reject anything except true
697 // RFC822 time specs.
699 wxDateTime::ParseRfc822Date(const wxString
& date
, wxString::const_iterator
*end
)
701 const wxString::const_iterator pEnd
= date
.end();
702 wxString::const_iterator p
= date
.begin();
705 const wxDateTime::WeekDay
706 wd
= GetWeekDayFromName(p
, pEnd
, Name_Abbr
, DateLang_English
);
707 if ( wd
== Inv_WeekDay
)
709 //else: ignore week day for now, we could also check that it really
710 // corresponds to the specified date
712 // 2. separating comma
713 if ( *p
++ != ',' || *p
++ != ' ' )
717 if ( !wxIsdigit(*p
) )
720 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - '0');
724 day
= (wxDateTime_t
)(day
+ (*p
++ - '0'));
731 const Month mon
= GetMonthFromName(p
, pEnd
, Name_Abbr
, DateLang_English
);
732 if ( mon
== Inv_Month
)
739 if ( !wxIsdigit(*p
) )
742 int year
= *p
++ - '0';
743 if ( !wxIsdigit(*p
) ) // should have at least 2 digits in the year
749 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
755 if ( !wxIsdigit(*p
) )
757 // no 3 digit years please
768 // 6. time in hh:mm:ss format with seconds being optional
769 if ( !wxIsdigit(*p
) )
772 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - '0');
774 if ( !wxIsdigit(*p
) )
778 hour
= (wxDateTime_t
)(hour
+ (*p
++ - '0'));
783 if ( !wxIsdigit(*p
) )
786 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - '0');
788 if ( !wxIsdigit(*p
) )
792 min
+= (wxDateTime_t
)(*p
++ - '0');
794 wxDateTime_t sec
= 0;
798 if ( !wxIsdigit(*p
) )
801 sec
= (wxDateTime_t
)(*p
++ - '0');
803 if ( !wxIsdigit(*p
) )
807 sec
+= (wxDateTime_t
)(*p
++ - '0');
813 // 7. now the interesting part: the timezone
814 int offset
= 0; // just to suppress warnings
815 if ( *p
== '-' || *p
== '+' )
817 // the explicit offset given: it has the form of hhmm
818 bool plus
= *p
++ == '+';
820 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
825 offset
= MIN_PER_HOUR
*(10*(*p
- '0') + (*(p
+ 1) - '0'));
829 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
833 offset
+= 10*(*p
- '0') + (*(p
+ 1) - '0');
842 // the symbolic timezone given: may be either military timezone or one
843 // of standard abbreviations
846 // military: Z = UTC, J unused, A = -1, ..., Y = +12
847 static const int offsets
[26] =
849 //A B C D E F G H I J K L M
850 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
851 //N O P R Q S T U V W Z Y Z
852 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
855 if ( *p
< wxT('A') || *p
> wxT('Z') || *p
== wxT('J') )
858 offset
= offsets
[*p
++ - 'A'];
863 const wxString
tz(p
, date
.end());
864 if ( tz
== wxT("UT") || tz
== wxT("UTC") || tz
== wxT("GMT") )
866 else if ( tz
== wxT("AST") )
868 else if ( tz
== wxT("ADT") )
870 else if ( tz
== wxT("EST") )
872 else if ( tz
== wxT("EDT") )
874 else if ( tz
== wxT("CST") )
876 else if ( tz
== wxT("CDT") )
878 else if ( tz
== wxT("MST") )
880 else if ( tz
== wxT("MDT") )
882 else if ( tz
== wxT("PST") )
884 else if ( tz
== wxT("PDT") )
893 offset
*= MIN_PER_HOUR
;
897 // the spec was correct, construct the date from the values we found
898 Set(day
, mon
, year
, hour
, min
, sec
);
899 MakeFromTimezone(TimeZone::Make(offset
*SEC_PER_MIN
));
908 wxDateTime::ParseFormat(const wxString
& date
,
909 const wxString
& format
,
910 const wxDateTime
& dateDef
,
911 wxString::const_iterator
*endParse
)
913 wxCHECK_MSG( !format
.empty(), false, "format can't be empty" );
914 wxCHECK_MSG( endParse
, false, "end iterator pointer must be specified" );
919 // what fields have we found?
920 bool haveWDay
= false,
930 bool hourIsIn12hFormat
= false, // or in 24h one?
931 isPM
= false; // AM by default
933 // and the value of the items we have (init them to get rid of warnings)
934 wxDateTime_t msec
= 0,
938 WeekDay wday
= Inv_WeekDay
;
939 wxDateTime_t yday
= 0,
941 wxDateTime::Month mon
= Inv_Month
;
944 wxString::const_iterator input
= date
.begin();
945 const wxString::const_iterator end
= date
.end();
946 for ( wxString::const_iterator fmt
= format
.begin(); fmt
!= format
.end(); ++fmt
)
948 if ( *fmt
!= wxT('%') )
950 if ( wxIsspace(*fmt
) )
952 // a white space in the format string matches 0 or more white
953 // spaces in the input
954 while ( input
!= end
&& wxIsspace(*input
) )
961 // any other character (not whitespace, not '%') must be
962 // matched by itself in the input
963 if ( input
== end
|| *input
++ != *fmt
)
970 // done with this format char
974 // start of a format specification
976 // parse the optional width
978 while ( wxIsdigit(*++fmt
) )
984 // the default widths for the various fields
987 switch ( (*fmt
).GetValue() )
989 case wxT('Y'): // year has 4 digits
993 case wxT('j'): // day of year has 3 digits
994 case wxT('l'): // milliseconds have 3 digits
998 case wxT('w'): // week day as number has only one
1003 // default for all other fields
1008 // then the format itself
1009 switch ( (*fmt
).GetValue() )
1011 case wxT('a'): // a weekday name
1014 wday
= GetWeekDayFromName
1017 *fmt
== 'a' ? Name_Abbr
: Name_Full
,
1020 if ( wday
== Inv_WeekDay
)
1029 case wxT('b'): // a month name
1032 mon
= GetMonthFromName
1035 *fmt
== 'b' ? Name_Abbr
: Name_Full
,
1038 if ( mon
== Inv_Month
)
1047 case wxT('c'): // locale default date and time representation
1053 fmtDateTime
= wxLocale::GetInfo(wxLOCALE_DATE_TIME_FMT
);
1054 if ( !fmtDateTime
.empty() )
1055 dt
= ParseFormatAt(input
, end
, fmtDateTime
);
1056 #endif // wxUSE_INTL
1057 if ( !dt
.IsValid() )
1059 // also try the format which corresponds to ctime()
1060 // output (i.e. the "C" locale default)
1061 dt
= ParseFormatAt(input
, end
, wxS("%a %b %d %H:%M:%S %Y"));
1064 if ( !dt
.IsValid() )
1066 // and finally also the two generic date/time formats
1067 dt
= ParseFormatAt(input
, end
, wxS("%x %X"), wxS("%X %x"));
1070 if ( !dt
.IsValid() )
1073 const Tm tm
= dt
.GetTm();
1083 haveDay
= haveMon
= haveYear
=
1084 haveHour
= haveMin
= haveSec
= true;
1088 case wxT('d'): // day of a month (01-31)
1089 case 'e': // day of a month (1-31) (GNU extension)
1090 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1091 (num
> 31) || (num
< 1) )
1097 // we can't check whether the day range is correct yet, will
1098 // do it later - assume ok for now
1100 mday
= (wxDateTime_t
)num
;
1103 case wxT('H'): // hour in 24h format (00-23)
1104 if ( !GetNumericToken(width
, input
, end
, &num
) || (num
> 23) )
1111 hour
= (wxDateTime_t
)num
;
1114 case wxT('I'): // hour in 12h format (01-12)
1115 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1116 !num
|| (num
> 12) )
1123 hourIsIn12hFormat
= true;
1124 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
1127 case wxT('j'): // day of the year
1128 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1129 !num
|| (num
> 366) )
1136 yday
= (wxDateTime_t
)num
;
1139 case wxT('l'): // milliseconds (0-999)
1140 if ( !GetNumericToken(width
, input
, end
, &num
) )
1144 msec
= (wxDateTime_t
)num
;
1147 case wxT('m'): // month as a number (01-12)
1148 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1149 !num
|| (num
> 12) )
1156 mon
= (Month
)(num
- 1);
1159 case wxT('M'): // minute as a decimal number (00-59)
1160 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1168 min
= (wxDateTime_t
)num
;
1171 case wxT('p'): // AM or PM string
1174 GetAmPmStrings(&am
, &pm
);
1176 // we can never match %p in locales which don't use AM/PM
1177 if ( am
.empty() || pm
.empty() )
1180 const size_t pos
= input
- date
.begin();
1181 if ( date
.compare(pos
, pm
.length(), pm
) == 0 )
1184 input
+= pm
.length();
1186 else if ( date
.compare(pos
, am
.length(), am
) == 0 )
1188 input
+= am
.length();
1197 case wxT('r'): // time as %I:%M:%S %p
1200 if ( !dt
.ParseFormat(wxString(input
, end
),
1201 wxS("%I:%M:%S %p"), &input
) )
1204 haveHour
= haveMin
= haveSec
= true;
1206 const Tm tm
= dt
.GetTm();
1213 case wxT('R'): // time as %H:%M
1216 dt
= ParseFormatAt(input
, end
, wxS("%H:%M"));
1217 if ( !dt
.IsValid() )
1223 const Tm tm
= dt
.GetTm();
1229 case wxT('S'): // second as a decimal number (00-61)
1230 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1238 sec
= (wxDateTime_t
)num
;
1241 case wxT('T'): // time as %H:%M:%S
1244 dt
= ParseFormatAt(input
, end
, wxS("%H:%M:%S"));
1245 if ( !dt
.IsValid() )
1252 const Tm tm
= dt
.GetTm();
1259 case wxT('w'): // weekday as a number (0-6), Sunday = 0
1260 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1268 wday
= (WeekDay
)num
;
1271 case wxT('x'): // locale default date representation
1275 fmtDate
= wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT
),
1276 fmtDateAlt
= wxLocale::GetInfo(wxLOCALE_LONG_DATE_FMT
);
1277 #else // !wxUSE_INTL
1278 wxString fmtDate
, fmtDateAlt
;
1279 #endif // wxUSE_INTL/!wxUSE_INTL
1280 if ( fmtDate
.empty() )
1282 if ( IsWestEuropeanCountry(GetCountry()) ||
1283 GetCountry() == Russia
)
1285 fmtDate
= wxS("%d/%m/%Y");
1286 fmtDateAlt
= wxS("%m/%d/%Y");
1290 fmtDate
= wxS("%m/%d/%Y");
1291 fmtDateAlt
= wxS("%d/%m/%Y");
1296 dt
= ParseFormatAt(input
, end
, fmtDate
, fmtDateAlt
);
1298 if ( !dt
.IsValid() )
1300 // try with short years too
1301 fmtDate
.Replace("%Y","%y");
1302 fmtDateAlt
.Replace("%Y","%y");
1303 dt
= ParseFormatAt(input
, end
, fmtDate
, fmtDateAlt
);
1305 if ( !dt
.IsValid() )
1309 const Tm tm
= dt
.GetTm();
1322 case wxT('X'): // locale default time representation
1325 wxString fmtTime
= wxLocale::GetInfo(wxLOCALE_TIME_FMT
),
1327 #else // !wxUSE_INTL
1328 wxString fmtTime
, fmtTimeAlt
;
1329 #endif // wxUSE_INTL/!wxUSE_INTL
1330 if ( fmtTime
.empty() )
1332 // try to parse what follows as "%H:%M:%S" and, if this
1333 // fails, as "%I:%M:%S %p" - this should catch the most
1340 dt
= ParseFormatAt(input
, end
, fmtTime
, fmtTimeAlt
);
1341 if ( !dt
.IsValid() )
1348 const Tm tm
= dt
.GetTm();
1355 case wxT('y'): // year without century (00-99)
1356 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1365 // TODO should have an option for roll over date instead of
1366 // hard coding it here
1367 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
1370 case wxT('Y'): // year with century
1371 if ( !GetNumericToken(width
, input
, end
, &num
) )
1378 year
= (wxDateTime_t
)num
;
1381 case wxT('Z'): // timezone name
1382 // FIXME: currently we just ignore everything that looks like a
1384 GetAlphaToken(input
, end
);
1387 case wxT('%'): // a percent sign
1388 if ( *input
++ != wxT('%') )
1395 case 0: // the end of string
1396 wxFAIL_MSG(wxT("unexpected format end"));
1400 default: // not a known format spec
1405 // format matched, try to construct a date from what we have now
1407 if ( dateDef
.IsValid() )
1409 // take this date as default
1410 tmDef
= dateDef
.GetTm();
1412 else if ( IsValid() )
1414 // if this date is valid, don't change it
1419 // no default and this date is invalid - fall back to Today()
1420 tmDef
= Today().GetTm();
1436 // TODO we don't check here that the values are consistent, if both year
1437 // day and month/day were found, we just ignore the year day and we
1438 // also always ignore the week day
1441 if ( mday
> GetNumberOfDays(tm
.mon
, tm
.year
) )
1446 else if ( haveYDay
)
1448 if ( yday
> GetNumberOfDays(tm
.year
) )
1451 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
1458 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
1460 // translate to 24hour format
1463 //else: either already in 24h format or no translation needed
1486 // finally check that the week day is consistent -- if we had it
1487 if ( haveWDay
&& GetWeekDay() != wday
)
1496 wxDateTime::ParseDateTime(const wxString
& date
, wxString::const_iterator
*end
)
1498 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1500 // Set to current day and hour, so strings like '14:00' becomes today at
1501 // 14, not some other random date
1502 wxDateTime dtDate
= wxDateTime::Today();
1503 wxDateTime dtTime
= wxDateTime::Today();
1505 wxString::const_iterator
1510 // If we got a date in the beginning, see if there is a time specified
1512 if ( dtDate
.ParseDate(date
, &endDate
) )
1514 // Skip spaces, as the ParseTime() function fails on spaces
1515 while ( endDate
!= date
.end() && wxIsspace(*endDate
) )
1518 const wxString
timestr(endDate
, date
.end());
1519 if ( !dtTime
.ParseTime(timestr
, &endTime
) )
1522 endBoth
= endDate
+ (endTime
- timestr
.begin());
1524 else // no date in the beginning
1526 // check if we have a time followed by a date
1527 if ( !dtTime
.ParseTime(date
, &endTime
) )
1530 while ( endTime
!= date
.end() && wxIsspace(*endTime
) )
1533 const wxString
datestr(endTime
, date
.end());
1534 if ( !dtDate
.ParseDate(datestr
, &endDate
) )
1537 endBoth
= endTime
+ (endDate
- datestr
.begin());
1540 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
1541 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
1542 dtTime
.GetMillisecond());
1550 wxDateTime::ParseDate(const wxString
& date
, wxString::const_iterator
*end
)
1552 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1554 // this is a simplified version of ParseDateTime() which understands only
1555 // "today" (for wxDate compatibility) and digits only otherwise (and not
1556 // all esoteric constructions ParseDateTime() knows about)
1558 const wxString::const_iterator pBegin
= date
.begin();
1559 const wxString::const_iterator pEnd
= date
.end();
1561 wxString::const_iterator p
= pBegin
;
1562 while ( p
!= pEnd
&& wxIsspace(*p
) )
1565 // some special cases
1569 int dayDiffFromToday
;
1572 { wxTRANSLATE("today"), 0 },
1573 { wxTRANSLATE("yesterday"), -1 },
1574 { wxTRANSLATE("tomorrow"), 1 },
1577 const size_t lenRest
= pEnd
- p
;
1578 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
1580 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
1581 size_t len
= dateStr
.length();
1583 if ( len
> lenRest
)
1586 const wxString::const_iterator pEnd
= p
+ len
;
1587 if ( wxString(p
, pEnd
).CmpNoCase(dateStr
) == 0 )
1589 // nothing can follow this, so stop here
1593 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
1595 if ( dayDiffFromToday
)
1597 *this += wxDateSpan::Days(dayDiffFromToday
);
1606 // We try to guess what we have here: for each new (numeric) token, we
1607 // determine if it can be a month, day or a year. Of course, there is an
1608 // ambiguity as some numbers may be days as well as months, so we also
1609 // have the ability to back track.
1612 bool haveDay
= false, // the months day?
1613 haveWDay
= false, // the day of week?
1614 haveMon
= false, // the month?
1615 haveYear
= false; // the year?
1617 bool monWasNumeric
= false; // was month specified as a number?
1619 // and the value of the items we have (init them to get rid of warnings)
1620 WeekDay wday
= Inv_WeekDay
;
1621 wxDateTime_t day
= 0;
1622 wxDateTime::Month mon
= Inv_Month
;
1625 // tokenize the string
1628 // skip white space and date delimiters
1629 while ( wxStrchr(".,/-\t\r\n ", *p
) )
1634 // modify copy of the iterator as we're not sure if the next token is
1635 // still part of the date at all
1636 wxString::const_iterator pCopy
= p
;
1638 // we can have either alphabetic or numeric token, start by testing if
1641 if ( GetNumericToken(10 /* max length */, pCopy
, pEnd
, &val
) )
1643 // guess what this number is
1649 if ( !haveMon
&& val
> 0 && val
<= 12 )
1651 // assume it is month
1654 else // not the month
1658 // this can only be the year
1661 else // may be either day or year
1663 // use a leap year if we don't have the year yet to allow
1664 // dates like 2/29/1976 which would be rejected otherwise
1665 wxDateTime_t max_days
= (wxDateTime_t
)(
1667 ? GetNumberOfDays(mon
, haveYear
? year
: 1976)
1672 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
1677 else // yes, suppose it's the day
1691 year
= (wxDateTime_t
)val
;
1700 day
= (wxDateTime_t
)val
;
1705 monWasNumeric
= true;
1707 mon
= (Month
)(val
- 1);
1710 else // not a number
1712 // be careful not to overwrite the current mon value
1713 Month mon2
= GetMonthFromName
1716 Name_Full
| Name_Abbr
,
1717 DateLang_Local
| DateLang_English
1719 if ( mon2
!= Inv_Month
)
1724 // but we already have a month - maybe we guessed wrong
1725 // when we had interpreted that numeric value as a month
1726 // and it was the day number instead?
1727 if ( haveDay
|| !monWasNumeric
)
1730 // assume we did and change our mind: reinterpret the month
1731 // value as a day (notice that there is no need to check
1732 // that it is valid as month values are always < 12, but
1733 // the days are counted from 1 unlike the months)
1734 day
= (wxDateTime_t
)(mon
+ 1);
1742 else // not a valid month name
1744 WeekDay wday2
= GetWeekDayFromName
1747 Name_Full
| Name_Abbr
,
1748 DateLang_Local
| DateLang_English
1750 if ( wday2
!= Inv_WeekDay
)
1760 else // not a valid weekday name
1763 static const char *const ordinals
[] =
1765 wxTRANSLATE("first"),
1766 wxTRANSLATE("second"),
1767 wxTRANSLATE("third"),
1768 wxTRANSLATE("fourth"),
1769 wxTRANSLATE("fifth"),
1770 wxTRANSLATE("sixth"),
1771 wxTRANSLATE("seventh"),
1772 wxTRANSLATE("eighth"),
1773 wxTRANSLATE("ninth"),
1774 wxTRANSLATE("tenth"),
1775 wxTRANSLATE("eleventh"),
1776 wxTRANSLATE("twelfth"),
1777 wxTRANSLATE("thirteenth"),
1778 wxTRANSLATE("fourteenth"),
1779 wxTRANSLATE("fifteenth"),
1780 wxTRANSLATE("sixteenth"),
1781 wxTRANSLATE("seventeenth"),
1782 wxTRANSLATE("eighteenth"),
1783 wxTRANSLATE("nineteenth"),
1784 wxTRANSLATE("twentieth"),
1785 // that's enough - otherwise we'd have problems with
1786 // composite (or not) ordinals
1790 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
1792 const wxString ord
= wxGetTranslation(ordinals
[n
]);
1793 const size_t len
= ord
.length();
1794 if ( date
.compare(p
- pBegin
, len
, ord
) == 0 )
1801 if ( n
== WXSIZEOF(ordinals
) )
1803 // stop here - something unknown
1810 // don't try anything here (as in case of numeric day
1811 // above) - the symbolic day spec should always
1812 // precede the month/year
1818 day
= (wxDateTime_t
)(n
+ 1);
1823 // advance iterator past a successfully parsed token
1827 // either no more tokens or the scan was stopped by something we couldn't
1828 // parse - in any case, see if we can construct a date from what we have
1829 if ( !haveDay
&& !haveWDay
)
1832 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
1833 !(haveDay
&& haveMon
&& haveYear
) )
1835 // without adjectives (which we don't support here) the week day only
1836 // makes sense completely separately or with the full date
1837 // specification (what would "Wed 1999" mean?)
1841 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
1843 // may be we have month and day instead of day and year?
1844 if ( haveDay
&& !haveMon
)
1848 // exchange day and month
1849 mon
= (wxDateTime::Month
)(day
- 1);
1851 // we're in the current year then
1852 if ( (year
> 0) && (year
<= (int)GetNumberOfDays(mon
, Inv_Year
)) )
1854 day
= (wxDateTime_t
)year
;
1859 //else: no, can't exchange, leave haveMon == false
1869 mon
= GetCurrentMonth();
1874 year
= GetCurrentYear();
1879 // normally we check the day above but the check is optimistic in case
1880 // we find the day before its month/year so we have to redo it now
1881 if ( day
> GetNumberOfDays(mon
, year
) )
1884 Set(day
, mon
, year
);
1888 // check that it is really the same
1889 if ( GetWeekDay() != wday
)
1897 SetToWeekDayInSameWeek(wday
);
1906 wxDateTime::ParseTime(const wxString
& time
, wxString::const_iterator
*end
)
1908 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1910 // first try some extra things
1917 { wxTRANSLATE("noon"), 12 },
1918 { wxTRANSLATE("midnight"), 00 },
1922 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
1924 const wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
1925 if ( timeString
.CmpNoCase(wxString(time
, timeString
.length())) == 0 )
1927 // casts required by DigitalMars
1928 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
1931 *end
= time
.begin() + timeString
.length();
1937 // try all time formats we may think about in the order from longest to
1939 static const char *const timeFormats
[] =
1941 "%I:%M:%S %p", // 12hour with AM/PM
1942 "%H:%M:%S", // could be the same or 24 hour one so try it too
1943 "%I:%M %p", // 12hour with AM/PM but without seconds
1944 "%H:%M", // and a possibly 24 hour version without seconds
1945 "%X", // possibly something from above or maybe something
1946 // completely different -- try it last
1948 // TODO: parse timezones
1951 for ( size_t nFmt
= 0; nFmt
< WXSIZEOF(timeFormats
); nFmt
++ )
1953 if ( ParseFormat(time
, timeFormats
[nFmt
], end
) )
1960 // ----------------------------------------------------------------------------
1961 // Workdays and holidays support
1962 // ----------------------------------------------------------------------------
1964 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
1966 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
1969 // ============================================================================
1971 // ============================================================================
1973 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
1976 return ds1
.Multiply(n
);
1979 // ============================================================================
1981 // ============================================================================
1983 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
1985 return wxTimeSpan(ts
).Multiply(n
);
1988 // this enum is only used in wxTimeSpan::Format() below but we can't declare
1989 // it locally to the method as it provokes an internal compiler error in egcs
1990 // 2.91.60 when building with -O2
2001 // not all strftime(3) format specifiers make sense here because, for example,
2002 // a time span doesn't have a year nor a timezone
2004 // Here are the ones which are supported (all of them are supported by strftime
2006 // %H hour in 24 hour format
2007 // %M minute (00 - 59)
2008 // %S second (00 - 59)
2011 // Also, for MFC CTimeSpan compatibility, we support
2012 // %D number of days
2014 // And, to be better than MFC :-), we also have
2015 // %E number of wEeks
2016 // %l milliseconds (000 - 999)
2017 wxString
wxTimeSpan::Format(const wxString
& format
) const
2019 // we deal with only positive time spans here and just add the sign in
2020 // front for the negative ones
2023 wxString
str(Negate().Format(format
));
2027 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
2028 wxT("NULL format in wxTimeSpan::Format") );
2031 str
.Alloc(format
.length());
2033 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
2035 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
2036 // question is what should ts.Format("%S") do? The code here returns "3273"
2037 // in this case (i.e. the total number of seconds, not just seconds % 60)
2038 // because, for me, this call means "give me entire time interval in
2039 // seconds" and not "give me the seconds part of the time interval"
2041 // If we agree that it should behave like this, it is clear that the
2042 // interpretation of each format specifier depends on the presence of the
2043 // other format specs in the string: if there was "%H" before "%M", we
2044 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
2046 // we remember the most important unit found so far
2047 TimeSpanPart partBiggest
= Part_MSec
;
2049 for ( wxString::const_iterator pch
= format
.begin(); pch
!= format
.end(); ++pch
)
2053 if ( ch
== wxT('%') )
2055 // the start of the format specification of the printf() below
2056 wxString
fmtPrefix(wxT('%'));
2061 // the number of digits for the format string, 0 if unused
2062 unsigned digits
= 0;
2064 ch
= *++pch
; // get the format spec char
2068 wxFAIL_MSG( wxT("invalid format character") );
2074 // skip the part below switch
2079 if ( partBiggest
< Part_Day
)
2085 partBiggest
= Part_Day
;
2090 partBiggest
= Part_Week
;
2096 if ( partBiggest
< Part_Hour
)
2102 partBiggest
= Part_Hour
;
2109 n
= GetMilliseconds().ToLong();
2110 if ( partBiggest
< Part_MSec
)
2114 //else: no need to reset partBiggest to Part_MSec, it is
2115 // the least significant one anyhow
2122 if ( partBiggest
< Part_Min
)
2128 partBiggest
= Part_Min
;
2135 n
= GetSeconds().ToLong();
2136 if ( partBiggest
< Part_Sec
)
2142 partBiggest
= Part_Sec
;
2151 fmtPrefix
<< wxT("0") << digits
;
2154 str
+= wxString::Format(fmtPrefix
+ wxT("ld"), n
);
2158 // normal character, just copy
2166 #endif // wxUSE_DATETIME