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 todays 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();
668 // Converts seconds to HHMM representation.
669 res
+= wxString::Format(fmt
,
670 100*(ofs
/3600) + (ofs
/60)%60
);
674 case wxT('Z'): // timezone name
675 #ifdef wxHAS_STRFTIME
676 res
+= CallStrftime(wxT("%Z"), &tmTimeOnly
);
681 // is it the format width?
683 *p
== wxT('-') || *p
== wxT('+') ||
684 *p
== wxT(' ') || wxIsdigit(*p
);
692 // we've only got the flags and width so far in fmt
693 fmt
.Prepend(wxT('%'));
694 fmt
.Append(wxT('d'));
701 // no, it wasn't the width
702 wxFAIL_MSG(wxT("unknown format specifier"));
704 // fall through and just copy it nevertheless
706 case wxT('%'): // a percent sign
710 case 0: // the end of string
711 wxFAIL_MSG(wxT("missing format at the end of string"));
713 // just put the '%' which was the last char in format
723 // this function parses a string in (strict) RFC 822 format: see the section 5
724 // of the RFC for the detailed description, but briefly it's something of the
725 // form "Sat, 18 Dec 1999 00:48:30 +0100"
727 // this function is "strict" by design - it must reject anything except true
728 // RFC822 time specs.
730 wxDateTime::ParseRfc822Date(const wxString
& date
, wxString::const_iterator
*end
)
732 const wxString::const_iterator pEnd
= date
.end();
733 wxString::const_iterator p
= date
.begin();
736 const wxDateTime::WeekDay
737 wd
= GetWeekDayFromName(p
, pEnd
, Name_Abbr
, DateLang_English
);
738 if ( wd
== Inv_WeekDay
)
740 //else: ignore week day for now, we could also check that it really
741 // corresponds to the specified date
743 // 2. separating comma
744 if ( *p
++ != ',' || *p
++ != ' ' )
748 if ( !wxIsdigit(*p
) )
751 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - '0');
755 day
= (wxDateTime_t
)(day
+ (*p
++ - '0'));
762 const Month mon
= GetMonthFromName(p
, pEnd
, Name_Abbr
, DateLang_English
);
763 if ( mon
== Inv_Month
)
770 if ( !wxIsdigit(*p
) )
773 int year
= *p
++ - '0';
774 if ( !wxIsdigit(*p
) ) // should have at least 2 digits in the year
780 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
786 if ( !wxIsdigit(*p
) )
788 // no 3 digit years please
799 // 6. time in hh:mm:ss format with seconds being optional
800 if ( !wxIsdigit(*p
) )
803 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - '0');
805 if ( !wxIsdigit(*p
) )
809 hour
= (wxDateTime_t
)(hour
+ (*p
++ - '0'));
814 if ( !wxIsdigit(*p
) )
817 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - '0');
819 if ( !wxIsdigit(*p
) )
823 min
+= (wxDateTime_t
)(*p
++ - '0');
825 wxDateTime_t sec
= 0;
829 if ( !wxIsdigit(*p
) )
832 sec
= (wxDateTime_t
)(*p
++ - '0');
834 if ( !wxIsdigit(*p
) )
838 sec
+= (wxDateTime_t
)(*p
++ - '0');
844 // 7. now the interesting part: the timezone
845 int offset
= 0; // just to suppress warnings
846 if ( *p
== '-' || *p
== '+' )
848 // the explicit offset given: it has the form of hhmm
849 bool plus
= *p
++ == '+';
851 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
856 offset
= MIN_PER_HOUR
*(10*(*p
- '0') + (*(p
+ 1) - '0'));
860 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
864 offset
+= 10*(*p
- '0') + (*(p
+ 1) - '0');
873 // the symbolic timezone given: may be either military timezone or one
874 // of standard abbreviations
877 // military: Z = UTC, J unused, A = -1, ..., Y = +12
878 static const int offsets
[26] =
880 //A B C D E F G H I J K L M
881 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
882 //N O P R Q S T U V W Z Y Z
883 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
886 if ( *p
< wxT('A') || *p
> wxT('Z') || *p
== wxT('J') )
889 offset
= offsets
[*p
++ - 'A'];
894 const wxString
tz(p
, date
.end());
895 if ( tz
== wxT("UT") || tz
== wxT("UTC") || tz
== wxT("GMT") )
897 else if ( tz
== wxT("AST") )
899 else if ( tz
== wxT("ADT") )
901 else if ( tz
== wxT("EST") )
903 else if ( tz
== wxT("EDT") )
905 else if ( tz
== wxT("CST") )
907 else if ( tz
== wxT("CDT") )
909 else if ( tz
== wxT("MST") )
911 else if ( tz
== wxT("MDT") )
913 else if ( tz
== wxT("PST") )
915 else if ( tz
== wxT("PDT") )
924 offset
*= MIN_PER_HOUR
;
928 // the spec was correct, construct the date from the values we found
929 Set(day
, mon
, year
, hour
, min
, sec
);
930 MakeFromTimezone(TimeZone::Make(offset
*SEC_PER_MIN
));
939 wxDateTime::ParseFormat(const wxString
& date
,
940 const wxString
& format
,
941 const wxDateTime
& dateDef
,
942 wxString::const_iterator
*endParse
)
944 wxCHECK_MSG( !format
.empty(), false, "format can't be empty" );
945 wxCHECK_MSG( endParse
, false, "end iterator pointer must be specified" );
950 // what fields have we found?
951 bool haveWDay
= false,
961 bool hourIsIn12hFormat
= false, // or in 24h one?
962 isPM
= false; // AM by default
964 bool haveTimeZone
= false;
966 // and the value of the items we have (init them to get rid of warnings)
967 wxDateTime_t msec
= 0,
971 WeekDay wday
= Inv_WeekDay
;
972 wxDateTime_t yday
= 0,
974 wxDateTime::Month mon
= Inv_Month
;
976 long timeZone
= 0; // time zone in seconds as expected in Tm structure
978 wxString::const_iterator input
= date
.begin();
979 const wxString::const_iterator end
= date
.end();
980 for ( wxString::const_iterator fmt
= format
.begin(); fmt
!= format
.end(); ++fmt
)
982 if ( *fmt
!= wxT('%') )
984 if ( wxIsspace(*fmt
) )
986 // a white space in the format string matches 0 or more white
987 // spaces in the input
988 while ( input
!= end
&& wxIsspace(*input
) )
995 // any other character (not whitespace, not '%') must be
996 // matched by itself in the input
997 if ( input
== end
|| *input
++ != *fmt
)
1004 // done with this format char
1008 // start of a format specification
1010 // parse the optional width
1012 while ( wxIsdigit(*++fmt
) )
1015 width
+= *fmt
- '0';
1018 // the default widths for the various fields
1021 switch ( (*fmt
).GetValue() )
1023 case wxT('Y'): // year has 4 digits
1027 case wxT('j'): // day of year has 3 digits
1028 case wxT('l'): // milliseconds have 3 digits
1032 case wxT('w'): // week day as number has only one
1037 // default for all other fields
1042 // then the format itself
1043 switch ( (*fmt
).GetValue() )
1045 case wxT('a'): // a weekday name
1048 wday
= GetWeekDayFromName
1051 *fmt
== 'a' ? Name_Abbr
: Name_Full
,
1054 if ( wday
== Inv_WeekDay
)
1063 case wxT('b'): // a month name
1066 mon
= GetMonthFromName
1069 *fmt
== 'b' ? Name_Abbr
: Name_Full
,
1072 if ( mon
== Inv_Month
)
1081 case wxT('c'): // locale default date and time representation
1087 fmtDateTime
= wxLocale::GetInfo(wxLOCALE_DATE_TIME_FMT
);
1088 if ( !fmtDateTime
.empty() )
1089 dt
= ParseFormatAt(input
, end
, fmtDateTime
);
1090 #endif // wxUSE_INTL
1091 if ( !dt
.IsValid() )
1093 // also try the format which corresponds to ctime()
1094 // output (i.e. the "C" locale default)
1095 dt
= ParseFormatAt(input
, end
, wxS("%a %b %d %H:%M:%S %Y"));
1098 if ( !dt
.IsValid() )
1100 // and finally also the two generic date/time formats
1101 dt
= ParseFormatAt(input
, end
, wxS("%x %X"), wxS("%X %x"));
1104 if ( !dt
.IsValid() )
1107 const Tm tm
= dt
.GetTm();
1117 haveDay
= haveMon
= haveYear
=
1118 haveHour
= haveMin
= haveSec
= true;
1122 case wxT('d'): // day of a month (01-31)
1123 case 'e': // day of a month (1-31) (GNU extension)
1124 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1125 (num
> 31) || (num
< 1) )
1131 // we can't check whether the day range is correct yet, will
1132 // do it later - assume ok for now
1134 mday
= (wxDateTime_t
)num
;
1137 case wxT('H'): // hour in 24h format (00-23)
1138 if ( !GetNumericToken(width
, input
, end
, &num
) || (num
> 23) )
1145 hour
= (wxDateTime_t
)num
;
1148 case wxT('I'): // hour in 12h format (01-12)
1149 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1150 !num
|| (num
> 12) )
1157 hourIsIn12hFormat
= true;
1158 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
1161 case wxT('j'): // day of the year
1162 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1163 !num
|| (num
> 366) )
1170 yday
= (wxDateTime_t
)num
;
1173 case wxT('l'): // milliseconds (0-999)
1174 if ( !GetNumericToken(width
, input
, end
, &num
) )
1178 msec
= (wxDateTime_t
)num
;
1181 case wxT('m'): // month as a number (01-12)
1182 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1183 !num
|| (num
> 12) )
1190 mon
= (Month
)(num
- 1);
1193 case wxT('M'): // minute as a decimal number (00-59)
1194 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1202 min
= (wxDateTime_t
)num
;
1205 case wxT('p'): // AM or PM string
1208 GetAmPmStrings(&am
, &pm
);
1210 // we can never match %p in locales which don't use AM/PM
1211 if ( am
.empty() || pm
.empty() )
1214 const size_t pos
= input
- date
.begin();
1215 if ( date
.compare(pos
, pm
.length(), pm
) == 0 )
1218 input
+= pm
.length();
1220 else if ( date
.compare(pos
, am
.length(), am
) == 0 )
1222 input
+= am
.length();
1231 case wxT('r'): // time as %I:%M:%S %p
1234 if ( !dt
.ParseFormat(wxString(input
, end
),
1235 wxS("%I:%M:%S %p"), &input
) )
1238 haveHour
= haveMin
= haveSec
= true;
1240 const Tm tm
= dt
.GetTm();
1247 case wxT('R'): // time as %H:%M
1250 dt
= ParseFormatAt(input
, end
, wxS("%H:%M"));
1251 if ( !dt
.IsValid() )
1257 const Tm tm
= dt
.GetTm();
1263 case wxT('S'): // second as a decimal number (00-61)
1264 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1272 sec
= (wxDateTime_t
)num
;
1275 case wxT('T'): // time as %H:%M:%S
1278 dt
= ParseFormatAt(input
, end
, wxS("%H:%M:%S"));
1279 if ( !dt
.IsValid() )
1286 const Tm tm
= dt
.GetTm();
1293 case wxT('w'): // weekday as a number (0-6), Sunday = 0
1294 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1302 wday
= (WeekDay
)num
;
1305 case wxT('x'): // locale default date representation
1309 fmtDate
= wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT
),
1310 fmtDateAlt
= wxLocale::GetInfo(wxLOCALE_LONG_DATE_FMT
);
1311 #else // !wxUSE_INTL
1312 wxString fmtDate
, fmtDateAlt
;
1313 #endif // wxUSE_INTL/!wxUSE_INTL
1314 if ( fmtDate
.empty() )
1316 if ( IsWestEuropeanCountry(GetCountry()) ||
1317 GetCountry() == Russia
)
1319 fmtDate
= wxS("%d/%m/%Y");
1320 fmtDateAlt
= wxS("%m/%d/%Y");
1324 fmtDate
= wxS("%m/%d/%Y");
1325 fmtDateAlt
= wxS("%d/%m/%Y");
1330 dt
= ParseFormatAt(input
, end
, fmtDate
, fmtDateAlt
);
1332 if ( !dt
.IsValid() )
1334 // try with short years too
1335 fmtDate
.Replace("%Y","%y");
1336 fmtDateAlt
.Replace("%Y","%y");
1337 dt
= ParseFormatAt(input
, end
, fmtDate
, fmtDateAlt
);
1339 if ( !dt
.IsValid() )
1343 const Tm tm
= dt
.GetTm();
1356 case wxT('X'): // locale default time representation
1359 wxString fmtTime
= wxLocale::GetInfo(wxLOCALE_TIME_FMT
),
1361 #else // !wxUSE_INTL
1362 wxString fmtTime
, fmtTimeAlt
;
1363 #endif // wxUSE_INTL/!wxUSE_INTL
1364 if ( fmtTime
.empty() )
1366 // try to parse what follows as "%H:%M:%S" and, if this
1367 // fails, as "%I:%M:%S %p" - this should catch the most
1374 dt
= ParseFormatAt(input
, end
, fmtTime
, fmtTimeAlt
);
1375 if ( !dt
.IsValid() )
1382 const Tm tm
= dt
.GetTm();
1389 case wxT('y'): // year without century (00-99)
1390 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1399 // TODO should have an option for roll over date instead of
1400 // hard coding it here
1401 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
1404 case wxT('Y'): // year with century
1405 if ( !GetNumericToken(width
, input
, end
, &num
) )
1412 year
= (wxDateTime_t
)num
;
1417 // check that we have something here at all
1421 // and then check that it's either plus or minus sign
1423 if ( *input
== wxT('-') )
1425 else if ( *input
== wxT('+') )
1428 return false; // no match
1430 // here should follow 4 digits HHMM
1432 unsigned long tzHourMin
;
1433 if ( !GetNumericToken(4, input
, end
, &tzHourMin
) )
1434 return false; // no match
1436 const unsigned hours
= tzHourMin
/ 100;
1437 const unsigned minutes
= tzHourMin
% 100;
1439 if ( hours
> 12 || minutes
> 59 )
1440 return false; // bad format
1442 timeZone
= 3600*hours
+ 60*minutes
;
1444 timeZone
= -timeZone
;
1446 haveTimeZone
= true;
1450 case wxT('Z'): // timezone name
1451 // FIXME: currently we just ignore everything that looks like a
1453 GetAlphaToken(input
, end
);
1456 case wxT('%'): // a percent sign
1457 if ( input
== end
|| *input
++ != wxT('%') )
1464 case 0: // the end of string
1465 wxFAIL_MSG(wxT("unexpected format end"));
1469 default: // not a known format spec
1474 // format matched, try to construct a date from what we have now
1476 if ( dateDef
.IsValid() )
1478 // take this date as default
1479 tmDef
= dateDef
.GetTm();
1481 else if ( IsValid() )
1483 // if this date is valid, don't change it
1488 // no default and this date is invalid - fall back to Today()
1489 tmDef
= Today().GetTm();
1505 // TODO we don't check here that the values are consistent, if both year
1506 // day and month/day were found, we just ignore the year day and we
1507 // also always ignore the week day
1510 if ( mday
> GetNumberOfDays(tm
.mon
, tm
.year
) )
1515 else if ( haveYDay
)
1517 if ( yday
> GetNumberOfDays(tm
.year
) )
1520 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
1527 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
1529 // translate to 24hour format
1532 //else: either already in 24h format or no translation needed
1555 // If a time zone was specified and it is not the local time zone, we need
1556 // to shift the time accordingly.
1558 // Note that avoiding the call to MakeFromTimeZone is necessary to avoid
1560 if ( haveTimeZone
&& timeZone
!= -wxGetTimeZone() )
1561 MakeFromTimezone(timeZone
);
1563 // finally check that the week day is consistent -- if we had it
1564 if ( haveWDay
&& GetWeekDay() != wday
)
1573 wxDateTime::ParseDateTime(const wxString
& date
, wxString::const_iterator
*end
)
1575 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1581 wxString::const_iterator
1586 // If we got a date in the beginning, see if there is a time specified
1588 if ( dtDate
.ParseDate(date
, &endDate
) )
1590 // Skip spaces, as the ParseTime() function fails on spaces
1591 while ( endDate
!= date
.end() && wxIsspace(*endDate
) )
1594 const wxString
timestr(endDate
, date
.end());
1595 if ( !dtTime
.ParseTime(timestr
, &endTime
) )
1598 endBoth
= endDate
+ (endTime
- timestr
.begin());
1600 else // no date in the beginning
1602 // check if we have a time followed by a date
1603 if ( !dtTime
.ParseTime(date
, &endTime
) )
1606 while ( endTime
!= date
.end() && wxIsspace(*endTime
) )
1609 const wxString
datestr(endTime
, date
.end());
1610 if ( !dtDate
.ParseDate(datestr
, &endDate
) )
1613 endBoth
= endTime
+ (endDate
- datestr
.begin());
1616 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
1617 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
1618 dtTime
.GetMillisecond());
1626 wxDateTime::ParseDate(const wxString
& date
, wxString::const_iterator
*end
)
1628 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1630 // this is a simplified version of ParseDateTime() which understands only
1631 // "today" (for wxDate compatibility) and digits only otherwise (and not
1632 // all esoteric constructions ParseDateTime() knows about)
1634 const wxString::const_iterator pBegin
= date
.begin();
1635 const wxString::const_iterator pEnd
= date
.end();
1637 wxString::const_iterator p
= pBegin
;
1638 while ( p
!= pEnd
&& wxIsspace(*p
) )
1641 // some special cases
1645 int dayDiffFromToday
;
1648 { wxTRANSLATE("today"), 0 },
1649 { wxTRANSLATE("yesterday"), -1 },
1650 { wxTRANSLATE("tomorrow"), 1 },
1653 const size_t lenRest
= pEnd
- p
;
1654 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
1656 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
1657 size_t len
= dateStr
.length();
1659 if ( len
> lenRest
)
1662 const wxString::const_iterator pEnd
= p
+ len
;
1663 if ( wxString(p
, pEnd
).CmpNoCase(dateStr
) == 0 )
1665 // nothing can follow this, so stop here
1669 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
1671 if ( dayDiffFromToday
)
1673 *this += wxDateSpan::Days(dayDiffFromToday
);
1682 // We try to guess what we have here: for each new (numeric) token, we
1683 // determine if it can be a month, day or a year. Of course, there is an
1684 // ambiguity as some numbers may be days as well as months, so we also
1685 // have the ability to back track.
1688 bool haveDay
= false, // the months day?
1689 haveWDay
= false, // the day of week?
1690 haveMon
= false, // the month?
1691 haveYear
= false; // the year?
1693 bool monWasNumeric
= false; // was month specified as a number?
1695 // and the value of the items we have (init them to get rid of warnings)
1696 WeekDay wday
= Inv_WeekDay
;
1697 wxDateTime_t day
= 0;
1698 wxDateTime::Month mon
= Inv_Month
;
1701 // tokenize the string
1704 // skip white space and date delimiters
1705 if ( wxStrchr(".,/-\t\r\n ", *p
) )
1711 // modify copy of the iterator as we're not sure if the next token is
1712 // still part of the date at all
1713 wxString::const_iterator pCopy
= p
;
1715 // we can have either alphabetic or numeric token, start by testing if
1718 if ( GetNumericToken(10 /* max length */, pCopy
, pEnd
, &val
) )
1720 // guess what this number is
1726 if ( !haveMon
&& val
> 0 && val
<= 12 )
1728 // assume it is month
1731 else // not the month
1735 // this can only be the year
1738 else // may be either day or year
1740 // use a leap year if we don't have the year yet to allow
1741 // dates like 2/29/1976 which would be rejected otherwise
1742 wxDateTime_t max_days
= (wxDateTime_t
)(
1744 ? GetNumberOfDays(mon
, haveYear
? year
: 1976)
1749 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
1754 else // yes, suppose it's the day
1768 year
= (wxDateTime_t
)val
;
1777 day
= (wxDateTime_t
)val
;
1782 monWasNumeric
= true;
1784 mon
= (Month
)(val
- 1);
1787 else // not a number
1789 // be careful not to overwrite the current mon value
1790 Month mon2
= GetMonthFromName
1793 Name_Full
| Name_Abbr
,
1794 DateLang_Local
| DateLang_English
1796 if ( mon2
!= Inv_Month
)
1801 // but we already have a month - maybe we guessed wrong
1802 // when we had interpreted that numeric value as a month
1803 // and it was the day number instead?
1804 if ( haveDay
|| !monWasNumeric
)
1807 // assume we did and change our mind: reinterpret the month
1808 // value as a day (notice that there is no need to check
1809 // that it is valid as month values are always < 12, but
1810 // the days are counted from 1 unlike the months)
1811 day
= (wxDateTime_t
)(mon
+ 1);
1819 else // not a valid month name
1821 WeekDay wday2
= GetWeekDayFromName
1824 Name_Full
| Name_Abbr
,
1825 DateLang_Local
| DateLang_English
1827 if ( wday2
!= Inv_WeekDay
)
1837 else // not a valid weekday name
1840 static const char *const ordinals
[] =
1842 wxTRANSLATE("first"),
1843 wxTRANSLATE("second"),
1844 wxTRANSLATE("third"),
1845 wxTRANSLATE("fourth"),
1846 wxTRANSLATE("fifth"),
1847 wxTRANSLATE("sixth"),
1848 wxTRANSLATE("seventh"),
1849 wxTRANSLATE("eighth"),
1850 wxTRANSLATE("ninth"),
1851 wxTRANSLATE("tenth"),
1852 wxTRANSLATE("eleventh"),
1853 wxTRANSLATE("twelfth"),
1854 wxTRANSLATE("thirteenth"),
1855 wxTRANSLATE("fourteenth"),
1856 wxTRANSLATE("fifteenth"),
1857 wxTRANSLATE("sixteenth"),
1858 wxTRANSLATE("seventeenth"),
1859 wxTRANSLATE("eighteenth"),
1860 wxTRANSLATE("nineteenth"),
1861 wxTRANSLATE("twentieth"),
1862 // that's enough - otherwise we'd have problems with
1863 // composite (or not) ordinals
1867 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
1869 const wxString ord
= wxGetTranslation(ordinals
[n
]);
1870 const size_t len
= ord
.length();
1871 if ( date
.compare(p
- pBegin
, len
, ord
) == 0 )
1878 if ( n
== WXSIZEOF(ordinals
) )
1880 // stop here - something unknown
1887 // don't try anything here (as in case of numeric day
1888 // above) - the symbolic day spec should always
1889 // precede the month/year
1895 day
= (wxDateTime_t
)(n
+ 1);
1900 // advance iterator past a successfully parsed token
1904 // either no more tokens or the scan was stopped by something we couldn't
1905 // parse - in any case, see if we can construct a date from what we have
1906 if ( !haveDay
&& !haveWDay
)
1909 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
1910 !(haveDay
&& haveMon
&& haveYear
) )
1912 // without adjectives (which we don't support here) the week day only
1913 // makes sense completely separately or with the full date
1914 // specification (what would "Wed 1999" mean?)
1918 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
1920 // may be we have month and day instead of day and year?
1921 if ( haveDay
&& !haveMon
)
1925 // exchange day and month
1926 mon
= (wxDateTime::Month
)(day
- 1);
1928 // we're in the current year then
1929 if ( (year
> 0) && (year
<= (int)GetNumberOfDays(mon
, Inv_Year
)) )
1931 day
= (wxDateTime_t
)year
;
1936 //else: no, can't exchange, leave haveMon == false
1946 mon
= GetCurrentMonth();
1951 year
= GetCurrentYear();
1956 // normally we check the day above but the check is optimistic in case
1957 // we find the day before its month/year so we have to redo it now
1958 if ( day
> GetNumberOfDays(mon
, year
) )
1961 Set(day
, mon
, year
);
1965 // check that it is really the same
1966 if ( GetWeekDay() != wday
)
1974 SetToWeekDayInSameWeek(wday
);
1983 wxDateTime::ParseTime(const wxString
& time
, wxString::const_iterator
*end
)
1985 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1987 // first try some extra things
1994 { wxTRANSLATE("noon"), 12 },
1995 { wxTRANSLATE("midnight"), 00 },
1999 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
2001 const wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
2002 if ( timeString
.CmpNoCase(wxString(time
, timeString
.length())) == 0 )
2004 // casts required by DigitalMars
2005 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
2008 *end
= time
.begin() + timeString
.length();
2014 // try all time formats we may think about in the order from longest to
2016 static const char *const timeFormats
[] =
2018 "%I:%M:%S %p", // 12hour with AM/PM
2019 "%H:%M:%S", // could be the same or 24 hour one so try it too
2020 "%I:%M %p", // 12hour with AM/PM but without seconds
2021 "%H:%M", // and a possibly 24 hour version without seconds
2022 "%X", // possibly something from above or maybe something
2023 // completely different -- try it last
2025 // TODO: parse timezones
2028 for ( size_t nFmt
= 0; nFmt
< WXSIZEOF(timeFormats
); nFmt
++ )
2030 if ( ParseFormat(time
, timeFormats
[nFmt
], end
) )
2037 // ----------------------------------------------------------------------------
2038 // Workdays and holidays support
2039 // ----------------------------------------------------------------------------
2041 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
2043 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
2046 // ============================================================================
2048 // ============================================================================
2050 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
2053 return ds1
.Multiply(n
);
2056 // ============================================================================
2058 // ============================================================================
2060 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
2062 return wxTimeSpan(ts
).Multiply(n
);
2065 // this enum is only used in wxTimeSpan::Format() below but we can't declare
2066 // it locally to the method as it provokes an internal compiler error in egcs
2067 // 2.91.60 when building with -O2
2078 // not all strftime(3) format specifiers make sense here because, for example,
2079 // a time span doesn't have a year nor a timezone
2081 // Here are the ones which are supported (all of them are supported by strftime
2083 // %H hour in 24 hour format
2084 // %M minute (00 - 59)
2085 // %S second (00 - 59)
2088 // Also, for MFC CTimeSpan compatibility, we support
2089 // %D number of days
2091 // And, to be better than MFC :-), we also have
2092 // %E number of wEeks
2093 // %l milliseconds (000 - 999)
2094 wxString
wxTimeSpan::Format(const wxString
& format
) const
2096 // we deal with only positive time spans here and just add the sign in
2097 // front for the negative ones
2100 wxString
str(Negate().Format(format
));
2104 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
2105 wxT("NULL format in wxTimeSpan::Format") );
2108 str
.Alloc(format
.length());
2110 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
2112 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
2113 // question is what should ts.Format("%S") do? The code here returns "3273"
2114 // in this case (i.e. the total number of seconds, not just seconds % 60)
2115 // because, for me, this call means "give me entire time interval in
2116 // seconds" and not "give me the seconds part of the time interval"
2118 // If we agree that it should behave like this, it is clear that the
2119 // interpretation of each format specifier depends on the presence of the
2120 // other format specs in the string: if there was "%H" before "%M", we
2121 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
2123 // we remember the most important unit found so far
2124 TimeSpanPart partBiggest
= Part_MSec
;
2126 for ( wxString::const_iterator pch
= format
.begin(); pch
!= format
.end(); ++pch
)
2130 if ( ch
== wxT('%') )
2132 // the start of the format specification of the printf() below
2133 wxString
fmtPrefix(wxT('%'));
2138 // the number of digits for the format string, 0 if unused
2139 unsigned digits
= 0;
2141 ch
= *++pch
; // get the format spec char
2145 wxFAIL_MSG( wxT("invalid format character") );
2151 // skip the part below switch
2156 if ( partBiggest
< Part_Day
)
2162 partBiggest
= Part_Day
;
2167 partBiggest
= Part_Week
;
2173 if ( partBiggest
< Part_Hour
)
2179 partBiggest
= Part_Hour
;
2186 n
= GetMilliseconds().ToLong();
2187 if ( partBiggest
< Part_MSec
)
2191 //else: no need to reset partBiggest to Part_MSec, it is
2192 // the least significant one anyhow
2199 if ( partBiggest
< Part_Min
)
2205 partBiggest
= Part_Min
;
2212 n
= GetSeconds().ToLong();
2213 if ( partBiggest
< Part_Sec
)
2219 partBiggest
= Part_Sec
;
2228 fmtPrefix
<< wxT("0") << digits
;
2231 str
+= wxString::Format(fmtPrefix
+ wxT("ld"), n
);
2235 // normal character, just copy
2243 #endif // wxUSE_DATETIME