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"
49 #include "wx/tokenzr.h"
60 #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 int GetTimeZone();
74 extern wxString
CallStrftime(const wxString
& format
, const tm
* tm
);
76 // ----------------------------------------------------------------------------
77 // constants (see also datetime.cpp)
78 // ----------------------------------------------------------------------------
80 static const int DAYS_PER_WEEK
= 7;
82 static const int HOURS_PER_DAY
= 24;
84 static const int SEC_PER_MIN
= 60;
86 static const int MIN_PER_HOUR
= 60;
88 // ----------------------------------------------------------------------------
90 // ----------------------------------------------------------------------------
101 // return the month if the string is a month name or Inv_Month otherwise
103 // flags can contain wxDateTime::Name_Abbr/Name_Full or both of them and lang
104 // can be either DateLang_Local (default) to interpret string as a localized
105 // month name or DateLang_English to parse it as a standard English name or
106 // their combination to interpret it in any way
108 GetMonthFromName(const wxString
& name
, int flags
, int lang
)
110 wxDateTime::Month mon
;
111 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
113 // case-insensitive comparison either one of or with both abbreviated
115 if ( flags
& wxDateTime::Name_Full
)
117 if ( lang
& DateLang_English
)
119 if ( name
.CmpNoCase(wxDateTime::GetEnglishMonthName(mon
,
120 wxDateTime::Name_Full
)) == 0 )
124 if ( lang
& DateLang_Local
)
126 if ( name
.CmpNoCase(wxDateTime::GetMonthName(mon
,
127 wxDateTime::Name_Full
)) == 0 )
132 if ( flags
& wxDateTime::Name_Abbr
)
134 if ( lang
& DateLang_English
)
136 if ( name
.CmpNoCase(wxDateTime::GetEnglishMonthName(mon
,
137 wxDateTime::Name_Abbr
)) == 0 )
141 if ( lang
& DateLang_Local
)
143 if ( name
.CmpNoCase(wxDateTime::GetMonthName(mon
,
144 wxDateTime::Name_Abbr
)) == 0 )
153 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
155 // flags and lang parameters have the same meaning as for GetMonthFromName()
158 GetWeekDayFromName(const wxString
& name
, int flags
, int lang
)
160 wxDateTime::WeekDay wd
;
161 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
163 if ( flags
& wxDateTime::Name_Full
)
165 if ( lang
& DateLang_English
)
167 if ( name
.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd
,
168 wxDateTime::Name_Full
)) == 0 )
172 if ( lang
& DateLang_Local
)
174 if ( name
.CmpNoCase(wxDateTime::GetWeekDayName(wd
,
175 wxDateTime::Name_Full
)) == 0 )
180 if ( flags
& wxDateTime::Name_Abbr
)
182 if ( lang
& DateLang_English
)
184 if ( name
.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd
,
185 wxDateTime::Name_Abbr
)) == 0 )
189 if ( lang
& DateLang_Local
)
191 if ( name
.CmpNoCase(wxDateTime::GetWeekDayName(wd
,
192 wxDateTime::Name_Abbr
)) == 0 )
201 // scans all digits (but no more than len) and returns the resulting number
202 bool GetNumericToken(size_t len
,
203 wxString::const_iterator
& p
,
204 const wxString::const_iterator
& end
,
205 unsigned long *number
)
209 while ( p
!= end
&& wxIsdigit(*p
) )
213 if ( len
&& ++n
> len
)
217 return !s
.empty() && s
.ToULong(number
);
220 // scans all alphabetic characters and returns the resulting string
222 GetAlphaToken(wxString::const_iterator
& p
,
223 const wxString::const_iterator
& end
)
226 while ( p
!= end
&& wxIsalpha(*p
) )
234 // scans all characters which can appear in a week day/month name
236 // this is different from GetAlphaToken() as some locales (e.g. fr_FR) use
237 // trailing periods after the abbreviated week day/month names
239 GetNameToken(wxString::const_iterator
& p
,
240 const wxString::const_iterator
& end
)
242 wxString token
= GetAlphaToken(p
, end
);
243 if ( p
!= end
&& *p
== '.' )
249 // parses string starting at given iterator using the specified format and,
250 // optionally, a fall back format (and optionally another one... but it stops
253 // if unsuccessful, returns invalid wxDateTime without changing p; otherwise
254 // advance p to the end of the match and returns wxDateTime containing the
255 // results of the parsing
257 ParseFormatAt(wxString::const_iterator
& p
,
258 const wxString::const_iterator
& end
,
260 // FIXME-VC6: using wxString() instead of wxEmptyString in the
261 // line below results in error C2062: type 'class
262 // wxString (__cdecl *)(void)' unexpected
263 const wxString
& fmtAlt
= wxEmptyString
)
265 const wxString
str(p
, end
);
266 wxString::const_iterator endParse
;
268 if ( dt
.ParseFormat(str
, fmt
, &endParse
) ||
269 (!fmtAlt
.empty() && dt
.ParseFormat(str
, fmtAlt
, &endParse
)) )
271 p
+= endParse
- str
.begin();
273 //else: all formats failed
278 } // anonymous namespace
280 // ----------------------------------------------------------------------------
281 // wxDateTime to/from text representations
282 // ----------------------------------------------------------------------------
284 wxString
wxDateTime::Format(const wxString
& formatp
, const TimeZone
& tz
) const
286 wxCHECK_MSG( !formatp
.empty(), wxEmptyString
,
287 _T("NULL format in wxDateTime::Format") );
289 wxString format
= formatp
;
291 format
.Replace("%c",wxLocale::GetInfo(wxLOCALE_DATE_TIME_FMT
));
292 format
.Replace("%x",wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT
));
293 format
.Replace("%X",wxLocale::GetInfo(wxLOCALE_TIME_FMT
));
295 // we have to use our own implementation if the date is out of range of
296 // strftime() or if we use non standard specificators
298 time_t time
= GetTicks();
300 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
305 if ( tz
.GetOffset() == -GetTimeZone() )
307 // we are working with local time
308 tm
= wxLocaltime_r(&time
, &tmstruct
);
310 // should never happen
311 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxLocaltime_r() failed") );
315 time
+= (int)tz
.GetOffset();
317 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
318 int time2
= (int) time
;
324 tm
= wxGmtime_r(&time
, &tmstruct
);
326 // should never happen
327 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxGmtime_r() failed") );
331 tm
= (struct tm
*)NULL
;
337 return CallStrftime(format
, tm
);
340 //else: use generic code below
341 #endif // HAVE_STRFTIME
343 // we only parse ANSI C format specifications here, no POSIX 2
344 // complications, no GNU extensions but we do add support for a "%l" format
345 // specifier allowing to get the number of milliseconds
348 // used for calls to strftime() when we only deal with time
349 struct tm tmTimeOnly
;
350 tmTimeOnly
.tm_hour
= tm
.hour
;
351 tmTimeOnly
.tm_min
= tm
.min
;
352 tmTimeOnly
.tm_sec
= tm
.sec
;
353 tmTimeOnly
.tm_wday
= 0;
354 tmTimeOnly
.tm_yday
= 0;
355 tmTimeOnly
.tm_mday
= 1; // any date will do
356 tmTimeOnly
.tm_mon
= 0;
357 tmTimeOnly
.tm_year
= 76;
358 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
360 wxString tmp
, res
, fmt
;
361 for ( wxString::const_iterator p
= format
.begin(); p
!= format
.end(); ++p
)
371 // set the default format
372 switch ( (*++p
).GetValue() )
374 case _T('Y'): // year has 4 digits
378 case _T('j'): // day of year has 3 digits
379 case _T('l'): // milliseconds have 3 digits
383 case _T('w'): // week day as number has only one
388 // it's either another valid format specifier in which case
389 // the format is "%02d" (for all the rest) or we have the
390 // field width preceding the format in which case it will
391 // override the default format anyhow
400 // start of the format specification
401 switch ( (*p
).GetValue() )
403 case _T('a'): // a weekday name
405 // second parameter should be true for abbreviated names
406 res
+= GetWeekDayName(tm
.GetWeekDay(),
407 *p
== _T('a') ? Name_Abbr
: Name_Full
);
410 case _T('b'): // a month name
412 res
+= GetMonthName(tm
.mon
,
413 *p
== _T('b') ? Name_Abbr
: Name_Full
);
416 case _T('c'): // locale default date and time representation
417 case _T('x'): // locale default date representation
420 // the problem: there is no way to know what do these format
421 // specifications correspond to for the current locale.
423 // the solution: use a hack and still use strftime(): first
424 // find the YEAR which is a year in the strftime() range (1970
425 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
426 // of the real year. Then make a copy of the format and
427 // replace all occurrences of YEAR in it with some unique
428 // string not appearing anywhere else in it, then use
429 // strftime() to format the date in year YEAR and then replace
430 // YEAR back by the real year and the unique replacement
431 // string back with YEAR. Notice that "all occurrences of YEAR"
432 // means all occurrences of 4 digit as well as 2 digit form!
434 // the bugs: we assume that neither of %c nor %x contains any
435 // fields which may change between the YEAR and real year. For
436 // example, the week number (%U, %W) and the day number (%j)
437 // will change if one of these years is leap and the other one
440 // find the YEAR: normally, for any year X, Jan 1 of the
441 // year X + 28 is the same weekday as Jan 1 of X (because
442 // the weekday advances by 1 for each normal X and by 2
443 // for each leap X, hence by 5 every 4 years or by 35
444 // which is 0 mod 7 every 28 years) but this rule breaks
445 // down if there are years between X and Y which are
446 // divisible by 4 but not leap (i.e. divisible by 100 but
447 // not 400), hence the correction.
449 int yearReal
= GetYear(tz
);
450 int mod28
= yearReal
% 28;
452 // be careful to not go too far - we risk to leave the
457 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
461 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
464 int nCentury
= year
/ 100,
465 nCenturyReal
= yearReal
/ 100;
467 // need to adjust for the years divisble by 400 which are
468 // not leap but are counted like leap ones if we just take
469 // the number of centuries in between for nLostWeekDays
470 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
471 (nCentury
/ 4 - nCenturyReal
/ 4);
473 // we have to gain back the "lost" weekdays: note that the
474 // effect of this loop is to not do anything to
475 // nLostWeekDays (which we won't use any more), but to
476 // (indirectly) set the year correctly
477 while ( (nLostWeekDays
% 7) != 0 )
479 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
482 // finally move the year below 2000 so that the 2-digit
483 // year number can never match the month or day of the
484 // month when we do the replacements below
488 wxASSERT_MSG( year
>= 1970 && year
< 2000,
489 _T("logic error in wxDateTime::Format") );
492 // use strftime() to format the same date but in supported
495 // NB: we assume that strftime() doesn't check for the
496 // date validity and will happily format the date
497 // corresponding to Feb 29 of a non leap year (which
498 // may happen if yearReal was leap and year is not)
499 struct tm tmAdjusted
;
501 tmAdjusted
.tm_hour
= tm
.hour
;
502 tmAdjusted
.tm_min
= tm
.min
;
503 tmAdjusted
.tm_sec
= tm
.sec
;
504 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
505 tmAdjusted
.tm_yday
= GetDayOfYear();
506 tmAdjusted
.tm_mday
= tm
.mday
;
507 tmAdjusted
.tm_mon
= tm
.mon
;
508 tmAdjusted
.tm_year
= year
- 1900;
509 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
510 wxString str
= CallStrftime(*p
== _T('c') ? _T("%c")
514 // now replace the replacement year with the real year:
515 // notice that we have to replace the 4 digit year with
516 // a unique string not appearing in strftime() output
517 // first to prevent the 2 digit year from matching any
518 // substring of the 4 digit year (but any day, month,
519 // hours or minutes components should be safe because
520 // they are never in 70-99 range)
521 wxString
replacement("|");
522 while ( str
.find(replacement
) != wxString::npos
)
525 str
.Replace(wxString::Format("%d", year
),
527 str
.Replace(wxString::Format("%d", year
% 100),
528 wxString::Format("%d", yearReal
% 100));
529 str
.Replace(replacement
,
530 wxString::Format("%d", yearReal
));
534 #else // !HAVE_STRFTIME
535 // Use "%m/%d/%y %H:%M:%S" format instead
536 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
537 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
538 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
541 case _T('d'): // day of a month (01-31)
542 res
+= wxString::Format(fmt
, tm
.mday
);
545 case _T('H'): // hour in 24h format (00-23)
546 res
+= wxString::Format(fmt
, tm
.hour
);
549 case _T('I'): // hour in 12h format (01-12)
551 // 24h -> 12h, 0h -> 12h too
552 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
553 : tm
.hour
? tm
.hour
: 12;
554 res
+= wxString::Format(fmt
, hour12
);
558 case _T('j'): // day of the year
559 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
562 case _T('l'): // milliseconds (NOT STANDARD)
563 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
566 case _T('m'): // month as a number (01-12)
567 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
570 case _T('M'): // minute as a decimal number (00-59)
571 res
+= wxString::Format(fmt
, tm
.min
);
574 case _T('p'): // AM or PM string
576 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
577 #else // !HAVE_STRFTIME
578 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
579 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
582 case _T('S'): // second as a decimal number (00-61)
583 res
+= wxString::Format(fmt
, tm
.sec
);
586 case _T('U'): // week number in the year (Sunday 1st week day)
587 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
590 case _T('W'): // week number in the year (Monday 1st week day)
591 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
594 case _T('w'): // weekday as a number (0-6), Sunday = 0
595 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
598 // case _T('x'): -- handled with "%c"
600 case _T('X'): // locale default time representation
601 // just use strftime() to format the time for us
603 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
604 #else // !HAVE_STRFTIME
605 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
606 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
609 case _T('y'): // year without century (00-99)
610 res
+= wxString::Format(fmt
, tm
.year
% 100);
613 case _T('Y'): // year with century
614 res
+= wxString::Format(fmt
, tm
.year
);
617 case _T('Z'): // timezone name
619 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
624 // is it the format width?
626 while ( *p
== _T('-') || *p
== _T('+') ||
627 *p
== _T(' ') || wxIsdigit(*p
) )
634 // we've only got the flags and width so far in fmt
635 fmt
.Prepend(_T('%'));
643 // no, it wasn't the width
644 wxFAIL_MSG(_T("unknown format specificator"));
646 // fall through and just copy it nevertheless
648 case _T('%'): // a percent sign
652 case 0: // the end of string
653 wxFAIL_MSG(_T("missing format at the end of string"));
655 // just put the '%' which was the last char in format
665 // this function parses a string in (strict) RFC 822 format: see the section 5
666 // of the RFC for the detailed description, but briefly it's something of the
667 // form "Sat, 18 Dec 1999 00:48:30 +0100"
669 // this function is "strict" by design - it must reject anything except true
670 // RFC822 time specs.
672 wxDateTime::ParseRfc822Date(const wxString
& date
, wxString::const_iterator
*end
)
674 wxString::const_iterator p
= date
.begin();
677 static const int WDAY_LEN
= 3;
678 const wxString::const_iterator endWday
= p
+ WDAY_LEN
;
679 const wxString
wday(p
, endWday
);
680 if ( GetWeekDayFromName(wday
, Name_Abbr
, DateLang_English
) == Inv_WeekDay
)
682 //else: ignore week day for now, we could also check that it really
683 // corresponds to the specified date
687 // 2. separating comma
688 if ( *p
++ != ',' || *p
++ != ' ' )
692 if ( !wxIsdigit(*p
) )
695 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - '0');
699 day
= (wxDateTime_t
)(day
+ (*p
++ - '0'));
706 static const int MONTH_LEN
= 3;
707 const wxString::const_iterator endMonth
= p
+ MONTH_LEN
;
708 const wxString
monName(p
, endMonth
);
709 Month mon
= GetMonthFromName(monName
, Name_Abbr
, DateLang_English
);
710 if ( mon
== Inv_Month
)
719 if ( !wxIsdigit(*p
) )
722 int year
= *p
++ - '0';
723 if ( !wxIsdigit(*p
) ) // should have at least 2 digits in the year
729 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
735 if ( !wxIsdigit(*p
) )
737 // no 3 digit years please
748 // 6. time in hh:mm:ss format with seconds being optional
749 if ( !wxIsdigit(*p
) )
752 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - '0');
754 if ( !wxIsdigit(*p
) )
758 hour
= (wxDateTime_t
)(hour
+ (*p
++ - '0'));
763 if ( !wxIsdigit(*p
) )
766 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - '0');
768 if ( !wxIsdigit(*p
) )
772 min
+= (wxDateTime_t
)(*p
++ - '0');
774 wxDateTime_t sec
= 0;
778 if ( !wxIsdigit(*p
) )
781 sec
= (wxDateTime_t
)(*p
++ - '0');
783 if ( !wxIsdigit(*p
) )
787 sec
+= (wxDateTime_t
)(*p
++ - '0');
793 // 7. now the interesting part: the timezone
794 int offset
wxDUMMY_INITIALIZE(0);
795 if ( *p
== '-' || *p
== '+' )
797 // the explicit offset given: it has the form of hhmm
798 bool plus
= *p
++ == '+';
800 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
805 offset
= MIN_PER_HOUR
*(10*(*p
- '0') + (*(p
+ 1) - '0'));
809 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
813 offset
+= 10*(*p
- '0') + (*(p
+ 1) - '0');
822 // the symbolic timezone given: may be either military timezone or one
823 // of standard abbreviations
826 // military: Z = UTC, J unused, A = -1, ..., Y = +12
827 static const int offsets
[26] =
829 //A B C D E F G H I J K L M
830 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
831 //N O P R Q S T U V W Z Y Z
832 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
835 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
838 offset
= offsets
[*p
++ - 'A'];
843 const wxString
tz(p
, date
.end());
844 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
846 else if ( tz
== _T("AST") )
848 else if ( tz
== _T("ADT") )
850 else if ( tz
== _T("EST") )
852 else if ( tz
== _T("EDT") )
854 else if ( tz
== _T("CST") )
856 else if ( tz
== _T("CDT") )
858 else if ( tz
== _T("MST") )
860 else if ( tz
== _T("MDT") )
862 else if ( tz
== _T("PST") )
864 else if ( tz
== _T("PDT") )
873 offset
*= MIN_PER_HOUR
;
877 // the spec was correct, construct the date from the values we found
878 Set(day
, mon
, year
, hour
, min
, sec
);
879 MakeFromTimezone(TimeZone::Make(offset
*SEC_PER_MIN
));
888 wxDateTime::ParseFormat(const wxString
& date
,
889 const wxString
& format
,
890 const wxDateTime
& dateDef
,
891 wxString::const_iterator
*endParse
)
893 wxCHECK_MSG( !format
.empty(), false, "format can't be empty" );
894 wxCHECK_MSG( endParse
, false, "end iterator pointer must be specified" );
899 // what fields have we found?
900 bool haveWDay
= false,
910 bool hourIsIn12hFormat
= false, // or in 24h one?
911 isPM
= false; // AM by default
913 // and the value of the items we have (init them to get rid of warnings)
914 wxDateTime_t msec
= 0,
918 WeekDay wday
= Inv_WeekDay
;
919 wxDateTime_t yday
= 0,
921 wxDateTime::Month mon
= Inv_Month
;
924 wxString::const_iterator input
= date
.begin();
925 const wxString::const_iterator end
= date
.end();
926 for ( wxString::const_iterator fmt
= format
.begin(); fmt
!= format
.end(); ++fmt
)
928 if ( *fmt
!= _T('%') )
930 if ( wxIsspace(*fmt
) )
932 // a white space in the format string matches 0 or more white
933 // spaces in the input
934 while ( input
!= end
&& wxIsspace(*input
) )
941 // any other character (not whitespace, not '%') must be
942 // matched by itself in the input
943 if ( input
== end
|| *input
++ != *fmt
)
950 // done with this format char
954 // start of a format specification
956 // parse the optional width
958 while ( wxIsdigit(*++fmt
) )
964 // the default widths for the various fields
967 switch ( (*fmt
).GetValue() )
969 case _T('Y'): // year has 4 digits
973 case _T('j'): // day of year has 3 digits
974 case _T('l'): // milliseconds have 3 digits
978 case _T('w'): // week day as number has only one
983 // default for all other fields
988 // then the format itself
989 switch ( (*fmt
).GetValue() )
991 case _T('a'): // a weekday name
994 wday
= GetWeekDayFromName
996 GetNameToken(input
, end
),
997 *fmt
== 'a' ? Name_Abbr
: Name_Full
,
1000 if ( wday
== Inv_WeekDay
)
1009 case _T('b'): // a month name
1012 mon
= GetMonthFromName
1014 GetNameToken(input
, end
),
1015 *fmt
== 'b' ? Name_Abbr
: Name_Full
,
1018 if ( mon
== Inv_Month
)
1027 case _T('c'): // locale default date and time representation
1032 fmtDateTime
= wxLocale::GetInfo(wxLOCALE_DATE_TIME_FMT
);
1033 if ( !fmtDateTime
.empty() )
1034 dt
= ParseFormatAt(input
, end
, fmtDateTime
);
1036 if ( !dt
.IsValid() )
1038 // also try the format which corresponds to ctime()
1039 // output (i.e. the "C" locale default)
1040 dt
= ParseFormatAt(input
, end
, wxS("%a %b %d %H:%M:%S %Y"));
1043 if ( !dt
.IsValid() )
1045 // and finally also the two generic date/time formats
1046 dt
= ParseFormatAt(input
, end
, wxS("%x %X"), wxS("%X %x"));
1049 if ( !dt
.IsValid() )
1052 const Tm tm
= dt
.GetTm();
1062 haveDay
= haveMon
= haveYear
=
1063 haveHour
= haveMin
= haveSec
= true;
1067 case _T('d'): // day of a month (01-31)
1068 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1069 (num
> 31) || (num
< 1) )
1075 // we can't check whether the day range is correct yet, will
1076 // do it later - assume ok for now
1078 mday
= (wxDateTime_t
)num
;
1081 case _T('H'): // hour in 24h format (00-23)
1082 if ( !GetNumericToken(width
, input
, end
, &num
) || (num
> 23) )
1089 hour
= (wxDateTime_t
)num
;
1092 case _T('I'): // hour in 12h format (01-12)
1093 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1094 !num
|| (num
> 12) )
1101 hourIsIn12hFormat
= true;
1102 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
1105 case _T('j'): // day of the year
1106 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1107 !num
|| (num
> 366) )
1114 yday
= (wxDateTime_t
)num
;
1117 case _T('l'): // milliseconds (0-999)
1118 if ( !GetNumericToken(width
, input
, end
, &num
) )
1122 msec
= (wxDateTime_t
)num
;
1125 case _T('m'): // month as a number (01-12)
1126 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1127 !num
|| (num
> 12) )
1134 mon
= (Month
)(num
- 1);
1137 case _T('M'): // minute as a decimal number (00-59)
1138 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1146 min
= (wxDateTime_t
)num
;
1149 case _T('p'): // AM or PM string
1151 wxString am
, pm
, token
= GetAlphaToken(input
, end
);
1153 // some locales have empty AM/PM tokens and thus when formatting
1154 // dates with the %p specifier nothing is generated; when trying to
1155 // parse them back, we get an empty token here... but that's not
1160 GetAmPmStrings(&am
, &pm
);
1161 if (am
.empty() && pm
.empty())
1162 return false; // no am/pm strings defined
1163 if ( token
.CmpNoCase(pm
) == 0 )
1167 else if ( token
.CmpNoCase(am
) != 0 )
1175 case _T('r'): // time as %I:%M:%S %p
1178 if ( !dt
.ParseFormat(wxString(input
, end
),
1179 wxS("%I:%M:%S %p"), &input
) )
1182 haveHour
= haveMin
= haveSec
= true;
1184 const Tm tm
= dt
.GetTm();
1191 case _T('R'): // time as %H:%M
1194 dt
= ParseFormatAt(input
, end
, wxS("%H:%M"));
1195 if ( !dt
.IsValid() )
1201 const Tm tm
= dt
.GetTm();
1207 case _T('S'): // second as a decimal number (00-61)
1208 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1216 sec
= (wxDateTime_t
)num
;
1219 case _T('T'): // time as %H:%M:%S
1222 dt
= ParseFormatAt(input
, end
, wxS("%H:%M:%S"));
1223 if ( !dt
.IsValid() )
1230 const Tm tm
= dt
.GetTm();
1237 case _T('w'): // weekday as a number (0-6), Sunday = 0
1238 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1246 wday
= (WeekDay
)num
;
1249 case _T('x'): // locale default date representation
1252 fmtDate
= wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT
),
1253 fmtDateAlt
= wxLocale::GetInfo(wxLOCALE_LONG_DATE_FMT
);
1255 if ( fmtDate
.empty() )
1257 if ( IsWestEuropeanCountry(GetCountry()) ||
1258 GetCountry() == Russia
)
1260 fmtDate
= wxS("%d/%m/%Y");
1261 fmtDateAlt
= wxS("%m/%d/%Y");
1265 fmtDate
= wxS("%m/%d/%Y");
1266 fmtDateAlt
= wxS("%d/%m/%Y");
1271 dt
= ParseFormatAt(input
, end
, fmtDate
, fmtDateAlt
);
1273 if ( !dt
.IsValid() )
1275 // try with short years too
1276 fmtDate
.Replace("%Y","%y");
1277 fmtDateAlt
.Replace("%Y","%y");
1278 dt
= ParseFormatAt(input
, end
, fmtDate
, fmtDateAlt
);
1280 if ( !dt
.IsValid() )
1284 const Tm tm
= dt
.GetTm();
1297 case _T('X'): // locale default time representation
1299 wxString fmtTime
= wxLocale::GetInfo(wxLOCALE_TIME_FMT
),
1302 if ( fmtTime
.empty() )
1304 // try to parse what follows as "%H:%M:%S" and, if this
1305 // fails, as "%I:%M:%S %p" - this should catch the most
1312 dt
= ParseFormatAt(input
, end
, fmtTime
, fmtTimeAlt
);
1313 if ( !dt
.IsValid() )
1320 const Tm tm
= dt
.GetTm();
1327 case _T('y'): // year without century (00-99)
1328 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1337 // TODO should have an option for roll over date instead of
1338 // hard coding it here
1339 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
1342 case _T('Y'): // year with century
1343 if ( !GetNumericToken(width
, input
, end
, &num
) )
1350 year
= (wxDateTime_t
)num
;
1353 case _T('Z'): // timezone name
1354 // FIXME: currently we just ignore everything that looks like a
1356 GetAlphaToken(input
, end
);
1359 case _T('%'): // a percent sign
1360 if ( *input
++ != _T('%') )
1367 case 0: // the end of string
1368 wxFAIL_MSG(_T("unexpected format end"));
1372 default: // not a known format spec
1377 // format matched, try to construct a date from what we have now
1379 if ( dateDef
.IsValid() )
1381 // take this date as default
1382 tmDef
= dateDef
.GetTm();
1384 else if ( IsValid() )
1386 // if this date is valid, don't change it
1391 // no default and this date is invalid - fall back to Today()
1392 tmDef
= Today().GetTm();
1408 // TODO we don't check here that the values are consistent, if both year
1409 // day and month/day were found, we just ignore the year day and we
1410 // also always ignore the week day
1413 if ( mday
> GetNumberOfDays(tm
.mon
, tm
.year
) )
1418 else if ( haveYDay
)
1420 if ( yday
> GetNumberOfDays(tm
.year
) )
1423 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
1430 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
1432 // translate to 24hour format
1435 //else: either already in 24h format or no translation needed
1458 // finally check that the week day is consistent -- if we had it
1459 if ( haveWDay
&& GetWeekDay() != wday
)
1468 wxDateTime::ParseDateTime(const wxString
& date
, wxString::const_iterator
*end
)
1470 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1472 // Set to current day and hour, so strings like '14:00' becomes today at
1473 // 14, not some other random date
1474 wxDateTime dtDate
= wxDateTime::Today();
1475 wxDateTime dtTime
= wxDateTime::Today();
1477 wxString::const_iterator
1482 // If we got a date in the beginning, see if there is a time specified
1484 if ( dtDate
.ParseDate(date
, &endDate
) )
1486 // Skip spaces, as the ParseTime() function fails on spaces
1487 while ( endDate
!= date
.end() && wxIsspace(*endDate
) )
1490 const wxString
timestr(endDate
, date
.end());
1491 if ( !dtTime
.ParseTime(timestr
, &endTime
) )
1494 endBoth
= endDate
+ (endTime
- timestr
.begin());
1496 else // no date in the beginning
1498 // check if we have a time followed by a date
1499 if ( !dtTime
.ParseTime(date
, &endTime
) )
1502 while ( endTime
!= date
.end() && wxIsspace(*endTime
) )
1505 const wxString
datestr(endTime
, date
.end());
1506 if ( !dtDate
.ParseDate(datestr
, &endDate
) )
1509 endBoth
= endTime
+ (endDate
- datestr
.begin());
1512 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
1513 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
1514 dtTime
.GetMillisecond());
1522 wxDateTime::ParseDate(const wxString
& date
, wxString::const_iterator
*end
)
1524 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1526 // this is a simplified version of ParseDateTime() which understands only
1527 // "today" (for wxDate compatibility) and digits only otherwise (and not
1528 // all esoteric constructions ParseDateTime() knows about)
1530 const wxString::const_iterator pBegin
= date
.begin();
1532 wxString::const_iterator p
= pBegin
;
1533 while ( wxIsspace(*p
) )
1536 // some special cases
1540 int dayDiffFromToday
;
1543 { wxTRANSLATE("today"), 0 },
1544 { wxTRANSLATE("yesterday"), -1 },
1545 { wxTRANSLATE("tomorrow"), 1 },
1548 const size_t lenRest
= date
.end() - p
;
1549 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
1551 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
1552 size_t len
= dateStr
.length();
1554 if ( len
> lenRest
)
1557 const wxString::const_iterator pEnd
= p
+ len
;
1558 if ( wxString(p
, pEnd
).CmpNoCase(dateStr
) == 0 )
1560 // nothing can follow this, so stop here
1564 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
1566 if ( dayDiffFromToday
)
1568 *this += wxDateSpan::Days(dayDiffFromToday
);
1577 // We try to guess what we have here: for each new (numeric) token, we
1578 // determine if it can be a month, day or a year. Of course, there is an
1579 // ambiguity as some numbers may be days as well as months, so we also
1580 // have the ability to back track.
1583 bool haveDay
= false, // the months day?
1584 haveWDay
= false, // the day of week?
1585 haveMon
= false, // the month?
1586 haveYear
= false; // the year?
1588 // and the value of the items we have (init them to get rid of warnings)
1589 WeekDay wday
= Inv_WeekDay
;
1590 wxDateTime_t day
= 0;
1591 wxDateTime::Month mon
= Inv_Month
;
1594 // tokenize the string
1596 static const wxStringCharType
*dateDelimiters
= wxS(".,/-\t\r\n ");
1597 wxStringTokenizer
tok(wxString(p
, date
.end()), dateDelimiters
);
1598 while ( tok
.HasMoreTokens() )
1600 wxString token
= tok
.GetNextToken();
1606 if ( token
.ToULong(&val
) )
1608 // guess what this number is
1614 if ( !haveMon
&& val
> 0 && val
<= 12 )
1616 // assume it is month
1619 else // not the month
1623 // this can only be the year
1626 else // may be either day or year
1628 // use a leap year if we don't have the year yet to allow
1629 // dates like 2/29/1976 which would be rejected otherwise
1630 wxDateTime_t max_days
= (wxDateTime_t
)(
1632 ? GetNumberOfDays(mon
, haveYear
? year
: 1976)
1637 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
1642 else // yes, suppose it's the day
1656 year
= (wxDateTime_t
)val
;
1665 day
= (wxDateTime_t
)val
;
1671 mon
= (Month
)(val
- 1);
1674 else // not a number
1676 // be careful not to overwrite the current mon value
1677 Month mon2
= GetMonthFromName
1680 Name_Full
| Name_Abbr
,
1681 DateLang_Local
| DateLang_English
1683 if ( mon2
!= Inv_Month
)
1688 // but we already have a month - maybe we guessed wrong?
1691 // no need to check in month range as always < 12, but
1692 // the days are counted from 1 unlike the months
1693 day
= (wxDateTime_t
)(mon
+ 1);
1698 // could possible be the year (doesn't the year come
1699 // before the month in the japanese format?) (FIXME)
1708 else // not a valid month name
1710 WeekDay wday2
= GetWeekDayFromName
1713 Name_Full
| Name_Abbr
,
1714 DateLang_Local
| DateLang_English
1716 if ( wday2
!= Inv_WeekDay
)
1728 else // not a valid weekday name
1731 static const char *ordinals
[] =
1733 wxTRANSLATE("first"),
1734 wxTRANSLATE("second"),
1735 wxTRANSLATE("third"),
1736 wxTRANSLATE("fourth"),
1737 wxTRANSLATE("fifth"),
1738 wxTRANSLATE("sixth"),
1739 wxTRANSLATE("seventh"),
1740 wxTRANSLATE("eighth"),
1741 wxTRANSLATE("ninth"),
1742 wxTRANSLATE("tenth"),
1743 wxTRANSLATE("eleventh"),
1744 wxTRANSLATE("twelfth"),
1745 wxTRANSLATE("thirteenth"),
1746 wxTRANSLATE("fourteenth"),
1747 wxTRANSLATE("fifteenth"),
1748 wxTRANSLATE("sixteenth"),
1749 wxTRANSLATE("seventeenth"),
1750 wxTRANSLATE("eighteenth"),
1751 wxTRANSLATE("nineteenth"),
1752 wxTRANSLATE("twentieth"),
1753 // that's enough - otherwise we'd have problems with
1754 // composite (or not) ordinals
1758 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
1760 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
1766 if ( n
== WXSIZEOF(ordinals
) )
1768 // stop here - something unknown
1775 // don't try anything here (as in case of numeric day
1776 // above) - the symbolic day spec should always
1777 // precede the month/year
1783 day
= (wxDateTime_t
)(n
+ 1);
1788 nPosCur
= tok
.GetPosition();
1791 // either no more tokens or the scan was stopped by something we couldn't
1792 // parse - in any case, see if we can construct a date from what we have
1793 if ( !haveDay
&& !haveWDay
)
1796 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
1797 !(haveDay
&& haveMon
&& haveYear
) )
1799 // without adjectives (which we don't support here) the week day only
1800 // makes sense completely separately or with the full date
1801 // specification (what would "Wed 1999" mean?)
1805 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
1807 // may be we have month and day instead of day and year?
1808 if ( haveDay
&& !haveMon
)
1812 // exchange day and month
1813 mon
= (wxDateTime::Month
)(day
- 1);
1815 // we're in the current year then
1816 if ( (year
> 0) && (year
<= (int)GetNumberOfDays(mon
, Inv_Year
)) )
1818 day
= (wxDateTime_t
)year
;
1823 //else: no, can't exchange, leave haveMon == false
1833 mon
= GetCurrentMonth();
1838 year
= GetCurrentYear();
1843 // normally we check the day above but the check is optimistic in case
1844 // we find the day before its month/year so we have to redo it now
1845 if ( day
> GetNumberOfDays(mon
, year
) )
1848 Set(day
, mon
, year
);
1852 // check that it is really the same
1853 if ( GetWeekDay() != wday
)
1861 SetToWeekDayInSameWeek(wday
);
1864 // return the pointer to the first unparsed char
1866 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
1868 // if we couldn't parse the token after the delimiter, put back the
1869 // delimiter as well
1879 wxDateTime::ParseTime(const wxString
& time
, wxString::const_iterator
*end
)
1881 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1883 // first try some extra things
1890 { wxTRANSLATE("noon"), 12 },
1891 { wxTRANSLATE("midnight"), 00 },
1895 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
1897 const wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
1898 const wxString::const_iterator p
= time
.begin() + timeString
.length();
1899 if ( timeString
.CmpNoCase(wxString(time
.begin(), p
)) == 0 )
1901 // casts required by DigitalMars
1902 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
1911 // try all time formats we may think about in the order from longest to
1913 static const char *timeFormats
[] =
1915 "%I:%M:%S %p", // 12hour with AM/PM
1916 "%H:%M:%S", // could be the same or 24 hour one so try it too
1917 "%I:%M %p", // 12hour with AM/PM but without seconds
1918 "%H:%M:%S", // and a possibly 24 hour version without seconds
1919 "%X", // possibly something from above or maybe something
1920 // completely different -- try it last
1922 // TODO: parse timezones
1925 for ( size_t nFmt
= 0; nFmt
< WXSIZEOF(timeFormats
); nFmt
++ )
1927 if ( ParseFormat(time
, timeFormats
[nFmt
], end
) )
1934 // ----------------------------------------------------------------------------
1935 // Workdays and holidays support
1936 // ----------------------------------------------------------------------------
1938 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
1940 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
1943 // ============================================================================
1945 // ============================================================================
1947 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
1950 return ds1
.Multiply(n
);
1953 // ============================================================================
1955 // ============================================================================
1957 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
1959 return wxTimeSpan(ts
).Multiply(n
);
1962 // this enum is only used in wxTimeSpan::Format() below but we can't declare
1963 // it locally to the method as it provokes an internal compiler error in egcs
1964 // 2.91.60 when building with -O2
1975 // not all strftime(3) format specifiers make sense here because, for example,
1976 // a time span doesn't have a year nor a timezone
1978 // Here are the ones which are supported (all of them are supported by strftime
1980 // %H hour in 24 hour format
1981 // %M minute (00 - 59)
1982 // %S second (00 - 59)
1985 // Also, for MFC CTimeSpan compatibility, we support
1986 // %D number of days
1988 // And, to be better than MFC :-), we also have
1989 // %E number of wEeks
1990 // %l milliseconds (000 - 999)
1991 wxString
wxTimeSpan::Format(const wxString
& format
) const
1993 // we deal with only positive time spans here and just add the sign in
1994 // front for the negative ones
1997 wxString
str(Negate().Format(format
));
2001 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
2002 _T("NULL format in wxTimeSpan::Format") );
2005 str
.Alloc(format
.length());
2007 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
2009 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
2010 // question is what should ts.Format("%S") do? The code here returns "3273"
2011 // in this case (i.e. the total number of seconds, not just seconds % 60)
2012 // because, for me, this call means "give me entire time interval in
2013 // seconds" and not "give me the seconds part of the time interval"
2015 // If we agree that it should behave like this, it is clear that the
2016 // interpretation of each format specifier depends on the presence of the
2017 // other format specs in the string: if there was "%H" before "%M", we
2018 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
2020 // we remember the most important unit found so far
2021 TimeSpanPart partBiggest
= Part_MSec
;
2023 for ( wxString::const_iterator pch
= format
.begin(); pch
!= format
.end(); ++pch
)
2027 if ( ch
== _T('%') )
2029 // the start of the format specification of the printf() below
2030 wxString
fmtPrefix(_T('%'));
2035 // the number of digits for the format string, 0 if unused
2036 unsigned digits
= 0;
2038 ch
= *++pch
; // get the format spec char
2042 wxFAIL_MSG( _T("invalid format character") );
2048 // skip the part below switch
2053 if ( partBiggest
< Part_Day
)
2059 partBiggest
= Part_Day
;
2064 partBiggest
= Part_Week
;
2070 if ( partBiggest
< Part_Hour
)
2076 partBiggest
= Part_Hour
;
2083 n
= GetMilliseconds().ToLong();
2084 if ( partBiggest
< Part_MSec
)
2088 //else: no need to reset partBiggest to Part_MSec, it is
2089 // the least significant one anyhow
2096 if ( partBiggest
< Part_Min
)
2102 partBiggest
= Part_Min
;
2109 n
= GetSeconds().ToLong();
2110 if ( partBiggest
< Part_Sec
)
2116 partBiggest
= Part_Sec
;
2125 fmtPrefix
<< _T("0") << digits
;
2128 str
+= wxString::Format(fmtPrefix
+ _T("ld"), n
);
2132 // normal character, just copy
2140 #endif // wxUSE_DATETIME