1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/datetimefmt.cpp
3 // Purpose: wxDateTime formatting & parsing code
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // parts of code taken from sndcal library by Scott E. Lee:
11 // Copyright 1993-1995, Scott E. Lee, all rights reserved.
12 // Permission granted to use, copy, modify, distribute and sell
13 // so long as the above copyright and this permission statement
14 // are retained in all copies.
16 // Licence: wxWindows licence
17 ///////////////////////////////////////////////////////////////////////////////
19 // ============================================================================
21 // ============================================================================
23 // ----------------------------------------------------------------------------
25 // ----------------------------------------------------------------------------
27 // For compilers that support precompilation, includes "wx.h".
28 #include "wx/wxprec.h"
34 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
38 #include "wx/msw/wrapwin.h"
40 #include "wx/string.h"
43 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
44 #include "wx/module.h"
48 #include "wx/thread.h"
49 #include "wx/tokenzr.h"
60 #include "wx/datetime.h"
62 // ============================================================================
63 // implementation of wxDateTime
64 // ============================================================================
66 // ----------------------------------------------------------------------------
67 // helpers shared between datetime.cpp and datetimefmt.cpp
68 // ----------------------------------------------------------------------------
70 extern void InitTm(struct tm
& tm
);
72 extern int GetTimeZone();
74 extern wxString
CallStrftime(const wxString
& format
, const tm
* tm
);
76 // ----------------------------------------------------------------------------
77 // constants (see also datetime.cpp)
78 // ----------------------------------------------------------------------------
80 static const int DAYS_PER_WEEK
= 7;
82 static const int HOURS_PER_DAY
= 24;
84 static const int SEC_PER_MIN
= 60;
86 static const int MIN_PER_HOUR
= 60;
88 // ----------------------------------------------------------------------------
90 // ----------------------------------------------------------------------------
94 #if wxUSE_UNIX && !defined(HAVE_STRPTIME_DECL)
95 // configure detected that we had strptime() but not its declaration,
96 // provide it ourselves
97 extern "C" char *strptime(const char *, const char *, struct tm
*);
100 // Unicode-friendly strptime() wrapper
101 static const wxStringCharType
*
102 CallStrptime(const wxStringCharType
*input
, const char *fmt
, tm
*tm
)
104 // the problem here is that strptime() returns pointer into the string we
105 // passed to it while we're really interested in the pointer into the
106 // original, Unicode, string so we try to transform the pointer back
107 #if wxUSE_UNICODE_WCHAR
108 wxCharBuffer
inputMB(wxConvertWX2MB(input
));
110 const char * const inputMB
= input
;
111 #endif // Unicode/Ascii
113 const char *result
= strptime(inputMB
, fmt
, tm
);
117 #if wxUSE_UNICODE_WCHAR
118 // FIXME: this is wrong in presence of surrogates &c
119 return input
+ (result
- inputMB
.data());
122 #endif // Unicode/Ascii
125 #endif // HAVE_STRPTIME
127 // return the month if the string is a month name or Inv_Month otherwise
128 static wxDateTime::Month
GetMonthFromName(const wxString
& name
, int flags
)
130 wxDateTime::Month mon
;
131 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
133 // case-insensitive comparison either one of or with both abbreviated
135 if ( flags
& wxDateTime::Name_Full
)
137 if ( name
.CmpNoCase(wxDateTime::
138 GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 )
144 if ( flags
& wxDateTime::Name_Abbr
)
146 if ( name
.CmpNoCase(wxDateTime::
147 GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 )
157 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
158 static wxDateTime::WeekDay
GetWeekDayFromName(const wxString
& name
, int flags
)
160 wxDateTime::WeekDay wd
;
161 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
163 // case-insensitive comparison either one of or with both abbreviated
165 if ( flags
& wxDateTime::Name_Full
)
167 if ( name
.CmpNoCase(wxDateTime::
168 GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 )
174 if ( flags
& wxDateTime::Name_Abbr
)
176 if ( name
.CmpNoCase(wxDateTime::
177 GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 )
187 // scans all digits (but no more than len) and returns the resulting number
188 static bool GetNumericToken(size_t len
,
189 const wxStringCharType
*& p
,
190 unsigned long *number
)
194 while ( wxIsdigit(*p
) )
198 if ( len
&& ++n
> len
)
202 return !s
.empty() && s
.ToULong(number
);
205 // scans all alphabetic characters and returns the resulting string
206 static wxString
GetAlphaToken(const wxStringCharType
*& p
)
209 while ( wxIsalpha(*p
) )
217 // ----------------------------------------------------------------------------
218 // wxDateTime to/from text representations
219 // ----------------------------------------------------------------------------
221 wxString
wxDateTime::Format(const wxString
& format
, const TimeZone
& tz
) const
223 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
224 _T("NULL format in wxDateTime::Format") );
226 // we have to use our own implementation if the date is out of range of
227 // strftime() or if we use non standard specificators
229 time_t time
= GetTicks();
231 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
236 if ( tz
.GetOffset() == -GetTimeZone() )
238 // we are working with local time
239 tm
= wxLocaltime_r(&time
, &tmstruct
);
241 // should never happen
242 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxLocaltime_r() failed") );
246 time
+= (int)tz
.GetOffset();
248 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
249 int time2
= (int) time
;
255 tm
= wxGmtime_r(&time
, &tmstruct
);
257 // should never happen
258 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxGmtime_r() failed") );
262 tm
= (struct tm
*)NULL
;
268 return CallStrftime(format
, tm
);
271 //else: use generic code below
272 #endif // HAVE_STRFTIME
274 // we only parse ANSI C format specifications here, no POSIX 2
275 // complications, no GNU extensions but we do add support for a "%l" format
276 // specifier allowing to get the number of milliseconds
279 // used for calls to strftime() when we only deal with time
280 struct tm tmTimeOnly
;
281 tmTimeOnly
.tm_hour
= tm
.hour
;
282 tmTimeOnly
.tm_min
= tm
.min
;
283 tmTimeOnly
.tm_sec
= tm
.sec
;
284 tmTimeOnly
.tm_wday
= 0;
285 tmTimeOnly
.tm_yday
= 0;
286 tmTimeOnly
.tm_mday
= 1; // any date will do
287 tmTimeOnly
.tm_mon
= 0;
288 tmTimeOnly
.tm_year
= 76;
289 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
291 wxString tmp
, res
, fmt
;
292 for ( wxString::const_iterator p
= format
.begin(); p
!= format
.end(); ++p
)
302 // set the default format
303 switch ( (*++p
).GetValue() )
305 case _T('Y'): // year has 4 digits
309 case _T('j'): // day of year has 3 digits
310 case _T('l'): // milliseconds have 3 digits
314 case _T('w'): // week day as number has only one
319 // it's either another valid format specifier in which case
320 // the format is "%02d" (for all the rest) or we have the
321 // field width preceding the format in which case it will
322 // override the default format anyhow
331 // start of the format specification
332 switch ( (*p
).GetValue() )
334 case _T('a'): // a weekday name
336 // second parameter should be true for abbreviated names
337 res
+= GetWeekDayName(tm
.GetWeekDay(),
338 *p
== _T('a') ? Name_Abbr
: Name_Full
);
341 case _T('b'): // a month name
343 res
+= GetMonthName(tm
.mon
,
344 *p
== _T('b') ? Name_Abbr
: Name_Full
);
347 case _T('c'): // locale default date and time representation
348 case _T('x'): // locale default date representation
351 // the problem: there is no way to know what do these format
352 // specifications correspond to for the current locale.
354 // the solution: use a hack and still use strftime(): first
355 // find the YEAR which is a year in the strftime() range (1970
356 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
357 // of the real year. Then make a copy of the format and
358 // replace all occurrences of YEAR in it with some unique
359 // string not appearing anywhere else in it, then use
360 // strftime() to format the date in year YEAR and then replace
361 // YEAR back by the real year and the unique replacement
362 // string back with YEAR. Notice that "all occurrences of YEAR"
363 // means all occurrences of 4 digit as well as 2 digit form!
365 // the bugs: we assume that neither of %c nor %x contains any
366 // fields which may change between the YEAR and real year. For
367 // example, the week number (%U, %W) and the day number (%j)
368 // will change if one of these years is leap and the other one
371 // find the YEAR: normally, for any year X, Jan 1 of the
372 // year X + 28 is the same weekday as Jan 1 of X (because
373 // the weekday advances by 1 for each normal X and by 2
374 // for each leap X, hence by 5 every 4 years or by 35
375 // which is 0 mod 7 every 28 years) but this rule breaks
376 // down if there are years between X and Y which are
377 // divisible by 4 but not leap (i.e. divisible by 100 but
378 // not 400), hence the correction.
380 int yearReal
= GetYear(tz
);
381 int mod28
= yearReal
% 28;
383 // be careful to not go too far - we risk to leave the
388 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
392 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
395 int nCentury
= year
/ 100,
396 nCenturyReal
= yearReal
/ 100;
398 // need to adjust for the years divisble by 400 which are
399 // not leap but are counted like leap ones if we just take
400 // the number of centuries in between for nLostWeekDays
401 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
402 (nCentury
/ 4 - nCenturyReal
/ 4);
404 // we have to gain back the "lost" weekdays: note that the
405 // effect of this loop is to not do anything to
406 // nLostWeekDays (which we won't use any more), but to
407 // (indirectly) set the year correctly
408 while ( (nLostWeekDays
% 7) != 0 )
410 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
413 // finally move the year below 2000 so that the 2-digit
414 // year number can never match the month or day of the
415 // month when we do the replacements below
419 wxASSERT_MSG( year
>= 1970 && year
< 2000,
420 _T("logic error in wxDateTime::Format") );
423 // use strftime() to format the same date but in supported
426 // NB: we assume that strftime() doesn't check for the
427 // date validity and will happily format the date
428 // corresponding to Feb 29 of a non leap year (which
429 // may happen if yearReal was leap and year is not)
430 struct tm tmAdjusted
;
432 tmAdjusted
.tm_hour
= tm
.hour
;
433 tmAdjusted
.tm_min
= tm
.min
;
434 tmAdjusted
.tm_sec
= tm
.sec
;
435 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
436 tmAdjusted
.tm_yday
= GetDayOfYear();
437 tmAdjusted
.tm_mday
= tm
.mday
;
438 tmAdjusted
.tm_mon
= tm
.mon
;
439 tmAdjusted
.tm_year
= year
- 1900;
440 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
441 wxString str
= CallStrftime(*p
== _T('c') ? _T("%c")
445 // now replace the replacement year with the real year:
446 // notice that we have to replace the 4 digit year with
447 // a unique string not appearing in strftime() output
448 // first to prevent the 2 digit year from matching any
449 // substring of the 4 digit year (but any day, month,
450 // hours or minutes components should be safe because
451 // they are never in 70-99 range)
452 wxString
replacement("|");
453 while ( str
.find(replacement
) != wxString::npos
)
456 str
.Replace(wxString::Format("%d", year
),
458 str
.Replace(wxString::Format("%d", year
% 100),
459 wxString::Format("%d", yearReal
% 100));
460 str
.Replace(replacement
,
461 wxString::Format("%d", yearReal
));
465 #else // !HAVE_STRFTIME
466 // Use "%m/%d/%y %H:%M:%S" format instead
467 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
468 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
469 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
472 case _T('d'): // day of a month (01-31)
473 res
+= wxString::Format(fmt
, tm
.mday
);
476 case _T('H'): // hour in 24h format (00-23)
477 res
+= wxString::Format(fmt
, tm
.hour
);
480 case _T('I'): // hour in 12h format (01-12)
482 // 24h -> 12h, 0h -> 12h too
483 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
484 : tm
.hour
? tm
.hour
: 12;
485 res
+= wxString::Format(fmt
, hour12
);
489 case _T('j'): // day of the year
490 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
493 case _T('l'): // milliseconds (NOT STANDARD)
494 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
497 case _T('m'): // month as a number (01-12)
498 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
501 case _T('M'): // minute as a decimal number (00-59)
502 res
+= wxString::Format(fmt
, tm
.min
);
505 case _T('p'): // AM or PM string
507 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
508 #else // !HAVE_STRFTIME
509 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
510 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
513 case _T('S'): // second as a decimal number (00-61)
514 res
+= wxString::Format(fmt
, tm
.sec
);
517 case _T('U'): // week number in the year (Sunday 1st week day)
518 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
521 case _T('W'): // week number in the year (Monday 1st week day)
522 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
525 case _T('w'): // weekday as a number (0-6), Sunday = 0
526 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
529 // case _T('x'): -- handled with "%c"
531 case _T('X'): // locale default time representation
532 // just use strftime() to format the time for us
534 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
535 #else // !HAVE_STRFTIME
536 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
537 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
540 case _T('y'): // year without century (00-99)
541 res
+= wxString::Format(fmt
, tm
.year
% 100);
544 case _T('Y'): // year with century
545 res
+= wxString::Format(fmt
, tm
.year
);
548 case _T('Z'): // timezone name
550 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
555 // is it the format width?
557 while ( *p
== _T('-') || *p
== _T('+') ||
558 *p
== _T(' ') || wxIsdigit(*p
) )
565 // we've only got the flags and width so far in fmt
566 fmt
.Prepend(_T('%'));
574 // no, it wasn't the width
575 wxFAIL_MSG(_T("unknown format specificator"));
577 // fall through and just copy it nevertheless
579 case _T('%'): // a percent sign
583 case 0: // the end of string
584 wxFAIL_MSG(_T("missing format at the end of string"));
586 // just put the '%' which was the last char in format
596 // this function parses a string in (strict) RFC 822 format: see the section 5
597 // of the RFC for the detailed description, but briefly it's something of the
598 // form "Sat, 18 Dec 1999 00:48:30 +0100"
600 // this function is "strict" by design - it must reject anything except true
601 // RFC822 time specs.
603 // TODO a great candidate for using reg exps
605 wxDateTime::ParseRfc822Date(const wxString
& date
, wxString::const_iterator
*end
)
607 // TODO: rewrite using iterators instead of wxChar pointers
608 const wxStringCharType
*p
= date
.wx_str();
609 const wxStringCharType
*comma
= wxStrchr(p
, wxS(','));
612 // the part before comma is the weekday
614 // skip it for now - we don't use but might check that it really
615 // corresponds to the specfied date
620 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
628 // the following 1 or 2 digits are the day number
629 if ( !wxIsdigit(*p
) )
631 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
636 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - _T('0'));
640 day
= (wxDateTime_t
)(day
+ (*p
++ - _T('0')));
643 if ( *p
++ != _T(' ') )
648 // the following 3 letters specify the month
649 wxString
monName(p
, 3);
651 if ( monName
== _T("Jan") )
653 else if ( monName
== _T("Feb") )
655 else if ( monName
== _T("Mar") )
657 else if ( monName
== _T("Apr") )
659 else if ( monName
== _T("May") )
661 else if ( monName
== _T("Jun") )
663 else if ( monName
== _T("Jul") )
665 else if ( monName
== _T("Aug") )
667 else if ( monName
== _T("Sep") )
669 else if ( monName
== _T("Oct") )
671 else if ( monName
== _T("Nov") )
673 else if ( monName
== _T("Dec") )
677 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str());
684 if ( *p
++ != _T(' ') )
690 if ( !wxIsdigit(*p
) )
696 int year
= *p
++ - _T('0');
698 if ( !wxIsdigit(*p
) )
700 // should have at least 2 digits in the year
705 year
+= *p
++ - _T('0');
707 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
711 year
+= *p
++ - _T('0');
713 if ( !wxIsdigit(*p
) )
715 // no 3 digit years please
720 year
+= *p
++ - _T('0');
723 if ( *p
++ != _T(' ') )
728 // time is in the format hh:mm:ss and seconds are optional
729 if ( !wxIsdigit(*p
) )
734 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - _T('0'));
736 if ( !wxIsdigit(*p
) )
742 hour
= (wxDateTime_t
)(hour
+ (*p
++ - _T('0')));
744 if ( *p
++ != _T(':') )
749 if ( !wxIsdigit(*p
) )
754 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - _T('0'));
756 if ( !wxIsdigit(*p
) )
762 min
= (wxDateTime_t
)(min
+ *p
++ - _T('0'));
764 wxDateTime_t sec
= 0;
768 if ( !wxIsdigit(*p
) )
773 sec
= (wxDateTime_t
)(*p
++ - _T('0'));
775 if ( !wxIsdigit(*p
) )
781 sec
= (wxDateTime_t
)(sec
+ *p
++ - _T('0'));
784 if ( *p
++ != _T(' ') )
789 // and now the interesting part: the timezone
790 int offset
wxDUMMY_INITIALIZE(0);
791 if ( *p
== _T('-') || *p
== _T('+') )
793 // the explicit offset given: it has the form of hhmm
794 bool plus
= *p
++ == _T('+');
796 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
802 offset
= MIN_PER_HOUR
*(10*(*p
- _T('0')) + (*(p
+ 1) - _T('0')));
806 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
812 offset
+= 10*(*p
- _T('0')) + (*(p
+ 1) - _T('0'));
823 // the symbolic timezone given: may be either military timezone or one
824 // of standard abbreviations
827 // military: Z = UTC, J unused, A = -1, ..., Y = +12
828 static const int offsets
[26] =
830 //A B C D E F G H I J K L M
831 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
832 //N O P R Q S T U V W Z Y Z
833 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
836 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
838 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
);
843 offset
= offsets
[*p
++ - _T('A')];
849 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
851 else if ( tz
== _T("AST") )
853 else if ( tz
== _T("ADT") )
855 else if ( tz
== _T("EST") )
857 else if ( tz
== _T("EDT") )
859 else if ( tz
== _T("CST") )
861 else if ( tz
== _T("CDT") )
863 else if ( tz
== _T("MST") )
865 else if ( tz
== _T("MDT") )
867 else if ( tz
== _T("PST") )
869 else if ( tz
== _T("PDT") )
873 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
);
882 offset
*= MIN_PER_HOUR
;
885 // the spec was correct, construct the date from the values we found
886 Set(day
, mon
, year
, hour
, min
, sec
);
887 MakeFromTimezone(TimeZone::Make(offset
*SEC_PER_MIN
));
889 const size_t endpos
= p
- date
.wx_str();
891 *end
= date
.begin() + endpos
;
893 return date
.c_str() + endpos
;
898 // returns the string containing strftime() format used for short dates in the
899 // current locale or an empty string
900 static wxString
GetLocaleDateFormat()
904 // there is no setlocale() under Windows CE, so just always query the
907 if ( strcmp(setlocale(LC_ALL
, NULL
), "C") != 0 )
910 // The locale was programatically set to non-C. We assume that this was
911 // done using wxLocale, in which case thread's current locale is also
912 // set to correct LCID value and we can use GetLocaleInfo to determine
913 // the correct formatting string:
915 LCID lcid
= LOCALE_USER_DEFAULT
;
917 LCID lcid
= GetThreadLocale();
919 // according to MSDN 80 chars is max allowed for short date format
921 if ( ::GetLocaleInfo(lcid
, LOCALE_SSHORTDATE
, fmt
, WXSIZEOF(fmt
)) )
923 wxChar chLast
= _T('\0');
924 size_t lastCount
= 0;
925 for ( const wxChar
*p
= fmt
; /* NUL handled inside */; p
++ )
935 // these characters come in groups, start counting them
945 // first deal with any special characters we have had
955 // these two are the same as we
956 // don't distinguish between 1 and
970 wxFAIL_MSG( _T("too many 'd's") );
979 // as for 'd' and 'dd' above
992 wxFAIL_MSG( _T("too many 'M's") );
1009 wxFAIL_MSG( _T("wrong number of 'y's") );
1014 // strftime() doesn't have era string,
1015 // ignore this format
1016 wxASSERT_MSG( lastCount
<= 2,
1017 _T("too many 'g's") );
1021 wxFAIL_MSG( _T("unreachable") );
1028 // not a special character so must be just a separator,
1030 if ( *p
!= _T('\0') )
1032 if ( *p
== _T('%') )
1034 // this one needs to be escaped
1042 if ( *p
== _T('\0') )
1046 //else: GetLocaleInfo() failed, leave fmtDate value unchanged and
1047 // try our luck with the default formats
1049 //else: default C locale, default formats should work
1054 #endif // __WINDOWS__
1057 wxDateTime::ParseFormat(const wxString
& date
,
1058 const wxString
& format
,
1059 const wxDateTime
& dateDef
,
1060 wxString::const_iterator
*end
)
1062 wxCHECK_MSG( !format
.empty(), NULL
, "format can't be empty" );
1067 // what fields have we found?
1068 bool haveWDay
= false,
1078 bool hourIsIn12hFormat
= false, // or in 24h one?
1079 isPM
= false; // AM by default
1081 // and the value of the items we have (init them to get rid of warnings)
1082 wxDateTime_t msec
= 0,
1086 WeekDay wday
= Inv_WeekDay
;
1087 wxDateTime_t yday
= 0,
1089 wxDateTime::Month mon
= Inv_Month
;
1092 const wxStringCharType
*input
= date
.wx_str();
1093 for ( wxString::const_iterator fmt
= format
.begin(); fmt
!= format
.end(); ++fmt
)
1095 if ( *fmt
!= _T('%') )
1097 if ( wxIsspace(*fmt
) )
1099 // a white space in the format string matches 0 or more white
1100 // spaces in the input
1101 while ( wxIsspace(*input
) )
1108 // any other character (not whitespace, not '%') must be
1109 // matched by itself in the input
1110 if ( *input
++ != *fmt
)
1117 // done with this format char
1121 // start of a format specification
1123 // parse the optional width
1125 while ( wxIsdigit(*++fmt
) )
1128 width
+= *fmt
- _T('0');
1131 // the default widths for the various fields
1134 switch ( (*fmt
).GetValue() )
1136 case _T('Y'): // year has 4 digits
1140 case _T('j'): // day of year has 3 digits
1141 case _T('l'): // milliseconds have 3 digits
1145 case _T('w'): // week day as number has only one
1150 // default for all other fields
1155 // then the format itself
1156 switch ( (*fmt
).GetValue() )
1158 case _T('a'): // a weekday name
1161 int flag
= *fmt
== _T('a') ? Name_Abbr
: Name_Full
;
1162 wday
= GetWeekDayFromName(GetAlphaToken(input
), flag
);
1163 if ( wday
== Inv_WeekDay
)
1172 case _T('b'): // a month name
1175 int flag
= *fmt
== _T('b') ? Name_Abbr
: Name_Full
;
1176 mon
= GetMonthFromName(GetAlphaToken(input
), flag
);
1177 if ( mon
== Inv_Month
)
1186 case _T('c'): // locale default date and time representation
1190 const wxString
inc(input
);
1192 // NOTE: %c is locale-dependent; try strptime
1193 #ifdef HAVE_STRPTIME
1196 // try using strptime() -- it may fail even if the input is
1197 // correct but the date is out of range, so we will fall back
1198 // to our generic code anyhow
1199 const wxStringCharType
*
1200 result
= CallStrptime(input
, "%c", &tm
);
1203 haveDay
= haveMon
= haveYear
=
1204 haveHour
= haveMin
= haveSec
= true;
1210 year
= 1900 + tm
.tm_year
;
1211 mon
= (Month
)tm
.tm_mon
;
1214 input
= result
; // proceed where strptime() ended
1218 // strptime() failed; try generic heuristic code
1219 #endif // HAVE_STRPTIME
1221 // try the format which corresponds to ctime() output first
1222 wxString::const_iterator endc
;
1223 if ( !dt
.ParseFormat(inc
, wxS("%a %b %d %H:%M:%S %Y"), &endc
) &&
1224 !dt
.ParseFormat(inc
, wxS("%x %X"), &endc
) &&
1225 !dt
.ParseFormat(inc
, wxS("%X %x"), &endc
) )
1227 // we've tried everything and still no match
1233 haveDay
= haveMon
= haveYear
=
1234 haveHour
= haveMin
= haveSec
= true;
1244 input
+= endc
- inc
.begin();
1245 #ifdef HAVE_STRPTIME
1247 #endif // HAVE_STRPTIME
1251 case _T('d'): // day of a month (01-31)
1252 if ( !GetNumericToken(width
, input
, &num
) ||
1253 (num
> 31) || (num
< 1) )
1259 // we can't check whether the day range is correct yet, will
1260 // do it later - assume ok for now
1262 mday
= (wxDateTime_t
)num
;
1265 case _T('H'): // hour in 24h format (00-23)
1266 if ( !GetNumericToken(width
, input
, &num
) || (num
> 23) )
1273 hour
= (wxDateTime_t
)num
;
1276 case _T('I'): // hour in 12h format (01-12)
1277 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
1284 hourIsIn12hFormat
= true;
1285 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
1288 case _T('j'): // day of the year
1289 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 366) )
1296 yday
= (wxDateTime_t
)num
;
1299 case _T('l'): // milliseconds (0-999)
1300 if ( !GetNumericToken(width
, input
, &num
) )
1304 msec
= (wxDateTime_t
)num
;
1307 case _T('m'): // month as a number (01-12)
1308 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
1315 mon
= (Month
)(num
- 1);
1318 case _T('M'): // minute as a decimal number (00-59)
1319 if ( !GetNumericToken(width
, input
, &num
) || (num
> 59) )
1326 min
= (wxDateTime_t
)num
;
1329 case _T('p'): // AM or PM string
1331 wxString am
, pm
, token
= GetAlphaToken(input
);
1333 // some locales have empty AM/PM tokens and thus when formatting
1334 // dates with the %p specifier nothing is generated; when trying to
1335 // parse them back, we get an empty token here... but that's not
1340 GetAmPmStrings(&am
, &pm
);
1341 if (am
.empty() && pm
.empty())
1342 return NULL
; // no am/pm strings defined
1343 if ( token
.CmpNoCase(pm
) == 0 )
1347 else if ( token
.CmpNoCase(am
) != 0 )
1355 case _T('r'): // time as %I:%M:%S %p
1358 input
= dt
.ParseFormat(input
, wxS("%I:%M:%S %p"));
1365 haveHour
= haveMin
= haveSec
= true;
1374 case _T('R'): // time as %H:%M
1377 input
= dt
.ParseFormat(input
, wxS("%H:%M"));
1384 haveHour
= haveMin
= true;
1392 case _T('S'): // second as a decimal number (00-61)
1393 if ( !GetNumericToken(width
, input
, &num
) || (num
> 61) )
1400 sec
= (wxDateTime_t
)num
;
1403 case _T('T'): // time as %H:%M:%S
1406 input
= dt
.ParseFormat(input
, _T("%H:%M:%S"));
1413 haveHour
= haveMin
= haveSec
= true;
1422 case _T('w'): // weekday as a number (0-6), Sunday = 0
1423 if ( !GetNumericToken(width
, input
, &num
) || (wday
> 6) )
1430 wday
= (WeekDay
)num
;
1433 case _T('x'): // locale default date representation
1434 #ifdef HAVE_STRPTIME
1435 // try using strptime() -- it may fail even if the input is
1436 // correct but the date is out of range, so we will fall back
1437 // to our generic code anyhow
1441 const wxStringCharType
*
1442 result
= CallStrptime(input
, "%x", &tm
);
1447 haveDay
= haveMon
= haveYear
= true;
1449 year
= 1900 + tm
.tm_year
;
1450 mon
= (Month
)tm
.tm_mon
;
1456 #endif // HAVE_STRPTIME
1464 // The above doesn't work for all locales, try to query
1465 // Windows for the right way of formatting the date:
1466 fmtDate
= GetLocaleDateFormat();
1467 if ( fmtDate
.empty() )
1468 #endif // __WINDOWS__
1470 if ( IsWestEuropeanCountry(GetCountry()) ||
1471 GetCountry() == Russia
)
1473 fmtDate
= _T("%d/%m/%y");
1474 fmtDateAlt
= _T("%m/%d/%y");
1478 fmtDate
= _T("%m/%d/%y");
1479 fmtDateAlt
= _T("%d/%m/%y");
1483 const wxString
indate(input
);
1484 wxString::const_iterator endDate
;
1485 if ( !dt
.ParseFormat(indate
, fmtDate
, &endDate
) )
1487 // try another one if we have it
1488 if ( fmtDateAlt
.empty() ||
1489 !dt
.ParseFormat(indate
, fmtDateAlt
, &endDate
) )
1505 input
+= endDate
- indate
.begin();
1510 case _T('X'): // locale default time representation
1511 #ifdef HAVE_STRPTIME
1513 // use strptime() to do it for us (FIXME !Unicode friendly)
1515 input
= CallStrptime(input
, "%X", &tm
);
1521 haveHour
= haveMin
= haveSec
= true;
1527 #else // !HAVE_STRPTIME
1528 // TODO under Win32 we can query the LOCALE_ITIME system
1529 // setting which says whether the default time format is
1532 // try to parse what follows as "%H:%M:%S" and, if this
1533 // fails, as "%I:%M:%S %p" - this should catch the most
1537 const wxStringCharType
*
1538 result
= dt
.ParseFormat(input
, wxS("%T"));
1541 result
= dt
.ParseFormat(input
, wxS("%r"));
1561 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
1564 case _T('y'): // year without century (00-99)
1565 if ( !GetNumericToken(width
, input
, &num
) || (num
> 99) )
1573 // TODO should have an option for roll over date instead of
1574 // hard coding it here
1575 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
1578 case _T('Y'): // year with century
1579 if ( !GetNumericToken(width
, input
, &num
) )
1586 year
= (wxDateTime_t
)num
;
1589 case _T('Z'): // timezone name
1590 wxFAIL_MSG(_T("TODO"));
1593 case _T('%'): // a percent sign
1594 if ( *input
++ != _T('%') )
1601 case 0: // the end of string
1602 wxFAIL_MSG(_T("unexpected format end"));
1606 default: // not a known format spec
1611 // format matched, try to construct a date from what we have now
1613 if ( dateDef
.IsValid() )
1615 // take this date as default
1616 tmDef
= dateDef
.GetTm();
1618 else if ( IsValid() )
1620 // if this date is valid, don't change it
1625 // no default and this date is invalid - fall back to Today()
1626 tmDef
= Today().GetTm();
1642 // TODO we don't check here that the values are consistent, if both year
1643 // day and month/day were found, we just ignore the year day and we
1644 // also always ignore the week day
1647 if ( mday
> GetNumberOfDays(tm
.mon
, tm
.year
) )
1649 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
1656 else if ( haveYDay
)
1658 if ( yday
> GetNumberOfDays(tm
.year
) )
1660 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
1665 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
1672 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
1674 // translate to 24hour format
1677 //else: either already in 24h format or no translation needed
1700 // finally check that the week day is consistent -- if we had it
1701 if ( haveWDay
&& GetWeekDay() != wday
)
1703 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
1708 const size_t endpos
= input
- date
.wx_str();
1710 *end
= date
.begin() + endpos
;
1712 return date
.c_str() + endpos
;
1716 wxDateTime::ParseDateTime(const wxString
& date
, wxString::const_iterator
*end
)
1718 // Set to current day and hour, so strings like '14:00' becomes today at
1719 // 14, not some other random date
1720 wxDateTime dtDate
= wxDateTime::Today();
1721 wxDateTime dtTime
= wxDateTime::Today();
1723 wxString::const_iterator
1728 // If we got a date in the beginning, see if there is a time specified
1730 if ( dtDate
.ParseDate(date
, &endDate
) )
1732 // Skip spaces, as the ParseTime() function fails on spaces
1733 while ( endDate
!= date
.end() && wxIsspace(*endDate
) )
1736 const wxString
timestr(endDate
, date
.end());
1737 if ( !dtTime
.ParseTime(timestr
, &endTime
) )
1740 endBoth
= endDate
+ (endTime
- timestr
.begin());
1742 else // no date in the beginning
1744 // check if we have a time followed by a date
1745 if ( !dtTime
.ParseTime(date
, &endTime
) )
1748 while ( endTime
!= date
.end() && wxIsspace(*endTime
) )
1751 const wxString
datestr(endTime
, date
.end());
1752 if ( !dtDate
.ParseDate(datestr
, &endDate
) )
1755 endBoth
= endTime
+ (endDate
- datestr
.begin());
1758 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
1759 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
1760 dtTime
.GetMillisecond());
1762 // Return endpoint of scan
1766 return date
.c_str() + (endBoth
- date
.begin());
1770 wxDateTime::ParseDate(const wxString
& date
, wxString::const_iterator
*end
)
1772 // this is a simplified version of ParseDateTime() which understands only
1773 // "today" (for wxDate compatibility) and digits only otherwise (and not
1774 // all esoteric constructions ParseDateTime() knows about)
1776 const wxStringCharType
*p
= date
.wx_str();
1777 while ( wxIsspace(*p
) )
1780 // some special cases
1784 int dayDiffFromToday
;
1787 { wxTRANSLATE("today"), 0 },
1788 { wxTRANSLATE("yesterday"), -1 },
1789 { wxTRANSLATE("tomorrow"), 1 },
1792 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
1794 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
1795 size_t len
= dateStr
.length();
1796 if ( wxStrlen(p
) >= len
)
1798 wxString
str(p
, len
);
1799 if ( str
.CmpNoCase(dateStr
) == 0 )
1801 // nothing can follow this, so stop here
1804 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
1806 if ( dayDiffFromToday
)
1808 *this += wxDateSpan::Days(dayDiffFromToday
);
1811 const size_t endpos
= p
- date
.wx_str();
1814 *end
= date
.begin() + endpos
;
1815 return date
.c_str() + endpos
;
1820 // We try to guess what we have here: for each new (numeric) token, we
1821 // determine if it can be a month, day or a year. Of course, there is an
1822 // ambiguity as some numbers may be days as well as months, so we also
1823 // have the ability to back track.
1826 bool haveDay
= false, // the months day?
1827 haveWDay
= false, // the day of week?
1828 haveMon
= false, // the month?
1829 haveYear
= false; // the year?
1831 // and the value of the items we have (init them to get rid of warnings)
1832 WeekDay wday
= Inv_WeekDay
;
1833 wxDateTime_t day
= 0;
1834 wxDateTime::Month mon
= Inv_Month
;
1837 // tokenize the string
1839 static const wxStringCharType
*dateDelimiters
= wxS(".,/-\t\r\n ");
1840 wxStringTokenizer
tok(p
, dateDelimiters
);
1841 while ( tok
.HasMoreTokens() )
1843 wxString token
= tok
.GetNextToken();
1849 if ( token
.ToULong(&val
) )
1851 // guess what this number is
1857 if ( !haveMon
&& val
> 0 && val
<= 12 )
1859 // assume it is month
1862 else // not the month
1866 // this can only be the year
1869 else // may be either day or year
1871 // use a leap year if we don't have the year yet to allow
1872 // dates like 2/29/1976 which would be rejected otherwise
1873 wxDateTime_t max_days
= (wxDateTime_t
)(
1875 ? GetNumberOfDays(mon
, haveYear
? year
: 1976)
1880 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
1885 else // yes, suppose it's the day
1899 year
= (wxDateTime_t
)val
;
1908 day
= (wxDateTime_t
)val
;
1914 mon
= (Month
)(val
- 1);
1917 else // not a number
1919 // be careful not to overwrite the current mon value
1920 Month mon2
= GetMonthFromName(token
, Name_Full
| Name_Abbr
);
1921 if ( mon2
!= Inv_Month
)
1926 // but we already have a month - maybe we guessed wrong?
1929 // no need to check in month range as always < 12, but
1930 // the days are counted from 1 unlike the months
1931 day
= (wxDateTime_t
)(mon
+ 1);
1936 // could possible be the year (doesn't the year come
1937 // before the month in the japanese format?) (FIXME)
1946 else // not a valid month name
1948 WeekDay wday2
= GetWeekDayFromName(token
, Name_Full
| Name_Abbr
);
1949 if ( wday2
!= Inv_WeekDay
)
1961 else // not a valid weekday name
1964 static const char *ordinals
[] =
1966 wxTRANSLATE("first"),
1967 wxTRANSLATE("second"),
1968 wxTRANSLATE("third"),
1969 wxTRANSLATE("fourth"),
1970 wxTRANSLATE("fifth"),
1971 wxTRANSLATE("sixth"),
1972 wxTRANSLATE("seventh"),
1973 wxTRANSLATE("eighth"),
1974 wxTRANSLATE("ninth"),
1975 wxTRANSLATE("tenth"),
1976 wxTRANSLATE("eleventh"),
1977 wxTRANSLATE("twelfth"),
1978 wxTRANSLATE("thirteenth"),
1979 wxTRANSLATE("fourteenth"),
1980 wxTRANSLATE("fifteenth"),
1981 wxTRANSLATE("sixteenth"),
1982 wxTRANSLATE("seventeenth"),
1983 wxTRANSLATE("eighteenth"),
1984 wxTRANSLATE("nineteenth"),
1985 wxTRANSLATE("twentieth"),
1986 // that's enough - otherwise we'd have problems with
1987 // composite (or not) ordinals
1991 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
1993 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
1999 if ( n
== WXSIZEOF(ordinals
) )
2001 // stop here - something unknown
2008 // don't try anything here (as in case of numeric day
2009 // above) - the symbolic day spec should always
2010 // precede the month/year
2016 day
= (wxDateTime_t
)(n
+ 1);
2021 nPosCur
= tok
.GetPosition();
2024 // either no more tokens or the scan was stopped by something we couldn't
2025 // parse - in any case, see if we can construct a date from what we have
2026 if ( !haveDay
&& !haveWDay
)
2028 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
2033 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
2034 !(haveDay
&& haveMon
&& haveYear
) )
2036 // without adjectives (which we don't support here) the week day only
2037 // makes sense completely separately or with the full date
2038 // specification (what would "Wed 1999" mean?)
2042 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
2044 // may be we have month and day instead of day and year?
2045 if ( haveDay
&& !haveMon
)
2049 // exchange day and month
2050 mon
= (wxDateTime::Month
)(day
- 1);
2052 // we're in the current year then
2053 if ( (year
> 0) && (year
<= (int)GetNumberOfDays(mon
, Inv_Year
)) )
2055 day
= (wxDateTime_t
)year
;
2060 //else: no, can't exchange, leave haveMon == false
2066 // if we give the year, month and day must be given too
2067 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
2075 mon
= GetCurrentMonth();
2080 year
= GetCurrentYear();
2085 // normally we check the day above but the check is optimistic in case
2086 // we find the day before its month/year so we have to redo it now
2087 if ( day
> GetNumberOfDays(mon
, year
) )
2090 Set(day
, mon
, year
);
2094 // check that it is really the same
2095 if ( GetWeekDay() != wday
)
2097 // inconsistency detected
2098 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
2108 SetToWeekDayInSameWeek(wday
);
2111 // return the pointer to the first unparsed char
2113 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
2115 // if we couldn't parse the token after the delimiter, put back the
2116 // delimiter as well
2120 const size_t endpos
= p
- date
.wx_str();
2122 *end
= date
.begin() + endpos
;
2124 return date
.c_str() + endpos
;
2128 wxDateTime::ParseTime(const wxString
& time
, wxString::const_iterator
*end
)
2130 // first try some extra things
2137 { wxTRANSLATE("noon"), 12 },
2138 { wxTRANSLATE("midnight"), 00 },
2142 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
2144 wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
2145 size_t len
= timeString
.length();
2146 if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 )
2148 // casts required by DigitalMars
2149 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
2152 *end
= time
.begin() + len
;
2154 return time
.c_str() + len
;
2158 // try all time formats we may think about in the order from longest to
2160 static const char *timeFormats
[] =
2162 "%I:%M:%S %p", // 12hour with AM/PM
2163 "%H:%M:%S", // could be the same or 24 hour one so try it too
2164 "%I:%M %p", // 12hour with AM/PM but without seconds
2165 "%H:%M:%S", // and a possibly 24 hour version without seconds
2166 "%X", // possibly something from above or maybe something
2167 // completely different -- try it last
2169 // TODO: parse timezones
2172 for ( size_t nFmt
= 0; nFmt
< WXSIZEOF(timeFormats
); nFmt
++ )
2174 const char *result
= ParseFormat(time
, timeFormats
[nFmt
], end
);
2182 // ----------------------------------------------------------------------------
2183 // Workdays and holidays support
2184 // ----------------------------------------------------------------------------
2186 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
2188 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
2191 // ============================================================================
2193 // ============================================================================
2195 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
2198 return ds1
.Multiply(n
);
2201 // ============================================================================
2203 // ============================================================================
2205 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
2207 return wxTimeSpan(ts
).Multiply(n
);
2210 // this enum is only used in wxTimeSpan::Format() below but we can't declare
2211 // it locally to the method as it provokes an internal compiler error in egcs
2212 // 2.91.60 when building with -O2
2223 // not all strftime(3) format specifiers make sense here because, for example,
2224 // a time span doesn't have a year nor a timezone
2226 // Here are the ones which are supported (all of them are supported by strftime
2228 // %H hour in 24 hour format
2229 // %M minute (00 - 59)
2230 // %S second (00 - 59)
2233 // Also, for MFC CTimeSpan compatibility, we support
2234 // %D number of days
2236 // And, to be better than MFC :-), we also have
2237 // %E number of wEeks
2238 // %l milliseconds (000 - 999)
2239 wxString
wxTimeSpan::Format(const wxString
& format
) const
2241 // we deal with only positive time spans here and just add the sign in
2242 // front for the negative ones
2245 wxString
str(Negate().Format(format
));
2249 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
2250 _T("NULL format in wxTimeSpan::Format") );
2253 str
.Alloc(format
.length());
2255 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
2257 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
2258 // question is what should ts.Format("%S") do? The code here returns "3273"
2259 // in this case (i.e. the total number of seconds, not just seconds % 60)
2260 // because, for me, this call means "give me entire time interval in
2261 // seconds" and not "give me the seconds part of the time interval"
2263 // If we agree that it should behave like this, it is clear that the
2264 // interpretation of each format specifier depends on the presence of the
2265 // other format specs in the string: if there was "%H" before "%M", we
2266 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
2268 // we remember the most important unit found so far
2269 TimeSpanPart partBiggest
= Part_MSec
;
2271 for ( wxString::const_iterator pch
= format
.begin(); pch
!= format
.end(); ++pch
)
2275 if ( ch
== _T('%') )
2277 // the start of the format specification of the printf() below
2278 wxString
fmtPrefix(_T('%'));
2283 // the number of digits for the format string, 0 if unused
2284 unsigned digits
= 0;
2286 ch
= *++pch
; // get the format spec char
2290 wxFAIL_MSG( _T("invalid format character") );
2296 // skip the part below switch
2301 if ( partBiggest
< Part_Day
)
2307 partBiggest
= Part_Day
;
2312 partBiggest
= Part_Week
;
2318 if ( partBiggest
< Part_Hour
)
2324 partBiggest
= Part_Hour
;
2331 n
= GetMilliseconds().ToLong();
2332 if ( partBiggest
< Part_MSec
)
2336 //else: no need to reset partBiggest to Part_MSec, it is
2337 // the least significant one anyhow
2344 if ( partBiggest
< Part_Min
)
2350 partBiggest
= Part_Min
;
2357 n
= GetSeconds().ToLong();
2358 if ( partBiggest
< Part_Sec
)
2364 partBiggest
= Part_Sec
;
2373 fmtPrefix
<< _T("0") << digits
;
2376 str
+= wxString::Format(fmtPrefix
+ _T("ld"), n
);
2380 // normal character, just copy
2388 #endif // wxUSE_DATETIME