1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/datetimefmt.cpp
3 // Purpose: wxDateTime formatting & parsing code
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // parts of code taken from sndcal library by Scott E. Lee:
11 // Copyright 1993-1995, Scott E. Lee, all rights reserved.
12 // Permission granted to use, copy, modify, distribute and sell
13 // so long as the above copyright and this permission statement
14 // are retained in all copies.
16 // Licence: wxWindows licence
17 ///////////////////////////////////////////////////////////////////////////////
19 // ============================================================================
21 // ============================================================================
23 // ----------------------------------------------------------------------------
25 // ----------------------------------------------------------------------------
27 // For compilers that support precompilation, includes "wx.h".
28 #include "wx/wxprec.h"
34 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
38 #include "wx/msw/wrapwin.h"
40 #include "wx/string.h"
43 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
44 #include "wx/module.h"
48 #include "wx/thread.h"
59 #include "wx/datetime.h"
62 // ============================================================================
63 // implementation of wxDateTime
64 // ============================================================================
66 // ----------------------------------------------------------------------------
67 // helpers shared between datetime.cpp and datetimefmt.cpp
68 // ----------------------------------------------------------------------------
70 extern void InitTm(struct tm
& tm
);
72 extern wxString
CallStrftime(const wxString
& format
, const tm
* tm
);
74 // ----------------------------------------------------------------------------
75 // constants (see also datetime.cpp)
76 // ----------------------------------------------------------------------------
78 static const int DAYS_PER_WEEK
= 7;
80 static const int HOURS_PER_DAY
= 24;
82 static const int SEC_PER_MIN
= 60;
84 static const int MIN_PER_HOUR
= 60;
86 // ----------------------------------------------------------------------------
88 // ----------------------------------------------------------------------------
93 // all the functions below taking non-const wxString::const_iterator p advance
94 // it until the end of the match
96 // scans all digits (but no more than len) and returns the resulting number
97 bool GetNumericToken(size_t len
,
98 wxString::const_iterator
& p
,
99 const wxString::const_iterator
& end
,
100 unsigned long *number
)
104 while ( p
!= end
&& wxIsdigit(*p
) )
108 if ( len
&& ++n
> len
)
112 return !s
.empty() && s
.ToULong(number
);
115 // scans all alphabetic characters and returns the resulting string
117 GetAlphaToken(wxString::const_iterator
& p
,
118 const wxString::const_iterator
& end
)
121 while ( p
!= end
&& wxIsalpha(*p
) )
131 DateLang_English
= 1,
135 // return the month if the string is a month name or Inv_Month otherwise
137 // flags can contain wxDateTime::Name_Abbr/Name_Full or both of them and lang
138 // can be either DateLang_Local (default) to interpret string as a localized
139 // month name or DateLang_English to parse it as a standard English name or
140 // their combination to interpret it in any way
142 GetMonthFromName(wxString::const_iterator
& p
,
143 const wxString::const_iterator
& end
,
147 const wxString::const_iterator pOrig
= p
;
148 const wxString name
= GetAlphaToken(p
, end
);
150 return wxDateTime::Inv_Month
;
152 wxDateTime::Month mon
;
153 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
155 // case-insensitive comparison either one of or with both abbreviated
157 if ( flags
& wxDateTime::Name_Full
)
159 if ( lang
& DateLang_English
)
161 if ( name
.CmpNoCase(wxDateTime::GetEnglishMonthName(mon
,
162 wxDateTime::Name_Full
)) == 0 )
166 if ( lang
& DateLang_Local
)
168 if ( name
.CmpNoCase(wxDateTime::GetMonthName(mon
,
169 wxDateTime::Name_Full
)) == 0 )
174 if ( flags
& wxDateTime::Name_Abbr
)
176 if ( lang
& DateLang_English
)
178 if ( name
.CmpNoCase(wxDateTime::GetEnglishMonthName(mon
,
179 wxDateTime::Name_Abbr
)) == 0 )
183 if ( lang
& DateLang_Local
)
185 // some locales (e.g. French one) use periods for the
186 // abbreviated month names but it's never part of name so
187 // compare it specially
188 wxString nameAbbr
= wxDateTime::GetMonthName(mon
,
189 wxDateTime::Name_Abbr
);
190 const bool hasPeriod
= *nameAbbr
.rbegin() == '.';
192 nameAbbr
.erase(nameAbbr
.end() - 1);
194 if ( name
.CmpNoCase(nameAbbr
) == 0 )
198 // skip trailing period if it was part of the match
201 else // no match as no matching period
211 if ( mon
== wxDateTime::Inv_Month
)
217 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
219 // flags and lang parameters have the same meaning as for GetMonthFromName()
222 GetWeekDayFromName(wxString::const_iterator
& p
,
223 const wxString::const_iterator
& end
,
226 const wxString::const_iterator pOrig
= p
;
227 const wxString name
= GetAlphaToken(p
, end
);
229 return wxDateTime::Inv_WeekDay
;
231 wxDateTime::WeekDay wd
;
232 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
234 if ( flags
& wxDateTime::Name_Full
)
236 if ( lang
& DateLang_English
)
238 if ( name
.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd
,
239 wxDateTime::Name_Full
)) == 0 )
243 if ( lang
& DateLang_Local
)
245 if ( name
.CmpNoCase(wxDateTime::GetWeekDayName(wd
,
246 wxDateTime::Name_Full
)) == 0 )
251 if ( flags
& wxDateTime::Name_Abbr
)
253 if ( lang
& DateLang_English
)
255 if ( name
.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd
,
256 wxDateTime::Name_Abbr
)) == 0 )
260 if ( lang
& DateLang_Local
)
262 if ( name
.CmpNoCase(wxDateTime::GetWeekDayName(wd
,
263 wxDateTime::Name_Abbr
)) == 0 )
269 if ( wd
== wxDateTime::Inv_WeekDay
)
275 // parses string starting at given iterator using the specified format and,
276 // optionally, a fall back format (and optionally another one... but it stops
279 // if unsuccessful, returns invalid wxDateTime without changing p; otherwise
280 // advance p to the end of the match and returns wxDateTime containing the
281 // results of the parsing
283 ParseFormatAt(wxString::const_iterator
& p
,
284 const wxString::const_iterator
& end
,
286 // FIXME-VC6: using wxString() instead of wxEmptyString in the
287 // line below results in error C2062: type 'class
288 // wxString (__cdecl *)(void)' unexpected
289 const wxString
& fmtAlt
= wxEmptyString
)
291 const wxString
str(p
, end
);
292 wxString::const_iterator endParse
;
295 // Use a default date outside of the DST period to avoid problems with
296 // parsing the time differently depending on the today's date (which is used
297 // as the fall back date if none is explicitly specified).
298 static const wxDateTime
dtDef(1, wxDateTime::Jan
, 2012);
300 if ( dt
.ParseFormat(str
, fmt
, dtDef
, &endParse
) ||
301 (!fmtAlt
.empty() && dt
.ParseFormat(str
, fmtAlt
, dtDef
, &endParse
)) )
303 p
+= endParse
- str
.begin();
305 //else: all formats failed
310 } // anonymous namespace
312 // ----------------------------------------------------------------------------
313 // wxDateTime to/from text representations
314 // ----------------------------------------------------------------------------
316 wxString
wxDateTime::Format(const wxString
& formatp
, const TimeZone
& tz
) const
318 wxCHECK_MSG( !formatp
.empty(), wxEmptyString
,
319 wxT("NULL format in wxDateTime::Format") );
321 wxString format
= formatp
;
323 format
.Replace("%c",wxLocale::GetInfo(wxLOCALE_DATE_TIME_FMT
));
324 format
.Replace("%x",wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT
));
325 format
.Replace("%X",wxLocale::GetInfo(wxLOCALE_TIME_FMT
));
327 // we have to use our own implementation if the date is out of range of
328 // strftime() or if we use non standard specifiers (notice that "%z" is
329 // special because it is de facto standard under Unix but is not supported
331 #ifdef wxHAS_STRFTIME
332 time_t time
= GetTicks();
334 if ( (time
!= (time_t)-1) && !wxStrstr(format
, wxT("%l"))
336 && !wxStrstr(format
, wxT("%z"))
343 if ( tz
.GetOffset() == -wxGetTimeZone() )
345 // we are working with local time
346 tm
= wxLocaltime_r(&time
, &tmstruct
);
348 // should never happen
349 wxCHECK_MSG( tm
, wxEmptyString
, wxT("wxLocaltime_r() failed") );
353 time
+= (int)tz
.GetOffset();
355 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
356 int time2
= (int) time
;
362 tm
= wxGmtime_r(&time
, &tmstruct
);
364 // should never happen
365 wxCHECK_MSG( tm
, wxEmptyString
, wxT("wxGmtime_r() failed") );
369 tm
= (struct tm
*)NULL
;
375 return CallStrftime(format
, tm
);
378 //else: use generic code below
379 #endif // wxHAS_STRFTIME
381 // we only parse ANSI C format specifications here, no POSIX 2
382 // complications, no GNU extensions but we do add support for a "%l" format
383 // specifier allowing to get the number of milliseconds
386 // used for calls to strftime() when we only deal with time
387 struct tm tmTimeOnly
;
388 memset(&tmTimeOnly
, 0, sizeof(tmTimeOnly
));
389 tmTimeOnly
.tm_hour
= tm
.hour
;
390 tmTimeOnly
.tm_min
= tm
.min
;
391 tmTimeOnly
.tm_sec
= tm
.sec
;
392 tmTimeOnly
.tm_mday
= 1; // any date will do, use 1976-01-01
393 tmTimeOnly
.tm_mon
= 0;
394 tmTimeOnly
.tm_year
= 76;
395 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
397 wxString tmp
, res
, fmt
;
398 for ( wxString::const_iterator p
= format
.begin(); p
!= format
.end(); ++p
)
400 if ( *p
!= wxT('%') )
408 // set the default format
409 switch ( (*++p
).GetValue() )
411 case wxT('Y'): // year has 4 digits
412 case wxT('z'): // time zone as well
416 case wxT('j'): // day of year has 3 digits
417 case wxT('l'): // milliseconds have 3 digits
421 case wxT('w'): // week day as number has only one
426 // it's either another valid format specifier in which case
427 // the format is "%02d" (for all the rest) or we have the
428 // field width preceding the format in which case it will
429 // override the default format anyhow
438 // start of the format specification
439 switch ( (*p
).GetValue() )
441 case wxT('a'): // a weekday name
443 // second parameter should be true for abbreviated names
444 res
+= GetWeekDayName(tm
.GetWeekDay(),
445 *p
== wxT('a') ? Name_Abbr
: Name_Full
);
448 case wxT('b'): // a month name
450 res
+= GetMonthName(tm
.mon
,
451 *p
== wxT('b') ? Name_Abbr
: Name_Full
);
454 case wxT('c'): // locale default date and time representation
455 case wxT('x'): // locale default date representation
456 #ifdef wxHAS_STRFTIME
458 // the problem: there is no way to know what do these format
459 // specifications correspond to for the current locale.
461 // the solution: use a hack and still use strftime(): first
462 // find the YEAR which is a year in the strftime() range (1970
463 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
464 // of the real year. Then make a copy of the format and
465 // replace all occurrences of YEAR in it with some unique
466 // string not appearing anywhere else in it, then use
467 // strftime() to format the date in year YEAR and then replace
468 // YEAR back by the real year and the unique replacement
469 // string back with YEAR. Notice that "all occurrences of YEAR"
470 // means all occurrences of 4 digit as well as 2 digit form!
472 // the bugs: we assume that neither of %c nor %x contains any
473 // fields which may change between the YEAR and real year. For
474 // example, the week number (%U, %W) and the day number (%j)
475 // will change if one of these years is leap and the other one
478 // find the YEAR: normally, for any year X, Jan 1 of the
479 // year X + 28 is the same weekday as Jan 1 of X (because
480 // the weekday advances by 1 for each normal X and by 2
481 // for each leap X, hence by 5 every 4 years or by 35
482 // which is 0 mod 7 every 28 years) but this rule breaks
483 // down if there are years between X and Y which are
484 // divisible by 4 but not leap (i.e. divisible by 100 but
485 // not 400), hence the correction.
487 int yearReal
= GetYear(tz
);
488 int mod28
= yearReal
% 28;
490 // be careful to not go too far - we risk to leave the
495 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
499 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
502 int nCentury
= year
/ 100,
503 nCenturyReal
= yearReal
/ 100;
505 // need to adjust for the years divisble by 400 which are
506 // not leap but are counted like leap ones if we just take
507 // the number of centuries in between for nLostWeekDays
508 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
509 (nCentury
/ 4 - nCenturyReal
/ 4);
511 // we have to gain back the "lost" weekdays: note that the
512 // effect of this loop is to not do anything to
513 // nLostWeekDays (which we won't use any more), but to
514 // (indirectly) set the year correctly
515 while ( (nLostWeekDays
% 7) != 0 )
517 nLostWeekDays
+= (year
++ % 4) ? 1 : 2;
520 // finally move the year below 2000 so that the 2-digit
521 // year number can never match the month or day of the
522 // month when we do the replacements below
526 wxASSERT_MSG( year
>= 1970 && year
< 2000,
527 wxT("logic error in wxDateTime::Format") );
530 // use strftime() to format the same date but in supported
533 // NB: we assume that strftime() doesn't check for the
534 // date validity and will happily format the date
535 // corresponding to Feb 29 of a non leap year (which
536 // may happen if yearReal was leap and year is not)
537 struct tm tmAdjusted
;
539 tmAdjusted
.tm_hour
= tm
.hour
;
540 tmAdjusted
.tm_min
= tm
.min
;
541 tmAdjusted
.tm_sec
= tm
.sec
;
542 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
543 tmAdjusted
.tm_yday
= GetDayOfYear();
544 tmAdjusted
.tm_mday
= tm
.mday
;
545 tmAdjusted
.tm_mon
= tm
.mon
;
546 tmAdjusted
.tm_year
= year
- 1900;
547 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
548 wxString str
= CallStrftime(*p
== wxT('c') ? wxT("%c")
552 // now replace the replacement year with the real year:
553 // notice that we have to replace the 4 digit year with
554 // a unique string not appearing in strftime() output
555 // first to prevent the 2 digit year from matching any
556 // substring of the 4 digit year (but any day, month,
557 // hours or minutes components should be safe because
558 // they are never in 70-99 range)
559 wxString
replacement("|");
560 while ( str
.find(replacement
) != wxString::npos
)
563 str
.Replace(wxString::Format("%d", year
),
565 str
.Replace(wxString::Format("%d", year
% 100),
566 wxString::Format("%d", yearReal
% 100));
567 str
.Replace(replacement
,
568 wxString::Format("%d", yearReal
));
572 #else // !wxHAS_STRFTIME
573 // Use "%m/%d/%y %H:%M:%S" format instead
574 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
575 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
576 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
579 case wxT('d'): // day of a month (01-31)
580 res
+= wxString::Format(fmt
, tm
.mday
);
583 case wxT('H'): // hour in 24h format (00-23)
584 res
+= wxString::Format(fmt
, tm
.hour
);
587 case wxT('I'): // hour in 12h format (01-12)
589 // 24h -> 12h, 0h -> 12h too
590 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
591 : tm
.hour
? tm
.hour
: 12;
592 res
+= wxString::Format(fmt
, hour12
);
596 case wxT('j'): // day of the year
597 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
600 case wxT('l'): // milliseconds (NOT STANDARD)
601 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
604 case wxT('m'): // month as a number (01-12)
605 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
608 case wxT('M'): // minute as a decimal number (00-59)
609 res
+= wxString::Format(fmt
, tm
.min
);
612 case wxT('p'): // AM or PM string
613 #ifdef wxHAS_STRFTIME
614 res
+= CallStrftime(wxT("%p"), &tmTimeOnly
);
615 #else // !wxHAS_STRFTIME
616 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
617 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
620 case wxT('S'): // second as a decimal number (00-61)
621 res
+= wxString::Format(fmt
, tm
.sec
);
624 case wxT('U'): // week number in the year (Sunday 1st week day)
625 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
628 case wxT('W'): // week number in the year (Monday 1st week day)
629 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
632 case wxT('w'): // weekday as a number (0-6), Sunday = 0
633 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
636 // case wxT('x'): -- handled with "%c"
638 case wxT('X'): // locale default time representation
639 // just use strftime() to format the time for us
640 #ifdef wxHAS_STRFTIME
641 res
+= CallStrftime(wxT("%X"), &tmTimeOnly
);
642 #else // !wxHAS_STRFTIME
643 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
644 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
647 case wxT('y'): // year without century (00-99)
648 res
+= wxString::Format(fmt
, tm
.year
% 100);
651 case wxT('Y'): // year with century
652 res
+= wxString::Format(fmt
, tm
.year
);
655 case wxT('z'): // time zone as [-+]HHMM
657 int ofs
= tz
.GetOffset();
659 // The time zone offset does not include the DST, but
660 // we do need to take it into account when showing the
661 // time in the local time zone to the user.
662 if ( ofs
== -wxGetTimeZone() && IsDST() == 1 )
664 // FIXME: As elsewhere in wxDateTime, we assume
665 // that the DST is always 1 hour, but this is not
680 // Converts seconds to HHMM representation.
681 res
+= wxString::Format(fmt
,
682 100*(ofs
/3600) + (ofs
/60)%60
);
686 case wxT('Z'): // timezone name
687 #ifdef wxHAS_STRFTIME
688 res
+= CallStrftime(wxT("%Z"), &tmTimeOnly
);
693 // is it the format width?
695 *p
== wxT('-') || *p
== wxT('+') ||
696 *p
== wxT(' ') || wxIsdigit(*p
);
704 // we've only got the flags and width so far in fmt
705 fmt
.Prepend(wxT('%'));
706 fmt
.Append(wxT('d'));
713 // no, it wasn't the width
714 wxFAIL_MSG(wxT("unknown format specifier"));
716 // fall through and just copy it nevertheless
718 case wxT('%'): // a percent sign
722 case 0: // the end of string
723 wxFAIL_MSG(wxT("missing format at the end of string"));
725 // just put the '%' which was the last char in format
735 // this function parses a string in (strict) RFC 822 format: see the section 5
736 // of the RFC for the detailed description, but briefly it's something of the
737 // form "Sat, 18 Dec 1999 00:48:30 +0100"
739 // this function is "strict" by design - it must reject anything except true
740 // RFC822 time specs.
742 wxDateTime::ParseRfc822Date(const wxString
& date
, wxString::const_iterator
*end
)
744 const wxString::const_iterator pEnd
= date
.end();
745 wxString::const_iterator p
= date
.begin();
748 const wxDateTime::WeekDay
749 wd
= GetWeekDayFromName(p
, pEnd
, Name_Abbr
, DateLang_English
);
750 if ( wd
== Inv_WeekDay
)
752 //else: ignore week day for now, we could also check that it really
753 // corresponds to the specified date
755 // 2. separating comma
756 if ( *p
++ != ',' || *p
++ != ' ' )
760 if ( !wxIsdigit(*p
) )
763 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - '0');
767 day
= (wxDateTime_t
)(day
+ (*p
++ - '0'));
774 const Month mon
= GetMonthFromName(p
, pEnd
, Name_Abbr
, DateLang_English
);
775 if ( mon
== Inv_Month
)
782 if ( !wxIsdigit(*p
) )
785 int year
= *p
++ - '0';
786 if ( !wxIsdigit(*p
) ) // should have at least 2 digits in the year
792 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
798 if ( !wxIsdigit(*p
) )
800 // no 3 digit years please
811 // 6. time in hh:mm:ss format with seconds being optional
812 if ( !wxIsdigit(*p
) )
815 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - '0');
817 if ( !wxIsdigit(*p
) )
821 hour
= (wxDateTime_t
)(hour
+ (*p
++ - '0'));
826 if ( !wxIsdigit(*p
) )
829 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - '0');
831 if ( !wxIsdigit(*p
) )
835 min
+= (wxDateTime_t
)(*p
++ - '0');
837 wxDateTime_t sec
= 0;
841 if ( !wxIsdigit(*p
) )
844 sec
= (wxDateTime_t
)(*p
++ - '0');
846 if ( !wxIsdigit(*p
) )
850 sec
+= (wxDateTime_t
)(*p
++ - '0');
856 // 7. now the interesting part: the timezone
857 int offset
= 0; // just to suppress warnings
858 if ( *p
== '-' || *p
== '+' )
860 // the explicit offset given: it has the form of hhmm
861 bool plus
= *p
++ == '+';
863 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
868 offset
= MIN_PER_HOUR
*(10*(*p
- '0') + (*(p
+ 1) - '0'));
872 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
876 offset
+= 10*(*p
- '0') + (*(p
+ 1) - '0');
885 // the symbolic timezone given: may be either military timezone or one
886 // of standard abbreviations
889 // military: Z = UTC, J unused, A = -1, ..., Y = +12
890 static const int offsets
[26] =
892 //A B C D E F G H I J K L M
893 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
894 //N O P R Q S T U V W Z Y Z
895 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
898 if ( *p
< wxT('A') || *p
> wxT('Z') || *p
== wxT('J') )
901 offset
= offsets
[*p
++ - 'A'];
906 const wxString
tz(p
, date
.end());
907 if ( tz
== wxT("UT") || tz
== wxT("UTC") || tz
== wxT("GMT") )
909 else if ( tz
== wxT("AST") )
911 else if ( tz
== wxT("ADT") )
913 else if ( tz
== wxT("EST") )
915 else if ( tz
== wxT("EDT") )
917 else if ( tz
== wxT("CST") )
919 else if ( tz
== wxT("CDT") )
921 else if ( tz
== wxT("MST") )
923 else if ( tz
== wxT("MDT") )
925 else if ( tz
== wxT("PST") )
927 else if ( tz
== wxT("PDT") )
936 offset
*= MIN_PER_HOUR
;
940 // the spec was correct, construct the date from the values we found
941 Set(day
, mon
, year
, hour
, min
, sec
);
942 MakeFromTimezone(TimeZone::Make(offset
*SEC_PER_MIN
));
950 const char* wxDateTime::ParseRfc822Date(const char* date
)
952 wxString::const_iterator end
;
953 wxString
dateStr(date
);
954 if ( !ParseRfc822Date(dateStr
, &end
) )
957 return date
+ dateStr
.IterOffsetInMBStr(end
);
960 const wchar_t* wxDateTime::ParseRfc822Date(const wchar_t* date
)
962 wxString::const_iterator end
;
963 wxString
dateStr(date
);
964 if ( !ParseRfc822Date(dateStr
, &end
) )
967 return date
+ (end
- dateStr
.begin());
971 wxDateTime::ParseFormat(const wxString
& date
,
972 const wxString
& format
,
973 const wxDateTime
& dateDef
,
974 wxString::const_iterator
*endParse
)
976 wxCHECK_MSG( !format
.empty(), false, "format can't be empty" );
977 wxCHECK_MSG( endParse
, false, "end iterator pointer must be specified" );
982 // what fields have we found?
983 bool haveWDay
= false,
993 bool hourIsIn12hFormat
= false, // or in 24h one?
994 isPM
= false; // AM by default
996 bool haveTimeZone
= false;
998 // and the value of the items we have (init them to get rid of warnings)
999 wxDateTime_t msec
= 0,
1003 WeekDay wday
= Inv_WeekDay
;
1004 wxDateTime_t yday
= 0,
1006 wxDateTime::Month mon
= Inv_Month
;
1008 long timeZone
= 0; // time zone in seconds as expected in Tm structure
1010 wxString::const_iterator input
= date
.begin();
1011 const wxString::const_iterator end
= date
.end();
1012 for ( wxString::const_iterator fmt
= format
.begin(); fmt
!= format
.end(); ++fmt
)
1014 if ( *fmt
!= wxT('%') )
1016 if ( wxIsspace(*fmt
) )
1018 // a white space in the format string matches 0 or more white
1019 // spaces in the input
1020 while ( input
!= end
&& wxIsspace(*input
) )
1027 // any other character (not whitespace, not '%') must be
1028 // matched by itself in the input
1029 if ( input
== end
|| *input
++ != *fmt
)
1036 // done with this format char
1040 // start of a format specification
1042 // parse the optional width
1044 while ( wxIsdigit(*++fmt
) )
1047 width
+= *fmt
- '0';
1050 // the default widths for the various fields
1053 switch ( (*fmt
).GetValue() )
1055 case wxT('Y'): // year has 4 digits
1059 case wxT('j'): // day of year has 3 digits
1060 case wxT('l'): // milliseconds have 3 digits
1064 case wxT('w'): // week day as number has only one
1069 // default for all other fields
1074 // then the format itself
1075 switch ( (*fmt
).GetValue() )
1077 case wxT('a'): // a weekday name
1080 wday
= GetWeekDayFromName
1083 *fmt
== 'a' ? Name_Abbr
: Name_Full
,
1086 if ( wday
== Inv_WeekDay
)
1095 case wxT('b'): // a month name
1098 mon
= GetMonthFromName
1101 *fmt
== 'b' ? Name_Abbr
: Name_Full
,
1104 if ( mon
== Inv_Month
)
1113 case wxT('c'): // locale default date and time representation
1119 fmtDateTime
= wxLocale::GetInfo(wxLOCALE_DATE_TIME_FMT
);
1120 if ( !fmtDateTime
.empty() )
1121 dt
= ParseFormatAt(input
, end
, fmtDateTime
);
1122 #endif // wxUSE_INTL
1123 if ( !dt
.IsValid() )
1125 // also try the format which corresponds to ctime()
1126 // output (i.e. the "C" locale default)
1127 dt
= ParseFormatAt(input
, end
, wxS("%a %b %d %H:%M:%S %Y"));
1130 if ( !dt
.IsValid() )
1132 // and finally also the two generic date/time formats
1133 dt
= ParseFormatAt(input
, end
, wxS("%x %X"), wxS("%X %x"));
1136 if ( !dt
.IsValid() )
1139 const Tm tm
= dt
.GetTm();
1149 haveDay
= haveMon
= haveYear
=
1150 haveHour
= haveMin
= haveSec
= true;
1154 case wxT('d'): // day of a month (01-31)
1155 case 'e': // day of a month (1-31) (GNU extension)
1156 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1157 (num
> 31) || (num
< 1) )
1163 // we can't check whether the day range is correct yet, will
1164 // do it later - assume ok for now
1166 mday
= (wxDateTime_t
)num
;
1169 case wxT('H'): // hour in 24h format (00-23)
1170 if ( !GetNumericToken(width
, input
, end
, &num
) || (num
> 23) )
1177 hour
= (wxDateTime_t
)num
;
1180 case wxT('I'): // hour in 12h format (01-12)
1181 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1182 !num
|| (num
> 12) )
1189 hourIsIn12hFormat
= true;
1190 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
1193 case wxT('j'): // day of the year
1194 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1195 !num
|| (num
> 366) )
1202 yday
= (wxDateTime_t
)num
;
1205 case wxT('l'): // milliseconds (0-999)
1206 if ( !GetNumericToken(width
, input
, end
, &num
) )
1210 msec
= (wxDateTime_t
)num
;
1213 case wxT('m'): // month as a number (01-12)
1214 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1215 !num
|| (num
> 12) )
1222 mon
= (Month
)(num
- 1);
1225 case wxT('M'): // minute as a decimal number (00-59)
1226 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1234 min
= (wxDateTime_t
)num
;
1237 case wxT('p'): // AM or PM string
1240 GetAmPmStrings(&am
, &pm
);
1242 // we can never match %p in locales which don't use AM/PM
1243 if ( am
.empty() || pm
.empty() )
1246 const size_t pos
= input
- date
.begin();
1247 if ( date
.compare(pos
, pm
.length(), pm
) == 0 )
1250 input
+= pm
.length();
1252 else if ( date
.compare(pos
, am
.length(), am
) == 0 )
1254 input
+= am
.length();
1263 case wxT('r'): // time as %I:%M:%S %p
1266 if ( !dt
.ParseFormat(wxString(input
, end
),
1267 wxS("%I:%M:%S %p"), &input
) )
1270 haveHour
= haveMin
= haveSec
= true;
1272 const Tm tm
= dt
.GetTm();
1279 case wxT('R'): // time as %H:%M
1282 dt
= ParseFormatAt(input
, end
, wxS("%H:%M"));
1283 if ( !dt
.IsValid() )
1289 const Tm tm
= dt
.GetTm();
1295 case wxT('S'): // second as a decimal number (00-61)
1296 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1304 sec
= (wxDateTime_t
)num
;
1307 case wxT('T'): // time as %H:%M:%S
1310 dt
= ParseFormatAt(input
, end
, wxS("%H:%M:%S"));
1311 if ( !dt
.IsValid() )
1318 const Tm tm
= dt
.GetTm();
1325 case wxT('w'): // weekday as a number (0-6), Sunday = 0
1326 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1334 wday
= (WeekDay
)num
;
1337 case wxT('x'): // locale default date representation
1341 fmtDate
= wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT
),
1342 fmtDateAlt
= wxLocale::GetInfo(wxLOCALE_LONG_DATE_FMT
);
1343 #else // !wxUSE_INTL
1344 wxString fmtDate
, fmtDateAlt
;
1345 #endif // wxUSE_INTL/!wxUSE_INTL
1346 if ( fmtDate
.empty() )
1348 if ( IsWestEuropeanCountry(GetCountry()) ||
1349 GetCountry() == Russia
)
1351 fmtDate
= wxS("%d/%m/%Y");
1352 fmtDateAlt
= wxS("%m/%d/%Y");
1356 fmtDate
= wxS("%m/%d/%Y");
1357 fmtDateAlt
= wxS("%d/%m/%Y");
1362 dt
= ParseFormatAt(input
, end
, fmtDate
, fmtDateAlt
);
1364 if ( !dt
.IsValid() )
1366 // try with short years too
1367 fmtDate
.Replace("%Y","%y");
1368 fmtDateAlt
.Replace("%Y","%y");
1369 dt
= ParseFormatAt(input
, end
, fmtDate
, fmtDateAlt
);
1371 if ( !dt
.IsValid() )
1375 const Tm tm
= dt
.GetTm();
1388 case wxT('X'): // locale default time representation
1391 wxString fmtTime
= wxLocale::GetInfo(wxLOCALE_TIME_FMT
),
1393 #else // !wxUSE_INTL
1394 wxString fmtTime
, fmtTimeAlt
;
1395 #endif // wxUSE_INTL/!wxUSE_INTL
1396 if ( fmtTime
.empty() )
1398 // try to parse what follows as "%H:%M:%S" and, if this
1399 // fails, as "%I:%M:%S %p" - this should catch the most
1406 dt
= ParseFormatAt(input
, end
, fmtTime
, fmtTimeAlt
);
1407 if ( !dt
.IsValid() )
1414 const Tm tm
= dt
.GetTm();
1421 case wxT('y'): // year without century (00-99)
1422 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1431 // TODO should have an option for roll over date instead of
1432 // hard coding it here
1433 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
1436 case wxT('Y'): // year with century
1437 if ( !GetNumericToken(width
, input
, end
, &num
) )
1444 year
= (wxDateTime_t
)num
;
1449 // check that we have something here at all
1453 // and then check that it's either plus or minus sign
1455 if ( *input
== wxT('-') )
1457 else if ( *input
== wxT('+') )
1460 return false; // no match
1462 // here should follow 4 digits HHMM
1464 unsigned long tzHourMin
;
1465 if ( !GetNumericToken(4, input
, end
, &tzHourMin
) )
1466 return false; // no match
1468 const unsigned hours
= tzHourMin
/ 100;
1469 const unsigned minutes
= tzHourMin
% 100;
1471 if ( hours
> 12 || minutes
> 59 )
1472 return false; // bad format
1474 timeZone
= 3600*hours
+ 60*minutes
;
1476 timeZone
= -timeZone
;
1478 haveTimeZone
= true;
1482 case wxT('Z'): // timezone name
1483 // FIXME: currently we just ignore everything that looks like a
1485 GetAlphaToken(input
, end
);
1488 case wxT('%'): // a percent sign
1489 if ( input
== end
|| *input
++ != wxT('%') )
1496 case 0: // the end of string
1497 wxFAIL_MSG(wxT("unexpected format end"));
1501 default: // not a known format spec
1506 // format matched, try to construct a date from what we have now
1508 if ( dateDef
.IsValid() )
1510 // take this date as default
1511 tmDef
= dateDef
.GetTm();
1513 else if ( IsValid() )
1515 // if this date is valid, don't change it
1520 // no default and this date is invalid - fall back to Today()
1521 tmDef
= Today().GetTm();
1537 // TODO we don't check here that the values are consistent, if both year
1538 // day and month/day were found, we just ignore the year day and we
1539 // also always ignore the week day
1542 if ( mday
> GetNumberOfDays(tm
.mon
, tm
.year
) )
1547 else if ( haveYDay
)
1549 if ( yday
> GetNumberOfDays(tm
.year
) )
1552 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
1559 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
1561 // translate to 24hour format
1564 //else: either already in 24h format or no translation needed
1587 // If a time zone was specified and it is not the local time zone, we need
1588 // to shift the time accordingly.
1590 // Note that avoiding the call to MakeFromTimeZone is necessary to avoid
1592 if ( haveTimeZone
&& timeZone
!= -wxGetTimeZone() )
1593 MakeFromTimezone(timeZone
);
1595 // finally check that the week day is consistent -- if we had it
1596 if ( haveWDay
&& GetWeekDay() != wday
)
1605 wxDateTime::ParseFormat(const char* date
,
1606 const wxString
& format
,
1607 const wxDateTime
& dateDef
)
1609 wxString::const_iterator end
;
1610 wxString
dateStr(date
);
1611 if ( !ParseFormat(dateStr
, format
, dateDef
, &end
) )
1614 return date
+ dateStr
.IterOffsetInMBStr(end
);
1618 wxDateTime::ParseFormat(const wchar_t* date
,
1619 const wxString
& format
,
1620 const wxDateTime
& dateDef
)
1622 wxString::const_iterator end
;
1623 wxString
dateStr(date
);
1624 if ( !ParseFormat(dateStr
, format
, dateDef
, &end
) )
1627 return date
+ (end
- dateStr
.begin());
1631 wxDateTime::ParseDateTime(const wxString
& date
, wxString::const_iterator
*end
)
1633 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1639 wxString::const_iterator
1644 // If we got a date in the beginning, see if there is a time specified
1646 if ( dtDate
.ParseDate(date
, &endDate
) )
1648 // Skip spaces, as the ParseTime() function fails on spaces
1649 while ( endDate
!= date
.end() && wxIsspace(*endDate
) )
1652 const wxString
timestr(endDate
, date
.end());
1653 if ( !dtTime
.ParseTime(timestr
, &endTime
) )
1656 endBoth
= endDate
+ (endTime
- timestr
.begin());
1658 else // no date in the beginning
1660 // check if we have a time followed by a date
1661 if ( !dtTime
.ParseTime(date
, &endTime
) )
1664 while ( endTime
!= date
.end() && wxIsspace(*endTime
) )
1667 const wxString
datestr(endTime
, date
.end());
1668 if ( !dtDate
.ParseDate(datestr
, &endDate
) )
1671 endBoth
= endTime
+ (endDate
- datestr
.begin());
1674 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
1675 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
1676 dtTime
.GetMillisecond());
1683 const char* wxDateTime::ParseDateTime(const char* date
)
1685 wxString::const_iterator end
;
1686 wxString
dateStr(date
);
1687 if ( !ParseDateTime(dateStr
, &end
) )
1690 return date
+ dateStr
.IterOffsetInMBStr(end
);
1693 const wchar_t* wxDateTime::ParseDateTime(const wchar_t* date
)
1695 wxString::const_iterator end
;
1696 wxString
dateStr(date
);
1697 if ( !ParseDateTime(dateStr
, &end
) )
1700 return date
+ (end
- dateStr
.begin());
1704 wxDateTime::ParseDate(const wxString
& date
, wxString::const_iterator
*end
)
1706 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1708 // this is a simplified version of ParseDateTime() which understands only
1709 // "today" (for wxDate compatibility) and digits only otherwise (and not
1710 // all esoteric constructions ParseDateTime() knows about)
1712 const wxString::const_iterator pBegin
= date
.begin();
1713 const wxString::const_iterator pEnd
= date
.end();
1715 wxString::const_iterator p
= pBegin
;
1716 while ( p
!= pEnd
&& wxIsspace(*p
) )
1719 // some special cases
1723 int dayDiffFromToday
;
1726 { wxTRANSLATE("today"), 0 },
1727 { wxTRANSLATE("yesterday"), -1 },
1728 { wxTRANSLATE("tomorrow"), 1 },
1731 const size_t lenRest
= pEnd
- p
;
1732 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
1734 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
1735 size_t len
= dateStr
.length();
1737 if ( len
> lenRest
)
1740 const wxString::const_iterator pEndStr
= p
+ len
;
1741 if ( wxString(p
, pEndStr
).CmpNoCase(dateStr
) == 0 )
1743 // nothing can follow this, so stop here
1747 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
1749 if ( dayDiffFromToday
)
1751 *this += wxDateSpan::Days(dayDiffFromToday
);
1760 // We try to guess what we have here: for each new (numeric) token, we
1761 // determine if it can be a month, day or a year. Of course, there is an
1762 // ambiguity as some numbers may be days as well as months, so we also
1763 // have the ability to back track.
1766 bool haveDay
= false, // the months day?
1767 haveWDay
= false, // the day of week?
1768 haveMon
= false, // the month?
1769 haveYear
= false; // the year?
1771 bool monWasNumeric
= false; // was month specified as a number?
1773 // and the value of the items we have (init them to get rid of warnings)
1774 WeekDay wday
= Inv_WeekDay
;
1775 wxDateTime_t day
= 0;
1776 wxDateTime::Month mon
= Inv_Month
;
1779 // tokenize the string
1782 // skip white space and date delimiters
1783 if ( wxStrchr(".,/-\t\r\n ", *p
) )
1789 // modify copy of the iterator as we're not sure if the next token is
1790 // still part of the date at all
1791 wxString::const_iterator pCopy
= p
;
1793 // we can have either alphabetic or numeric token, start by testing if
1796 if ( GetNumericToken(10 /* max length */, pCopy
, pEnd
, &val
) )
1798 // guess what this number is
1804 if ( !haveMon
&& val
> 0 && val
<= 12 )
1806 // assume it is month
1809 else // not the month
1813 // this can only be the year
1816 else // may be either day or year
1818 // use a leap year if we don't have the year yet to allow
1819 // dates like 2/29/1976 which would be rejected otherwise
1820 wxDateTime_t max_days
= (wxDateTime_t
)(
1822 ? GetNumberOfDays(mon
, haveYear
? year
: 1976)
1827 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
1832 else // yes, suppose it's the day
1846 year
= (wxDateTime_t
)val
;
1855 day
= (wxDateTime_t
)val
;
1860 monWasNumeric
= true;
1862 mon
= (Month
)(val
- 1);
1865 else // not a number
1867 // be careful not to overwrite the current mon value
1868 Month mon2
= GetMonthFromName
1871 Name_Full
| Name_Abbr
,
1872 DateLang_Local
| DateLang_English
1874 if ( mon2
!= Inv_Month
)
1879 // but we already have a month - maybe we guessed wrong
1880 // when we had interpreted that numeric value as a month
1881 // and it was the day number instead?
1882 if ( haveDay
|| !monWasNumeric
)
1885 // assume we did and change our mind: reinterpret the month
1886 // value as a day (notice that there is no need to check
1887 // that it is valid as month values are always < 12, but
1888 // the days are counted from 1 unlike the months)
1889 day
= (wxDateTime_t
)(mon
+ 1);
1897 else // not a valid month name
1899 WeekDay wday2
= GetWeekDayFromName
1902 Name_Full
| Name_Abbr
,
1903 DateLang_Local
| DateLang_English
1905 if ( wday2
!= Inv_WeekDay
)
1915 else // not a valid weekday name
1918 static const char *const ordinals
[] =
1920 wxTRANSLATE("first"),
1921 wxTRANSLATE("second"),
1922 wxTRANSLATE("third"),
1923 wxTRANSLATE("fourth"),
1924 wxTRANSLATE("fifth"),
1925 wxTRANSLATE("sixth"),
1926 wxTRANSLATE("seventh"),
1927 wxTRANSLATE("eighth"),
1928 wxTRANSLATE("ninth"),
1929 wxTRANSLATE("tenth"),
1930 wxTRANSLATE("eleventh"),
1931 wxTRANSLATE("twelfth"),
1932 wxTRANSLATE("thirteenth"),
1933 wxTRANSLATE("fourteenth"),
1934 wxTRANSLATE("fifteenth"),
1935 wxTRANSLATE("sixteenth"),
1936 wxTRANSLATE("seventeenth"),
1937 wxTRANSLATE("eighteenth"),
1938 wxTRANSLATE("nineteenth"),
1939 wxTRANSLATE("twentieth"),
1940 // that's enough - otherwise we'd have problems with
1941 // composite (or not) ordinals
1945 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
1947 const wxString ord
= wxGetTranslation(ordinals
[n
]);
1948 const size_t len
= ord
.length();
1949 if ( date
.compare(p
- pBegin
, len
, ord
) == 0 )
1956 if ( n
== WXSIZEOF(ordinals
) )
1958 // stop here - something unknown
1965 // don't try anything here (as in case of numeric day
1966 // above) - the symbolic day spec should always
1967 // precede the month/year
1973 day
= (wxDateTime_t
)(n
+ 1);
1978 // advance iterator past a successfully parsed token
1982 // either no more tokens or the scan was stopped by something we couldn't
1983 // parse - in any case, see if we can construct a date from what we have
1984 if ( !haveDay
&& !haveWDay
)
1987 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
1988 !(haveDay
&& haveMon
&& haveYear
) )
1990 // without adjectives (which we don't support here) the week day only
1991 // makes sense completely separately or with the full date
1992 // specification (what would "Wed 1999" mean?)
1996 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
1998 // may be we have month and day instead of day and year?
1999 if ( haveDay
&& !haveMon
)
2003 // exchange day and month
2004 mon
= (wxDateTime::Month
)(day
- 1);
2006 // we're in the current year then
2007 if ( (year
> 0) && (year
<= (int)GetNumberOfDays(mon
, Inv_Year
)) )
2009 day
= (wxDateTime_t
)year
;
2014 //else: no, can't exchange, leave haveMon == false
2024 mon
= GetCurrentMonth();
2029 year
= GetCurrentYear();
2034 // normally we check the day above but the check is optimistic in case
2035 // we find the day before its month/year so we have to redo it now
2036 if ( day
> GetNumberOfDays(mon
, year
) )
2039 Set(day
, mon
, year
);
2043 // check that it is really the same
2044 if ( GetWeekDay() != wday
)
2052 SetToWeekDayInSameWeek(wday
);
2060 const char* wxDateTime::ParseDate(const char* date
)
2062 wxString::const_iterator end
;
2063 wxString
dateStr(date
);
2064 if ( !ParseDate(dateStr
, &end
) )
2067 return date
+ dateStr
.IterOffsetInMBStr(end
);
2070 const wchar_t* wxDateTime::ParseDate(const wchar_t* date
)
2072 wxString::const_iterator end
;
2073 wxString
dateStr(date
);
2074 if ( !ParseDate(dateStr
, &end
) )
2077 return date
+ (end
- dateStr
.begin());
2081 wxDateTime::ParseTime(const wxString
& time
, wxString::const_iterator
*end
)
2083 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
2085 // first try some extra things
2092 { wxTRANSLATE("noon"), 12 },
2093 { wxTRANSLATE("midnight"), 00 },
2097 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
2099 const wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
2100 if ( timeString
.CmpNoCase(wxString(time
, timeString
.length())) == 0 )
2102 // casts required by DigitalMars
2103 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
2106 *end
= time
.begin() + timeString
.length();
2112 // try all time formats we may think about in the order from longest to
2114 static const char *const timeFormats
[] =
2116 "%I:%M:%S %p", // 12hour with AM/PM
2117 "%H:%M:%S", // could be the same or 24 hour one so try it too
2118 "%I:%M %p", // 12hour with AM/PM but without seconds
2119 "%H:%M", // and a possibly 24 hour version without seconds
2120 "%I %p", // just hour with AM/AM
2121 "%H", // just hour in 24 hour version
2122 "%X", // possibly something from above or maybe something
2123 // completely different -- try it last
2125 // TODO: parse timezones
2128 for ( size_t nFmt
= 0; nFmt
< WXSIZEOF(timeFormats
); nFmt
++ )
2130 if ( ParseFormat(time
, timeFormats
[nFmt
], end
) )
2137 const char* wxDateTime::ParseTime(const char* date
)
2139 wxString::const_iterator end
;
2140 wxString
dateStr(date
);
2141 if ( !ParseTime(dateStr
, &end
) )
2144 return date
+ dateStr
.IterOffsetInMBStr(end
);
2147 const wchar_t* wxDateTime::ParseTime(const wchar_t* date
)
2149 wxString::const_iterator end
;
2150 wxString
dateStr(date
);
2151 if ( !ParseTime(dateStr
, &end
) )
2154 return date
+ (end
- dateStr
.begin());
2157 // ----------------------------------------------------------------------------
2158 // Workdays and holidays support
2159 // ----------------------------------------------------------------------------
2161 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
2163 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
2166 // ============================================================================
2168 // ============================================================================
2170 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
2173 return ds1
.Multiply(n
);
2176 // ============================================================================
2178 // ============================================================================
2180 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
2182 return wxTimeSpan(ts
).Multiply(n
);
2185 // this enum is only used in wxTimeSpan::Format() below but we can't declare
2186 // it locally to the method as it provokes an internal compiler error in egcs
2187 // 2.91.60 when building with -O2
2198 // not all strftime(3) format specifiers make sense here because, for example,
2199 // a time span doesn't have a year nor a timezone
2201 // Here are the ones which are supported (all of them are supported by strftime
2203 // %H hour in 24 hour format
2204 // %M minute (00 - 59)
2205 // %S second (00 - 59)
2208 // Also, for MFC CTimeSpan compatibility, we support
2209 // %D number of days
2211 // And, to be better than MFC :-), we also have
2212 // %E number of wEeks
2213 // %l milliseconds (000 - 999)
2214 wxString
wxTimeSpan::Format(const wxString
& format
) const
2216 // we deal with only positive time spans here and just add the sign in
2217 // front for the negative ones
2220 wxString
str(Negate().Format(format
));
2224 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
2225 wxT("NULL format in wxTimeSpan::Format") );
2228 str
.Alloc(format
.length());
2230 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
2232 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
2233 // question is what should ts.Format("%S") do? The code here returns "3273"
2234 // in this case (i.e. the total number of seconds, not just seconds % 60)
2235 // because, for me, this call means "give me entire time interval in
2236 // seconds" and not "give me the seconds part of the time interval"
2238 // If we agree that it should behave like this, it is clear that the
2239 // interpretation of each format specifier depends on the presence of the
2240 // other format specs in the string: if there was "%H" before "%M", we
2241 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
2243 // we remember the most important unit found so far
2244 TimeSpanPart partBiggest
= Part_MSec
;
2246 for ( wxString::const_iterator pch
= format
.begin(); pch
!= format
.end(); ++pch
)
2250 if ( ch
== wxT('%') )
2252 // the start of the format specification of the printf() below
2253 wxString
fmtPrefix(wxT('%'));
2258 // the number of digits for the format string, 0 if unused
2259 unsigned digits
= 0;
2261 ch
= *++pch
; // get the format spec char
2265 wxFAIL_MSG( wxT("invalid format character") );
2271 // skip the part below switch
2276 if ( partBiggest
< Part_Day
)
2282 partBiggest
= Part_Day
;
2287 partBiggest
= Part_Week
;
2293 if ( partBiggest
< Part_Hour
)
2299 partBiggest
= Part_Hour
;
2306 n
= GetMilliseconds().ToLong();
2307 if ( partBiggest
< Part_MSec
)
2311 //else: no need to reset partBiggest to Part_MSec, it is
2312 // the least significant one anyhow
2319 if ( partBiggest
< Part_Min
)
2325 partBiggest
= Part_Min
;
2332 n
= GetSeconds().ToLong();
2333 if ( partBiggest
< Part_Sec
)
2339 partBiggest
= Part_Sec
;
2348 fmtPrefix
<< wxT("0") << digits
;
2351 str
+= wxString::Format(fmtPrefix
+ wxT("ld"), n
);
2355 // normal character, just copy
2363 #endif // wxUSE_DATETIME