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 // constants (see also datetime.cpp)
68 // ----------------------------------------------------------------------------
70 static const int DAYS_PER_WEEK
= 7;
72 static const int HOURS_PER_DAY
= 24;
74 static const int SEC_PER_MIN
= 60;
76 static const int MIN_PER_HOUR
= 60;
78 // ----------------------------------------------------------------------------
80 // ----------------------------------------------------------------------------
84 #if wxUSE_UNIX && !defined(HAVE_STRPTIME_DECL)
85 // configure detected that we had strptime() but not its declaration,
86 // provide it ourselves
87 extern "C" char *strptime(const char *, const char *, struct tm
*);
90 // Unicode-friendly strptime() wrapper
91 static const wxStringCharType
*
92 CallStrptime(const wxStringCharType
*input
, const char *fmt
, tm
*tm
)
94 // the problem here is that strptime() returns pointer into the string we
95 // passed to it while we're really interested in the pointer into the
96 // original, Unicode, string so we try to transform the pointer back
97 #if wxUSE_UNICODE_WCHAR
98 wxCharBuffer
inputMB(wxConvertWX2MB(input
));
100 const char * const inputMB
= input
;
101 #endif // Unicode/Ascii
103 const char *result
= strptime(inputMB
, fmt
, tm
);
107 #if wxUSE_UNICODE_WCHAR
108 // FIXME: this is wrong in presence of surrogates &c
109 return input
+ (result
- inputMB
.data());
112 #endif // Unicode/Ascii
115 #endif // HAVE_STRPTIME
117 // return the month if the string is a month name or Inv_Month otherwise
118 static wxDateTime
::Month
GetMonthFromName(const wxString
& name
, int flags
)
120 wxDateTime
::Month mon
;
121 for ( mon
= wxDateTime
::Jan
; mon
< wxDateTime
::Inv_Month
; wxNextMonth(mon
) )
123 // case-insensitive comparison either one of or with both abbreviated
125 if ( flags
& wxDateTime
::Name_Full
)
127 if ( name
.CmpNoCase(wxDateTime
::
128 GetMonthName(mon
, wxDateTime
::Name_Full
)) == 0 )
134 if ( flags
& wxDateTime
::Name_Abbr
)
136 if ( name
.CmpNoCase(wxDateTime
::
137 GetMonthName(mon
, wxDateTime
::Name_Abbr
)) == 0 )
147 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
148 static wxDateTime
::WeekDay
GetWeekDayFromName(const wxString
& name
, int flags
)
150 wxDateTime
::WeekDay wd
;
151 for ( wd
= wxDateTime
::Sun
; wd
< wxDateTime
::Inv_WeekDay
; wxNextWDay(wd
) )
153 // case-insensitive comparison either one of or with both abbreviated
155 if ( flags
& wxDateTime
::Name_Full
)
157 if ( name
.CmpNoCase(wxDateTime
::
158 GetWeekDayName(wd
, wxDateTime
::Name_Full
)) == 0 )
164 if ( flags
& wxDateTime
::Name_Abbr
)
166 if ( name
.CmpNoCase(wxDateTime
::
167 GetWeekDayName(wd
, wxDateTime
::Name_Abbr
)) == 0 )
178 struct tm
*wxDateTime
::GetTmNow(struct tm
*tmstruct
)
180 time_t t
= GetTimeNow();
181 return wxLocaltime_r(&t
, tmstruct
);
184 // scans all digits (but no more than len) and returns the resulting number
185 static bool GetNumericToken(size_t len
,
186 const wxStringCharType
*& p
,
187 unsigned long *number
)
191 while ( wxIsdigit(*p
) )
195 if ( len
&& ++n
> len
)
199 return !s
.empty() && s
.ToULong(number
);
202 // scans all alphabetic characters and returns the resulting string
203 static wxString
GetAlphaToken(const wxStringCharType
*& p
)
206 while ( wxIsalpha(*p
) )
214 // ----------------------------------------------------------------------------
215 // wxDateTime to/from text representations
216 // ----------------------------------------------------------------------------
218 wxString wxDateTime
::Format(const wxString
& format
, const TimeZone
& tz
) const
220 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
221 _T("NULL format in wxDateTime::Format") );
223 // we have to use our own implementation if the date is out of range of
224 // strftime() or if we use non standard specificators
226 time_t time
= GetTicks();
228 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
233 if ( tz
.GetOffset() == -GetTimeZone() )
235 // we are working with local time
236 tm
= wxLocaltime_r(&time
, &tmstruct
);
238 // should never happen
239 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxLocaltime_r() failed") );
243 time
+= (int)tz
.GetOffset();
245 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
246 int time2
= (int) time
;
252 tm
= wxGmtime_r(&time
, &tmstruct
);
254 // should never happen
255 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxGmtime_r() failed") );
259 tm
= (struct tm
*)NULL
;
265 return CallStrftime(format
, tm
);
268 //else: use generic code below
269 #endif // HAVE_STRFTIME
271 // we only parse ANSI C format specifications here, no POSIX 2
272 // complications, no GNU extensions but we do add support for a "%l" format
273 // specifier allowing to get the number of milliseconds
276 // used for calls to strftime() when we only deal with time
277 struct tm tmTimeOnly
;
278 tmTimeOnly
.tm_hour
= tm
.hour
;
279 tmTimeOnly
.tm_min
= tm
.min
;
280 tmTimeOnly
.tm_sec
= tm
.sec
;
281 tmTimeOnly
.tm_wday
= 0;
282 tmTimeOnly
.tm_yday
= 0;
283 tmTimeOnly
.tm_mday
= 1; // any date will do
284 tmTimeOnly
.tm_mon
= 0;
285 tmTimeOnly
.tm_year
= 76;
286 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
288 wxString tmp
, res
, fmt
;
289 for ( wxString
::const_iterator p
= format
.begin(); p
!= format
.end(); ++p
)
299 // set the default format
300 switch ( (*++p
).GetValue() )
302 case _T('Y'): // year has 4 digits
306 case _T('j'): // day of year has 3 digits
307 case _T('l'): // milliseconds have 3 digits
311 case _T('w'): // week day as number has only one
316 // it's either another valid format specifier in which case
317 // the format is "%02d" (for all the rest) or we have the
318 // field width preceding the format in which case it will
319 // override the default format anyhow
328 // start of the format specification
329 switch ( (*p
).GetValue() )
331 case _T('a'): // a weekday name
333 // second parameter should be true for abbreviated names
334 res
+= GetWeekDayName(tm
.GetWeekDay(),
335 *p
== _T('a') ? Name_Abbr
: Name_Full
);
338 case _T('b'): // a month name
340 res
+= GetMonthName(tm
.mon
,
341 *p
== _T('b') ? Name_Abbr
: Name_Full
);
344 case _T('c'): // locale default date and time representation
345 case _T('x'): // locale default date representation
348 // the problem: there is no way to know what do these format
349 // specifications correspond to for the current locale.
351 // the solution: use a hack and still use strftime(): first
352 // find the YEAR which is a year in the strftime() range (1970
353 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
354 // of the real year. Then make a copy of the format and
355 // replace all occurrences of YEAR in it with some unique
356 // string not appearing anywhere else in it, then use
357 // strftime() to format the date in year YEAR and then replace
358 // YEAR back by the real year and the unique replacement
359 // string back with YEAR. Notice that "all occurrences of YEAR"
360 // means all occurrences of 4 digit as well as 2 digit form!
362 // the bugs: we assume that neither of %c nor %x contains any
363 // fields which may change between the YEAR and real year. For
364 // example, the week number (%U, %W) and the day number (%j)
365 // will change if one of these years is leap and the other one
368 // find the YEAR: normally, for any year X, Jan 1 of the
369 // year X + 28 is the same weekday as Jan 1 of X (because
370 // the weekday advances by 1 for each normal X and by 2
371 // for each leap X, hence by 5 every 4 years or by 35
372 // which is 0 mod 7 every 28 years) but this rule breaks
373 // down if there are years between X and Y which are
374 // divisible by 4 but not leap (i.e. divisible by 100 but
375 // not 400), hence the correction.
377 int yearReal
= GetYear(tz
);
378 int mod28
= yearReal
% 28;
380 // be careful to not go too far - we risk to leave the
385 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
389 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
392 int nCentury
= year
/ 100,
393 nCenturyReal
= yearReal
/ 100;
395 // need to adjust for the years divisble by 400 which are
396 // not leap but are counted like leap ones if we just take
397 // the number of centuries in between for nLostWeekDays
398 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
399 (nCentury
/ 4 - nCenturyReal
/ 4);
401 // we have to gain back the "lost" weekdays: note that the
402 // effect of this loop is to not do anything to
403 // nLostWeekDays (which we won't use any more), but to
404 // (indirectly) set the year correctly
405 while ( (nLostWeekDays
% 7) != 0 )
407 nLostWeekDays
+= year
++ % 4 ?
1 : 2;
410 // finally move the year below 2000 so that the 2-digit
411 // year number can never match the month or day of the
412 // month when we do the replacements below
416 wxASSERT_MSG( year
>= 1970 && year
< 2000,
417 _T("logic error in wxDateTime::Format") );
420 // use strftime() to format the same date but in supported
423 // NB: we assume that strftime() doesn't check for the
424 // date validity and will happily format the date
425 // corresponding to Feb 29 of a non leap year (which
426 // may happen if yearReal was leap and year is not)
427 struct tm tmAdjusted
;
429 tmAdjusted
.tm_hour
= tm
.hour
;
430 tmAdjusted
.tm_min
= tm
.min
;
431 tmAdjusted
.tm_sec
= tm
.sec
;
432 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
433 tmAdjusted
.tm_yday
= GetDayOfYear();
434 tmAdjusted
.tm_mday
= tm
.mday
;
435 tmAdjusted
.tm_mon
= tm
.mon
;
436 tmAdjusted
.tm_year
= year
- 1900;
437 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
438 wxString str
= CallStrftime(*p
== _T('c') ?
_T("%c")
442 // now replace the replacement year with the real year:
443 // notice that we have to replace the 4 digit year with
444 // a unique string not appearing in strftime() output
445 // first to prevent the 2 digit year from matching any
446 // substring of the 4 digit year (but any day, month,
447 // hours or minutes components should be safe because
448 // they are never in 70-99 range)
449 wxString
replacement("|");
450 while ( str
.find(replacement
) != wxString
::npos
)
453 str
.Replace(wxString
::Format("%d", year
),
455 str
.Replace(wxString
::Format("%d", year
% 100),
456 wxString
::Format("%d", yearReal
% 100));
457 str
.Replace(replacement
,
458 wxString
::Format("%d", yearReal
));
462 #else // !HAVE_STRFTIME
463 // Use "%m/%d/%y %H:%M:%S" format instead
464 res
+= wxString
::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
465 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
466 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
469 case _T('d'): // day of a month (01-31)
470 res
+= wxString
::Format(fmt
, tm
.mday
);
473 case _T('H'): // hour in 24h format (00-23)
474 res
+= wxString
::Format(fmt
, tm
.hour
);
477 case _T('I'): // hour in 12h format (01-12)
479 // 24h -> 12h, 0h -> 12h too
480 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
481 : tm
.hour ? tm
.hour
: 12;
482 res
+= wxString
::Format(fmt
, hour12
);
486 case _T('j'): // day of the year
487 res
+= wxString
::Format(fmt
, GetDayOfYear(tz
));
490 case _T('l'): // milliseconds (NOT STANDARD)
491 res
+= wxString
::Format(fmt
, GetMillisecond(tz
));
494 case _T('m'): // month as a number (01-12)
495 res
+= wxString
::Format(fmt
, tm
.mon
+ 1);
498 case _T('M'): // minute as a decimal number (00-59)
499 res
+= wxString
::Format(fmt
, tm
.min
);
502 case _T('p'): // AM or PM string
504 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
505 #else // !HAVE_STRFTIME
506 res
+= (tmTimeOnly
.tm_hour
> 12) ?
wxT("pm") : wxT("am");
507 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
510 case _T('S'): // second as a decimal number (00-61)
511 res
+= wxString
::Format(fmt
, tm
.sec
);
514 case _T('U'): // week number in the year (Sunday 1st week day)
515 res
+= wxString
::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
518 case _T('W'): // week number in the year (Monday 1st week day)
519 res
+= wxString
::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
522 case _T('w'): // weekday as a number (0-6), Sunday = 0
523 res
+= wxString
::Format(fmt
, tm
.GetWeekDay());
526 // case _T('x'): -- handled with "%c"
528 case _T('X'): // locale default time representation
529 // just use strftime() to format the time for us
531 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
532 #else // !HAVE_STRFTIME
533 res
+= wxString
::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
534 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
537 case _T('y'): // year without century (00-99)
538 res
+= wxString
::Format(fmt
, tm
.year
% 100);
541 case _T('Y'): // year with century
542 res
+= wxString
::Format(fmt
, tm
.year
);
545 case _T('Z'): // timezone name
547 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
552 // is it the format width?
554 while ( *p
== _T('-') || *p
== _T('+') ||
555 *p
== _T(' ') || wxIsdigit(*p
) )
562 // we've only got the flags and width so far in fmt
563 fmt
.Prepend(_T('%'));
571 // no, it wasn't the width
572 wxFAIL_MSG(_T("unknown format specificator"));
574 // fall through and just copy it nevertheless
576 case _T('%'): // a percent sign
580 case 0: // the end of string
581 wxFAIL_MSG(_T("missing format at the end of string"));
583 // just put the '%' which was the last char in format
593 // this function parses a string in (strict) RFC 822 format: see the section 5
594 // of the RFC for the detailed description, but briefly it's something of the
595 // form "Sat, 18 Dec 1999 00:48:30 +0100"
597 // this function is "strict" by design - it must reject anything except true
598 // RFC822 time specs.
600 // TODO a great candidate for using reg exps
602 wxDateTime
::ParseRfc822Date(const wxString
& date
, wxString
::const_iterator
*end
)
604 // TODO: rewrite using iterators instead of wxChar pointers
605 const wxStringCharType
*p
= date
.wx_str();
606 const wxStringCharType
*comma
= wxStrchr(p
, wxS(','));
609 // the part before comma is the weekday
611 // skip it for now - we don't use but might check that it really
612 // corresponds to the specfied date
617 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
625 // the following 1 or 2 digits are the day number
626 if ( !wxIsdigit(*p
) )
628 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
633 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - _T('0'));
637 day
= (wxDateTime_t
)(day
+ (*p
++ - _T('0')));
640 if ( *p
++ != _T(' ') )
645 // the following 3 letters specify the month
646 wxString
monName(p
, 3);
648 if ( monName
== _T("Jan") )
650 else if ( monName
== _T("Feb") )
652 else if ( monName
== _T("Mar") )
654 else if ( monName
== _T("Apr") )
656 else if ( monName
== _T("May") )
658 else if ( monName
== _T("Jun") )
660 else if ( monName
== _T("Jul") )
662 else if ( monName
== _T("Aug") )
664 else if ( monName
== _T("Sep") )
666 else if ( monName
== _T("Oct") )
668 else if ( monName
== _T("Nov") )
670 else if ( monName
== _T("Dec") )
674 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str());
681 if ( *p
++ != _T(' ') )
687 if ( !wxIsdigit(*p
) )
693 int year
= *p
++ - _T('0');
695 if ( !wxIsdigit(*p
) )
697 // should have at least 2 digits in the year
702 year
+= *p
++ - _T('0');
704 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
708 year
+= *p
++ - _T('0');
710 if ( !wxIsdigit(*p
) )
712 // no 3 digit years please
717 year
+= *p
++ - _T('0');
720 if ( *p
++ != _T(' ') )
725 // time is in the format hh:mm:ss and seconds are optional
726 if ( !wxIsdigit(*p
) )
731 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - _T('0'));
733 if ( !wxIsdigit(*p
) )
739 hour
= (wxDateTime_t
)(hour
+ (*p
++ - _T('0')));
741 if ( *p
++ != _T(':') )
746 if ( !wxIsdigit(*p
) )
751 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - _T('0'));
753 if ( !wxIsdigit(*p
) )
759 min
= (wxDateTime_t
)(min
+ *p
++ - _T('0'));
761 wxDateTime_t sec
= 0;
765 if ( !wxIsdigit(*p
) )
770 sec
= (wxDateTime_t
)(*p
++ - _T('0'));
772 if ( !wxIsdigit(*p
) )
778 sec
= (wxDateTime_t
)(sec
+ *p
++ - _T('0'));
781 if ( *p
++ != _T(' ') )
786 // and now the interesting part: the timezone
787 int offset
wxDUMMY_INITIALIZE(0);
788 if ( *p
== _T('-') || *p
== _T('+') )
790 // the explicit offset given: it has the form of hhmm
791 bool plus
= *p
++ == _T('+');
793 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
799 offset
= MIN_PER_HOUR
*(10*(*p
- _T('0')) + (*(p
+ 1) - _T('0')));
803 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
809 offset
+= 10*(*p
- _T('0')) + (*(p
+ 1) - _T('0'));
820 // the symbolic timezone given: may be either military timezone or one
821 // of standard abbreviations
824 // military: Z = UTC, J unused, A = -1, ..., Y = +12
825 static const int offsets
[26] =
827 //A B C D E F G H I J K L M
828 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
829 //N O P R Q S T U V W Z Y Z
830 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
833 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
835 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
);
840 offset
= offsets
[*p
++ - _T('A')];
846 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
848 else if ( tz
== _T("AST") )
850 else if ( tz
== _T("ADT") )
852 else if ( tz
== _T("EST") )
854 else if ( tz
== _T("EDT") )
856 else if ( tz
== _T("CST") )
858 else if ( tz
== _T("CDT") )
860 else if ( tz
== _T("MST") )
862 else if ( tz
== _T("MDT") )
864 else if ( tz
== _T("PST") )
866 else if ( tz
== _T("PDT") )
870 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
);
879 offset
*= MIN_PER_HOUR
;
882 // the spec was correct, construct the date from the values we found
883 Set(day
, mon
, year
, hour
, min
, sec
);
884 MakeFromTimezone(TimeZone
::Make(offset
*SEC_PER_MIN
));
886 const size_t endpos
= p
- date
.wx_str();
888 *end
= date
.begin() + endpos
;
890 return date
.c_str() + endpos
;
895 // returns the string containing strftime() format used for short dates in the
896 // current locale or an empty string
897 static wxString
GetLocaleDateFormat()
901 // there is no setlocale() under Windows CE, so just always query the
904 if ( strcmp(setlocale(LC_ALL
, NULL
), "C") != 0 )
907 // The locale was programatically set to non-C. We assume that this was
908 // done using wxLocale, in which case thread's current locale is also
909 // set to correct LCID value and we can use GetLocaleInfo to determine
910 // the correct formatting string:
912 LCID lcid
= LOCALE_USER_DEFAULT
;
914 LCID lcid
= GetThreadLocale();
916 // according to MSDN 80 chars is max allowed for short date format
918 if ( ::GetLocaleInfo(lcid
, LOCALE_SSHORTDATE
, fmt
, WXSIZEOF(fmt
)) )
920 wxChar chLast
= _T('\0');
921 size_t lastCount
= 0;
922 for ( const wxChar
*p
= fmt
; /* NUL handled inside */; p
++ )
932 // these characters come in groups, start counting them
942 // first deal with any special characters we have had
952 // these two are the same as we
953 // don't distinguish between 1 and
967 wxFAIL_MSG( _T("too many 'd's") );
976 // as for 'd' and 'dd' above
989 wxFAIL_MSG( _T("too many 'M's") );
1006 wxFAIL_MSG( _T("wrong number of 'y's") );
1011 // strftime() doesn't have era string,
1012 // ignore this format
1013 wxASSERT_MSG( lastCount
<= 2,
1014 _T("too many 'g's") );
1018 wxFAIL_MSG( _T("unreachable") );
1025 // not a special character so must be just a separator,
1027 if ( *p
!= _T('\0') )
1029 if ( *p
== _T('%') )
1031 // this one needs to be escaped
1039 if ( *p
== _T('\0') )
1043 //else: GetLocaleInfo() failed, leave fmtDate value unchanged and
1044 // try our luck with the default formats
1046 //else: default C locale, default formats should work
1051 #endif // __WINDOWS__
1054 wxDateTime
::ParseFormat(const wxString
& date
,
1055 const wxString
& format
,
1056 const wxDateTime
& dateDef
,
1057 wxString
::const_iterator
*end
)
1059 wxCHECK_MSG( !format
.empty(), NULL
, "format can't be empty" );
1064 // what fields have we found?
1065 bool haveWDay
= false,
1075 bool hourIsIn12hFormat
= false, // or in 24h one?
1076 isPM
= false; // AM by default
1078 // and the value of the items we have (init them to get rid of warnings)
1079 wxDateTime_t msec
= 0,
1083 WeekDay wday
= Inv_WeekDay
;
1084 wxDateTime_t yday
= 0,
1086 wxDateTime
::Month mon
= Inv_Month
;
1089 const wxStringCharType
*input
= date
.wx_str();
1090 for ( wxString
::const_iterator fmt
= format
.begin(); fmt
!= format
.end(); ++fmt
)
1092 if ( *fmt
!= _T('%') )
1094 if ( wxIsspace(*fmt
) )
1096 // a white space in the format string matches 0 or more white
1097 // spaces in the input
1098 while ( wxIsspace(*input
) )
1105 // any other character (not whitespace, not '%') must be
1106 // matched by itself in the input
1107 if ( *input
++ != *fmt
)
1114 // done with this format char
1118 // start of a format specification
1120 // parse the optional width
1122 while ( wxIsdigit(*++fmt
) )
1125 width
+= *fmt
- _T('0');
1128 // the default widths for the various fields
1131 switch ( (*fmt
).GetValue() )
1133 case _T('Y'): // year has 4 digits
1137 case _T('j'): // day of year has 3 digits
1138 case _T('l'): // milliseconds have 3 digits
1142 case _T('w'): // week day as number has only one
1147 // default for all other fields
1152 // then the format itself
1153 switch ( (*fmt
).GetValue() )
1155 case _T('a'): // a weekday name
1158 int flag
= *fmt
== _T('a') ? Name_Abbr
: Name_Full
;
1159 wday
= GetWeekDayFromName(GetAlphaToken(input
), flag
);
1160 if ( wday
== Inv_WeekDay
)
1169 case _T('b'): // a month name
1172 int flag
= *fmt
== _T('b') ? Name_Abbr
: Name_Full
;
1173 mon
= GetMonthFromName(GetAlphaToken(input
), flag
);
1174 if ( mon
== Inv_Month
)
1183 case _T('c'): // locale default date and time representation
1188 const wxString
inc(input
);
1190 // NOTE: %c is locale-dependent; try strptime
1191 #ifdef HAVE_STRPTIME
1192 // try using strptime() -- it may fail even if the input is
1193 // correct but the date is out of range, so we will fall back
1194 // to our generic code anyhow
1195 const wxStringCharType
*
1196 result
= CallStrptime(input
, "%c", &tm
);
1199 haveDay
= haveMon
= haveYear
=
1200 haveHour
= haveMin
= haveSec
= true;
1206 year
= 1900 + tm
.tm_year
;
1207 mon
= (Month
)tm
.tm_mon
;
1210 input
= result
; // proceed where strptime() ended
1214 // strptime() failed; try generic heuristic code
1215 #endif // HAVE_STRPTIME
1217 // try the format which corresponds to ctime() output first
1218 wxString
::const_iterator endc
;
1219 if ( !dt
.ParseFormat(inc
, wxS("%a %b %d %H:%M:%S %Y"), &endc
) &&
1220 !dt
.ParseFormat(inc
, wxS("%x %X"), &endc
) &&
1221 !dt
.ParseFormat(inc
, wxS("%X %x"), &endc
) )
1223 // we've tried everything and still no match
1229 haveDay
= haveMon
= haveYear
=
1230 haveHour
= haveMin
= haveSec
= true;
1240 input
+= endc
- inc
.begin();
1241 #ifdef HAVE_STRPTIME
1243 #endif // HAVE_STRPTIME
1247 case _T('d'): // day of a month (01-31)
1248 if ( !GetNumericToken(width
, input
, &num
) ||
1249 (num
> 31) || (num
< 1) )
1255 // we can't check whether the day range is correct yet, will
1256 // do it later - assume ok for now
1258 mday
= (wxDateTime_t
)num
;
1261 case _T('H'): // hour in 24h format (00-23)
1262 if ( !GetNumericToken(width
, input
, &num
) || (num
> 23) )
1269 hour
= (wxDateTime_t
)num
;
1272 case _T('I'): // hour in 12h format (01-12)
1273 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
1280 hourIsIn12hFormat
= true;
1281 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
1284 case _T('j'): // day of the year
1285 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 366) )
1292 yday
= (wxDateTime_t
)num
;
1295 case _T('l'): // milliseconds (0-999)
1296 if ( !GetNumericToken(width
, input
, &num
) )
1300 msec
= (wxDateTime_t
)num
;
1303 case _T('m'): // month as a number (01-12)
1304 if ( !GetNumericToken(width
, input
, &num
) || !num
|| (num
> 12) )
1311 mon
= (Month
)(num
- 1);
1314 case _T('M'): // minute as a decimal number (00-59)
1315 if ( !GetNumericToken(width
, input
, &num
) || (num
> 59) )
1322 min
= (wxDateTime_t
)num
;
1325 case _T('p'): // AM or PM string
1327 wxString am
, pm
, token
= GetAlphaToken(input
);
1329 GetAmPmStrings(&am
, &pm
);
1330 if (am
.empty() && pm
.empty())
1331 return NULL
; // no am/pm strings defined
1332 if ( token
.CmpNoCase(pm
) == 0 )
1336 else if ( token
.CmpNoCase(am
) != 0 )
1344 case _T('r'): // time as %I:%M:%S %p
1347 input
= dt
.ParseFormat(input
, wxS("%I:%M:%S %p"));
1354 haveHour
= haveMin
= haveSec
= true;
1363 case _T('R'): // time as %H:%M
1366 input
= dt
.ParseFormat(input
, wxS("%H:%M"));
1373 haveHour
= haveMin
= true;
1381 case _T('S'): // second as a decimal number (00-61)
1382 if ( !GetNumericToken(width
, input
, &num
) || (num
> 61) )
1389 sec
= (wxDateTime_t
)num
;
1392 case _T('T'): // time as %H:%M:%S
1395 input
= dt
.ParseFormat(input
, _T("%H:%M:%S"));
1402 haveHour
= haveMin
= haveSec
= true;
1411 case _T('w'): // weekday as a number (0-6), Sunday = 0
1412 if ( !GetNumericToken(width
, input
, &num
) || (wday
> 6) )
1419 wday
= (WeekDay
)num
;
1422 case _T('x'): // locale default date representation
1423 #ifdef HAVE_STRPTIME
1424 // try using strptime() -- it may fail even if the input is
1425 // correct but the date is out of range, so we will fall back
1426 // to our generic code anyhow
1430 const wxStringCharType
*
1431 result
= CallStrptime(input
, "%x", &tm
);
1436 haveDay
= haveMon
= haveYear
= true;
1438 year
= 1900 + tm
.tm_year
;
1439 mon
= (Month
)tm
.tm_mon
;
1445 #endif // HAVE_STRPTIME
1453 // The above doesn't work for all locales, try to query
1454 // Windows for the right way of formatting the date:
1455 fmtDate
= GetLocaleDateFormat();
1456 if ( fmtDate
.empty() )
1457 #endif // __WINDOWS__
1459 if ( IsWestEuropeanCountry(GetCountry()) ||
1460 GetCountry() == Russia
)
1462 fmtDate
= _T("%d/%m/%y");
1463 fmtDateAlt
= _T("%m/%d/%y");
1467 fmtDate
= _T("%m/%d/%y");
1468 fmtDateAlt
= _T("%d/%m/%y");
1472 const wxString
indate(input
);
1473 wxString
::const_iterator endDate
;
1474 if ( !dt
.ParseFormat(indate
, fmtDate
, &endDate
) )
1476 // try another one if we have it
1477 if ( fmtDateAlt
.empty() ||
1478 !dt
.ParseFormat(indate
, fmtDateAlt
, &endDate
) )
1494 input
+= endDate
- indate
.begin();
1499 case _T('X'): // locale default time representation
1500 #ifdef HAVE_STRPTIME
1502 // use strptime() to do it for us (FIXME !Unicode friendly)
1504 input
= CallStrptime(input
, "%X", &tm
);
1510 haveHour
= haveMin
= haveSec
= true;
1516 #else // !HAVE_STRPTIME
1517 // TODO under Win32 we can query the LOCALE_ITIME system
1518 // setting which says whether the default time format is
1521 // try to parse what follows as "%H:%M:%S" and, if this
1522 // fails, as "%I:%M:%S %p" - this should catch the most
1526 const wxStringCharType
*
1527 result
= dt
.ParseFormat(input
, wxS("%T"));
1530 result
= dt
.ParseFormat(input
, wxS("%r"));
1550 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
1553 case _T('y'): // year without century (00-99)
1554 if ( !GetNumericToken(width
, input
, &num
) || (num
> 99) )
1562 // TODO should have an option for roll over date instead of
1563 // hard coding it here
1564 year
= (num
> 30 ?
1900 : 2000) + (wxDateTime_t
)num
;
1567 case _T('Y'): // year with century
1568 if ( !GetNumericToken(width
, input
, &num
) )
1575 year
= (wxDateTime_t
)num
;
1578 case _T('Z'): // timezone name
1579 wxFAIL_MSG(_T("TODO"));
1582 case _T('%'): // a percent sign
1583 if ( *input
++ != _T('%') )
1590 case 0: // the end of string
1591 wxFAIL_MSG(_T("unexpected format end"));
1595 default: // not a known format spec
1600 // format matched, try to construct a date from what we have now
1602 if ( dateDef
.IsValid() )
1604 // take this date as default
1605 tmDef
= dateDef
.GetTm();
1607 else if ( IsValid() )
1609 // if this date is valid, don't change it
1614 // no default and this date is invalid - fall back to Today()
1615 tmDef
= Today().GetTm();
1631 // TODO we don't check here that the values are consistent, if both year
1632 // day and month/day were found, we just ignore the year day and we
1633 // also always ignore the week day
1636 if ( mday
> GetNumberOfDays(tm
.mon
, tm
.year
) )
1638 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
1645 else if ( haveYDay
)
1647 if ( yday
> GetNumberOfDays(tm
.year
) )
1649 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
1654 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
1661 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
1663 // translate to 24hour format
1666 //else: either already in 24h format or no translation needed
1689 // finally check that the week day is consistent -- if we had it
1690 if ( haveWDay
&& GetWeekDay() != wday
)
1692 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
1697 const size_t endpos
= input
- date
.wx_str();
1699 *end
= date
.begin() + endpos
;
1701 return date
.c_str() + endpos
;
1705 wxDateTime
::ParseDateTime(const wxString
& date
, wxString
::const_iterator
*end
)
1707 // Set to current day and hour, so strings like '14:00' becomes today at
1708 // 14, not some other random date
1709 wxDateTime dtDate
= wxDateTime
::Today();
1710 wxDateTime dtTime
= wxDateTime
::Today();
1712 wxString
::const_iterator
1717 // If we got a date in the beginning, see if there is a time specified
1719 if ( dtDate
.ParseDate(date
, &endDate
) )
1721 // Skip spaces, as the ParseTime() function fails on spaces
1722 while ( endDate
!= date
.end() && wxIsspace(*endDate
) )
1725 const wxString
timestr(endDate
, date
.end());
1726 if ( !dtTime
.ParseTime(timestr
, &endTime
) )
1729 endBoth
= endDate
+ (endTime
- timestr
.begin());
1731 else // no date in the beginning
1733 // check if we have a time followed by a date
1734 if ( !dtTime
.ParseTime(date
, &endTime
) )
1737 while ( endTime
!= date
.end() && wxIsspace(*endTime
) )
1740 const wxString
datestr(endTime
, date
.end());
1741 if ( !dtDate
.ParseDate(datestr
, &endDate
) )
1744 endBoth
= endTime
+ (endDate
- datestr
.begin());
1747 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
1748 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
1749 dtTime
.GetMillisecond());
1751 // Return endpoint of scan
1755 return date
.c_str() + (endBoth
- date
.begin());
1759 wxDateTime
::ParseDate(const wxString
& date
, wxString
::const_iterator
*end
)
1761 // this is a simplified version of ParseDateTime() which understands only
1762 // "today" (for wxDate compatibility) and digits only otherwise (and not
1763 // all esoteric constructions ParseDateTime() knows about)
1765 const wxStringCharType
*p
= date
.wx_str();
1766 while ( wxIsspace(*p
) )
1769 // some special cases
1773 int dayDiffFromToday
;
1776 { wxTRANSLATE("today"), 0 },
1777 { wxTRANSLATE("yesterday"), -1 },
1778 { wxTRANSLATE("tomorrow"), 1 },
1781 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
1783 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
1784 size_t len
= dateStr
.length();
1785 if ( wxStrlen(p
) >= len
)
1787 wxString
str(p
, len
);
1788 if ( str
.CmpNoCase(dateStr
) == 0 )
1790 // nothing can follow this, so stop here
1793 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
1795 if ( dayDiffFromToday
)
1797 *this += wxDateSpan
::Days(dayDiffFromToday
);
1800 const size_t endpos
= p
- date
.wx_str();
1803 *end
= date
.begin() + endpos
;
1804 return date
.c_str() + endpos
;
1809 // We try to guess what we have here: for each new (numeric) token, we
1810 // determine if it can be a month, day or a year. Of course, there is an
1811 // ambiguity as some numbers may be days as well as months, so we also
1812 // have the ability to back track.
1815 bool haveDay
= false, // the months day?
1816 haveWDay
= false, // the day of week?
1817 haveMon
= false, // the month?
1818 haveYear
= false; // the year?
1820 // and the value of the items we have (init them to get rid of warnings)
1821 WeekDay wday
= Inv_WeekDay
;
1822 wxDateTime_t day
= 0;
1823 wxDateTime
::Month mon
= Inv_Month
;
1826 // tokenize the string
1828 static const wxStringCharType
*dateDelimiters
= wxS(".,/-\t\r\n ");
1829 wxStringTokenizer
tok(p
, dateDelimiters
);
1830 while ( tok
.HasMoreTokens() )
1832 wxString token
= tok
.GetNextToken();
1838 if ( token
.ToULong(&val
) )
1840 // guess what this number is
1846 if ( !haveMon
&& val
> 0 && val
<= 12 )
1848 // assume it is month
1851 else // not the month
1855 // this can only be the year
1858 else // may be either day or year
1860 // use a leap year if we don't have the year yet to allow
1861 // dates like 2/29/1976 which would be rejected otherwise
1862 wxDateTime_t max_days
= (wxDateTime_t
)(
1864 ?
GetNumberOfDays(mon
, haveYear ? year
: 1976)
1869 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
1874 else // yes, suppose it's the day
1888 year
= (wxDateTime_t
)val
;
1897 day
= (wxDateTime_t
)val
;
1903 mon
= (Month
)(val
- 1);
1906 else // not a number
1908 // be careful not to overwrite the current mon value
1909 Month mon2
= GetMonthFromName(token
, Name_Full
| Name_Abbr
);
1910 if ( mon2
!= Inv_Month
)
1915 // but we already have a month - maybe we guessed wrong?
1918 // no need to check in month range as always < 12, but
1919 // the days are counted from 1 unlike the months
1920 day
= (wxDateTime_t
)(mon
+ 1);
1925 // could possible be the year (doesn't the year come
1926 // before the month in the japanese format?) (FIXME)
1935 else // not a valid month name
1937 WeekDay wday2
= GetWeekDayFromName(token
, Name_Full
| Name_Abbr
);
1938 if ( wday2
!= Inv_WeekDay
)
1950 else // not a valid weekday name
1953 static const char *ordinals
[] =
1955 wxTRANSLATE("first"),
1956 wxTRANSLATE("second"),
1957 wxTRANSLATE("third"),
1958 wxTRANSLATE("fourth"),
1959 wxTRANSLATE("fifth"),
1960 wxTRANSLATE("sixth"),
1961 wxTRANSLATE("seventh"),
1962 wxTRANSLATE("eighth"),
1963 wxTRANSLATE("ninth"),
1964 wxTRANSLATE("tenth"),
1965 wxTRANSLATE("eleventh"),
1966 wxTRANSLATE("twelfth"),
1967 wxTRANSLATE("thirteenth"),
1968 wxTRANSLATE("fourteenth"),
1969 wxTRANSLATE("fifteenth"),
1970 wxTRANSLATE("sixteenth"),
1971 wxTRANSLATE("seventeenth"),
1972 wxTRANSLATE("eighteenth"),
1973 wxTRANSLATE("nineteenth"),
1974 wxTRANSLATE("twentieth"),
1975 // that's enough - otherwise we'd have problems with
1976 // composite (or not) ordinals
1980 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
1982 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
1988 if ( n
== WXSIZEOF(ordinals
) )
1990 // stop here - something unknown
1997 // don't try anything here (as in case of numeric day
1998 // above) - the symbolic day spec should always
1999 // precede the month/year
2005 day
= (wxDateTime_t
)(n
+ 1);
2010 nPosCur
= tok
.GetPosition();
2013 // either no more tokens or the scan was stopped by something we couldn't
2014 // parse - in any case, see if we can construct a date from what we have
2015 if ( !haveDay
&& !haveWDay
)
2017 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
2022 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
2023 !(haveDay
&& haveMon
&& haveYear
) )
2025 // without adjectives (which we don't support here) the week day only
2026 // makes sense completely separately or with the full date
2027 // specification (what would "Wed 1999" mean?)
2031 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
2033 // may be we have month and day instead of day and year?
2034 if ( haveDay
&& !haveMon
)
2038 // exchange day and month
2039 mon
= (wxDateTime
::Month
)(day
- 1);
2041 // we're in the current year then
2042 if ( (year
> 0) && (year
<= (int)GetNumberOfDays(mon
, Inv_Year
)) )
2044 day
= (wxDateTime_t
)year
;
2049 //else: no, can't exchange, leave haveMon == false
2055 // if we give the year, month and day must be given too
2056 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
2064 mon
= GetCurrentMonth();
2069 year
= GetCurrentYear();
2074 // normally we check the day above but the check is optimistic in case
2075 // we find the day before its month/year so we have to redo it now
2076 if ( day
> GetNumberOfDays(mon
, year
) )
2079 Set(day
, mon
, year
);
2083 // check that it is really the same
2084 if ( GetWeekDay() != wday
)
2086 // inconsistency detected
2087 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
2097 SetToWeekDayInSameWeek(wday
);
2100 // return the pointer to the first unparsed char
2102 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
2104 // if we couldn't parse the token after the delimiter, put back the
2105 // delimiter as well
2109 const size_t endpos
= p
- date
.wx_str();
2111 *end
= date
.begin() + endpos
;
2113 return date
.c_str() + endpos
;
2117 wxDateTime
::ParseTime(const wxString
& time
, wxString
::const_iterator
*end
)
2119 // first try some extra things
2126 { wxTRANSLATE("noon"), 12 },
2127 { wxTRANSLATE("midnight"), 00 },
2131 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
2133 wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
2134 size_t len
= timeString
.length();
2135 if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 )
2137 // casts required by DigitalMars
2138 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
2141 *end
= time
.begin() + len
;
2143 return time
.c_str() + len
;
2147 // try all time formats we may think about in the order from longest to
2149 static const char *timeFormats
[] =
2151 "%I:%M:%S %p", // 12hour with AM/PM
2152 "%H:%M:%S", // could be the same or 24 hour one so try it too
2153 "%I:%M %p", // 12hour with AM/PM but without seconds
2154 "%H:%M:%S", // and a possibly 24 hour version without seconds
2155 "%X", // possibly something from above or maybe something
2156 // completely different -- try it last
2158 // TODO: parse timezones
2161 for ( size_t nFmt
= 0; nFmt
< WXSIZEOF(timeFormats
); nFmt
++ )
2163 const char *result
= ParseFormat(time
, timeFormats
[nFmt
], end
);
2171 // ----------------------------------------------------------------------------
2172 // Workdays and holidays support
2173 // ----------------------------------------------------------------------------
2175 bool wxDateTime
::IsWorkDay(Country
WXUNUSED(country
)) const
2177 return !wxDateTimeHolidayAuthority
::IsHoliday(*this);
2180 // ============================================================================
2182 // ============================================================================
2184 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
2187 return ds1
.Multiply(n
);
2190 // ============================================================================
2192 // ============================================================================
2194 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
2196 return wxTimeSpan(ts
).Multiply(n
);
2199 // this enum is only used in wxTimeSpan::Format() below but we can't declare
2200 // it locally to the method as it provokes an internal compiler error in egcs
2201 // 2.91.60 when building with -O2
2212 // not all strftime(3) format specifiers make sense here because, for example,
2213 // a time span doesn't have a year nor a timezone
2215 // Here are the ones which are supported (all of them are supported by strftime
2217 // %H hour in 24 hour format
2218 // %M minute (00 - 59)
2219 // %S second (00 - 59)
2222 // Also, for MFC CTimeSpan compatibility, we support
2223 // %D number of days
2225 // And, to be better than MFC :-), we also have
2226 // %E number of wEeks
2227 // %l milliseconds (000 - 999)
2228 wxString wxTimeSpan
::Format(const wxString
& format
) const
2230 // we deal with only positive time spans here and just add the sign in
2231 // front for the negative ones
2234 wxString
str(Negate().Format(format
));
2238 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
2239 _T("NULL format in wxTimeSpan::Format") );
2242 str
.Alloc(format
.length());
2244 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
2246 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
2247 // question is what should ts.Format("%S") do? The code here returns "3273"
2248 // in this case (i.e. the total number of seconds, not just seconds % 60)
2249 // because, for me, this call means "give me entire time interval in
2250 // seconds" and not "give me the seconds part of the time interval"
2252 // If we agree that it should behave like this, it is clear that the
2253 // interpretation of each format specifier depends on the presence of the
2254 // other format specs in the string: if there was "%H" before "%M", we
2255 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
2257 // we remember the most important unit found so far
2258 TimeSpanPart partBiggest
= Part_MSec
;
2260 for ( wxString
::const_iterator pch
= format
.begin(); pch
!= format
.end(); ++pch
)
2264 if ( ch
== _T('%') )
2266 // the start of the format specification of the printf() below
2267 wxString
fmtPrefix(_T('%'));
2272 // the number of digits for the format string, 0 if unused
2273 unsigned digits
= 0;
2275 ch
= *++pch
; // get the format spec char
2279 wxFAIL_MSG( _T("invalid format character") );
2285 // skip the part below switch
2290 if ( partBiggest
< Part_Day
)
2296 partBiggest
= Part_Day
;
2301 partBiggest
= Part_Week
;
2307 if ( partBiggest
< Part_Hour
)
2313 partBiggest
= Part_Hour
;
2320 n
= GetMilliseconds().ToLong();
2321 if ( partBiggest
< Part_MSec
)
2325 //else: no need to reset partBiggest to Part_MSec, it is
2326 // the least significant one anyhow
2333 if ( partBiggest
< Part_Min
)
2339 partBiggest
= Part_Min
;
2346 n
= GetSeconds().ToLong();
2347 if ( partBiggest
< Part_Sec
)
2353 partBiggest
= Part_Sec
;
2362 fmtPrefix
<< _T("0") << digits
;
2365 str
+= wxString
::Format(fmtPrefix
+ _T("ld"), n
);
2369 // normal character, just copy
2377 #endif // wxUSE_DATETIME