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 // ----------------------------------------------------------------------------
97 #if wxUSE_UNIX && !defined(HAVE_STRPTIME_DECL)
98 // configure detected that we had strptime() but not its declaration,
99 // provide it ourselves
100 extern "C" char *strptime(const char *, const char *, struct tm
*);
103 // strptime() wrapper: call strptime() for the string starting at the given
104 // iterator and fill output tm struct with the results and modify input to
105 // point to the end of the string consumed by strptime() if successful,
106 // otherwise return false and don't modify anything
108 CallStrptime(const wxString
& str
,
109 wxString::const_iterator
& p
,
113 const char *start
= str
.mb_str();
114 start
= wxStringOperations::AddToIter(start
, p
- str
.begin());
116 const char * const end
= strptime(start
, fmt
, tm
);
120 p
+= wxStringOperations::DiffIters(end
, start
);
124 #endif // HAVE_STRPTIME
126 // return the month if the string is a month name or Inv_Month otherwise
127 wxDateTime::Month
GetMonthFromName(const wxString
& name
, int flags
)
129 wxDateTime::Month mon
;
130 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
132 // case-insensitive comparison either one of or with both abbreviated
134 if ( flags
& wxDateTime::Name_Full
)
136 if ( name
.CmpNoCase(wxDateTime::
137 GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 )
143 if ( flags
& wxDateTime::Name_Abbr
)
145 if ( name
.CmpNoCase(wxDateTime::
146 GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 )
156 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
157 wxDateTime::WeekDay
GetWeekDayFromName(const wxString
& name
, int flags
)
159 wxDateTime::WeekDay wd
;
160 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
162 // case-insensitive comparison either one of or with both abbreviated
164 if ( flags
& wxDateTime::Name_Full
)
166 if ( name
.CmpNoCase(wxDateTime::
167 GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 )
173 if ( flags
& wxDateTime::Name_Abbr
)
175 if ( name
.CmpNoCase(wxDateTime::
176 GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 )
186 // scans all digits (but no more than len) and returns the resulting number
187 bool GetNumericToken(size_t len
,
188 wxString::const_iterator
& p
,
189 unsigned long *number
)
193 while ( wxIsdigit(*p
) )
197 if ( len
&& ++n
> len
)
201 return !s
.empty() && s
.ToULong(number
);
204 // scans all alphabetic characters and returns the resulting string
205 wxString
GetAlphaToken(wxString::const_iterator
& p
)
208 while ( wxIsalpha(*p
) )
216 // parses string starting at given iterator using the specified format and,
217 // optionally, a fall back format (and optionally another one... but it stops
220 // if unsuccessful, returns invalid wxDateTime without changing p; otherwise
221 // advance p to the end of the match and returns wxDateTime containing the
222 // results of the parsing
224 ParseFormatAt(wxString::const_iterator
& p
,
225 const wxString::const_iterator
& end
,
227 const wxString
& fmtAlt
= wxString(),
228 const wxString
& fmtAlt2
= wxString())
230 const wxString
str(p
, end
);
231 wxString::const_iterator endParse
;
233 if ( dt
.ParseFormat(str
, fmt
, &endParse
) ||
234 (!fmtAlt
.empty() && dt
.ParseFormat(str
, fmtAlt
, &endParse
)) ||
235 (!fmtAlt2
.empty() && dt
.ParseFormat(str
, fmtAlt2
, &endParse
)) )
237 p
+= endParse
- str
.begin();
239 //else: all formats failed
244 } // anonymous namespace
246 // ----------------------------------------------------------------------------
247 // wxDateTime to/from text representations
248 // ----------------------------------------------------------------------------
250 wxString
wxDateTime::Format(const wxString
& format
, const TimeZone
& tz
) const
252 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
253 _T("NULL format in wxDateTime::Format") );
255 // we have to use our own implementation if the date is out of range of
256 // strftime() or if we use non standard specificators
258 time_t time
= GetTicks();
260 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
265 if ( tz
.GetOffset() == -GetTimeZone() )
267 // we are working with local time
268 tm
= wxLocaltime_r(&time
, &tmstruct
);
270 // should never happen
271 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxLocaltime_r() failed") );
275 time
+= (int)tz
.GetOffset();
277 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
278 int time2
= (int) time
;
284 tm
= wxGmtime_r(&time
, &tmstruct
);
286 // should never happen
287 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxGmtime_r() failed") );
291 tm
= (struct tm
*)NULL
;
297 return CallStrftime(format
, tm
);
300 //else: use generic code below
301 #endif // HAVE_STRFTIME
303 // we only parse ANSI C format specifications here, no POSIX 2
304 // complications, no GNU extensions but we do add support for a "%l" format
305 // specifier allowing to get the number of milliseconds
308 // used for calls to strftime() when we only deal with time
309 struct tm tmTimeOnly
;
310 tmTimeOnly
.tm_hour
= tm
.hour
;
311 tmTimeOnly
.tm_min
= tm
.min
;
312 tmTimeOnly
.tm_sec
= tm
.sec
;
313 tmTimeOnly
.tm_wday
= 0;
314 tmTimeOnly
.tm_yday
= 0;
315 tmTimeOnly
.tm_mday
= 1; // any date will do
316 tmTimeOnly
.tm_mon
= 0;
317 tmTimeOnly
.tm_year
= 76;
318 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
320 wxString tmp
, res
, fmt
;
321 for ( wxString::const_iterator p
= format
.begin(); p
!= format
.end(); ++p
)
331 // set the default format
332 switch ( (*++p
).GetValue() )
334 case _T('Y'): // year has 4 digits
338 case _T('j'): // day of year has 3 digits
339 case _T('l'): // milliseconds have 3 digits
343 case _T('w'): // week day as number has only one
348 // it's either another valid format specifier in which case
349 // the format is "%02d" (for all the rest) or we have the
350 // field width preceding the format in which case it will
351 // override the default format anyhow
360 // start of the format specification
361 switch ( (*p
).GetValue() )
363 case _T('a'): // a weekday name
365 // second parameter should be true for abbreviated names
366 res
+= GetWeekDayName(tm
.GetWeekDay(),
367 *p
== _T('a') ? Name_Abbr
: Name_Full
);
370 case _T('b'): // a month name
372 res
+= GetMonthName(tm
.mon
,
373 *p
== _T('b') ? Name_Abbr
: Name_Full
);
376 case _T('c'): // locale default date and time representation
377 case _T('x'): // locale default date representation
380 // the problem: there is no way to know what do these format
381 // specifications correspond to for the current locale.
383 // the solution: use a hack and still use strftime(): first
384 // find the YEAR which is a year in the strftime() range (1970
385 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
386 // of the real year. Then make a copy of the format and
387 // replace all occurrences of YEAR in it with some unique
388 // string not appearing anywhere else in it, then use
389 // strftime() to format the date in year YEAR and then replace
390 // YEAR back by the real year and the unique replacement
391 // string back with YEAR. Notice that "all occurrences of YEAR"
392 // means all occurrences of 4 digit as well as 2 digit form!
394 // the bugs: we assume that neither of %c nor %x contains any
395 // fields which may change between the YEAR and real year. For
396 // example, the week number (%U, %W) and the day number (%j)
397 // will change if one of these years is leap and the other one
400 // find the YEAR: normally, for any year X, Jan 1 of the
401 // year X + 28 is the same weekday as Jan 1 of X (because
402 // the weekday advances by 1 for each normal X and by 2
403 // for each leap X, hence by 5 every 4 years or by 35
404 // which is 0 mod 7 every 28 years) but this rule breaks
405 // down if there are years between X and Y which are
406 // divisible by 4 but not leap (i.e. divisible by 100 but
407 // not 400), hence the correction.
409 int yearReal
= GetYear(tz
);
410 int mod28
= yearReal
% 28;
412 // be careful to not go too far - we risk to leave the
417 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
421 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
424 int nCentury
= year
/ 100,
425 nCenturyReal
= yearReal
/ 100;
427 // need to adjust for the years divisble by 400 which are
428 // not leap but are counted like leap ones if we just take
429 // the number of centuries in between for nLostWeekDays
430 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
431 (nCentury
/ 4 - nCenturyReal
/ 4);
433 // we have to gain back the "lost" weekdays: note that the
434 // effect of this loop is to not do anything to
435 // nLostWeekDays (which we won't use any more), but to
436 // (indirectly) set the year correctly
437 while ( (nLostWeekDays
% 7) != 0 )
439 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
442 // finally move the year below 2000 so that the 2-digit
443 // year number can never match the month or day of the
444 // month when we do the replacements below
448 wxASSERT_MSG( year
>= 1970 && year
< 2000,
449 _T("logic error in wxDateTime::Format") );
452 // use strftime() to format the same date but in supported
455 // NB: we assume that strftime() doesn't check for the
456 // date validity and will happily format the date
457 // corresponding to Feb 29 of a non leap year (which
458 // may happen if yearReal was leap and year is not)
459 struct tm tmAdjusted
;
461 tmAdjusted
.tm_hour
= tm
.hour
;
462 tmAdjusted
.tm_min
= tm
.min
;
463 tmAdjusted
.tm_sec
= tm
.sec
;
464 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
465 tmAdjusted
.tm_yday
= GetDayOfYear();
466 tmAdjusted
.tm_mday
= tm
.mday
;
467 tmAdjusted
.tm_mon
= tm
.mon
;
468 tmAdjusted
.tm_year
= year
- 1900;
469 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
470 wxString str
= CallStrftime(*p
== _T('c') ? _T("%c")
474 // now replace the replacement year with the real year:
475 // notice that we have to replace the 4 digit year with
476 // a unique string not appearing in strftime() output
477 // first to prevent the 2 digit year from matching any
478 // substring of the 4 digit year (but any day, month,
479 // hours or minutes components should be safe because
480 // they are never in 70-99 range)
481 wxString
replacement("|");
482 while ( str
.find(replacement
) != wxString::npos
)
485 str
.Replace(wxString::Format("%d", year
),
487 str
.Replace(wxString::Format("%d", year
% 100),
488 wxString::Format("%d", yearReal
% 100));
489 str
.Replace(replacement
,
490 wxString::Format("%d", yearReal
));
494 #else // !HAVE_STRFTIME
495 // Use "%m/%d/%y %H:%M:%S" format instead
496 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
497 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
498 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
501 case _T('d'): // day of a month (01-31)
502 res
+= wxString::Format(fmt
, tm
.mday
);
505 case _T('H'): // hour in 24h format (00-23)
506 res
+= wxString::Format(fmt
, tm
.hour
);
509 case _T('I'): // hour in 12h format (01-12)
511 // 24h -> 12h, 0h -> 12h too
512 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
513 : tm
.hour
? tm
.hour
: 12;
514 res
+= wxString::Format(fmt
, hour12
);
518 case _T('j'): // day of the year
519 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
522 case _T('l'): // milliseconds (NOT STANDARD)
523 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
526 case _T('m'): // month as a number (01-12)
527 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
530 case _T('M'): // minute as a decimal number (00-59)
531 res
+= wxString::Format(fmt
, tm
.min
);
534 case _T('p'): // AM or PM string
536 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
537 #else // !HAVE_STRFTIME
538 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
539 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
542 case _T('S'): // second as a decimal number (00-61)
543 res
+= wxString::Format(fmt
, tm
.sec
);
546 case _T('U'): // week number in the year (Sunday 1st week day)
547 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
550 case _T('W'): // week number in the year (Monday 1st week day)
551 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
554 case _T('w'): // weekday as a number (0-6), Sunday = 0
555 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
558 // case _T('x'): -- handled with "%c"
560 case _T('X'): // locale default time representation
561 // just use strftime() to format the time for us
563 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
564 #else // !HAVE_STRFTIME
565 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
566 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
569 case _T('y'): // year without century (00-99)
570 res
+= wxString::Format(fmt
, tm
.year
% 100);
573 case _T('Y'): // year with century
574 res
+= wxString::Format(fmt
, tm
.year
);
577 case _T('Z'): // timezone name
579 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
584 // is it the format width?
586 while ( *p
== _T('-') || *p
== _T('+') ||
587 *p
== _T(' ') || wxIsdigit(*p
) )
594 // we've only got the flags and width so far in fmt
595 fmt
.Prepend(_T('%'));
603 // no, it wasn't the width
604 wxFAIL_MSG(_T("unknown format specificator"));
606 // fall through and just copy it nevertheless
608 case _T('%'): // a percent sign
612 case 0: // the end of string
613 wxFAIL_MSG(_T("missing format at the end of string"));
615 // just put the '%' which was the last char in format
625 // this function parses a string in (strict) RFC 822 format: see the section 5
626 // of the RFC for the detailed description, but briefly it's something of the
627 // form "Sat, 18 Dec 1999 00:48:30 +0100"
629 // this function is "strict" by design - it must reject anything except true
630 // RFC822 time specs.
632 // TODO a great candidate for using reg exps
634 wxDateTime::ParseRfc822Date(const wxString
& date
, wxString::const_iterator
*end
)
636 // TODO: rewrite using iterators instead of wxChar pointers
637 const wxStringCharType
*p
= date
.wx_str();
638 const wxStringCharType
*comma
= wxStrchr(p
, wxS(','));
641 // the part before comma is the weekday
643 // skip it for now - we don't use but might check that it really
644 // corresponds to the specfied date
649 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
657 // the following 1 or 2 digits are the day number
658 if ( !wxIsdigit(*p
) )
660 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
665 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - _T('0'));
669 day
= (wxDateTime_t
)(day
+ (*p
++ - _T('0')));
672 if ( *p
++ != _T(' ') )
677 // the following 3 letters specify the month
678 wxString
monName(p
, 3);
680 if ( monName
== _T("Jan") )
682 else if ( monName
== _T("Feb") )
684 else if ( monName
== _T("Mar") )
686 else if ( monName
== _T("Apr") )
688 else if ( monName
== _T("May") )
690 else if ( monName
== _T("Jun") )
692 else if ( monName
== _T("Jul") )
694 else if ( monName
== _T("Aug") )
696 else if ( monName
== _T("Sep") )
698 else if ( monName
== _T("Oct") )
700 else if ( monName
== _T("Nov") )
702 else if ( monName
== _T("Dec") )
706 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str());
713 if ( *p
++ != _T(' ') )
719 if ( !wxIsdigit(*p
) )
725 int year
= *p
++ - _T('0');
727 if ( !wxIsdigit(*p
) )
729 // should have at least 2 digits in the year
734 year
+= *p
++ - _T('0');
736 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
740 year
+= *p
++ - _T('0');
742 if ( !wxIsdigit(*p
) )
744 // no 3 digit years please
749 year
+= *p
++ - _T('0');
752 if ( *p
++ != _T(' ') )
757 // time is in the format hh:mm:ss and seconds are optional
758 if ( !wxIsdigit(*p
) )
763 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - _T('0'));
765 if ( !wxIsdigit(*p
) )
771 hour
= (wxDateTime_t
)(hour
+ (*p
++ - _T('0')));
773 if ( *p
++ != _T(':') )
778 if ( !wxIsdigit(*p
) )
783 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - _T('0'));
785 if ( !wxIsdigit(*p
) )
791 min
= (wxDateTime_t
)(min
+ *p
++ - _T('0'));
793 wxDateTime_t sec
= 0;
797 if ( !wxIsdigit(*p
) )
802 sec
= (wxDateTime_t
)(*p
++ - _T('0'));
804 if ( !wxIsdigit(*p
) )
810 sec
= (wxDateTime_t
)(sec
+ *p
++ - _T('0'));
813 if ( *p
++ != _T(' ') )
818 // and now the interesting part: the timezone
819 int offset
wxDUMMY_INITIALIZE(0);
820 if ( *p
== _T('-') || *p
== _T('+') )
822 // the explicit offset given: it has the form of hhmm
823 bool plus
= *p
++ == _T('+');
825 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
831 offset
= MIN_PER_HOUR
*(10*(*p
- _T('0')) + (*(p
+ 1) - _T('0')));
835 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
841 offset
+= 10*(*p
- _T('0')) + (*(p
+ 1) - _T('0'));
852 // the symbolic timezone given: may be either military timezone or one
853 // of standard abbreviations
856 // military: Z = UTC, J unused, A = -1, ..., Y = +12
857 static const int offsets
[26] =
859 //A B C D E F G H I J K L M
860 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
861 //N O P R Q S T U V W Z Y Z
862 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
865 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
867 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
);
872 offset
= offsets
[*p
++ - _T('A')];
878 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
880 else if ( tz
== _T("AST") )
882 else if ( tz
== _T("ADT") )
884 else if ( tz
== _T("EST") )
886 else if ( tz
== _T("EDT") )
888 else if ( tz
== _T("CST") )
890 else if ( tz
== _T("CDT") )
892 else if ( tz
== _T("MST") )
894 else if ( tz
== _T("MDT") )
896 else if ( tz
== _T("PST") )
898 else if ( tz
== _T("PDT") )
902 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
);
911 offset
*= MIN_PER_HOUR
;
914 // the spec was correct, construct the date from the values we found
915 Set(day
, mon
, year
, hour
, min
, sec
);
916 MakeFromTimezone(TimeZone::Make(offset
*SEC_PER_MIN
));
918 const size_t endpos
= p
- date
.wx_str();
920 *end
= date
.begin() + endpos
;
922 return date
.c_str() + endpos
;
927 // returns the string containing strftime() format used for short dates in the
928 // current locale or an empty string
929 static wxString
GetLocaleDateFormat()
933 // there is no setlocale() under Windows CE, so just always query the
936 if ( strcmp(setlocale(LC_ALL
, NULL
), "C") != 0 )
939 // The locale was programatically set to non-C. We assume that this was
940 // done using wxLocale, in which case thread's current locale is also
941 // set to correct LCID value and we can use GetLocaleInfo to determine
942 // the correct formatting string:
944 LCID lcid
= LOCALE_USER_DEFAULT
;
946 LCID lcid
= GetThreadLocale();
948 // according to MSDN 80 chars is max allowed for short date format
950 if ( ::GetLocaleInfo(lcid
, LOCALE_SSHORTDATE
, fmt
, WXSIZEOF(fmt
)) )
952 wxChar chLast
= _T('\0');
953 size_t lastCount
= 0;
954 for ( const wxChar
*p
= fmt
; /* NUL handled inside */; p
++ )
964 // these characters come in groups, start counting them
974 // first deal with any special characters we have had
984 // these two are the same as we
985 // don't distinguish between 1 and
999 wxFAIL_MSG( _T("too many 'd's") );
1004 switch ( lastCount
)
1008 // as for 'd' and 'dd' above
1021 wxFAIL_MSG( _T("too many 'M's") );
1026 switch ( lastCount
)
1038 wxFAIL_MSG( _T("wrong number of 'y's") );
1043 // strftime() doesn't have era string,
1044 // ignore this format
1045 wxASSERT_MSG( lastCount
<= 2,
1046 _T("too many 'g's") );
1050 wxFAIL_MSG( _T("unreachable") );
1057 // not a special character so must be just a separator,
1059 if ( *p
!= _T('\0') )
1061 if ( *p
== _T('%') )
1063 // this one needs to be escaped
1071 if ( *p
== _T('\0') )
1075 //else: GetLocaleInfo() failed, leave fmtDate value unchanged and
1076 // try our luck with the default formats
1078 //else: default C locale, default formats should work
1083 #endif // __WINDOWS__
1086 wxDateTime::ParseFormat(const wxString
& date
,
1087 const wxString
& format
,
1088 const wxDateTime
& dateDef
,
1089 wxString::const_iterator
*end
)
1091 wxCHECK_MSG( !format
.empty(), NULL
, "format can't be empty" );
1096 // what fields have we found?
1097 bool haveWDay
= false,
1107 bool hourIsIn12hFormat
= false, // or in 24h one?
1108 isPM
= false; // AM by default
1110 // and the value of the items we have (init them to get rid of warnings)
1111 wxDateTime_t msec
= 0,
1115 WeekDay wday
= Inv_WeekDay
;
1116 wxDateTime_t yday
= 0,
1118 wxDateTime::Month mon
= Inv_Month
;
1121 wxString::const_iterator input
= date
.begin();
1122 for ( wxString::const_iterator fmt
= format
.begin(); fmt
!= format
.end(); ++fmt
)
1124 if ( *fmt
!= _T('%') )
1126 if ( wxIsspace(*fmt
) )
1128 // a white space in the format string matches 0 or more white
1129 // spaces in the input
1130 while ( wxIsspace(*input
) )
1137 // any other character (not whitespace, not '%') must be
1138 // matched by itself in the input
1139 if ( *input
++ != *fmt
)
1146 // done with this format char
1150 // start of a format specification
1152 // parse the optional width
1154 while ( wxIsdigit(*++fmt
) )
1157 width
+= *fmt
- _T('0');
1160 // the default widths for the various fields
1163 switch ( (*fmt
).GetValue() )
1165 case _T('Y'): // year has 4 digits
1169 case _T('j'): // day of year has 3 digits
1170 case _T('l'): // milliseconds have 3 digits
1174 case _T('w'): // week day as number has only one
1179 // default for all other fields
1184 // then the format itself
1185 switch ( (*fmt
).GetValue() )
1187 case _T('a'): // a weekday name
1190 int flag
= *fmt
== _T('a') ? Name_Abbr
: Name_Full
;
1191 wday
= GetWeekDayFromName(GetAlphaToken(input
), flag
);
1192 if ( wday
== Inv_WeekDay
)
1201 case _T('b'): // a month name
1204 int flag
= *fmt
== _T('b') ? Name_Abbr
: Name_Full
;
1205 mon
= GetMonthFromName(GetAlphaToken(input
), flag
);
1206 if ( mon
== Inv_Month
)
1215 case _T('c'): // locale default date and time representation
1217 #ifdef HAVE_STRPTIME
1220 // try using strptime() -- it may fail even if the input is
1221 // correct but the date is out of range, so we will fall back
1222 // to our generic code anyhow
1223 if ( CallStrptime(date
, input
, "%c", &tm
) )
1229 year
= 1900 + tm
.tm_year
;
1230 mon
= (Month
)tm
.tm_mon
;
1233 else // strptime() failed; try generic heuristic code
1234 #endif // HAVE_STRPTIME
1237 // try the format which corresponds to ctime() output
1238 // first, then the generic date/time formats
1239 const wxDateTime dt
= ParseFormatAt
1243 wxS("%a %b %d %H:%M:%S %Y"),
1247 if ( !dt
.IsValid() )
1261 haveDay
= haveMon
= haveYear
=
1262 haveHour
= haveMin
= haveSec
= true;
1266 case _T('d'): // day of a month (01-31)
1267 if ( !GetNumericToken(width
, input
, &num
) ||
1268 (num
> 31) || (num
< 1) )
1274 // we can't check whether the day range is correct yet, will
1275 // do it later - assume ok for now
1277 mday
= (wxDateTime_t
)num
;
1280 case _T('H'): // hour in 24h format (00-23)
1281 if ( !GetNumericToken(width
, input
, &num
) || (num
> 23) )
1288 hour
= (wxDateTime_t
)num
;
1291 case _T('I'): // hour in 12h format (01-12)
1292 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
1299 hourIsIn12hFormat
= true;
1300 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
1303 case _T('j'): // day of the year
1304 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 366) )
1311 yday
= (wxDateTime_t
)num
;
1314 case _T('l'): // milliseconds (0-999)
1315 if ( !GetNumericToken(width
, input
, &num
) )
1319 msec
= (wxDateTime_t
)num
;
1322 case _T('m'): // month as a number (01-12)
1323 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
1330 mon
= (Month
)(num
- 1);
1333 case _T('M'): // minute as a decimal number (00-59)
1334 if ( !GetNumericToken(width
, input
, &num
) || (num
> 59) )
1341 min
= (wxDateTime_t
)num
;
1344 case _T('p'): // AM or PM string
1346 wxString am
, pm
, token
= GetAlphaToken(input
);
1348 // some locales have empty AM/PM tokens and thus when formatting
1349 // dates with the %p specifier nothing is generated; when trying to
1350 // parse them back, we get an empty token here... but that's not
1355 GetAmPmStrings(&am
, &pm
);
1356 if (am
.empty() && pm
.empty())
1357 return NULL
; // no am/pm strings defined
1358 if ( token
.CmpNoCase(pm
) == 0 )
1362 else if ( token
.CmpNoCase(am
) != 0 )
1370 case _T('r'): // time as %I:%M:%S %p
1373 if ( !dt
.ParseFormat(wxString(input
, date
.end()),
1374 wxS("%I:%M:%S %p"), &input
) )
1377 haveHour
= haveMin
= haveSec
= true;
1386 case _T('R'): // time as %H:%M
1389 dt
= ParseFormatAt(input
, date
.end(), wxS("%H:%M"));
1390 if ( !dt
.IsValid() )
1402 case _T('S'): // second as a decimal number (00-61)
1403 if ( !GetNumericToken(width
, input
, &num
) || (num
> 61) )
1410 sec
= (wxDateTime_t
)num
;
1413 case _T('T'): // time as %H:%M:%S
1416 dt
= ParseFormatAt(input
, date
.end(), wxS("%H:%M:%S"));
1417 if ( !dt
.IsValid() )
1431 case _T('w'): // weekday as a number (0-6), Sunday = 0
1432 if ( !GetNumericToken(width
, input
, &num
) || (wday
> 6) )
1439 wday
= (WeekDay
)num
;
1442 case _T('x'): // locale default date representation
1443 #ifdef HAVE_STRPTIME
1444 // try using strptime() -- it may fail even if the input is
1445 // correct but the date is out of range, so we will fall back
1446 // to our generic code anyhow
1450 if ( CallStrptime(date
, input
, "%x", &tm
) )
1452 haveDay
= haveMon
= haveYear
= true;
1454 year
= 1900 + tm
.tm_year
;
1455 mon
= (Month
)tm
.tm_mon
;
1461 #endif // HAVE_STRPTIME
1468 // The above doesn't work for all locales, try to query
1469 // Windows for the right way of formatting the date:
1470 fmtDate
= GetLocaleDateFormat();
1471 if ( fmtDate
.empty() )
1472 #endif // __WINDOWS__
1474 if ( IsWestEuropeanCountry(GetCountry()) ||
1475 GetCountry() == Russia
)
1477 fmtDate
= _T("%d/%m/%y");
1478 fmtDateAlt
= _T("%m/%d/%y");
1482 fmtDate
= _T("%m/%d/%y");
1483 fmtDateAlt
= _T("%d/%m/%y");
1488 dt
= ParseFormatAt(input
, date
.end(),
1489 fmtDate
, fmtDateAlt
);
1490 if ( !dt
.IsValid() )
1506 case _T('X'): // locale default time representation
1507 #ifdef HAVE_STRPTIME
1509 // use strptime() to do it for us (FIXME !Unicode friendly)
1511 if ( !CallStrptime(date
, input
, "%X", &tm
) )
1514 haveHour
= haveMin
= haveSec
= true;
1520 #else // !HAVE_STRPTIME
1521 // TODO under Win32 we can query the LOCALE_ITIME system
1522 // setting which says whether the default time format is
1525 // try to parse what follows as "%H:%M:%S" and, if this
1526 // fails, as "%I:%M:%S %p" - this should catch the most
1529 dt
= ParseFormatAt(input
, date
.end(), "%T", "%r");
1530 if ( !dt
.IsValid() )
1542 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
1545 case _T('y'): // year without century (00-99)
1546 if ( !GetNumericToken(width
, input
, &num
) || (num
> 99) )
1554 // TODO should have an option for roll over date instead of
1555 // hard coding it here
1556 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
1559 case _T('Y'): // year with century
1560 if ( !GetNumericToken(width
, input
, &num
) )
1567 year
= (wxDateTime_t
)num
;
1570 case _T('Z'): // timezone name
1571 wxFAIL_MSG(_T("TODO"));
1574 case _T('%'): // a percent sign
1575 if ( *input
++ != _T('%') )
1582 case 0: // the end of string
1583 wxFAIL_MSG(_T("unexpected format end"));
1587 default: // not a known format spec
1592 // format matched, try to construct a date from what we have now
1594 if ( dateDef
.IsValid() )
1596 // take this date as default
1597 tmDef
= dateDef
.GetTm();
1599 else if ( IsValid() )
1601 // if this date is valid, don't change it
1606 // no default and this date is invalid - fall back to Today()
1607 tmDef
= Today().GetTm();
1623 // TODO we don't check here that the values are consistent, if both year
1624 // day and month/day were found, we just ignore the year day and we
1625 // also always ignore the week day
1628 if ( mday
> GetNumberOfDays(tm
.mon
, tm
.year
) )
1630 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
1637 else if ( haveYDay
)
1639 if ( yday
> GetNumberOfDays(tm
.year
) )
1641 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
1646 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
1653 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
1655 // translate to 24hour format
1658 //else: either already in 24h format or no translation needed
1681 // finally check that the week day is consistent -- if we had it
1682 if ( haveWDay
&& GetWeekDay() != wday
)
1684 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
1692 return date
.c_str() + (input
- date
.begin());
1696 wxDateTime::ParseDateTime(const wxString
& date
, wxString::const_iterator
*end
)
1698 // Set to current day and hour, so strings like '14:00' becomes today at
1699 // 14, not some other random date
1700 wxDateTime dtDate
= wxDateTime::Today();
1701 wxDateTime dtTime
= wxDateTime::Today();
1703 wxString::const_iterator
1708 // If we got a date in the beginning, see if there is a time specified
1710 if ( dtDate
.ParseDate(date
, &endDate
) )
1712 // Skip spaces, as the ParseTime() function fails on spaces
1713 while ( endDate
!= date
.end() && wxIsspace(*endDate
) )
1716 const wxString
timestr(endDate
, date
.end());
1717 if ( !dtTime
.ParseTime(timestr
, &endTime
) )
1720 endBoth
= endDate
+ (endTime
- timestr
.begin());
1722 else // no date in the beginning
1724 // check if we have a time followed by a date
1725 if ( !dtTime
.ParseTime(date
, &endTime
) )
1728 while ( endTime
!= date
.end() && wxIsspace(*endTime
) )
1731 const wxString
datestr(endTime
, date
.end());
1732 if ( !dtDate
.ParseDate(datestr
, &endDate
) )
1735 endBoth
= endTime
+ (endDate
- datestr
.begin());
1738 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
1739 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
1740 dtTime
.GetMillisecond());
1742 // Return endpoint of scan
1746 return date
.c_str() + (endBoth
- date
.begin());
1750 wxDateTime::ParseDate(const wxString
& date
, wxString::const_iterator
*end
)
1752 // this is a simplified version of ParseDateTime() which understands only
1753 // "today" (for wxDate compatibility) and digits only otherwise (and not
1754 // all esoteric constructions ParseDateTime() knows about)
1756 const wxString::const_iterator pBegin
= date
.begin();
1758 wxString::const_iterator p
= pBegin
;
1759 while ( wxIsspace(*p
) )
1762 // some special cases
1766 int dayDiffFromToday
;
1769 { wxTRANSLATE("today"), 0 },
1770 { wxTRANSLATE("yesterday"), -1 },
1771 { wxTRANSLATE("tomorrow"), 1 },
1774 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
1776 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
1777 size_t len
= dateStr
.length();
1779 const wxString::const_iterator pEnd
= p
+ len
;
1780 if ( wxString(p
, pEnd
).CmpNoCase(dateStr
) == 0 )
1782 // nothing can follow this, so stop here
1786 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
1788 if ( dayDiffFromToday
)
1790 *this += wxDateSpan::Days(dayDiffFromToday
);
1796 return wxStringOperations::AddToIter(date
.c_str().AsChar(),
1801 // We try to guess what we have here: for each new (numeric) token, we
1802 // determine if it can be a month, day or a year. Of course, there is an
1803 // ambiguity as some numbers may be days as well as months, so we also
1804 // have the ability to back track.
1807 bool haveDay
= false, // the months day?
1808 haveWDay
= false, // the day of week?
1809 haveMon
= false, // the month?
1810 haveYear
= false; // the year?
1812 // and the value of the items we have (init them to get rid of warnings)
1813 WeekDay wday
= Inv_WeekDay
;
1814 wxDateTime_t day
= 0;
1815 wxDateTime::Month mon
= Inv_Month
;
1818 // tokenize the string
1820 static const wxStringCharType
*dateDelimiters
= wxS(".,/-\t\r\n ");
1821 wxStringTokenizer
tok(wxString(p
, date
.end()), dateDelimiters
);
1822 while ( tok
.HasMoreTokens() )
1824 wxString token
= tok
.GetNextToken();
1830 if ( token
.ToULong(&val
) )
1832 // guess what this number is
1838 if ( !haveMon
&& val
> 0 && val
<= 12 )
1840 // assume it is month
1843 else // not the month
1847 // this can only be the year
1850 else // may be either day or year
1852 // use a leap year if we don't have the year yet to allow
1853 // dates like 2/29/1976 which would be rejected otherwise
1854 wxDateTime_t max_days
= (wxDateTime_t
)(
1856 ? GetNumberOfDays(mon
, haveYear
? year
: 1976)
1861 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
1866 else // yes, suppose it's the day
1880 year
= (wxDateTime_t
)val
;
1889 day
= (wxDateTime_t
)val
;
1895 mon
= (Month
)(val
- 1);
1898 else // not a number
1900 // be careful not to overwrite the current mon value
1901 Month mon2
= GetMonthFromName(token
, Name_Full
| Name_Abbr
);
1902 if ( mon2
!= Inv_Month
)
1907 // but we already have a month - maybe we guessed wrong?
1910 // no need to check in month range as always < 12, but
1911 // the days are counted from 1 unlike the months
1912 day
= (wxDateTime_t
)(mon
+ 1);
1917 // could possible be the year (doesn't the year come
1918 // before the month in the japanese format?) (FIXME)
1927 else // not a valid month name
1929 WeekDay wday2
= GetWeekDayFromName(token
, Name_Full
| Name_Abbr
);
1930 if ( wday2
!= Inv_WeekDay
)
1942 else // not a valid weekday name
1945 static const char *ordinals
[] =
1947 wxTRANSLATE("first"),
1948 wxTRANSLATE("second"),
1949 wxTRANSLATE("third"),
1950 wxTRANSLATE("fourth"),
1951 wxTRANSLATE("fifth"),
1952 wxTRANSLATE("sixth"),
1953 wxTRANSLATE("seventh"),
1954 wxTRANSLATE("eighth"),
1955 wxTRANSLATE("ninth"),
1956 wxTRANSLATE("tenth"),
1957 wxTRANSLATE("eleventh"),
1958 wxTRANSLATE("twelfth"),
1959 wxTRANSLATE("thirteenth"),
1960 wxTRANSLATE("fourteenth"),
1961 wxTRANSLATE("fifteenth"),
1962 wxTRANSLATE("sixteenth"),
1963 wxTRANSLATE("seventeenth"),
1964 wxTRANSLATE("eighteenth"),
1965 wxTRANSLATE("nineteenth"),
1966 wxTRANSLATE("twentieth"),
1967 // that's enough - otherwise we'd have problems with
1968 // composite (or not) ordinals
1972 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
1974 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
1980 if ( n
== WXSIZEOF(ordinals
) )
1982 // stop here - something unknown
1989 // don't try anything here (as in case of numeric day
1990 // above) - the symbolic day spec should always
1991 // precede the month/year
1997 day
= (wxDateTime_t
)(n
+ 1);
2002 nPosCur
= tok
.GetPosition();
2005 // either no more tokens or the scan was stopped by something we couldn't
2006 // parse - in any case, see if we can construct a date from what we have
2007 if ( !haveDay
&& !haveWDay
)
2009 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
2014 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
2015 !(haveDay
&& haveMon
&& haveYear
) )
2017 // without adjectives (which we don't support here) the week day only
2018 // makes sense completely separately or with the full date
2019 // specification (what would "Wed 1999" mean?)
2023 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
2025 // may be we have month and day instead of day and year?
2026 if ( haveDay
&& !haveMon
)
2030 // exchange day and month
2031 mon
= (wxDateTime::Month
)(day
- 1);
2033 // we're in the current year then
2034 if ( (year
> 0) && (year
<= (int)GetNumberOfDays(mon
, Inv_Year
)) )
2036 day
= (wxDateTime_t
)year
;
2041 //else: no, can't exchange, leave haveMon == false
2047 // if we give the year, month and day must be given too
2048 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
2056 mon
= GetCurrentMonth();
2061 year
= GetCurrentYear();
2066 // normally we check the day above but the check is optimistic in case
2067 // we find the day before its month/year so we have to redo it now
2068 if ( day
> GetNumberOfDays(mon
, year
) )
2071 Set(day
, mon
, year
);
2075 // check that it is really the same
2076 if ( GetWeekDay() != wday
)
2078 // inconsistency detected
2079 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
2089 SetToWeekDayInSameWeek(wday
);
2092 // return the pointer to the first unparsed char
2094 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
2096 // if we couldn't parse the token after the delimiter, put back the
2097 // delimiter as well
2104 return wxStringOperations::AddToIter(date
.c_str().AsChar(), p
- pBegin
);
2108 wxDateTime::ParseTime(const wxString
& time
, wxString::const_iterator
*end
)
2110 // first try some extra things
2117 { wxTRANSLATE("noon"), 12 },
2118 { wxTRANSLATE("midnight"), 00 },
2122 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
2124 wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
2125 size_t len
= timeString
.length();
2126 if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 )
2128 // casts required by DigitalMars
2129 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
2132 *end
= time
.begin() + len
;
2134 return time
.c_str() + len
;
2138 // try all time formats we may think about in the order from longest to
2140 static const char *timeFormats
[] =
2142 "%I:%M:%S %p", // 12hour with AM/PM
2143 "%H:%M:%S", // could be the same or 24 hour one so try it too
2144 "%I:%M %p", // 12hour with AM/PM but without seconds
2145 "%H:%M:%S", // and a possibly 24 hour version without seconds
2146 "%X", // possibly something from above or maybe something
2147 // completely different -- try it last
2149 // TODO: parse timezones
2152 for ( size_t nFmt
= 0; nFmt
< WXSIZEOF(timeFormats
); nFmt
++ )
2154 const char *result
= ParseFormat(time
, timeFormats
[nFmt
], end
);
2162 // ----------------------------------------------------------------------------
2163 // Workdays and holidays support
2164 // ----------------------------------------------------------------------------
2166 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
2168 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
2171 // ============================================================================
2173 // ============================================================================
2175 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
2178 return ds1
.Multiply(n
);
2181 // ============================================================================
2183 // ============================================================================
2185 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
2187 return wxTimeSpan(ts
).Multiply(n
);
2190 // this enum is only used in wxTimeSpan::Format() below but we can't declare
2191 // it locally to the method as it provokes an internal compiler error in egcs
2192 // 2.91.60 when building with -O2
2203 // not all strftime(3) format specifiers make sense here because, for example,
2204 // a time span doesn't have a year nor a timezone
2206 // Here are the ones which are supported (all of them are supported by strftime
2208 // %H hour in 24 hour format
2209 // %M minute (00 - 59)
2210 // %S second (00 - 59)
2213 // Also, for MFC CTimeSpan compatibility, we support
2214 // %D number of days
2216 // And, to be better than MFC :-), we also have
2217 // %E number of wEeks
2218 // %l milliseconds (000 - 999)
2219 wxString
wxTimeSpan::Format(const wxString
& format
) const
2221 // we deal with only positive time spans here and just add the sign in
2222 // front for the negative ones
2225 wxString
str(Negate().Format(format
));
2229 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
2230 _T("NULL format in wxTimeSpan::Format") );
2233 str
.Alloc(format
.length());
2235 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
2237 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
2238 // question is what should ts.Format("%S") do? The code here returns "3273"
2239 // in this case (i.e. the total number of seconds, not just seconds % 60)
2240 // because, for me, this call means "give me entire time interval in
2241 // seconds" and not "give me the seconds part of the time interval"
2243 // If we agree that it should behave like this, it is clear that the
2244 // interpretation of each format specifier depends on the presence of the
2245 // other format specs in the string: if there was "%H" before "%M", we
2246 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
2248 // we remember the most important unit found so far
2249 TimeSpanPart partBiggest
= Part_MSec
;
2251 for ( wxString::const_iterator pch
= format
.begin(); pch
!= format
.end(); ++pch
)
2255 if ( ch
== _T('%') )
2257 // the start of the format specification of the printf() below
2258 wxString
fmtPrefix(_T('%'));
2263 // the number of digits for the format string, 0 if unused
2264 unsigned digits
= 0;
2266 ch
= *++pch
; // get the format spec char
2270 wxFAIL_MSG( _T("invalid format character") );
2276 // skip the part below switch
2281 if ( partBiggest
< Part_Day
)
2287 partBiggest
= Part_Day
;
2292 partBiggest
= Part_Week
;
2298 if ( partBiggest
< Part_Hour
)
2304 partBiggest
= Part_Hour
;
2311 n
= GetMilliseconds().ToLong();
2312 if ( partBiggest
< Part_MSec
)
2316 //else: no need to reset partBiggest to Part_MSec, it is
2317 // the least significant one anyhow
2324 if ( partBiggest
< Part_Min
)
2330 partBiggest
= Part_Min
;
2337 n
= GetSeconds().ToLong();
2338 if ( partBiggest
< Part_Sec
)
2344 partBiggest
= Part_Sec
;
2353 fmtPrefix
<< _T("0") << digits
;
2356 str
+= wxString::Format(fmtPrefix
+ _T("ld"), n
);
2360 // normal character, just copy
2368 #endif // wxUSE_DATETIME