1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/datetimefmt.cpp
3 // Purpose: wxDateTime formatting & parsing code
4 // Author: Vadim Zeitlin
7 // Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
8 // parts of code taken from sndcal library by Scott E. Lee:
10 // Copyright 1993-1995, Scott E. Lee, all rights reserved.
11 // Permission granted to use, copy, modify, distribute and sell
12 // so long as the above copyright and this permission statement
13 // are retained in all copies.
15 // Licence: wxWindows licence
16 ///////////////////////////////////////////////////////////////////////////////
18 // ============================================================================
20 // ============================================================================
22 // ----------------------------------------------------------------------------
24 // ----------------------------------------------------------------------------
26 // For compilers that support precompilation, includes "wx.h".
27 #include "wx/wxprec.h"
33 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
37 #include "wx/msw/wrapwin.h"
39 #include "wx/string.h"
42 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
43 #include "wx/module.h"
47 #include "wx/thread.h"
58 #include "wx/datetime.h"
61 // ============================================================================
62 // implementation of wxDateTime
63 // ============================================================================
65 // ----------------------------------------------------------------------------
66 // helpers shared between datetime.cpp and datetimefmt.cpp
67 // ----------------------------------------------------------------------------
69 extern void InitTm(struct tm
& tm
);
71 extern wxString
CallStrftime(const wxString
& format
, const tm
* tm
);
73 // ----------------------------------------------------------------------------
74 // constants (see also datetime.cpp)
75 // ----------------------------------------------------------------------------
77 static const int DAYS_PER_WEEK
= 7;
79 static const int HOURS_PER_DAY
= 24;
81 static const int SEC_PER_MIN
= 60;
83 static const int MIN_PER_HOUR
= 60;
85 // ----------------------------------------------------------------------------
87 // ----------------------------------------------------------------------------
92 // all the functions below taking non-const wxString::const_iterator p advance
93 // it until the end of the match
95 // scans all digits (but no more than len) and returns the resulting number
96 bool GetNumericToken(size_t len
,
97 wxString::const_iterator
& p
,
98 const wxString::const_iterator
& end
,
99 unsigned long *number
)
103 while ( p
!= end
&& wxIsdigit(*p
) )
107 if ( len
&& ++n
> len
)
111 return !s
.empty() && s
.ToULong(number
);
114 // scans all alphabetic characters and returns the resulting string
116 GetAlphaToken(wxString::const_iterator
& p
,
117 const wxString::const_iterator
& end
)
120 while ( p
!= end
&& wxIsalpha(*p
) )
130 DateLang_English
= 1,
134 // return the month if the string is a month name or Inv_Month otherwise
136 // flags can contain wxDateTime::Name_Abbr/Name_Full or both of them and lang
137 // can be either DateLang_Local (default) to interpret string as a localized
138 // month name or DateLang_English to parse it as a standard English name or
139 // their combination to interpret it in any way
141 GetMonthFromName(wxString::const_iterator
& p
,
142 const wxString::const_iterator
& end
,
146 const wxString::const_iterator pOrig
= p
;
147 const wxString name
= GetAlphaToken(p
, end
);
149 return wxDateTime::Inv_Month
;
151 wxDateTime::Month mon
;
152 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
154 // case-insensitive comparison either one of or with both abbreviated
156 if ( flags
& wxDateTime::Name_Full
)
158 if ( lang
& DateLang_English
)
160 if ( name
.CmpNoCase(wxDateTime::GetEnglishMonthName(mon
,
161 wxDateTime::Name_Full
)) == 0 )
165 if ( lang
& DateLang_Local
)
167 if ( name
.CmpNoCase(wxDateTime::GetMonthName(mon
,
168 wxDateTime::Name_Full
)) == 0 )
173 if ( flags
& wxDateTime::Name_Abbr
)
175 if ( lang
& DateLang_English
)
177 if ( name
.CmpNoCase(wxDateTime::GetEnglishMonthName(mon
,
178 wxDateTime::Name_Abbr
)) == 0 )
182 if ( lang
& DateLang_Local
)
184 // some locales (e.g. French one) use periods for the
185 // abbreviated month names but it's never part of name so
186 // compare it specially
187 wxString nameAbbr
= wxDateTime::GetMonthName(mon
,
188 wxDateTime::Name_Abbr
);
189 const bool hasPeriod
= *nameAbbr
.rbegin() == '.';
191 nameAbbr
.erase(nameAbbr
.end() - 1);
193 if ( name
.CmpNoCase(nameAbbr
) == 0 )
197 // skip trailing period if it was part of the match
200 else // no match as no matching period
210 if ( mon
== wxDateTime::Inv_Month
)
216 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
218 // flags and lang parameters have the same meaning as for GetMonthFromName()
221 GetWeekDayFromName(wxString::const_iterator
& p
,
222 const wxString::const_iterator
& end
,
225 const wxString::const_iterator pOrig
= p
;
226 const wxString name
= GetAlphaToken(p
, end
);
228 return wxDateTime::Inv_WeekDay
;
230 wxDateTime::WeekDay wd
;
231 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
233 if ( flags
& wxDateTime::Name_Full
)
235 if ( lang
& DateLang_English
)
237 if ( name
.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd
,
238 wxDateTime::Name_Full
)) == 0 )
242 if ( lang
& DateLang_Local
)
244 if ( name
.CmpNoCase(wxDateTime::GetWeekDayName(wd
,
245 wxDateTime::Name_Full
)) == 0 )
250 if ( flags
& wxDateTime::Name_Abbr
)
252 if ( lang
& DateLang_English
)
254 if ( name
.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd
,
255 wxDateTime::Name_Abbr
)) == 0 )
259 if ( lang
& DateLang_Local
)
261 if ( name
.CmpNoCase(wxDateTime::GetWeekDayName(wd
,
262 wxDateTime::Name_Abbr
)) == 0 )
268 if ( wd
== wxDateTime::Inv_WeekDay
)
274 // parses string starting at given iterator using the specified format and,
275 // optionally, a fall back format (and optionally another one... but it stops
278 // if unsuccessful, returns invalid wxDateTime without changing p; otherwise
279 // advance p to the end of the match and returns wxDateTime containing the
280 // results of the parsing
282 ParseFormatAt(wxString::const_iterator
& p
,
283 const wxString::const_iterator
& end
,
285 // FIXME-VC6: using wxString() instead of wxEmptyString in the
286 // line below results in error C2062: type 'class
287 // wxString (__cdecl *)(void)' unexpected
288 const wxString
& fmtAlt
= wxEmptyString
)
290 const wxString
str(p
, end
);
291 wxString::const_iterator endParse
;
294 // Use a default date outside of the DST period to avoid problems with
295 // parsing the time differently depending on the today's date (which is used
296 // as the fall back date if none is explicitly specified).
297 static const wxDateTime
dtDef(1, wxDateTime::Jan
, 2012);
299 if ( dt
.ParseFormat(str
, fmt
, dtDef
, &endParse
) ||
300 (!fmtAlt
.empty() && dt
.ParseFormat(str
, fmtAlt
, dtDef
, &endParse
)) )
302 p
+= endParse
- str
.begin();
304 //else: all formats failed
309 } // anonymous namespace
311 // ----------------------------------------------------------------------------
312 // wxDateTime to/from text representations
313 // ----------------------------------------------------------------------------
315 wxString
wxDateTime::Format(const wxString
& formatp
, const TimeZone
& tz
) const
317 wxCHECK_MSG( !formatp
.empty(), wxEmptyString
,
318 wxT("NULL format in wxDateTime::Format") );
320 wxString format
= formatp
;
322 format
.Replace("%c",wxLocale::GetInfo(wxLOCALE_DATE_TIME_FMT
));
323 format
.Replace("%x",wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT
));
324 format
.Replace("%X",wxLocale::GetInfo(wxLOCALE_TIME_FMT
));
326 // we have to use our own implementation if the date is out of range of
327 // strftime() or if we use non standard specifiers (notice that "%z" is
328 // special because it is de facto standard under Unix but is not supported
330 #ifdef wxHAS_STRFTIME
331 time_t time
= GetTicks();
333 if ( (time
!= (time_t)-1) && !wxStrstr(format
, wxT("%l"))
335 && !wxStrstr(format
, wxT("%z"))
342 if ( tz
.GetOffset() == -wxGetTimeZone() )
344 // we are working with local time
345 tm
= wxLocaltime_r(&time
, &tmstruct
);
347 // should never happen
348 wxCHECK_MSG( tm
, wxEmptyString
, wxT("wxLocaltime_r() failed") );
352 time
+= (int)tz
.GetOffset();
354 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
355 int time2
= (int) time
;
361 tm
= wxGmtime_r(&time
, &tmstruct
);
363 // should never happen
364 wxCHECK_MSG( tm
, wxEmptyString
, wxT("wxGmtime_r() failed") );
368 tm
= (struct tm
*)NULL
;
374 return CallStrftime(format
, tm
);
377 //else: use generic code below
378 #endif // wxHAS_STRFTIME
380 // we only parse ANSI C format specifications here, no POSIX 2
381 // complications, no GNU extensions but we do add support for a "%l" format
382 // specifier allowing to get the number of milliseconds
385 // used for calls to strftime() when we only deal with time
386 struct tm tmTimeOnly
;
387 memset(&tmTimeOnly
, 0, sizeof(tmTimeOnly
));
388 tmTimeOnly
.tm_hour
= tm
.hour
;
389 tmTimeOnly
.tm_min
= tm
.min
;
390 tmTimeOnly
.tm_sec
= tm
.sec
;
391 tmTimeOnly
.tm_mday
= 1; // any date will do, use 1976-01-01
392 tmTimeOnly
.tm_mon
= 0;
393 tmTimeOnly
.tm_year
= 76;
394 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
396 wxString tmp
, res
, fmt
;
397 for ( wxString::const_iterator p
= format
.begin(); p
!= format
.end(); ++p
)
399 if ( *p
!= wxT('%') )
407 // set the default format
408 switch ( (*++p
).GetValue() )
410 case wxT('Y'): // year has 4 digits
411 case wxT('z'): // time zone as well
415 case wxT('j'): // day of year has 3 digits
416 case wxT('l'): // milliseconds have 3 digits
420 case wxT('w'): // week day as number has only one
425 // it's either another valid format specifier in which case
426 // the format is "%02d" (for all the rest) or we have the
427 // field width preceding the format in which case it will
428 // override the default format anyhow
437 // start of the format specification
438 switch ( (*p
).GetValue() )
440 case wxT('a'): // a weekday name
442 // second parameter should be true for abbreviated names
443 res
+= GetWeekDayName(tm
.GetWeekDay(),
444 *p
== wxT('a') ? Name_Abbr
: Name_Full
);
447 case wxT('b'): // a month name
449 res
+= GetMonthName(tm
.mon
,
450 *p
== wxT('b') ? Name_Abbr
: Name_Full
);
453 case wxT('c'): // locale default date and time representation
454 case wxT('x'): // locale default date representation
455 #ifdef wxHAS_STRFTIME
457 // the problem: there is no way to know what do these format
458 // specifications correspond to for the current locale.
460 // the solution: use a hack and still use strftime(): first
461 // find the YEAR which is a year in the strftime() range (1970
462 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
463 // of the real year. Then make a copy of the format and
464 // replace all occurrences of YEAR in it with some unique
465 // string not appearing anywhere else in it, then use
466 // strftime() to format the date in year YEAR and then replace
467 // YEAR back by the real year and the unique replacement
468 // string back with YEAR. Notice that "all occurrences of YEAR"
469 // means all occurrences of 4 digit as well as 2 digit form!
471 // the bugs: we assume that neither of %c nor %x contains any
472 // fields which may change between the YEAR and real year. For
473 // example, the week number (%U, %W) and the day number (%j)
474 // will change if one of these years is leap and the other one
477 // find the YEAR: normally, for any year X, Jan 1 of the
478 // year X + 28 is the same weekday as Jan 1 of X (because
479 // the weekday advances by 1 for each normal X and by 2
480 // for each leap X, hence by 5 every 4 years or by 35
481 // which is 0 mod 7 every 28 years) but this rule breaks
482 // down if there are years between X and Y which are
483 // divisible by 4 but not leap (i.e. divisible by 100 but
484 // not 400), hence the correction.
486 int yearReal
= GetYear(tz
);
487 int mod28
= yearReal
% 28;
489 // be careful to not go too far - we risk to leave the
494 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
498 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
501 int nCentury
= year
/ 100,
502 nCenturyReal
= yearReal
/ 100;
504 // need to adjust for the years divisble by 400 which are
505 // not leap but are counted like leap ones if we just take
506 // the number of centuries in between for nLostWeekDays
507 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
508 (nCentury
/ 4 - nCenturyReal
/ 4);
510 // we have to gain back the "lost" weekdays: note that the
511 // effect of this loop is to not do anything to
512 // nLostWeekDays (which we won't use any more), but to
513 // (indirectly) set the year correctly
514 while ( (nLostWeekDays
% 7) != 0 )
516 nLostWeekDays
+= (year
++ % 4) ? 1 : 2;
519 // finally move the year below 2000 so that the 2-digit
520 // year number can never match the month or day of the
521 // month when we do the replacements below
525 wxASSERT_MSG( year
>= 1970 && year
< 2000,
526 wxT("logic error in wxDateTime::Format") );
529 // use strftime() to format the same date but in supported
532 // NB: we assume that strftime() doesn't check for the
533 // date validity and will happily format the date
534 // corresponding to Feb 29 of a non leap year (which
535 // may happen if yearReal was leap and year is not)
536 struct tm tmAdjusted
;
538 tmAdjusted
.tm_hour
= tm
.hour
;
539 tmAdjusted
.tm_min
= tm
.min
;
540 tmAdjusted
.tm_sec
= tm
.sec
;
541 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
542 tmAdjusted
.tm_yday
= GetDayOfYear();
543 tmAdjusted
.tm_mday
= tm
.mday
;
544 tmAdjusted
.tm_mon
= tm
.mon
;
545 tmAdjusted
.tm_year
= year
- 1900;
546 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
547 wxString str
= CallStrftime(*p
== wxT('c') ? wxT("%c")
551 // now replace the replacement year with the real year:
552 // notice that we have to replace the 4 digit year with
553 // a unique string not appearing in strftime() output
554 // first to prevent the 2 digit year from matching any
555 // substring of the 4 digit year (but any day, month,
556 // hours or minutes components should be safe because
557 // they are never in 70-99 range)
558 wxString
replacement("|");
559 while ( str
.find(replacement
) != wxString::npos
)
562 str
.Replace(wxString::Format("%d", year
),
564 str
.Replace(wxString::Format("%d", year
% 100),
565 wxString::Format("%d", yearReal
% 100));
566 str
.Replace(replacement
,
567 wxString::Format("%d", yearReal
));
571 #else // !wxHAS_STRFTIME
572 // Use "%m/%d/%y %H:%M:%S" format instead
573 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
574 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
575 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
578 case wxT('d'): // day of a month (01-31)
579 res
+= wxString::Format(fmt
, tm
.mday
);
582 case wxT('H'): // hour in 24h format (00-23)
583 res
+= wxString::Format(fmt
, tm
.hour
);
586 case wxT('I'): // hour in 12h format (01-12)
588 // 24h -> 12h, 0h -> 12h too
589 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
590 : tm
.hour
? tm
.hour
: 12;
591 res
+= wxString::Format(fmt
, hour12
);
595 case wxT('j'): // day of the year
596 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
599 case wxT('l'): // milliseconds (NOT STANDARD)
600 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
603 case wxT('m'): // month as a number (01-12)
604 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
607 case wxT('M'): // minute as a decimal number (00-59)
608 res
+= wxString::Format(fmt
, tm
.min
);
611 case wxT('p'): // AM or PM string
612 #ifdef wxHAS_STRFTIME
613 res
+= CallStrftime(wxT("%p"), &tmTimeOnly
);
614 #else // !wxHAS_STRFTIME
615 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
616 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
619 case wxT('S'): // second as a decimal number (00-61)
620 res
+= wxString::Format(fmt
, tm
.sec
);
623 case wxT('U'): // week number in the year (Sunday 1st week day)
624 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
627 case wxT('W'): // week number in the year (Monday 1st week day)
628 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
631 case wxT('w'): // weekday as a number (0-6), Sunday = 0
632 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
635 // case wxT('x'): -- handled with "%c"
637 case wxT('X'): // locale default time representation
638 // just use strftime() to format the time for us
639 #ifdef wxHAS_STRFTIME
640 res
+= CallStrftime(wxT("%X"), &tmTimeOnly
);
641 #else // !wxHAS_STRFTIME
642 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
643 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
646 case wxT('y'): // year without century (00-99)
647 res
+= wxString::Format(fmt
, tm
.year
% 100);
650 case wxT('Y'): // year with century
651 res
+= wxString::Format(fmt
, tm
.year
);
654 case wxT('z'): // time zone as [-+]HHMM
656 int ofs
= tz
.GetOffset();
658 // The time zone offset does not include the DST, but
659 // we do need to take it into account when showing the
660 // time in the local time zone to the user.
661 if ( ofs
== -wxGetTimeZone() && IsDST() == 1 )
663 // FIXME: As elsewhere in wxDateTime, we assume
664 // that the DST is always 1 hour, but this is not
679 // Converts seconds to HHMM representation.
680 res
+= wxString::Format(fmt
,
681 100*(ofs
/3600) + (ofs
/60)%60
);
685 case wxT('Z'): // timezone name
686 #ifdef wxHAS_STRFTIME
687 res
+= CallStrftime(wxT("%Z"), &tmTimeOnly
);
692 // is it the format width?
694 *p
== wxT('-') || *p
== wxT('+') ||
695 *p
== wxT(' ') || wxIsdigit(*p
);
703 // we've only got the flags and width so far in fmt
704 fmt
.Prepend(wxT('%'));
705 fmt
.Append(wxT('d'));
712 // no, it wasn't the width
713 wxFAIL_MSG(wxT("unknown format specifier"));
715 // fall through and just copy it nevertheless
717 case wxT('%'): // a percent sign
721 case 0: // the end of string
722 wxFAIL_MSG(wxT("missing format at the end of string"));
724 // just put the '%' which was the last char in format
734 // this function parses a string in (strict) RFC 822 format: see the section 5
735 // of the RFC for the detailed description, but briefly it's something of the
736 // form "Sat, 18 Dec 1999 00:48:30 +0100"
738 // this function is "strict" by design - it must reject anything except true
739 // RFC822 time specs.
741 wxDateTime::ParseRfc822Date(const wxString
& date
, wxString::const_iterator
*end
)
743 const wxString::const_iterator pEnd
= date
.end();
744 wxString::const_iterator p
= date
.begin();
747 const wxDateTime::WeekDay
748 wd
= GetWeekDayFromName(p
, pEnd
, Name_Abbr
, DateLang_English
);
749 if ( wd
== Inv_WeekDay
)
751 //else: ignore week day for now, we could also check that it really
752 // corresponds to the specified date
754 // 2. separating comma
755 if ( *p
++ != ',' || *p
++ != ' ' )
759 if ( !wxIsdigit(*p
) )
762 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - '0');
766 day
= (wxDateTime_t
)(day
+ (*p
++ - '0'));
773 const Month mon
= GetMonthFromName(p
, pEnd
, Name_Abbr
, DateLang_English
);
774 if ( mon
== Inv_Month
)
781 if ( !wxIsdigit(*p
) )
784 int year
= *p
++ - '0';
785 if ( !wxIsdigit(*p
) ) // should have at least 2 digits in the year
791 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
797 if ( !wxIsdigit(*p
) )
799 // no 3 digit years please
810 // 6. time in hh:mm:ss format with seconds being optional
811 if ( !wxIsdigit(*p
) )
814 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - '0');
816 if ( !wxIsdigit(*p
) )
820 hour
= (wxDateTime_t
)(hour
+ (*p
++ - '0'));
825 if ( !wxIsdigit(*p
) )
828 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - '0');
830 if ( !wxIsdigit(*p
) )
834 min
+= (wxDateTime_t
)(*p
++ - '0');
836 wxDateTime_t sec
= 0;
840 if ( !wxIsdigit(*p
) )
843 sec
= (wxDateTime_t
)(*p
++ - '0');
845 if ( !wxIsdigit(*p
) )
849 sec
+= (wxDateTime_t
)(*p
++ - '0');
855 // 7. now the interesting part: the timezone
856 int offset
= 0; // just to suppress warnings
857 if ( *p
== '-' || *p
== '+' )
859 // the explicit offset given: it has the form of hhmm
860 bool plus
= *p
++ == '+';
862 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
867 offset
= MIN_PER_HOUR
*(10*(*p
- '0') + (*(p
+ 1) - '0'));
871 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
875 offset
+= 10*(*p
- '0') + (*(p
+ 1) - '0');
884 // the symbolic timezone given: may be either military timezone or one
885 // of standard abbreviations
888 // military: Z = UTC, J unused, A = -1, ..., Y = +12
889 static const int offsets
[26] =
891 //A B C D E F G H I J K L M
892 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
893 //N O P R Q S T U V W Z Y Z
894 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
897 if ( *p
< wxT('A') || *p
> wxT('Z') || *p
== wxT('J') )
900 offset
= offsets
[*p
++ - 'A'];
905 const wxString
tz(p
, date
.end());
906 if ( tz
== wxT("UT") || tz
== wxT("UTC") || tz
== wxT("GMT") )
908 else if ( tz
== wxT("AST") )
910 else if ( tz
== wxT("ADT") )
912 else if ( tz
== wxT("EST") )
914 else if ( tz
== wxT("EDT") )
916 else if ( tz
== wxT("CST") )
918 else if ( tz
== wxT("CDT") )
920 else if ( tz
== wxT("MST") )
922 else if ( tz
== wxT("MDT") )
924 else if ( tz
== wxT("PST") )
926 else if ( tz
== wxT("PDT") )
935 offset
*= MIN_PER_HOUR
;
939 // the spec was correct, construct the date from the values we found
940 Set(day
, mon
, year
, hour
, min
, sec
);
941 MakeFromTimezone(TimeZone::Make(offset
*SEC_PER_MIN
));
949 const char* wxDateTime::ParseRfc822Date(const char* date
)
951 wxString::const_iterator end
;
952 wxString
dateStr(date
);
953 if ( !ParseRfc822Date(dateStr
, &end
) )
956 return date
+ dateStr
.IterOffsetInMBStr(end
);
959 const wchar_t* wxDateTime::ParseRfc822Date(const wchar_t* date
)
961 wxString::const_iterator end
;
962 wxString
dateStr(date
);
963 if ( !ParseRfc822Date(dateStr
, &end
) )
966 return date
+ (end
- dateStr
.begin());
970 wxDateTime::ParseFormat(const wxString
& date
,
971 const wxString
& format
,
972 const wxDateTime
& dateDef
,
973 wxString::const_iterator
*endParse
)
975 wxCHECK_MSG( !format
.empty(), false, "format can't be empty" );
976 wxCHECK_MSG( endParse
, false, "end iterator pointer must be specified" );
981 // what fields have we found?
982 bool haveWDay
= false,
992 bool hourIsIn12hFormat
= false, // or in 24h one?
993 isPM
= false; // AM by default
995 bool haveTimeZone
= false;
997 // and the value of the items we have (init them to get rid of warnings)
998 wxDateTime_t msec
= 0,
1002 WeekDay wday
= Inv_WeekDay
;
1003 wxDateTime_t yday
= 0,
1005 wxDateTime::Month mon
= Inv_Month
;
1007 long timeZone
= 0; // time zone in seconds as expected in Tm structure
1009 wxString::const_iterator input
= date
.begin();
1010 const wxString::const_iterator end
= date
.end();
1011 for ( wxString::const_iterator fmt
= format
.begin(); fmt
!= format
.end(); ++fmt
)
1013 if ( *fmt
!= wxT('%') )
1015 if ( wxIsspace(*fmt
) )
1017 // a white space in the format string matches 0 or more white
1018 // spaces in the input
1019 while ( input
!= end
&& wxIsspace(*input
) )
1026 // any other character (not whitespace, not '%') must be
1027 // matched by itself in the input
1028 if ( input
== end
|| *input
++ != *fmt
)
1035 // done with this format char
1039 // start of a format specification
1041 // parse the optional width
1043 while ( wxIsdigit(*++fmt
) )
1046 width
+= *fmt
- '0';
1049 // the default widths for the various fields
1052 switch ( (*fmt
).GetValue() )
1054 case wxT('Y'): // year has 4 digits
1058 case wxT('j'): // day of year has 3 digits
1059 case wxT('l'): // milliseconds have 3 digits
1063 case wxT('w'): // week day as number has only one
1068 // default for all other fields
1073 // then the format itself
1074 switch ( (*fmt
).GetValue() )
1076 case wxT('a'): // a weekday name
1079 wday
= GetWeekDayFromName
1082 *fmt
== 'a' ? Name_Abbr
: Name_Full
,
1085 if ( wday
== Inv_WeekDay
)
1094 case wxT('b'): // a month name
1097 mon
= GetMonthFromName
1100 *fmt
== 'b' ? Name_Abbr
: Name_Full
,
1103 if ( mon
== Inv_Month
)
1112 case wxT('c'): // locale default date and time representation
1118 fmtDateTime
= wxLocale::GetInfo(wxLOCALE_DATE_TIME_FMT
);
1119 if ( !fmtDateTime
.empty() )
1120 dt
= ParseFormatAt(input
, end
, fmtDateTime
);
1121 #endif // wxUSE_INTL
1122 if ( !dt
.IsValid() )
1124 // also try the format which corresponds to ctime()
1125 // output (i.e. the "C" locale default)
1126 dt
= ParseFormatAt(input
, end
, wxS("%a %b %d %H:%M:%S %Y"));
1129 if ( !dt
.IsValid() )
1131 // and finally also the two generic date/time formats
1132 dt
= ParseFormatAt(input
, end
, wxS("%x %X"), wxS("%X %x"));
1135 if ( !dt
.IsValid() )
1138 const Tm tm
= dt
.GetTm();
1148 haveDay
= haveMon
= haveYear
=
1149 haveHour
= haveMin
= haveSec
= true;
1153 case wxT('d'): // day of a month (01-31)
1154 case 'e': // day of a month (1-31) (GNU extension)
1155 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1156 (num
> 31) || (num
< 1) )
1162 // we can't check whether the day range is correct yet, will
1163 // do it later - assume ok for now
1165 mday
= (wxDateTime_t
)num
;
1168 case wxT('H'): // hour in 24h format (00-23)
1169 if ( !GetNumericToken(width
, input
, end
, &num
) || (num
> 23) )
1176 hour
= (wxDateTime_t
)num
;
1179 case wxT('I'): // hour in 12h format (01-12)
1180 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1181 !num
|| (num
> 12) )
1188 hourIsIn12hFormat
= true;
1189 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
1192 case wxT('j'): // day of the year
1193 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1194 !num
|| (num
> 366) )
1201 yday
= (wxDateTime_t
)num
;
1204 case wxT('l'): // milliseconds (0-999)
1205 if ( !GetNumericToken(width
, input
, end
, &num
) )
1209 msec
= (wxDateTime_t
)num
;
1212 case wxT('m'): // month as a number (01-12)
1213 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1214 !num
|| (num
> 12) )
1221 mon
= (Month
)(num
- 1);
1224 case wxT('M'): // minute as a decimal number (00-59)
1225 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1233 min
= (wxDateTime_t
)num
;
1236 case wxT('p'): // AM or PM string
1239 GetAmPmStrings(&am
, &pm
);
1241 // we can never match %p in locales which don't use AM/PM
1242 if ( am
.empty() || pm
.empty() )
1245 const size_t pos
= input
- date
.begin();
1246 if ( date
.compare(pos
, pm
.length(), pm
) == 0 )
1249 input
+= pm
.length();
1251 else if ( date
.compare(pos
, am
.length(), am
) == 0 )
1253 input
+= am
.length();
1262 case wxT('r'): // time as %I:%M:%S %p
1265 if ( !dt
.ParseFormat(wxString(input
, end
),
1266 wxS("%I:%M:%S %p"), &input
) )
1269 haveHour
= haveMin
= haveSec
= true;
1271 const Tm tm
= dt
.GetTm();
1278 case wxT('R'): // time as %H:%M
1281 dt
= ParseFormatAt(input
, end
, wxS("%H:%M"));
1282 if ( !dt
.IsValid() )
1288 const Tm tm
= dt
.GetTm();
1294 case wxT('S'): // second as a decimal number (00-61)
1295 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1303 sec
= (wxDateTime_t
)num
;
1306 case wxT('T'): // time as %H:%M:%S
1309 dt
= ParseFormatAt(input
, end
, wxS("%H:%M:%S"));
1310 if ( !dt
.IsValid() )
1317 const Tm tm
= dt
.GetTm();
1324 case wxT('w'): // weekday as a number (0-6), Sunday = 0
1325 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1333 wday
= (WeekDay
)num
;
1336 case wxT('x'): // locale default date representation
1340 fmtDate
= wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT
),
1341 fmtDateAlt
= wxLocale::GetInfo(wxLOCALE_LONG_DATE_FMT
);
1342 #else // !wxUSE_INTL
1343 wxString fmtDate
, fmtDateAlt
;
1344 #endif // wxUSE_INTL/!wxUSE_INTL
1345 if ( fmtDate
.empty() )
1347 if ( IsWestEuropeanCountry(GetCountry()) ||
1348 GetCountry() == Russia
)
1350 fmtDate
= wxS("%d/%m/%Y");
1351 fmtDateAlt
= wxS("%m/%d/%Y");
1355 fmtDate
= wxS("%m/%d/%Y");
1356 fmtDateAlt
= wxS("%d/%m/%Y");
1361 dt
= ParseFormatAt(input
, end
, fmtDate
, fmtDateAlt
);
1363 if ( !dt
.IsValid() )
1365 // try with short years too
1366 fmtDate
.Replace("%Y","%y");
1367 fmtDateAlt
.Replace("%Y","%y");
1368 dt
= ParseFormatAt(input
, end
, fmtDate
, fmtDateAlt
);
1370 if ( !dt
.IsValid() )
1374 const Tm tm
= dt
.GetTm();
1387 case wxT('X'): // locale default time representation
1390 wxString fmtTime
= wxLocale::GetInfo(wxLOCALE_TIME_FMT
),
1392 #else // !wxUSE_INTL
1393 wxString fmtTime
, fmtTimeAlt
;
1394 #endif // wxUSE_INTL/!wxUSE_INTL
1395 if ( fmtTime
.empty() )
1397 // try to parse what follows as "%H:%M:%S" and, if this
1398 // fails, as "%I:%M:%S %p" - this should catch the most
1405 dt
= ParseFormatAt(input
, end
, fmtTime
, fmtTimeAlt
);
1406 if ( !dt
.IsValid() )
1413 const Tm tm
= dt
.GetTm();
1420 case wxT('y'): // year without century (00-99)
1421 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1430 // TODO should have an option for roll over date instead of
1431 // hard coding it here
1432 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
1435 case wxT('Y'): // year with century
1436 if ( !GetNumericToken(width
, input
, end
, &num
) )
1443 year
= (wxDateTime_t
)num
;
1448 // check that we have something here at all
1452 // and then check that it's either plus or minus sign
1454 if ( *input
== wxT('-') )
1456 else if ( *input
== wxT('+') )
1459 return false; // no match
1461 // here should follow 4 digits HHMM
1463 unsigned long tzHourMin
;
1464 if ( !GetNumericToken(4, input
, end
, &tzHourMin
) )
1465 return false; // no match
1467 const unsigned hours
= tzHourMin
/ 100;
1468 const unsigned minutes
= tzHourMin
% 100;
1470 if ( hours
> 12 || minutes
> 59 )
1471 return false; // bad format
1473 timeZone
= 3600*hours
+ 60*minutes
;
1475 timeZone
= -timeZone
;
1477 haveTimeZone
= true;
1481 case wxT('Z'): // timezone name
1482 // FIXME: currently we just ignore everything that looks like a
1484 GetAlphaToken(input
, end
);
1487 case wxT('%'): // a percent sign
1488 if ( input
== end
|| *input
++ != wxT('%') )
1495 case 0: // the end of string
1496 wxFAIL_MSG(wxT("unexpected format end"));
1500 default: // not a known format spec
1505 // format matched, try to construct a date from what we have now
1507 if ( dateDef
.IsValid() )
1509 // take this date as default
1510 tmDef
= dateDef
.GetTm();
1512 else if ( IsValid() )
1514 // if this date is valid, don't change it
1519 // no default and this date is invalid - fall back to Today()
1520 tmDef
= Today().GetTm();
1536 // TODO we don't check here that the values are consistent, if both year
1537 // day and month/day were found, we just ignore the year day and we
1538 // also always ignore the week day
1541 if ( mday
> GetNumberOfDays(tm
.mon
, tm
.year
) )
1546 else if ( haveYDay
)
1548 if ( yday
> GetNumberOfDays(tm
.year
) )
1551 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
1558 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
1560 // translate to 24hour format
1563 //else: either already in 24h format or no translation needed
1586 // If a time zone was specified and it is not the local time zone, we need
1587 // to shift the time accordingly.
1589 // Note that avoiding the call to MakeFromTimeZone is necessary to avoid
1591 if ( haveTimeZone
&& timeZone
!= -wxGetTimeZone() )
1592 MakeFromTimezone(timeZone
);
1594 // finally check that the week day is consistent -- if we had it
1595 if ( haveWDay
&& GetWeekDay() != wday
)
1604 wxDateTime::ParseFormat(const char* date
,
1605 const wxString
& format
,
1606 const wxDateTime
& dateDef
)
1608 wxString::const_iterator end
;
1609 wxString
dateStr(date
);
1610 if ( !ParseFormat(dateStr
, format
, dateDef
, &end
) )
1613 return date
+ dateStr
.IterOffsetInMBStr(end
);
1617 wxDateTime::ParseFormat(const wchar_t* date
,
1618 const wxString
& format
,
1619 const wxDateTime
& dateDef
)
1621 wxString::const_iterator end
;
1622 wxString
dateStr(date
);
1623 if ( !ParseFormat(dateStr
, format
, dateDef
, &end
) )
1626 return date
+ (end
- dateStr
.begin());
1630 wxDateTime::ParseDateTime(const wxString
& date
, wxString::const_iterator
*end
)
1632 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1638 wxString::const_iterator
1643 // If we got a date in the beginning, see if there is a time specified
1645 if ( dtDate
.ParseDate(date
, &endDate
) )
1647 // Skip spaces, as the ParseTime() function fails on spaces
1648 while ( endDate
!= date
.end() && wxIsspace(*endDate
) )
1651 const wxString
timestr(endDate
, date
.end());
1652 if ( !dtTime
.ParseTime(timestr
, &endTime
) )
1655 endBoth
= endDate
+ (endTime
- timestr
.begin());
1657 else // no date in the beginning
1659 // check if we have a time followed by a date
1660 if ( !dtTime
.ParseTime(date
, &endTime
) )
1663 while ( endTime
!= date
.end() && wxIsspace(*endTime
) )
1666 const wxString
datestr(endTime
, date
.end());
1667 if ( !dtDate
.ParseDate(datestr
, &endDate
) )
1670 endBoth
= endTime
+ (endDate
- datestr
.begin());
1673 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
1674 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
1675 dtTime
.GetMillisecond());
1682 const char* wxDateTime::ParseDateTime(const char* date
)
1684 wxString::const_iterator end
;
1685 wxString
dateStr(date
);
1686 if ( !ParseDateTime(dateStr
, &end
) )
1689 return date
+ dateStr
.IterOffsetInMBStr(end
);
1692 const wchar_t* wxDateTime::ParseDateTime(const wchar_t* date
)
1694 wxString::const_iterator end
;
1695 wxString
dateStr(date
);
1696 if ( !ParseDateTime(dateStr
, &end
) )
1699 return date
+ (end
- dateStr
.begin());
1703 wxDateTime::ParseDate(const wxString
& date
, wxString::const_iterator
*end
)
1705 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1707 // this is a simplified version of ParseDateTime() which understands only
1708 // "today" (for wxDate compatibility) and digits only otherwise (and not
1709 // all esoteric constructions ParseDateTime() knows about)
1711 const wxString::const_iterator pBegin
= date
.begin();
1712 const wxString::const_iterator pEnd
= date
.end();
1714 wxString::const_iterator p
= pBegin
;
1715 while ( p
!= pEnd
&& wxIsspace(*p
) )
1718 // some special cases
1722 int dayDiffFromToday
;
1725 { wxTRANSLATE("today"), 0 },
1726 { wxTRANSLATE("yesterday"), -1 },
1727 { wxTRANSLATE("tomorrow"), 1 },
1730 const size_t lenRest
= pEnd
- p
;
1731 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
1733 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
1734 size_t len
= dateStr
.length();
1736 if ( len
> lenRest
)
1739 const wxString::const_iterator pEndStr
= p
+ len
;
1740 if ( wxString(p
, pEndStr
).CmpNoCase(dateStr
) == 0 )
1742 // nothing can follow this, so stop here
1746 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
1748 if ( dayDiffFromToday
)
1750 *this += wxDateSpan::Days(dayDiffFromToday
);
1759 // We try to guess what we have here: for each new (numeric) token, we
1760 // determine if it can be a month, day or a year. Of course, there is an
1761 // ambiguity as some numbers may be days as well as months, so we also
1762 // have the ability to back track.
1765 bool haveDay
= false, // the months day?
1766 haveWDay
= false, // the day of week?
1767 haveMon
= false, // the month?
1768 haveYear
= false; // the year?
1770 bool monWasNumeric
= false; // was month specified as a number?
1772 // and the value of the items we have (init them to get rid of warnings)
1773 WeekDay wday
= Inv_WeekDay
;
1774 wxDateTime_t day
= 0;
1775 wxDateTime::Month mon
= Inv_Month
;
1778 // tokenize the string
1781 // skip white space and date delimiters
1782 if ( wxStrchr(".,/-\t\r\n ", *p
) )
1788 // modify copy of the iterator as we're not sure if the next token is
1789 // still part of the date at all
1790 wxString::const_iterator pCopy
= p
;
1792 // we can have either alphabetic or numeric token, start by testing if
1795 if ( GetNumericToken(10 /* max length */, pCopy
, pEnd
, &val
) )
1797 // guess what this number is
1803 if ( !haveMon
&& val
> 0 && val
<= 12 )
1805 // assume it is month
1808 else // not the month
1812 // this can only be the year
1815 else // may be either day or year
1817 // use a leap year if we don't have the year yet to allow
1818 // dates like 2/29/1976 which would be rejected otherwise
1819 wxDateTime_t max_days
= (wxDateTime_t
)(
1821 ? GetNumberOfDays(mon
, haveYear
? year
: 1976)
1826 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
1831 else // yes, suppose it's the day
1845 year
= (wxDateTime_t
)val
;
1854 day
= (wxDateTime_t
)val
;
1859 monWasNumeric
= true;
1861 mon
= (Month
)(val
- 1);
1864 else // not a number
1866 // be careful not to overwrite the current mon value
1867 Month mon2
= GetMonthFromName
1870 Name_Full
| Name_Abbr
,
1871 DateLang_Local
| DateLang_English
1873 if ( mon2
!= Inv_Month
)
1878 // but we already have a month - maybe we guessed wrong
1879 // when we had interpreted that numeric value as a month
1880 // and it was the day number instead?
1881 if ( haveDay
|| !monWasNumeric
)
1884 // assume we did and change our mind: reinterpret the month
1885 // value as a day (notice that there is no need to check
1886 // that it is valid as month values are always < 12, but
1887 // the days are counted from 1 unlike the months)
1888 day
= (wxDateTime_t
)(mon
+ 1);
1896 else // not a valid month name
1898 WeekDay wday2
= GetWeekDayFromName
1901 Name_Full
| Name_Abbr
,
1902 DateLang_Local
| DateLang_English
1904 if ( wday2
!= Inv_WeekDay
)
1914 else // not a valid weekday name
1917 static const char *const ordinals
[] =
1919 wxTRANSLATE("first"),
1920 wxTRANSLATE("second"),
1921 wxTRANSLATE("third"),
1922 wxTRANSLATE("fourth"),
1923 wxTRANSLATE("fifth"),
1924 wxTRANSLATE("sixth"),
1925 wxTRANSLATE("seventh"),
1926 wxTRANSLATE("eighth"),
1927 wxTRANSLATE("ninth"),
1928 wxTRANSLATE("tenth"),
1929 wxTRANSLATE("eleventh"),
1930 wxTRANSLATE("twelfth"),
1931 wxTRANSLATE("thirteenth"),
1932 wxTRANSLATE("fourteenth"),
1933 wxTRANSLATE("fifteenth"),
1934 wxTRANSLATE("sixteenth"),
1935 wxTRANSLATE("seventeenth"),
1936 wxTRANSLATE("eighteenth"),
1937 wxTRANSLATE("nineteenth"),
1938 wxTRANSLATE("twentieth"),
1939 // that's enough - otherwise we'd have problems with
1940 // composite (or not) ordinals
1944 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
1946 const wxString ord
= wxGetTranslation(ordinals
[n
]);
1947 const size_t len
= ord
.length();
1948 if ( date
.compare(p
- pBegin
, len
, ord
) == 0 )
1955 if ( n
== WXSIZEOF(ordinals
) )
1957 // stop here - something unknown
1964 // don't try anything here (as in case of numeric day
1965 // above) - the symbolic day spec should always
1966 // precede the month/year
1972 day
= (wxDateTime_t
)(n
+ 1);
1977 // advance iterator past a successfully parsed token
1981 // either no more tokens or the scan was stopped by something we couldn't
1982 // parse - in any case, see if we can construct a date from what we have
1983 if ( !haveDay
&& !haveWDay
)
1986 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
1987 !(haveDay
&& haveMon
&& haveYear
) )
1989 // without adjectives (which we don't support here) the week day only
1990 // makes sense completely separately or with the full date
1991 // specification (what would "Wed 1999" mean?)
1995 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
1997 // may be we have month and day instead of day and year?
1998 if ( haveDay
&& !haveMon
)
2002 // exchange day and month
2003 mon
= (wxDateTime::Month
)(day
- 1);
2005 // we're in the current year then
2006 if ( (year
> 0) && (year
<= (int)GetNumberOfDays(mon
, Inv_Year
)) )
2008 day
= (wxDateTime_t
)year
;
2013 //else: no, can't exchange, leave haveMon == false
2023 mon
= GetCurrentMonth();
2028 year
= GetCurrentYear();
2033 // normally we check the day above but the check is optimistic in case
2034 // we find the day before its month/year so we have to redo it now
2035 if ( day
> GetNumberOfDays(mon
, year
) )
2038 Set(day
, mon
, year
);
2042 // check that it is really the same
2043 if ( GetWeekDay() != wday
)
2051 SetToWeekDayInSameWeek(wday
);
2059 const char* wxDateTime::ParseDate(const char* date
)
2061 wxString::const_iterator end
;
2062 wxString
dateStr(date
);
2063 if ( !ParseDate(dateStr
, &end
) )
2066 return date
+ dateStr
.IterOffsetInMBStr(end
);
2069 const wchar_t* wxDateTime::ParseDate(const wchar_t* date
)
2071 wxString::const_iterator end
;
2072 wxString
dateStr(date
);
2073 if ( !ParseDate(dateStr
, &end
) )
2076 return date
+ (end
- dateStr
.begin());
2080 wxDateTime::ParseTime(const wxString
& time
, wxString::const_iterator
*end
)
2082 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
2084 // first try some extra things
2091 { wxTRANSLATE("noon"), 12 },
2092 { wxTRANSLATE("midnight"), 00 },
2096 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
2098 const wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
2099 if ( timeString
.CmpNoCase(wxString(time
, timeString
.length())) == 0 )
2101 // casts required by DigitalMars
2102 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
2105 *end
= time
.begin() + timeString
.length();
2111 // try all time formats we may think about in the order from longest to
2113 static const char *const timeFormats
[] =
2115 "%I:%M:%S %p", // 12hour with AM/PM
2116 "%H:%M:%S", // could be the same or 24 hour one so try it too
2117 "%I:%M %p", // 12hour with AM/PM but without seconds
2118 "%H:%M", // and a possibly 24 hour version without seconds
2119 "%I %p", // just hour with AM/AM
2120 "%H", // just hour in 24 hour version
2121 "%X", // possibly something from above or maybe something
2122 // completely different -- try it last
2124 // TODO: parse timezones
2127 for ( size_t nFmt
= 0; nFmt
< WXSIZEOF(timeFormats
); nFmt
++ )
2129 if ( ParseFormat(time
, timeFormats
[nFmt
], end
) )
2136 const char* wxDateTime::ParseTime(const char* date
)
2138 wxString::const_iterator end
;
2139 wxString
dateStr(date
);
2140 if ( !ParseTime(dateStr
, &end
) )
2143 return date
+ dateStr
.IterOffsetInMBStr(end
);
2146 const wchar_t* wxDateTime::ParseTime(const wchar_t* date
)
2148 wxString::const_iterator end
;
2149 wxString
dateStr(date
);
2150 if ( !ParseTime(dateStr
, &end
) )
2153 return date
+ (end
- dateStr
.begin());
2156 // ----------------------------------------------------------------------------
2157 // Workdays and holidays support
2158 // ----------------------------------------------------------------------------
2160 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
2162 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
2165 // ============================================================================
2167 // ============================================================================
2169 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
2172 return ds1
.Multiply(n
);
2175 // ============================================================================
2177 // ============================================================================
2179 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
2181 return wxTimeSpan(ts
).Multiply(n
);
2184 // this enum is only used in wxTimeSpan::Format() below but we can't declare
2185 // it locally to the method as it provokes an internal compiler error in egcs
2186 // 2.91.60 when building with -O2
2197 // not all strftime(3) format specifiers make sense here because, for example,
2198 // a time span doesn't have a year nor a timezone
2200 // Here are the ones which are supported (all of them are supported by strftime
2202 // %H hour in 24 hour format
2203 // %M minute (00 - 59)
2204 // %S second (00 - 59)
2207 // Also, for MFC CTimeSpan compatibility, we support
2208 // %D number of days
2210 // And, to be better than MFC :-), we also have
2211 // %E number of wEeks
2212 // %l milliseconds (000 - 999)
2213 wxString
wxTimeSpan::Format(const wxString
& format
) const
2215 // we deal with only positive time spans here and just add the sign in
2216 // front for the negative ones
2219 wxString
str(Negate().Format(format
));
2223 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
2224 wxT("NULL format in wxTimeSpan::Format") );
2227 str
.Alloc(format
.length());
2229 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
2231 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
2232 // question is what should ts.Format("%S") do? The code here returns "3273"
2233 // in this case (i.e. the total number of seconds, not just seconds % 60)
2234 // because, for me, this call means "give me entire time interval in
2235 // seconds" and not "give me the seconds part of the time interval"
2237 // If we agree that it should behave like this, it is clear that the
2238 // interpretation of each format specifier depends on the presence of the
2239 // other format specs in the string: if there was "%H" before "%M", we
2240 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
2242 // we remember the most important unit found so far
2243 TimeSpanPart partBiggest
= Part_MSec
;
2245 for ( wxString::const_iterator pch
= format
.begin(); pch
!= format
.end(); ++pch
)
2249 if ( ch
== wxT('%') )
2251 // the start of the format specification of the printf() below
2252 wxString
fmtPrefix(wxT('%'));
2257 // the number of digits for the format string, 0 if unused
2258 unsigned digits
= 0;
2260 ch
= *++pch
; // get the format spec char
2264 wxFAIL_MSG( wxT("invalid format character") );
2270 // skip the part below switch
2275 if ( partBiggest
< Part_Day
)
2281 partBiggest
= Part_Day
;
2286 partBiggest
= Part_Week
;
2292 if ( partBiggest
< Part_Hour
)
2298 partBiggest
= Part_Hour
;
2305 n
= GetMilliseconds().ToLong();
2306 if ( partBiggest
< Part_MSec
)
2310 //else: no need to reset partBiggest to Part_MSec, it is
2311 // the least significant one anyhow
2318 if ( partBiggest
< Part_Min
)
2324 partBiggest
= Part_Min
;
2331 n
= GetSeconds().ToLong();
2332 if ( partBiggest
< Part_Sec
)
2338 partBiggest
= Part_Sec
;
2347 fmtPrefix
<< wxT("0") << digits
;
2350 str
+= wxString::Format(fmtPrefix
+ wxT("ld"), n
);
2354 // normal character, just copy
2362 #endif // wxUSE_DATETIME