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
;
294 if ( dt
.ParseFormat(str
, fmt
, &endParse
) ||
295 (!fmtAlt
.empty() && dt
.ParseFormat(str
, fmtAlt
, &endParse
)) )
297 p
+= endParse
- str
.begin();
299 //else: all formats failed
304 } // anonymous namespace
306 // ----------------------------------------------------------------------------
307 // wxDateTime to/from text representations
308 // ----------------------------------------------------------------------------
310 wxString
wxDateTime::Format(const wxString
& formatp
, const TimeZone
& tz
) const
312 wxCHECK_MSG( !formatp
.empty(), wxEmptyString
,
313 wxT("NULL format in wxDateTime::Format") );
315 wxString format
= formatp
;
317 format
.Replace("%c",wxLocale::GetInfo(wxLOCALE_DATE_TIME_FMT
));
318 format
.Replace("%x",wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT
));
319 format
.Replace("%X",wxLocale::GetInfo(wxLOCALE_TIME_FMT
));
321 // we have to use our own implementation if the date is out of range of
322 // strftime() or if we use non standard specifiers (notice that "%z" is
323 // special because it is de facto standard under Unix but is not supported
325 #ifdef wxHAS_STRFTIME
326 time_t time
= GetTicks();
328 if ( (time
!= (time_t)-1) && !wxStrstr(format
, wxT("%l"))
330 && !wxStrstr(format
, wxT("%z"))
337 if ( tz
.GetOffset() == -wxGetTimeZone() )
339 // we are working with local time
340 tm
= wxLocaltime_r(&time
, &tmstruct
);
342 // should never happen
343 wxCHECK_MSG( tm
, wxEmptyString
, wxT("wxLocaltime_r() failed") );
347 time
+= (int)tz
.GetOffset();
349 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
350 int time2
= (int) time
;
356 tm
= wxGmtime_r(&time
, &tmstruct
);
358 // should never happen
359 wxCHECK_MSG( tm
, wxEmptyString
, wxT("wxGmtime_r() failed") );
363 tm
= (struct tm
*)NULL
;
369 return CallStrftime(format
, tm
);
372 //else: use generic code below
373 #endif // wxHAS_STRFTIME
375 // we only parse ANSI C format specifications here, no POSIX 2
376 // complications, no GNU extensions but we do add support for a "%l" format
377 // specifier allowing to get the number of milliseconds
380 // used for calls to strftime() when we only deal with time
381 struct tm tmTimeOnly
;
382 memset(&tmTimeOnly
, 0, sizeof(tmTimeOnly
));
383 tmTimeOnly
.tm_hour
= tm
.hour
;
384 tmTimeOnly
.tm_min
= tm
.min
;
385 tmTimeOnly
.tm_sec
= tm
.sec
;
386 tmTimeOnly
.tm_mday
= 1; // any date will do, use 1976-01-01
387 tmTimeOnly
.tm_mon
= 0;
388 tmTimeOnly
.tm_year
= 76;
389 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
391 wxString tmp
, res
, fmt
;
392 for ( wxString::const_iterator p
= format
.begin(); p
!= format
.end(); ++p
)
394 if ( *p
!= wxT('%') )
402 // set the default format
403 switch ( (*++p
).GetValue() )
405 case wxT('Y'): // year has 4 digits
406 case wxT('z'): // time zone as well
410 case wxT('j'): // day of year has 3 digits
411 case wxT('l'): // milliseconds have 3 digits
415 case wxT('w'): // week day as number has only one
420 // it's either another valid format specifier in which case
421 // the format is "%02d" (for all the rest) or we have the
422 // field width preceding the format in which case it will
423 // override the default format anyhow
432 // start of the format specification
433 switch ( (*p
).GetValue() )
435 case wxT('a'): // a weekday name
437 // second parameter should be true for abbreviated names
438 res
+= GetWeekDayName(tm
.GetWeekDay(),
439 *p
== wxT('a') ? Name_Abbr
: Name_Full
);
442 case wxT('b'): // a month name
444 res
+= GetMonthName(tm
.mon
,
445 *p
== wxT('b') ? Name_Abbr
: Name_Full
);
448 case wxT('c'): // locale default date and time representation
449 case wxT('x'): // locale default date representation
450 #ifdef wxHAS_STRFTIME
452 // the problem: there is no way to know what do these format
453 // specifications correspond to for the current locale.
455 // the solution: use a hack and still use strftime(): first
456 // find the YEAR which is a year in the strftime() range (1970
457 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
458 // of the real year. Then make a copy of the format and
459 // replace all occurrences of YEAR in it with some unique
460 // string not appearing anywhere else in it, then use
461 // strftime() to format the date in year YEAR and then replace
462 // YEAR back by the real year and the unique replacement
463 // string back with YEAR. Notice that "all occurrences of YEAR"
464 // means all occurrences of 4 digit as well as 2 digit form!
466 // the bugs: we assume that neither of %c nor %x contains any
467 // fields which may change between the YEAR and real year. For
468 // example, the week number (%U, %W) and the day number (%j)
469 // will change if one of these years is leap and the other one
472 // find the YEAR: normally, for any year X, Jan 1 of the
473 // year X + 28 is the same weekday as Jan 1 of X (because
474 // the weekday advances by 1 for each normal X and by 2
475 // for each leap X, hence by 5 every 4 years or by 35
476 // which is 0 mod 7 every 28 years) but this rule breaks
477 // down if there are years between X and Y which are
478 // divisible by 4 but not leap (i.e. divisible by 100 but
479 // not 400), hence the correction.
481 int yearReal
= GetYear(tz
);
482 int mod28
= yearReal
% 28;
484 // be careful to not go too far - we risk to leave the
489 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
493 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
496 int nCentury
= year
/ 100,
497 nCenturyReal
= yearReal
/ 100;
499 // need to adjust for the years divisble by 400 which are
500 // not leap but are counted like leap ones if we just take
501 // the number of centuries in between for nLostWeekDays
502 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
503 (nCentury
/ 4 - nCenturyReal
/ 4);
505 // we have to gain back the "lost" weekdays: note that the
506 // effect of this loop is to not do anything to
507 // nLostWeekDays (which we won't use any more), but to
508 // (indirectly) set the year correctly
509 while ( (nLostWeekDays
% 7) != 0 )
511 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
514 // finally move the year below 2000 so that the 2-digit
515 // year number can never match the month or day of the
516 // month when we do the replacements below
520 wxASSERT_MSG( year
>= 1970 && year
< 2000,
521 wxT("logic error in wxDateTime::Format") );
524 // use strftime() to format the same date but in supported
527 // NB: we assume that strftime() doesn't check for the
528 // date validity and will happily format the date
529 // corresponding to Feb 29 of a non leap year (which
530 // may happen if yearReal was leap and year is not)
531 struct tm tmAdjusted
;
533 tmAdjusted
.tm_hour
= tm
.hour
;
534 tmAdjusted
.tm_min
= tm
.min
;
535 tmAdjusted
.tm_sec
= tm
.sec
;
536 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
537 tmAdjusted
.tm_yday
= GetDayOfYear();
538 tmAdjusted
.tm_mday
= tm
.mday
;
539 tmAdjusted
.tm_mon
= tm
.mon
;
540 tmAdjusted
.tm_year
= year
- 1900;
541 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
542 wxString str
= CallStrftime(*p
== wxT('c') ? wxT("%c")
546 // now replace the replacement year with the real year:
547 // notice that we have to replace the 4 digit year with
548 // a unique string not appearing in strftime() output
549 // first to prevent the 2 digit year from matching any
550 // substring of the 4 digit year (but any day, month,
551 // hours or minutes components should be safe because
552 // they are never in 70-99 range)
553 wxString
replacement("|");
554 while ( str
.find(replacement
) != wxString::npos
)
557 str
.Replace(wxString::Format("%d", year
),
559 str
.Replace(wxString::Format("%d", year
% 100),
560 wxString::Format("%d", yearReal
% 100));
561 str
.Replace(replacement
,
562 wxString::Format("%d", yearReal
));
566 #else // !wxHAS_STRFTIME
567 // Use "%m/%d/%y %H:%M:%S" format instead
568 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
569 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
570 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
573 case wxT('d'): // day of a month (01-31)
574 res
+= wxString::Format(fmt
, tm
.mday
);
577 case wxT('H'): // hour in 24h format (00-23)
578 res
+= wxString::Format(fmt
, tm
.hour
);
581 case wxT('I'): // hour in 12h format (01-12)
583 // 24h -> 12h, 0h -> 12h too
584 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
585 : tm
.hour
? tm
.hour
: 12;
586 res
+= wxString::Format(fmt
, hour12
);
590 case wxT('j'): // day of the year
591 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
594 case wxT('l'): // milliseconds (NOT STANDARD)
595 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
598 case wxT('m'): // month as a number (01-12)
599 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
602 case wxT('M'): // minute as a decimal number (00-59)
603 res
+= wxString::Format(fmt
, tm
.min
);
606 case wxT('p'): // AM or PM string
607 #ifdef wxHAS_STRFTIME
608 res
+= CallStrftime(wxT("%p"), &tmTimeOnly
);
609 #else // !wxHAS_STRFTIME
610 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
611 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
614 case wxT('S'): // second as a decimal number (00-61)
615 res
+= wxString::Format(fmt
, tm
.sec
);
618 case wxT('U'): // week number in the year (Sunday 1st week day)
619 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
622 case wxT('W'): // week number in the year (Monday 1st week day)
623 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
626 case wxT('w'): // weekday as a number (0-6), Sunday = 0
627 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
630 // case wxT('x'): -- handled with "%c"
632 case wxT('X'): // locale default time representation
633 // just use strftime() to format the time for us
634 #ifdef wxHAS_STRFTIME
635 res
+= CallStrftime(wxT("%X"), &tmTimeOnly
);
636 #else // !wxHAS_STRFTIME
637 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
638 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
641 case wxT('y'): // year without century (00-99)
642 res
+= wxString::Format(fmt
, tm
.year
% 100);
645 case wxT('Y'): // year with century
646 res
+= wxString::Format(fmt
, tm
.year
);
649 case wxT('z'): // time zone as [-+]HHMM
651 int ofs
= tz
.GetOffset();
662 // Converts seconds to HHMM representation.
663 res
+= wxString::Format(fmt
,
664 100*(ofs
/3600) + (ofs
/60)%60
);
668 case wxT('Z'): // timezone name
669 #ifdef wxHAS_STRFTIME
670 res
+= CallStrftime(wxT("%Z"), &tmTimeOnly
);
675 // is it the format width?
677 *p
== wxT('-') || *p
== wxT('+') ||
678 *p
== wxT(' ') || wxIsdigit(*p
);
686 // we've only got the flags and width so far in fmt
687 fmt
.Prepend(wxT('%'));
688 fmt
.Append(wxT('d'));
695 // no, it wasn't the width
696 wxFAIL_MSG(wxT("unknown format specifier"));
698 // fall through and just copy it nevertheless
700 case wxT('%'): // a percent sign
704 case 0: // the end of string
705 wxFAIL_MSG(wxT("missing format at the end of string"));
707 // just put the '%' which was the last char in format
717 // this function parses a string in (strict) RFC 822 format: see the section 5
718 // of the RFC for the detailed description, but briefly it's something of the
719 // form "Sat, 18 Dec 1999 00:48:30 +0100"
721 // this function is "strict" by design - it must reject anything except true
722 // RFC822 time specs.
724 wxDateTime::ParseRfc822Date(const wxString
& date
, wxString::const_iterator
*end
)
726 const wxString::const_iterator pEnd
= date
.end();
727 wxString::const_iterator p
= date
.begin();
730 const wxDateTime::WeekDay
731 wd
= GetWeekDayFromName(p
, pEnd
, Name_Abbr
, DateLang_English
);
732 if ( wd
== Inv_WeekDay
)
734 //else: ignore week day for now, we could also check that it really
735 // corresponds to the specified date
737 // 2. separating comma
738 if ( *p
++ != ',' || *p
++ != ' ' )
742 if ( !wxIsdigit(*p
) )
745 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - '0');
749 day
= (wxDateTime_t
)(day
+ (*p
++ - '0'));
756 const Month mon
= GetMonthFromName(p
, pEnd
, Name_Abbr
, DateLang_English
);
757 if ( mon
== Inv_Month
)
764 if ( !wxIsdigit(*p
) )
767 int year
= *p
++ - '0';
768 if ( !wxIsdigit(*p
) ) // should have at least 2 digits in the year
774 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
780 if ( !wxIsdigit(*p
) )
782 // no 3 digit years please
793 // 6. time in hh:mm:ss format with seconds being optional
794 if ( !wxIsdigit(*p
) )
797 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - '0');
799 if ( !wxIsdigit(*p
) )
803 hour
= (wxDateTime_t
)(hour
+ (*p
++ - '0'));
808 if ( !wxIsdigit(*p
) )
811 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - '0');
813 if ( !wxIsdigit(*p
) )
817 min
+= (wxDateTime_t
)(*p
++ - '0');
819 wxDateTime_t sec
= 0;
823 if ( !wxIsdigit(*p
) )
826 sec
= (wxDateTime_t
)(*p
++ - '0');
828 if ( !wxIsdigit(*p
) )
832 sec
+= (wxDateTime_t
)(*p
++ - '0');
838 // 7. now the interesting part: the timezone
839 int offset
= 0; // just to suppress warnings
840 if ( *p
== '-' || *p
== '+' )
842 // the explicit offset given: it has the form of hhmm
843 bool plus
= *p
++ == '+';
845 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
850 offset
= MIN_PER_HOUR
*(10*(*p
- '0') + (*(p
+ 1) - '0'));
854 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
858 offset
+= 10*(*p
- '0') + (*(p
+ 1) - '0');
867 // the symbolic timezone given: may be either military timezone or one
868 // of standard abbreviations
871 // military: Z = UTC, J unused, A = -1, ..., Y = +12
872 static const int offsets
[26] =
874 //A B C D E F G H I J K L M
875 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
876 //N O P R Q S T U V W Z Y Z
877 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
880 if ( *p
< wxT('A') || *p
> wxT('Z') || *p
== wxT('J') )
883 offset
= offsets
[*p
++ - 'A'];
888 const wxString
tz(p
, date
.end());
889 if ( tz
== wxT("UT") || tz
== wxT("UTC") || tz
== wxT("GMT") )
891 else if ( tz
== wxT("AST") )
893 else if ( tz
== wxT("ADT") )
895 else if ( tz
== wxT("EST") )
897 else if ( tz
== wxT("EDT") )
899 else if ( tz
== wxT("CST") )
901 else if ( tz
== wxT("CDT") )
903 else if ( tz
== wxT("MST") )
905 else if ( tz
== wxT("MDT") )
907 else if ( tz
== wxT("PST") )
909 else if ( tz
== wxT("PDT") )
918 offset
*= MIN_PER_HOUR
;
922 // the spec was correct, construct the date from the values we found
923 Set(day
, mon
, year
, hour
, min
, sec
);
924 MakeFromTimezone(TimeZone::Make(offset
*SEC_PER_MIN
));
933 wxDateTime::ParseFormat(const wxString
& date
,
934 const wxString
& format
,
935 const wxDateTime
& dateDef
,
936 wxString::const_iterator
*endParse
)
938 wxCHECK_MSG( !format
.empty(), false, "format can't be empty" );
939 wxCHECK_MSG( endParse
, false, "end iterator pointer must be specified" );
944 // what fields have we found?
945 bool haveWDay
= false,
955 bool hourIsIn12hFormat
= false, // or in 24h one?
956 isPM
= false; // AM by default
958 bool haveTimeZone
= false;
960 // and the value of the items we have (init them to get rid of warnings)
961 wxDateTime_t msec
= 0,
965 WeekDay wday
= Inv_WeekDay
;
966 wxDateTime_t yday
= 0,
968 wxDateTime::Month mon
= Inv_Month
;
970 long timeZone
= 0; // time zone in seconds as expected in Tm structure
972 wxString::const_iterator input
= date
.begin();
973 const wxString::const_iterator end
= date
.end();
974 for ( wxString::const_iterator fmt
= format
.begin(); fmt
!= format
.end(); ++fmt
)
976 if ( *fmt
!= wxT('%') )
978 if ( wxIsspace(*fmt
) )
980 // a white space in the format string matches 0 or more white
981 // spaces in the input
982 while ( input
!= end
&& wxIsspace(*input
) )
989 // any other character (not whitespace, not '%') must be
990 // matched by itself in the input
991 if ( input
== end
|| *input
++ != *fmt
)
998 // done with this format char
1002 // start of a format specification
1004 // parse the optional width
1006 while ( wxIsdigit(*++fmt
) )
1009 width
+= *fmt
- '0';
1012 // the default widths for the various fields
1015 switch ( (*fmt
).GetValue() )
1017 case wxT('Y'): // year has 4 digits
1021 case wxT('j'): // day of year has 3 digits
1022 case wxT('l'): // milliseconds have 3 digits
1026 case wxT('w'): // week day as number has only one
1031 // default for all other fields
1036 // then the format itself
1037 switch ( (*fmt
).GetValue() )
1039 case wxT('a'): // a weekday name
1042 wday
= GetWeekDayFromName
1045 *fmt
== 'a' ? Name_Abbr
: Name_Full
,
1048 if ( wday
== Inv_WeekDay
)
1057 case wxT('b'): // a month name
1060 mon
= GetMonthFromName
1063 *fmt
== 'b' ? Name_Abbr
: Name_Full
,
1066 if ( mon
== Inv_Month
)
1075 case wxT('c'): // locale default date and time representation
1081 fmtDateTime
= wxLocale::GetInfo(wxLOCALE_DATE_TIME_FMT
);
1082 if ( !fmtDateTime
.empty() )
1083 dt
= ParseFormatAt(input
, end
, fmtDateTime
);
1084 #endif // wxUSE_INTL
1085 if ( !dt
.IsValid() )
1087 // also try the format which corresponds to ctime()
1088 // output (i.e. the "C" locale default)
1089 dt
= ParseFormatAt(input
, end
, wxS("%a %b %d %H:%M:%S %Y"));
1092 if ( !dt
.IsValid() )
1094 // and finally also the two generic date/time formats
1095 dt
= ParseFormatAt(input
, end
, wxS("%x %X"), wxS("%X %x"));
1098 if ( !dt
.IsValid() )
1101 const Tm tm
= dt
.GetTm();
1111 haveDay
= haveMon
= haveYear
=
1112 haveHour
= haveMin
= haveSec
= true;
1116 case wxT('d'): // day of a month (01-31)
1117 case 'e': // day of a month (1-31) (GNU extension)
1118 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1119 (num
> 31) || (num
< 1) )
1125 // we can't check whether the day range is correct yet, will
1126 // do it later - assume ok for now
1128 mday
= (wxDateTime_t
)num
;
1131 case wxT('H'): // hour in 24h format (00-23)
1132 if ( !GetNumericToken(width
, input
, end
, &num
) || (num
> 23) )
1139 hour
= (wxDateTime_t
)num
;
1142 case wxT('I'): // hour in 12h format (01-12)
1143 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1144 !num
|| (num
> 12) )
1151 hourIsIn12hFormat
= true;
1152 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
1155 case wxT('j'): // day of the year
1156 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1157 !num
|| (num
> 366) )
1164 yday
= (wxDateTime_t
)num
;
1167 case wxT('l'): // milliseconds (0-999)
1168 if ( !GetNumericToken(width
, input
, end
, &num
) )
1172 msec
= (wxDateTime_t
)num
;
1175 case wxT('m'): // month as a number (01-12)
1176 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1177 !num
|| (num
> 12) )
1184 mon
= (Month
)(num
- 1);
1187 case wxT('M'): // minute as a decimal number (00-59)
1188 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1196 min
= (wxDateTime_t
)num
;
1199 case wxT('p'): // AM or PM string
1202 GetAmPmStrings(&am
, &pm
);
1204 // we can never match %p in locales which don't use AM/PM
1205 if ( am
.empty() || pm
.empty() )
1208 const size_t pos
= input
- date
.begin();
1209 if ( date
.compare(pos
, pm
.length(), pm
) == 0 )
1212 input
+= pm
.length();
1214 else if ( date
.compare(pos
, am
.length(), am
) == 0 )
1216 input
+= am
.length();
1225 case wxT('r'): // time as %I:%M:%S %p
1228 if ( !dt
.ParseFormat(wxString(input
, end
),
1229 wxS("%I:%M:%S %p"), &input
) )
1232 haveHour
= haveMin
= haveSec
= true;
1234 const Tm tm
= dt
.GetTm();
1241 case wxT('R'): // time as %H:%M
1244 dt
= ParseFormatAt(input
, end
, wxS("%H:%M"));
1245 if ( !dt
.IsValid() )
1251 const Tm tm
= dt
.GetTm();
1257 case wxT('S'): // second as a decimal number (00-61)
1258 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1266 sec
= (wxDateTime_t
)num
;
1269 case wxT('T'): // time as %H:%M:%S
1272 dt
= ParseFormatAt(input
, end
, wxS("%H:%M:%S"));
1273 if ( !dt
.IsValid() )
1280 const Tm tm
= dt
.GetTm();
1287 case wxT('w'): // weekday as a number (0-6), Sunday = 0
1288 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1296 wday
= (WeekDay
)num
;
1299 case wxT('x'): // locale default date representation
1303 fmtDate
= wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT
),
1304 fmtDateAlt
= wxLocale::GetInfo(wxLOCALE_LONG_DATE_FMT
);
1305 #else // !wxUSE_INTL
1306 wxString fmtDate
, fmtDateAlt
;
1307 #endif // wxUSE_INTL/!wxUSE_INTL
1308 if ( fmtDate
.empty() )
1310 if ( IsWestEuropeanCountry(GetCountry()) ||
1311 GetCountry() == Russia
)
1313 fmtDate
= wxS("%d/%m/%Y");
1314 fmtDateAlt
= wxS("%m/%d/%Y");
1318 fmtDate
= wxS("%m/%d/%Y");
1319 fmtDateAlt
= wxS("%d/%m/%Y");
1324 dt
= ParseFormatAt(input
, end
, fmtDate
, fmtDateAlt
);
1326 if ( !dt
.IsValid() )
1328 // try with short years too
1329 fmtDate
.Replace("%Y","%y");
1330 fmtDateAlt
.Replace("%Y","%y");
1331 dt
= ParseFormatAt(input
, end
, fmtDate
, fmtDateAlt
);
1333 if ( !dt
.IsValid() )
1337 const Tm tm
= dt
.GetTm();
1350 case wxT('X'): // locale default time representation
1353 wxString fmtTime
= wxLocale::GetInfo(wxLOCALE_TIME_FMT
),
1355 #else // !wxUSE_INTL
1356 wxString fmtTime
, fmtTimeAlt
;
1357 #endif // wxUSE_INTL/!wxUSE_INTL
1358 if ( fmtTime
.empty() )
1360 // try to parse what follows as "%H:%M:%S" and, if this
1361 // fails, as "%I:%M:%S %p" - this should catch the most
1368 dt
= ParseFormatAt(input
, end
, fmtTime
, fmtTimeAlt
);
1369 if ( !dt
.IsValid() )
1376 const Tm tm
= dt
.GetTm();
1383 case wxT('y'): // year without century (00-99)
1384 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1393 // TODO should have an option for roll over date instead of
1394 // hard coding it here
1395 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
1398 case wxT('Y'): // year with century
1399 if ( !GetNumericToken(width
, input
, end
, &num
) )
1406 year
= (wxDateTime_t
)num
;
1412 if ( *input
== wxT('-') )
1414 else if ( *input
== wxT('+') )
1417 return false; // no match
1419 // here should follow 4 digits HHMM
1421 unsigned long tzHourMin
;
1422 if ( !GetNumericToken(4, input
, end
, &tzHourMin
) )
1423 return false; // no match
1425 const unsigned hours
= tzHourMin
/ 100;
1426 const unsigned minutes
= tzHourMin
% 100;
1428 if ( hours
> 12 || minutes
> 59 )
1429 return false; // bad format
1431 timeZone
= 3600*hours
+ 60*minutes
;
1433 timeZone
= -timeZone
;
1435 haveTimeZone
= true;
1439 case wxT('Z'): // timezone name
1440 // FIXME: currently we just ignore everything that looks like a
1442 GetAlphaToken(input
, end
);
1445 case wxT('%'): // a percent sign
1446 if ( *input
++ != wxT('%') )
1453 case 0: // the end of string
1454 wxFAIL_MSG(wxT("unexpected format end"));
1458 default: // not a known format spec
1463 // format matched, try to construct a date from what we have now
1465 if ( dateDef
.IsValid() )
1467 // take this date as default
1468 tmDef
= dateDef
.GetTm();
1470 else if ( IsValid() )
1472 // if this date is valid, don't change it
1477 // no default and this date is invalid - fall back to Today()
1478 tmDef
= Today().GetTm();
1494 // TODO we don't check here that the values are consistent, if both year
1495 // day and month/day were found, we just ignore the year day and we
1496 // also always ignore the week day
1499 if ( mday
> GetNumberOfDays(tm
.mon
, tm
.year
) )
1504 else if ( haveYDay
)
1506 if ( yday
> GetNumberOfDays(tm
.year
) )
1509 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
1516 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
1518 // translate to 24hour format
1521 //else: either already in 24h format or no translation needed
1544 // If a time zone was specified and it is not the local time zone, we need
1545 // to shift the time accordingly.
1547 // Note that avoiding the call to MakeFromTimeZone is necessary to avoid
1549 if ( haveTimeZone
&& timeZone
!= -wxGetTimeZone() )
1550 MakeFromTimezone(timeZone
);
1552 // finally check that the week day is consistent -- if we had it
1553 if ( haveWDay
&& GetWeekDay() != wday
)
1562 wxDateTime::ParseDateTime(const wxString
& date
, wxString::const_iterator
*end
)
1564 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1570 wxString::const_iterator
1575 // If we got a date in the beginning, see if there is a time specified
1577 if ( dtDate
.ParseDate(date
, &endDate
) )
1579 // Skip spaces, as the ParseTime() function fails on spaces
1580 while ( endDate
!= date
.end() && wxIsspace(*endDate
) )
1583 const wxString
timestr(endDate
, date
.end());
1584 if ( !dtTime
.ParseTime(timestr
, &endTime
) )
1587 endBoth
= endDate
+ (endTime
- timestr
.begin());
1589 else // no date in the beginning
1591 // check if we have a time followed by a date
1592 if ( !dtTime
.ParseTime(date
, &endTime
) )
1595 while ( endTime
!= date
.end() && wxIsspace(*endTime
) )
1598 const wxString
datestr(endTime
, date
.end());
1599 if ( !dtDate
.ParseDate(datestr
, &endDate
) )
1602 endBoth
= endTime
+ (endDate
- datestr
.begin());
1605 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
1606 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
1607 dtTime
.GetMillisecond());
1615 wxDateTime::ParseDate(const wxString
& date
, wxString::const_iterator
*end
)
1617 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1619 // this is a simplified version of ParseDateTime() which understands only
1620 // "today" (for wxDate compatibility) and digits only otherwise (and not
1621 // all esoteric constructions ParseDateTime() knows about)
1623 const wxString::const_iterator pBegin
= date
.begin();
1624 const wxString::const_iterator pEnd
= date
.end();
1626 wxString::const_iterator p
= pBegin
;
1627 while ( p
!= pEnd
&& wxIsspace(*p
) )
1630 // some special cases
1634 int dayDiffFromToday
;
1637 { wxTRANSLATE("today"), 0 },
1638 { wxTRANSLATE("yesterday"), -1 },
1639 { wxTRANSLATE("tomorrow"), 1 },
1642 const size_t lenRest
= pEnd
- p
;
1643 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
1645 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
1646 size_t len
= dateStr
.length();
1648 if ( len
> lenRest
)
1651 const wxString::const_iterator pEnd
= p
+ len
;
1652 if ( wxString(p
, pEnd
).CmpNoCase(dateStr
) == 0 )
1654 // nothing can follow this, so stop here
1658 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
1660 if ( dayDiffFromToday
)
1662 *this += wxDateSpan::Days(dayDiffFromToday
);
1671 // We try to guess what we have here: for each new (numeric) token, we
1672 // determine if it can be a month, day or a year. Of course, there is an
1673 // ambiguity as some numbers may be days as well as months, so we also
1674 // have the ability to back track.
1677 bool haveDay
= false, // the months day?
1678 haveWDay
= false, // the day of week?
1679 haveMon
= false, // the month?
1680 haveYear
= false; // the year?
1682 bool monWasNumeric
= false; // was month specified as a number?
1684 // and the value of the items we have (init them to get rid of warnings)
1685 WeekDay wday
= Inv_WeekDay
;
1686 wxDateTime_t day
= 0;
1687 wxDateTime::Month mon
= Inv_Month
;
1690 // tokenize the string
1693 // skip white space and date delimiters
1694 if ( wxStrchr(".,/-\t\r\n ", *p
) )
1700 // modify copy of the iterator as we're not sure if the next token is
1701 // still part of the date at all
1702 wxString::const_iterator pCopy
= p
;
1704 // we can have either alphabetic or numeric token, start by testing if
1707 if ( GetNumericToken(10 /* max length */, pCopy
, pEnd
, &val
) )
1709 // guess what this number is
1715 if ( !haveMon
&& val
> 0 && val
<= 12 )
1717 // assume it is month
1720 else // not the month
1724 // this can only be the year
1727 else // may be either day or year
1729 // use a leap year if we don't have the year yet to allow
1730 // dates like 2/29/1976 which would be rejected otherwise
1731 wxDateTime_t max_days
= (wxDateTime_t
)(
1733 ? GetNumberOfDays(mon
, haveYear
? year
: 1976)
1738 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
1743 else // yes, suppose it's the day
1757 year
= (wxDateTime_t
)val
;
1766 day
= (wxDateTime_t
)val
;
1771 monWasNumeric
= true;
1773 mon
= (Month
)(val
- 1);
1776 else // not a number
1778 // be careful not to overwrite the current mon value
1779 Month mon2
= GetMonthFromName
1782 Name_Full
| Name_Abbr
,
1783 DateLang_Local
| DateLang_English
1785 if ( mon2
!= Inv_Month
)
1790 // but we already have a month - maybe we guessed wrong
1791 // when we had interpreted that numeric value as a month
1792 // and it was the day number instead?
1793 if ( haveDay
|| !monWasNumeric
)
1796 // assume we did and change our mind: reinterpret the month
1797 // value as a day (notice that there is no need to check
1798 // that it is valid as month values are always < 12, but
1799 // the days are counted from 1 unlike the months)
1800 day
= (wxDateTime_t
)(mon
+ 1);
1808 else // not a valid month name
1810 WeekDay wday2
= GetWeekDayFromName
1813 Name_Full
| Name_Abbr
,
1814 DateLang_Local
| DateLang_English
1816 if ( wday2
!= Inv_WeekDay
)
1826 else // not a valid weekday name
1829 static const char *const ordinals
[] =
1831 wxTRANSLATE("first"),
1832 wxTRANSLATE("second"),
1833 wxTRANSLATE("third"),
1834 wxTRANSLATE("fourth"),
1835 wxTRANSLATE("fifth"),
1836 wxTRANSLATE("sixth"),
1837 wxTRANSLATE("seventh"),
1838 wxTRANSLATE("eighth"),
1839 wxTRANSLATE("ninth"),
1840 wxTRANSLATE("tenth"),
1841 wxTRANSLATE("eleventh"),
1842 wxTRANSLATE("twelfth"),
1843 wxTRANSLATE("thirteenth"),
1844 wxTRANSLATE("fourteenth"),
1845 wxTRANSLATE("fifteenth"),
1846 wxTRANSLATE("sixteenth"),
1847 wxTRANSLATE("seventeenth"),
1848 wxTRANSLATE("eighteenth"),
1849 wxTRANSLATE("nineteenth"),
1850 wxTRANSLATE("twentieth"),
1851 // that's enough - otherwise we'd have problems with
1852 // composite (or not) ordinals
1856 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
1858 const wxString ord
= wxGetTranslation(ordinals
[n
]);
1859 const size_t len
= ord
.length();
1860 if ( date
.compare(p
- pBegin
, len
, ord
) == 0 )
1867 if ( n
== WXSIZEOF(ordinals
) )
1869 // stop here - something unknown
1876 // don't try anything here (as in case of numeric day
1877 // above) - the symbolic day spec should always
1878 // precede the month/year
1884 day
= (wxDateTime_t
)(n
+ 1);
1889 // advance iterator past a successfully parsed token
1893 // either no more tokens or the scan was stopped by something we couldn't
1894 // parse - in any case, see if we can construct a date from what we have
1895 if ( !haveDay
&& !haveWDay
)
1898 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
1899 !(haveDay
&& haveMon
&& haveYear
) )
1901 // without adjectives (which we don't support here) the week day only
1902 // makes sense completely separately or with the full date
1903 // specification (what would "Wed 1999" mean?)
1907 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
1909 // may be we have month and day instead of day and year?
1910 if ( haveDay
&& !haveMon
)
1914 // exchange day and month
1915 mon
= (wxDateTime::Month
)(day
- 1);
1917 // we're in the current year then
1918 if ( (year
> 0) && (year
<= (int)GetNumberOfDays(mon
, Inv_Year
)) )
1920 day
= (wxDateTime_t
)year
;
1925 //else: no, can't exchange, leave haveMon == false
1935 mon
= GetCurrentMonth();
1940 year
= GetCurrentYear();
1945 // normally we check the day above but the check is optimistic in case
1946 // we find the day before its month/year so we have to redo it now
1947 if ( day
> GetNumberOfDays(mon
, year
) )
1950 Set(day
, mon
, year
);
1954 // check that it is really the same
1955 if ( GetWeekDay() != wday
)
1963 SetToWeekDayInSameWeek(wday
);
1972 wxDateTime::ParseTime(const wxString
& time
, wxString::const_iterator
*end
)
1974 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1976 // first try some extra things
1983 { wxTRANSLATE("noon"), 12 },
1984 { wxTRANSLATE("midnight"), 00 },
1988 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
1990 const wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
1991 if ( timeString
.CmpNoCase(wxString(time
, timeString
.length())) == 0 )
1993 // casts required by DigitalMars
1994 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
1997 *end
= time
.begin() + timeString
.length();
2003 // try all time formats we may think about in the order from longest to
2005 static const char *const timeFormats
[] =
2007 "%I:%M:%S %p", // 12hour with AM/PM
2008 "%H:%M:%S", // could be the same or 24 hour one so try it too
2009 "%I:%M %p", // 12hour with AM/PM but without seconds
2010 "%H:%M", // and a possibly 24 hour version without seconds
2011 "%X", // possibly something from above or maybe something
2012 // completely different -- try it last
2014 // TODO: parse timezones
2017 for ( size_t nFmt
= 0; nFmt
< WXSIZEOF(timeFormats
); nFmt
++ )
2019 if ( ParseFormat(time
, timeFormats
[nFmt
], end
) )
2026 // ----------------------------------------------------------------------------
2027 // Workdays and holidays support
2028 // ----------------------------------------------------------------------------
2030 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
2032 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
2035 // ============================================================================
2037 // ============================================================================
2039 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
2042 return ds1
.Multiply(n
);
2045 // ============================================================================
2047 // ============================================================================
2049 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
2051 return wxTimeSpan(ts
).Multiply(n
);
2054 // this enum is only used in wxTimeSpan::Format() below but we can't declare
2055 // it locally to the method as it provokes an internal compiler error in egcs
2056 // 2.91.60 when building with -O2
2067 // not all strftime(3) format specifiers make sense here because, for example,
2068 // a time span doesn't have a year nor a timezone
2070 // Here are the ones which are supported (all of them are supported by strftime
2072 // %H hour in 24 hour format
2073 // %M minute (00 - 59)
2074 // %S second (00 - 59)
2077 // Also, for MFC CTimeSpan compatibility, we support
2078 // %D number of days
2080 // And, to be better than MFC :-), we also have
2081 // %E number of wEeks
2082 // %l milliseconds (000 - 999)
2083 wxString
wxTimeSpan::Format(const wxString
& format
) const
2085 // we deal with only positive time spans here and just add the sign in
2086 // front for the negative ones
2089 wxString
str(Negate().Format(format
));
2093 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
2094 wxT("NULL format in wxTimeSpan::Format") );
2097 str
.Alloc(format
.length());
2099 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
2101 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
2102 // question is what should ts.Format("%S") do? The code here returns "3273"
2103 // in this case (i.e. the total number of seconds, not just seconds % 60)
2104 // because, for me, this call means "give me entire time interval in
2105 // seconds" and not "give me the seconds part of the time interval"
2107 // If we agree that it should behave like this, it is clear that the
2108 // interpretation of each format specifier depends on the presence of the
2109 // other format specs in the string: if there was "%H" before "%M", we
2110 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
2112 // we remember the most important unit found so far
2113 TimeSpanPart partBiggest
= Part_MSec
;
2115 for ( wxString::const_iterator pch
= format
.begin(); pch
!= format
.end(); ++pch
)
2119 if ( ch
== wxT('%') )
2121 // the start of the format specification of the printf() below
2122 wxString
fmtPrefix(wxT('%'));
2127 // the number of digits for the format string, 0 if unused
2128 unsigned digits
= 0;
2130 ch
= *++pch
; // get the format spec char
2134 wxFAIL_MSG( wxT("invalid format character") );
2140 // skip the part below switch
2145 if ( partBiggest
< Part_Day
)
2151 partBiggest
= Part_Day
;
2156 partBiggest
= Part_Week
;
2162 if ( partBiggest
< Part_Hour
)
2168 partBiggest
= Part_Hour
;
2175 n
= GetMilliseconds().ToLong();
2176 if ( partBiggest
< Part_MSec
)
2180 //else: no need to reset partBiggest to Part_MSec, it is
2181 // the least significant one anyhow
2188 if ( partBiggest
< Part_Min
)
2194 partBiggest
= Part_Min
;
2201 n
= GetSeconds().ToLong();
2202 if ( partBiggest
< Part_Sec
)
2208 partBiggest
= Part_Sec
;
2217 fmtPrefix
<< wxT("0") << digits
;
2220 str
+= wxString::Format(fmtPrefix
+ wxT("ld"), n
);
2224 // normal character, just copy
2232 #endif // wxUSE_DATETIME