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 // constants (see also datetime.cpp)
68 // ----------------------------------------------------------------------------
70 static const int DAYS_PER_WEEK
= 7;
72 static const int HOURS_PER_DAY
= 24;
74 static const int SEC_PER_MIN
= 60;
76 static const int MIN_PER_HOUR
= 60;
78 // ----------------------------------------------------------------------------
80 // ----------------------------------------------------------------------------
83 wxDateTime::wxDateTime_t
GetNumOfDaysInMonth(int year
, wxDateTime::Month month
);
85 // return the month if the string is a month name or Inv_Month otherwise
86 static wxDateTime::Month
GetMonthFromName(const wxString
& name
, int flags
)
88 wxDateTime::Month mon
;
89 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
91 // case-insensitive comparison either one of or with both abbreviated
93 if ( flags
& wxDateTime::Name_Full
)
95 if ( name
.CmpNoCase(wxDateTime::
96 GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 )
102 if ( flags
& wxDateTime::Name_Abbr
)
104 if ( name
.CmpNoCase(wxDateTime::
105 GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 )
115 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
116 static wxDateTime::WeekDay
GetWeekDayFromName(const wxString
& name
, int flags
)
118 wxDateTime::WeekDay wd
;
119 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
121 // case-insensitive comparison either one of or with both abbreviated
123 if ( flags
& wxDateTime::Name_Full
)
125 if ( name
.CmpNoCase(wxDateTime::
126 GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 )
132 if ( flags
& wxDateTime::Name_Abbr
)
134 if ( name
.CmpNoCase(wxDateTime::
135 GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 )
146 struct tm
*wxDateTime::GetTmNow(struct tm
*tmstruct
)
148 time_t t
= GetTimeNow();
149 return wxLocaltime_r(&t
, tmstruct
);
152 // scans all digits (but no more than len) and returns the resulting number
153 static bool GetNumericToken(size_t len
,
154 const wxStringCharType
*& p
,
155 unsigned long *number
)
159 while ( wxIsdigit(*p
) )
163 if ( len
&& ++n
> len
)
167 return !s
.empty() && s
.ToULong(number
);
170 // scans all alphabetic characters and returns the resulting string
171 static wxString
GetAlphaToken(const wxStringCharType
*& p
)
174 while ( wxIsalpha(*p
) )
182 // ----------------------------------------------------------------------------
183 // wxDateTime to/from text representations
184 // ----------------------------------------------------------------------------
186 wxString
wxDateTime::Format(const wxString
& format
, const TimeZone
& tz
) const
188 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
189 _T("NULL format in wxDateTime::Format") );
191 // we have to use our own implementation if the date is out of range of
192 // strftime() or if we use non standard specificators
194 time_t time
= GetTicks();
196 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
201 if ( tz
.GetOffset() == -GetTimeZone() )
203 // we are working with local time
204 tm
= wxLocaltime_r(&time
, &tmstruct
);
206 // should never happen
207 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxLocaltime_r() failed") );
211 time
+= (int)tz
.GetOffset();
213 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
214 int time2
= (int) time
;
220 tm
= wxGmtime_r(&time
, &tmstruct
);
222 // should never happen
223 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxGmtime_r() failed") );
227 tm
= (struct tm
*)NULL
;
233 return CallStrftime(format
, tm
);
236 //else: use generic code below
237 #endif // HAVE_STRFTIME
239 // we only parse ANSI C format specifications here, no POSIX 2
240 // complications, no GNU extensions but we do add support for a "%l" format
241 // specifier allowing to get the number of milliseconds
244 // used for calls to strftime() when we only deal with time
245 struct tm tmTimeOnly
;
246 tmTimeOnly
.tm_hour
= tm
.hour
;
247 tmTimeOnly
.tm_min
= tm
.min
;
248 tmTimeOnly
.tm_sec
= tm
.sec
;
249 tmTimeOnly
.tm_wday
= 0;
250 tmTimeOnly
.tm_yday
= 0;
251 tmTimeOnly
.tm_mday
= 1; // any date will do
252 tmTimeOnly
.tm_mon
= 0;
253 tmTimeOnly
.tm_year
= 76;
254 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
256 wxString tmp
, res
, fmt
;
257 for ( wxString::const_iterator p
= format
.begin(); p
!= format
.end(); ++p
)
267 // set the default format
268 switch ( (*++p
).GetValue() )
270 case _T('Y'): // year has 4 digits
274 case _T('j'): // day of year has 3 digits
275 case _T('l'): // milliseconds have 3 digits
279 case _T('w'): // week day as number has only one
284 // it's either another valid format specifier in which case
285 // the format is "%02d" (for all the rest) or we have the
286 // field width preceding the format in which case it will
287 // override the default format anyhow
296 // start of the format specification
297 switch ( (*p
).GetValue() )
299 case _T('a'): // a weekday name
301 // second parameter should be true for abbreviated names
302 res
+= GetWeekDayName(tm
.GetWeekDay(),
303 *p
== _T('a') ? Name_Abbr
: Name_Full
);
306 case _T('b'): // a month name
308 res
+= GetMonthName(tm
.mon
,
309 *p
== _T('b') ? Name_Abbr
: Name_Full
);
312 case _T('c'): // locale default date and time representation
313 case _T('x'): // locale default date representation
316 // the problem: there is no way to know what do these format
317 // specifications correspond to for the current locale.
319 // the solution: use a hack and still use strftime(): first
320 // find the YEAR which is a year in the strftime() range (1970
321 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
322 // of the real year. Then make a copy of the format and
323 // replace all occurrences of YEAR in it with some unique
324 // string not appearing anywhere else in it, then use
325 // strftime() to format the date in year YEAR and then replace
326 // YEAR back by the real year and the unique replacement
327 // string back with YEAR. Notice that "all occurrences of YEAR"
328 // means all occurrences of 4 digit as well as 2 digit form!
330 // the bugs: we assume that neither of %c nor %x contains any
331 // fields which may change between the YEAR and real year. For
332 // example, the week number (%U, %W) and the day number (%j)
333 // will change if one of these years is leap and the other one
336 // find the YEAR: normally, for any year X, Jan 1 of the
337 // year X + 28 is the same weekday as Jan 1 of X (because
338 // the weekday advances by 1 for each normal X and by 2
339 // for each leap X, hence by 5 every 4 years or by 35
340 // which is 0 mod 7 every 28 years) but this rule breaks
341 // down if there are years between X and Y which are
342 // divisible by 4 but not leap (i.e. divisible by 100 but
343 // not 400), hence the correction.
345 int yearReal
= GetYear(tz
);
346 int mod28
= yearReal
% 28;
348 // be careful to not go too far - we risk to leave the
353 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
357 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
360 int nCentury
= year
/ 100,
361 nCenturyReal
= yearReal
/ 100;
363 // need to adjust for the years divisble by 400 which are
364 // not leap but are counted like leap ones if we just take
365 // the number of centuries in between for nLostWeekDays
366 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
367 (nCentury
/ 4 - nCenturyReal
/ 4);
369 // we have to gain back the "lost" weekdays: note that the
370 // effect of this loop is to not do anything to
371 // nLostWeekDays (which we won't use any more), but to
372 // (indirectly) set the year correctly
373 while ( (nLostWeekDays
% 7) != 0 )
375 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
378 // finally move the year below 2000 so that the 2-digit
379 // year number can never match the month or day of the
380 // month when we do the replacements below
384 wxASSERT_MSG( year
>= 1970 && year
< 2000,
385 _T("logic error in wxDateTime::Format") );
388 // use strftime() to format the same date but in supported
391 // NB: we assume that strftime() doesn't check for the
392 // date validity and will happily format the date
393 // corresponding to Feb 29 of a non leap year (which
394 // may happen if yearReal was leap and year is not)
395 struct tm tmAdjusted
;
397 tmAdjusted
.tm_hour
= tm
.hour
;
398 tmAdjusted
.tm_min
= tm
.min
;
399 tmAdjusted
.tm_sec
= tm
.sec
;
400 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
401 tmAdjusted
.tm_yday
= GetDayOfYear();
402 tmAdjusted
.tm_mday
= tm
.mday
;
403 tmAdjusted
.tm_mon
= tm
.mon
;
404 tmAdjusted
.tm_year
= year
- 1900;
405 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
406 wxString str
= CallStrftime(*p
== _T('c') ? _T("%c")
410 // now replace the replacement year with the real year:
411 // notice that we have to replace the 4 digit year with
412 // a unique string not appearing in strftime() output
413 // first to prevent the 2 digit year from matching any
414 // substring of the 4 digit year (but any day, month,
415 // hours or minutes components should be safe because
416 // they are never in 70-99 range)
417 wxString
replacement("|");
418 while ( str
.find(replacement
) != wxString::npos
)
421 str
.Replace(wxString::Format("%d", year
),
423 str
.Replace(wxString::Format("%d", year
% 100),
424 wxString::Format("%d", yearReal
% 100));
425 str
.Replace(replacement
,
426 wxString::Format("%d", yearReal
));
430 #else // !HAVE_STRFTIME
431 // Use "%m/%d/%y %H:%M:%S" format instead
432 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
433 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
434 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
437 case _T('d'): // day of a month (01-31)
438 res
+= wxString::Format(fmt
, tm
.mday
);
441 case _T('H'): // hour in 24h format (00-23)
442 res
+= wxString::Format(fmt
, tm
.hour
);
445 case _T('I'): // hour in 12h format (01-12)
447 // 24h -> 12h, 0h -> 12h too
448 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
449 : tm
.hour
? tm
.hour
: 12;
450 res
+= wxString::Format(fmt
, hour12
);
454 case _T('j'): // day of the year
455 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
458 case _T('l'): // milliseconds (NOT STANDARD)
459 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
462 case _T('m'): // month as a number (01-12)
463 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
466 case _T('M'): // minute as a decimal number (00-59)
467 res
+= wxString::Format(fmt
, tm
.min
);
470 case _T('p'): // AM or PM string
472 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
473 #else // !HAVE_STRFTIME
474 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
475 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
478 case _T('S'): // second as a decimal number (00-61)
479 res
+= wxString::Format(fmt
, tm
.sec
);
482 case _T('U'): // week number in the year (Sunday 1st week day)
483 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
486 case _T('W'): // week number in the year (Monday 1st week day)
487 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
490 case _T('w'): // weekday as a number (0-6), Sunday = 0
491 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
494 // case _T('x'): -- handled with "%c"
496 case _T('X'): // locale default time representation
497 // just use strftime() to format the time for us
499 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
500 #else // !HAVE_STRFTIME
501 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
502 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
505 case _T('y'): // year without century (00-99)
506 res
+= wxString::Format(fmt
, tm
.year
% 100);
509 case _T('Y'): // year with century
510 res
+= wxString::Format(fmt
, tm
.year
);
513 case _T('Z'): // timezone name
515 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
520 // is it the format width?
522 while ( *p
== _T('-') || *p
== _T('+') ||
523 *p
== _T(' ') || wxIsdigit(*p
) )
530 // we've only got the flags and width so far in fmt
531 fmt
.Prepend(_T('%'));
539 // no, it wasn't the width
540 wxFAIL_MSG(_T("unknown format specificator"));
542 // fall through and just copy it nevertheless
544 case _T('%'): // a percent sign
548 case 0: // the end of string
549 wxFAIL_MSG(_T("missing format at the end of string"));
551 // just put the '%' which was the last char in format
561 // this function parses a string in (strict) RFC 822 format: see the section 5
562 // of the RFC for the detailed description, but briefly it's something of the
563 // form "Sat, 18 Dec 1999 00:48:30 +0100"
565 // this function is "strict" by design - it must reject anything except true
566 // RFC822 time specs.
568 // TODO a great candidate for using reg exps
570 wxDateTime::ParseRfc822Date(const wxString
& date
, wxString::const_iterator
*end
)
572 // TODO: rewrite using iterators instead of wxChar pointers
573 const wxStringCharType
*p
= date
.wx_str();
574 const wxStringCharType
*comma
= wxStrchr(p
, wxS(','));
577 // the part before comma is the weekday
579 // skip it for now - we don't use but might check that it really
580 // corresponds to the specfied date
585 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
593 // the following 1 or 2 digits are the day number
594 if ( !wxIsdigit(*p
) )
596 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
601 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - _T('0'));
605 day
= (wxDateTime_t
)(day
+ (*p
++ - _T('0')));
608 if ( *p
++ != _T(' ') )
613 // the following 3 letters specify the month
614 wxString
monName(p
, 3);
616 if ( monName
== _T("Jan") )
618 else if ( monName
== _T("Feb") )
620 else if ( monName
== _T("Mar") )
622 else if ( monName
== _T("Apr") )
624 else if ( monName
== _T("May") )
626 else if ( monName
== _T("Jun") )
628 else if ( monName
== _T("Jul") )
630 else if ( monName
== _T("Aug") )
632 else if ( monName
== _T("Sep") )
634 else if ( monName
== _T("Oct") )
636 else if ( monName
== _T("Nov") )
638 else if ( monName
== _T("Dec") )
642 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str());
649 if ( *p
++ != _T(' ') )
655 if ( !wxIsdigit(*p
) )
661 int year
= *p
++ - _T('0');
663 if ( !wxIsdigit(*p
) )
665 // should have at least 2 digits in the year
670 year
+= *p
++ - _T('0');
672 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
676 year
+= *p
++ - _T('0');
678 if ( !wxIsdigit(*p
) )
680 // no 3 digit years please
685 year
+= *p
++ - _T('0');
688 if ( *p
++ != _T(' ') )
693 // time is in the format hh:mm:ss and seconds are optional
694 if ( !wxIsdigit(*p
) )
699 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - _T('0'));
701 if ( !wxIsdigit(*p
) )
707 hour
= (wxDateTime_t
)(hour
+ (*p
++ - _T('0')));
709 if ( *p
++ != _T(':') )
714 if ( !wxIsdigit(*p
) )
719 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - _T('0'));
721 if ( !wxIsdigit(*p
) )
727 min
= (wxDateTime_t
)(min
+ *p
++ - _T('0'));
729 wxDateTime_t sec
= 0;
733 if ( !wxIsdigit(*p
) )
738 sec
= (wxDateTime_t
)(*p
++ - _T('0'));
740 if ( !wxIsdigit(*p
) )
746 sec
= (wxDateTime_t
)(sec
+ *p
++ - _T('0'));
749 if ( *p
++ != _T(' ') )
754 // and now the interesting part: the timezone
755 int offset
wxDUMMY_INITIALIZE(0);
756 if ( *p
== _T('-') || *p
== _T('+') )
758 // the explicit offset given: it has the form of hhmm
759 bool plus
= *p
++ == _T('+');
761 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
767 offset
= MIN_PER_HOUR
*(10*(*p
- _T('0')) + (*(p
+ 1) - _T('0')));
771 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
777 offset
+= 10*(*p
- _T('0')) + (*(p
+ 1) - _T('0'));
788 // the symbolic timezone given: may be either military timezone or one
789 // of standard abbreviations
792 // military: Z = UTC, J unused, A = -1, ..., Y = +12
793 static const int offsets
[26] =
795 //A B C D E F G H I J K L M
796 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
797 //N O P R Q S T U V W Z Y Z
798 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
801 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
803 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
);
808 offset
= offsets
[*p
++ - _T('A')];
814 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
816 else if ( tz
== _T("AST") )
818 else if ( tz
== _T("ADT") )
820 else if ( tz
== _T("EST") )
822 else if ( tz
== _T("EDT") )
824 else if ( tz
== _T("CST") )
826 else if ( tz
== _T("CDT") )
828 else if ( tz
== _T("MST") )
830 else if ( tz
== _T("MDT") )
832 else if ( tz
== _T("PST") )
834 else if ( tz
== _T("PDT") )
838 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
);
847 offset
*= MIN_PER_HOUR
;
850 // the spec was correct, construct the date from the values we found
851 Set(day
, mon
, year
, hour
, min
, sec
);
852 MakeFromTimezone(TimeZone::Make(offset
*SEC_PER_MIN
));
854 const size_t endpos
= p
- date
.wx_str();
856 *end
= date
.begin() + endpos
;
858 return date
.c_str() + endpos
;
863 // returns the string containing strftime() format used for short dates in the
864 // current locale or an empty string
865 static wxString
GetLocaleDateFormat()
869 // there is no setlocale() under Windows CE, so just always query the
872 if ( strcmp(setlocale(LC_ALL
, NULL
), "C") != 0 )
875 // The locale was programatically set to non-C. We assume that this was
876 // done using wxLocale, in which case thread's current locale is also
877 // set to correct LCID value and we can use GetLocaleInfo to determine
878 // the correct formatting string:
880 LCID lcid
= LOCALE_USER_DEFAULT
;
882 LCID lcid
= GetThreadLocale();
884 // according to MSDN 80 chars is max allowed for short date format
886 if ( ::GetLocaleInfo(lcid
, LOCALE_SSHORTDATE
, fmt
, WXSIZEOF(fmt
)) )
888 wxChar chLast
= _T('\0');
889 size_t lastCount
= 0;
890 for ( const wxChar
*p
= fmt
; /* NUL handled inside */; p
++ )
900 // these characters come in groups, start counting them
910 // first deal with any special characters we have had
920 // these two are the same as we
921 // don't distinguish between 1 and
935 wxFAIL_MSG( _T("too many 'd's") );
944 // as for 'd' and 'dd' above
957 wxFAIL_MSG( _T("too many 'M's") );
974 wxFAIL_MSG( _T("wrong number of 'y's") );
979 // strftime() doesn't have era string,
980 // ignore this format
981 wxASSERT_MSG( lastCount
<= 2,
982 _T("too many 'g's") );
986 wxFAIL_MSG( _T("unreachable") );
993 // not a special character so must be just a separator,
995 if ( *p
!= _T('\0') )
999 // this one needs to be escaped
1007 if ( *p
== _T('\0') )
1011 //else: GetLocaleInfo() failed, leave fmtDate value unchanged and
1012 // try our luck with the default formats
1014 //else: default C locale, default formats should work
1019 #endif // __WINDOWS__
1022 wxDateTime::ParseFormat(const wxString
& date
,
1023 const wxString
& format
,
1024 const wxDateTime
& dateDef
,
1025 wxString::const_iterator
*end
)
1027 wxCHECK_MSG( !format
.empty(), NULL
, "format can't be empty" );
1032 // what fields have we found?
1033 bool haveWDay
= false,
1043 bool hourIsIn12hFormat
= false, // or in 24h one?
1044 isPM
= false; // AM by default
1046 // and the value of the items we have (init them to get rid of warnings)
1047 wxDateTime_t msec
= 0,
1051 WeekDay wday
= Inv_WeekDay
;
1052 wxDateTime_t yday
= 0,
1054 wxDateTime::Month mon
= Inv_Month
;
1057 const wxStringCharType
*input
= date
.wx_str();
1058 for ( wxString::const_iterator fmt
= format
.begin(); fmt
!= format
.end(); ++fmt
)
1060 if ( *fmt
!= _T('%') )
1062 if ( wxIsspace(*fmt
) )
1064 // a white space in the format string matches 0 or more white
1065 // spaces in the input
1066 while ( wxIsspace(*input
) )
1073 // any other character (not whitespace, not '%') must be
1074 // matched by itself in the input
1075 if ( *input
++ != *fmt
)
1082 // done with this format char
1086 // start of a format specification
1088 // parse the optional width
1090 while ( wxIsdigit(*++fmt
) )
1093 width
+= *fmt
- _T('0');
1096 // the default widths for the various fields
1099 switch ( (*fmt
).GetValue() )
1101 case _T('Y'): // year has 4 digits
1105 case _T('j'): // day of year has 3 digits
1106 case _T('l'): // milliseconds have 3 digits
1110 case _T('w'): // week day as number has only one
1115 // default for all other fields
1120 // then the format itself
1121 switch ( (*fmt
).GetValue() )
1123 case _T('a'): // a weekday name
1126 int flag
= *fmt
== _T('a') ? Name_Abbr
: Name_Full
;
1127 wday
= GetWeekDayFromName(GetAlphaToken(input
), flag
);
1128 if ( wday
== Inv_WeekDay
)
1137 case _T('b'): // a month name
1140 int flag
= *fmt
== _T('b') ? Name_Abbr
: Name_Full
;
1141 mon
= GetMonthFromName(GetAlphaToken(input
), flag
);
1142 if ( mon
== Inv_Month
)
1151 case _T('c'): // locale default date and time representation
1155 const wxString
inc(input
);
1157 // try the format which corresponds to ctime() output first
1158 wxString::const_iterator endc
;
1159 if ( !dt
.ParseFormat(inc
, wxS("%a %b %d %H:%M:%S %Y"), &endc
) &&
1160 !dt
.ParseFormat(inc
, wxS("%x %X"), &endc
) &&
1161 !dt
.ParseFormat(inc
, wxS("%X %x"), &endc
) )
1163 // we've tried everything and still no match
1169 haveDay
= haveMon
= haveYear
=
1170 haveHour
= haveMin
= haveSec
= true;
1180 input
+= endc
- inc
.begin();
1184 case _T('d'): // day of a month (01-31)
1185 if ( !GetNumericToken(width
, input
, &num
) ||
1186 (num
> 31) || (num
< 1) )
1192 // we can't check whether the day range is correct yet, will
1193 // do it later - assume ok for now
1195 mday
= (wxDateTime_t
)num
;
1198 case _T('H'): // hour in 24h format (00-23)
1199 if ( !GetNumericToken(width
, input
, &num
) || (num
> 23) )
1206 hour
= (wxDateTime_t
)num
;
1209 case _T('I'): // hour in 12h format (01-12)
1210 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
1217 hourIsIn12hFormat
= true;
1218 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
1221 case _T('j'): // day of the year
1222 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 366) )
1229 yday
= (wxDateTime_t
)num
;
1232 case _T('l'): // milliseconds (0-999)
1233 if ( !GetNumericToken(width
, input
, &num
) )
1237 msec
= (wxDateTime_t
)num
;
1240 case _T('m'): // month as a number (01-12)
1241 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
1248 mon
= (Month
)(num
- 1);
1251 case _T('M'): // minute as a decimal number (00-59)
1252 if ( !GetNumericToken(width
, input
, &num
) || (num
> 59) )
1259 min
= (wxDateTime_t
)num
;
1262 case _T('p'): // AM or PM string
1264 wxString am
, pm
, token
= GetAlphaToken(input
);
1266 GetAmPmStrings(&am
, &pm
);
1267 if (am
.empty() && pm
.empty())
1268 return NULL
; // no am/pm strings defined
1269 if ( token
.CmpNoCase(pm
) == 0 )
1273 else if ( token
.CmpNoCase(am
) != 0 )
1281 case _T('r'): // time as %I:%M:%S %p
1284 input
= dt
.ParseFormat(input
, wxS("%I:%M:%S %p"));
1291 haveHour
= haveMin
= haveSec
= true;
1300 case _T('R'): // time as %H:%M
1303 input
= dt
.ParseFormat(input
, wxS("%H:%M"));
1310 haveHour
= haveMin
= true;
1318 case _T('S'): // second as a decimal number (00-61)
1319 if ( !GetNumericToken(width
, input
, &num
) || (num
> 61) )
1326 sec
= (wxDateTime_t
)num
;
1329 case _T('T'): // time as %H:%M:%S
1332 input
= dt
.ParseFormat(input
, _T("%H:%M:%S"));
1339 haveHour
= haveMin
= haveSec
= true;
1348 case _T('w'): // weekday as a number (0-6), Sunday = 0
1349 if ( !GetNumericToken(width
, input
, &num
) || (wday
> 6) )
1356 wday
= (WeekDay
)num
;
1359 case _T('x'): // locale default date representation
1360 #ifdef HAVE_STRPTIME
1361 // try using strptime() -- it may fail even if the input is
1362 // correct but the date is out of range, so we will fall back
1363 // to our generic code anyhow
1367 const wxStringCharType
*
1368 result
= CallStrptime(input
, "%x", &tm
);
1373 haveDay
= haveMon
= haveYear
= true;
1375 year
= 1900 + tm
.tm_year
;
1376 mon
= (Month
)tm
.tm_mon
;
1382 #endif // HAVE_STRPTIME
1390 // The above doesn't work for all locales, try to query
1391 // Windows for the right way of formatting the date:
1392 fmtDate
= GetLocaleDateFormat();
1393 if ( fmtDate
.empty() )
1394 #endif // __WINDOWS__
1396 if ( IsWestEuropeanCountry(GetCountry()) ||
1397 GetCountry() == Russia
)
1399 fmtDate
= _T("%d/%m/%y");
1400 fmtDateAlt
= _T("%m/%d/%y");
1404 fmtDate
= _T("%m/%d/%y");
1405 fmtDateAlt
= _T("%d/%m/%y");
1409 const wxString
indate(input
);
1410 wxString::const_iterator endDate
;
1411 if ( !dt
.ParseFormat(indate
, fmtDate
, &endDate
) )
1413 // try another one if we have it
1414 if ( fmtDateAlt
.empty() ||
1415 !dt
.ParseFormat(indate
, fmtDateAlt
, &endDate
) )
1431 input
+= endDate
- indate
.begin();
1436 case _T('X'): // locale default time representation
1437 #ifdef HAVE_STRPTIME
1439 // use strptime() to do it for us (FIXME !Unicode friendly)
1441 input
= CallStrptime(input
, "%X", &tm
);
1447 haveHour
= haveMin
= haveSec
= true;
1453 #else // !HAVE_STRPTIME
1454 // TODO under Win32 we can query the LOCALE_ITIME system
1455 // setting which says whether the default time format is
1458 // try to parse what follows as "%H:%M:%S" and, if this
1459 // fails, as "%I:%M:%S %p" - this should catch the most
1463 const wxStringCharType
*
1464 result
= dt
.ParseFormat(input
, wxS("%T"));
1467 result
= dt
.ParseFormat(input
, wxS("%r"));
1487 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
1490 case _T('y'): // year without century (00-99)
1491 if ( !GetNumericToken(width
, input
, &num
) || (num
> 99) )
1499 // TODO should have an option for roll over date instead of
1500 // hard coding it here
1501 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
1504 case _T('Y'): // year with century
1505 if ( !GetNumericToken(width
, input
, &num
) )
1512 year
= (wxDateTime_t
)num
;
1515 case _T('Z'): // timezone name
1516 wxFAIL_MSG(_T("TODO"));
1519 case _T('%'): // a percent sign
1520 if ( *input
++ != _T('%') )
1527 case 0: // the end of string
1528 wxFAIL_MSG(_T("unexpected format end"));
1532 default: // not a known format spec
1537 // format matched, try to construct a date from what we have now
1539 if ( dateDef
.IsValid() )
1541 // take this date as default
1542 tmDef
= dateDef
.GetTm();
1544 else if ( IsValid() )
1546 // if this date is valid, don't change it
1551 // no default and this date is invalid - fall back to Today()
1552 tmDef
= Today().GetTm();
1568 // TODO we don't check here that the values are consistent, if both year
1569 // day and month/day were found, we just ignore the year day and we
1570 // also always ignore the week day
1573 if ( mday
> GetNumOfDaysInMonth(tm
.year
, tm
.mon
) )
1575 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
1582 else if ( haveYDay
)
1584 if ( yday
> GetNumberOfDays(tm
.year
) )
1586 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
1591 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
1598 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
1600 // translate to 24hour format
1603 //else: either already in 24h format or no translation needed
1626 // finally check that the week day is consistent -- if we had it
1627 if ( haveWDay
&& GetWeekDay() != wday
)
1629 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
1634 const size_t endpos
= input
- date
.wx_str();
1636 *end
= date
.begin() + endpos
;
1638 return date
.c_str() + endpos
;
1642 wxDateTime::ParseDateTime(const wxString
& date
, wxString::const_iterator
*end
)
1644 // Set to current day and hour, so strings like '14:00' becomes today at
1645 // 14, not some other random date
1646 wxDateTime dtDate
= wxDateTime::Today();
1647 wxDateTime dtTime
= wxDateTime::Today();
1649 wxString::const_iterator
1654 // If we got a date in the beginning, see if there is a time specified
1656 if ( dtDate
.ParseDate(date
, &endDate
) )
1658 // Skip spaces, as the ParseTime() function fails on spaces
1659 while ( endDate
!= date
.end() && wxIsspace(*endDate
) )
1662 const wxString
timestr(endDate
, date
.end());
1663 if ( !dtTime
.ParseTime(timestr
, &endTime
) )
1666 endBoth
= endDate
+ (endTime
- timestr
.begin());
1668 else // no date in the beginning
1670 // check if we have a time followed by a date
1671 if ( !dtTime
.ParseTime(date
, &endTime
) )
1674 while ( endTime
!= date
.end() && wxIsspace(*endTime
) )
1677 const wxString
datestr(endTime
, date
.end());
1678 if ( !dtDate
.ParseDate(datestr
, &endDate
) )
1681 endBoth
= endTime
+ (endDate
- datestr
.begin());
1684 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
1685 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
1686 dtTime
.GetMillisecond());
1688 // Return endpoint of scan
1692 return date
.c_str() + (endBoth
- date
.begin());
1696 wxDateTime::ParseDate(const wxString
& date
, wxString::const_iterator
*end
)
1698 // this is a simplified version of ParseDateTime() which understands only
1699 // "today" (for wxDate compatibility) and digits only otherwise (and not
1700 // all esoteric constructions ParseDateTime() knows about)
1702 const wxStringCharType
*p
= date
.wx_str();
1703 while ( wxIsspace(*p
) )
1706 // some special cases
1710 int dayDiffFromToday
;
1713 { wxTRANSLATE("today"), 0 },
1714 { wxTRANSLATE("yesterday"), -1 },
1715 { wxTRANSLATE("tomorrow"), 1 },
1718 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
1720 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
1721 size_t len
= dateStr
.length();
1722 if ( wxStrlen(p
) >= len
)
1724 wxString
str(p
, len
);
1725 if ( str
.CmpNoCase(dateStr
) == 0 )
1727 // nothing can follow this, so stop here
1730 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
1732 if ( dayDiffFromToday
)
1734 *this += wxDateSpan::Days(dayDiffFromToday
);
1737 const size_t endpos
= p
- date
.wx_str();
1740 *end
= date
.begin() + endpos
;
1741 return date
.c_str() + endpos
;
1746 // We try to guess what we have here: for each new (numeric) token, we
1747 // determine if it can be a month, day or a year. Of course, there is an
1748 // ambiguity as some numbers may be days as well as months, so we also
1749 // have the ability to back track.
1752 bool haveDay
= false, // the months day?
1753 haveWDay
= false, // the day of week?
1754 haveMon
= false, // the month?
1755 haveYear
= false; // the year?
1757 // and the value of the items we have (init them to get rid of warnings)
1758 WeekDay wday
= Inv_WeekDay
;
1759 wxDateTime_t day
= 0;
1760 wxDateTime::Month mon
= Inv_Month
;
1763 // tokenize the string
1765 static const wxStringCharType
*dateDelimiters
= wxS(".,/-\t\r\n ");
1766 wxStringTokenizer
tok(p
, dateDelimiters
);
1767 while ( tok
.HasMoreTokens() )
1769 wxString token
= tok
.GetNextToken();
1775 if ( token
.ToULong(&val
) )
1777 // guess what this number is
1783 if ( !haveMon
&& val
> 0 && val
<= 12 )
1785 // assume it is month
1788 else // not the month
1792 // this can only be the year
1795 else // may be either day or year
1797 // use a leap year if we don't have the year yet to allow
1798 // dates like 2/29/1976 which would be rejected otherwise
1799 wxDateTime_t max_days
= (wxDateTime_t
)(
1801 ? GetNumOfDaysInMonth(haveYear
? year
: 1976, mon
)
1806 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
1811 else // yes, suppose it's the day
1825 year
= (wxDateTime_t
)val
;
1834 day
= (wxDateTime_t
)val
;
1840 mon
= (Month
)(val
- 1);
1843 else // not a number
1845 // be careful not to overwrite the current mon value
1846 Month mon2
= GetMonthFromName(token
, Name_Full
| Name_Abbr
);
1847 if ( mon2
!= Inv_Month
)
1852 // but we already have a month - maybe we guessed wrong?
1855 // no need to check in month range as always < 12, but
1856 // the days are counted from 1 unlike the months
1857 day
= (wxDateTime_t
)(mon
+ 1);
1862 // could possible be the year (doesn't the year come
1863 // before the month in the japanese format?) (FIXME)
1872 else // not a valid month name
1874 WeekDay wday2
= GetWeekDayFromName(token
, Name_Full
| Name_Abbr
);
1875 if ( wday2
!= Inv_WeekDay
)
1887 else // not a valid weekday name
1890 static const char *ordinals
[] =
1892 wxTRANSLATE("first"),
1893 wxTRANSLATE("second"),
1894 wxTRANSLATE("third"),
1895 wxTRANSLATE("fourth"),
1896 wxTRANSLATE("fifth"),
1897 wxTRANSLATE("sixth"),
1898 wxTRANSLATE("seventh"),
1899 wxTRANSLATE("eighth"),
1900 wxTRANSLATE("ninth"),
1901 wxTRANSLATE("tenth"),
1902 wxTRANSLATE("eleventh"),
1903 wxTRANSLATE("twelfth"),
1904 wxTRANSLATE("thirteenth"),
1905 wxTRANSLATE("fourteenth"),
1906 wxTRANSLATE("fifteenth"),
1907 wxTRANSLATE("sixteenth"),
1908 wxTRANSLATE("seventeenth"),
1909 wxTRANSLATE("eighteenth"),
1910 wxTRANSLATE("nineteenth"),
1911 wxTRANSLATE("twentieth"),
1912 // that's enough - otherwise we'd have problems with
1913 // composite (or not) ordinals
1917 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
1919 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
1925 if ( n
== WXSIZEOF(ordinals
) )
1927 // stop here - something unknown
1934 // don't try anything here (as in case of numeric day
1935 // above) - the symbolic day spec should always
1936 // precede the month/year
1942 day
= (wxDateTime_t
)(n
+ 1);
1947 nPosCur
= tok
.GetPosition();
1950 // either no more tokens or the scan was stopped by something we couldn't
1951 // parse - in any case, see if we can construct a date from what we have
1952 if ( !haveDay
&& !haveWDay
)
1954 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
1959 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
1960 !(haveDay
&& haveMon
&& haveYear
) )
1962 // without adjectives (which we don't support here) the week day only
1963 // makes sense completely separately or with the full date
1964 // specification (what would "Wed 1999" mean?)
1968 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
1970 // may be we have month and day instead of day and year?
1971 if ( haveDay
&& !haveMon
)
1975 // exchange day and month
1976 mon
= (wxDateTime::Month
)(day
- 1);
1978 // we're in the current year then
1979 if ( (year
> 0) && (year
<= (int)GetNumOfDaysInMonth(Inv_Year
, mon
)) )
1981 day
= (wxDateTime_t
)year
;
1986 //else: no, can't exchange, leave haveMon == false
1992 // if we give the year, month and day must be given too
1993 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
2001 mon
= GetCurrentMonth();
2006 year
= GetCurrentYear();
2011 // normally we check the day above but the check is optimistic in case
2012 // we find the day before its month/year so we have to redo it now
2013 if ( day
> GetNumOfDaysInMonth(year
, mon
) )
2016 Set(day
, mon
, year
);
2020 // check that it is really the same
2021 if ( GetWeekDay() != wday
)
2023 // inconsistency detected
2024 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
2034 SetToWeekDayInSameWeek(wday
);
2037 // return the pointer to the first unparsed char
2039 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
2041 // if we couldn't parse the token after the delimiter, put back the
2042 // delimiter as well
2046 const size_t endpos
= p
- date
.wx_str();
2048 *end
= date
.begin() + endpos
;
2050 return date
.c_str() + endpos
;
2054 wxDateTime::ParseTime(const wxString
& time
, wxString::const_iterator
*end
)
2056 // first try some extra things
2063 { wxTRANSLATE("noon"), 12 },
2064 { wxTRANSLATE("midnight"), 00 },
2068 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
2070 wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
2071 size_t len
= timeString
.length();
2072 if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 )
2074 // casts required by DigitalMars
2075 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
2078 *end
= time
.begin() + len
;
2080 return time
.c_str() + len
;
2084 // try all time formats we may think about in the order from longest to
2086 static const char *timeFormats
[] =
2088 "%I:%M:%S %p", // 12hour with AM/PM
2089 "%H:%M:%S", // could be the same or 24 hour one so try it too
2090 "%I:%M %p", // 12hour with AM/PM but without seconds
2091 "%H:%M:%S", // and a possibly 24 hour version without seconds
2092 "%X", // possibly something from above or maybe something
2093 // completely different -- try it last
2095 // TODO: parse timezones
2098 for ( size_t nFmt
= 0; nFmt
< WXSIZEOF(timeFormats
); nFmt
++ )
2100 const char *result
= ParseFormat(time
, timeFormats
[nFmt
], end
);
2108 // ----------------------------------------------------------------------------
2109 // Workdays and holidays support
2110 // ----------------------------------------------------------------------------
2112 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
2114 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
2117 // ============================================================================
2119 // ============================================================================
2121 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
2124 return ds1
.Multiply(n
);
2127 // ============================================================================
2129 // ============================================================================
2131 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
2133 return wxTimeSpan(ts
).Multiply(n
);
2136 // this enum is only used in wxTimeSpan::Format() below but we can't declare
2137 // it locally to the method as it provokes an internal compiler error in egcs
2138 // 2.91.60 when building with -O2
2149 // not all strftime(3) format specifiers make sense here because, for example,
2150 // a time span doesn't have a year nor a timezone
2152 // Here are the ones which are supported (all of them are supported by strftime
2154 // %H hour in 24 hour format
2155 // %M minute (00 - 59)
2156 // %S second (00 - 59)
2159 // Also, for MFC CTimeSpan compatibility, we support
2160 // %D number of days
2162 // And, to be better than MFC :-), we also have
2163 // %E number of wEeks
2164 // %l milliseconds (000 - 999)
2165 wxString
wxTimeSpan::Format(const wxString
& format
) const
2167 // we deal with only positive time spans here and just add the sign in
2168 // front for the negative ones
2171 wxString
str(Negate().Format(format
));
2175 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
2176 _T("NULL format in wxTimeSpan::Format") );
2179 str
.Alloc(format
.length());
2181 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
2183 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
2184 // question is what should ts.Format("%S") do? The code here returns "3273"
2185 // in this case (i.e. the total number of seconds, not just seconds % 60)
2186 // because, for me, this call means "give me entire time interval in
2187 // seconds" and not "give me the seconds part of the time interval"
2189 // If we agree that it should behave like this, it is clear that the
2190 // interpretation of each format specifier depends on the presence of the
2191 // other format specs in the string: if there was "%H" before "%M", we
2192 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
2194 // we remember the most important unit found so far
2195 TimeSpanPart partBiggest
= Part_MSec
;
2197 for ( wxString::const_iterator pch
= format
.begin(); pch
!= format
.end(); ++pch
)
2201 if ( ch
== _T('%') )
2203 // the start of the format specification of the printf() below
2204 wxString
fmtPrefix(_T('%'));
2209 // the number of digits for the format string, 0 if unused
2210 unsigned digits
= 0;
2212 ch
= *++pch
; // get the format spec char
2216 wxFAIL_MSG( _T("invalid format character") );
2222 // skip the part below switch
2227 if ( partBiggest
< Part_Day
)
2233 partBiggest
= Part_Day
;
2238 partBiggest
= Part_Week
;
2244 if ( partBiggest
< Part_Hour
)
2250 partBiggest
= Part_Hour
;
2257 n
= GetMilliseconds().ToLong();
2258 if ( partBiggest
< Part_MSec
)
2262 //else: no need to reset partBiggest to Part_MSec, it is
2263 // the least significant one anyhow
2270 if ( partBiggest
< Part_Min
)
2276 partBiggest
= Part_Min
;
2283 n
= GetSeconds().ToLong();
2284 if ( partBiggest
< Part_Sec
)
2290 partBiggest
= Part_Sec
;
2299 fmtPrefix
<< _T("0") << digits
;
2302 str
+= wxString::Format(fmtPrefix
+ _T("ld"), n
);
2306 // normal character, just copy
2314 #endif // wxUSE_DATETIME