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 // convert from iterator to char pointer: this is simple as wxCStrData
114 // already supports this
115 const char * const start
= str
.c_str() + (p
- str
.begin());
117 const char * const end
= strptime(start
, fmt
, tm
);
121 // convert back from char pointer to iterator: unfortunately we have no way
122 // to do it efficiently currently so create a temporary string just to
123 // compute the number of characters between start and end
124 p
+= wxString(start
, end
- start
).length();
129 #endif // HAVE_STRPTIME
133 DateLang_English
= 1,
137 // return the month if the string is a month name or Inv_Month otherwise
139 // flags can contain wxDateTime::Name_Abbr/Name_Full or both of them and lang
140 // can be either DateLang_Local (default) to interpret string as a localized
141 // month name or DateLang_English to parse it as a standard English name or
142 // their combination to interpret it in any way
144 GetMonthFromName(const wxString
& name
, int flags
, int lang
)
146 wxDateTime::Month mon
;
147 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
149 // case-insensitive comparison either one of or with both abbreviated
151 if ( flags
& wxDateTime::Name_Full
)
153 if ( lang
& DateLang_English
)
155 if ( name
.CmpNoCase(wxDateTime::GetEnglishMonthName(mon
,
156 wxDateTime::Name_Full
)) == 0 )
160 if ( lang
& DateLang_Local
)
162 if ( name
.CmpNoCase(wxDateTime::GetMonthName(mon
,
163 wxDateTime::Name_Full
)) == 0 )
168 if ( flags
& wxDateTime::Name_Abbr
)
170 if ( lang
& DateLang_English
)
172 if ( name
.CmpNoCase(wxDateTime::GetEnglishMonthName(mon
,
173 wxDateTime::Name_Abbr
)) == 0 )
177 if ( lang
& DateLang_Local
)
179 if ( name
.CmpNoCase(wxDateTime::GetMonthName(mon
,
180 wxDateTime::Name_Abbr
)) == 0 )
189 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
191 // flags and lang parameters have the same meaning as for GetMonthFromName()
194 GetWeekDayFromName(const wxString
& name
, int flags
, int lang
)
196 wxDateTime::WeekDay wd
;
197 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
199 if ( flags
& wxDateTime::Name_Full
)
201 if ( lang
& DateLang_English
)
203 if ( name
.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd
,
204 wxDateTime::Name_Full
)) == 0 )
208 if ( lang
& DateLang_Local
)
210 if ( name
.CmpNoCase(wxDateTime::GetWeekDayName(wd
,
211 wxDateTime::Name_Full
)) == 0 )
216 if ( flags
& wxDateTime::Name_Abbr
)
218 if ( lang
& DateLang_English
)
220 if ( name
.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd
,
221 wxDateTime::Name_Abbr
)) == 0 )
225 if ( lang
& DateLang_Local
)
227 if ( name
.CmpNoCase(wxDateTime::GetWeekDayName(wd
,
228 wxDateTime::Name_Abbr
)) == 0 )
237 // scans all digits (but no more than len) and returns the resulting number
238 bool GetNumericToken(size_t len
,
239 wxString::const_iterator
& p
,
240 unsigned long *number
)
244 while ( wxIsdigit(*p
) )
248 if ( len
&& ++n
> len
)
252 return !s
.empty() && s
.ToULong(number
);
255 // scans all alphabetic characters and returns the resulting string
256 wxString
GetAlphaToken(wxString::const_iterator
& p
)
259 while ( wxIsalpha(*p
) )
267 // parses string starting at given iterator using the specified format and,
268 // optionally, a fall back format (and optionally another one... but it stops
271 // if unsuccessful, returns invalid wxDateTime without changing p; otherwise
272 // advance p to the end of the match and returns wxDateTime containing the
273 // results of the parsing
275 ParseFormatAt(wxString::const_iterator
& p
,
276 const wxString::const_iterator
& end
,
278 // FIXME-VC6: using wxString() instead of wxEmptyString in the
279 // line below results in error C2062: type 'class
280 // wxString (__cdecl *)(void)' unexpected
281 const wxString
& fmtAlt
= wxEmptyString
,
282 const wxString
& fmtAlt2
= wxString())
284 const wxString
str(p
, end
);
285 wxString::const_iterator endParse
;
287 if ( dt
.ParseFormat(str
, fmt
, &endParse
) ||
288 (!fmtAlt
.empty() && dt
.ParseFormat(str
, fmtAlt
, &endParse
)) ||
289 (!fmtAlt2
.empty() && dt
.ParseFormat(str
, fmtAlt2
, &endParse
)) )
291 p
+= endParse
- str
.begin();
293 //else: all formats failed
298 } // anonymous namespace
300 // ----------------------------------------------------------------------------
301 // wxDateTime to/from text representations
302 // ----------------------------------------------------------------------------
304 wxString
wxDateTime::Format(const wxString
& format
, const TimeZone
& tz
) const
306 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
307 _T("NULL format in wxDateTime::Format") );
309 // we have to use our own implementation if the date is out of range of
310 // strftime() or if we use non standard specificators
312 time_t time
= GetTicks();
314 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
319 if ( tz
.GetOffset() == -GetTimeZone() )
321 // we are working with local time
322 tm
= wxLocaltime_r(&time
, &tmstruct
);
324 // should never happen
325 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxLocaltime_r() failed") );
329 time
+= (int)tz
.GetOffset();
331 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
332 int time2
= (int) time
;
338 tm
= wxGmtime_r(&time
, &tmstruct
);
340 // should never happen
341 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxGmtime_r() failed") );
345 tm
= (struct tm
*)NULL
;
351 return CallStrftime(format
, tm
);
354 //else: use generic code below
355 #endif // HAVE_STRFTIME
357 // we only parse ANSI C format specifications here, no POSIX 2
358 // complications, no GNU extensions but we do add support for a "%l" format
359 // specifier allowing to get the number of milliseconds
362 // used for calls to strftime() when we only deal with time
363 struct tm tmTimeOnly
;
364 tmTimeOnly
.tm_hour
= tm
.hour
;
365 tmTimeOnly
.tm_min
= tm
.min
;
366 tmTimeOnly
.tm_sec
= tm
.sec
;
367 tmTimeOnly
.tm_wday
= 0;
368 tmTimeOnly
.tm_yday
= 0;
369 tmTimeOnly
.tm_mday
= 1; // any date will do
370 tmTimeOnly
.tm_mon
= 0;
371 tmTimeOnly
.tm_year
= 76;
372 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
374 wxString tmp
, res
, fmt
;
375 for ( wxString::const_iterator p
= format
.begin(); p
!= format
.end(); ++p
)
385 // set the default format
386 switch ( (*++p
).GetValue() )
388 case _T('Y'): // year has 4 digits
392 case _T('j'): // day of year has 3 digits
393 case _T('l'): // milliseconds have 3 digits
397 case _T('w'): // week day as number has only one
402 // it's either another valid format specifier in which case
403 // the format is "%02d" (for all the rest) or we have the
404 // field width preceding the format in which case it will
405 // override the default format anyhow
414 // start of the format specification
415 switch ( (*p
).GetValue() )
417 case _T('a'): // a weekday name
419 // second parameter should be true for abbreviated names
420 res
+= GetWeekDayName(tm
.GetWeekDay(),
421 *p
== _T('a') ? Name_Abbr
: Name_Full
);
424 case _T('b'): // a month name
426 res
+= GetMonthName(tm
.mon
,
427 *p
== _T('b') ? Name_Abbr
: Name_Full
);
430 case _T('c'): // locale default date and time representation
431 case _T('x'): // locale default date representation
434 // the problem: there is no way to know what do these format
435 // specifications correspond to for the current locale.
437 // the solution: use a hack and still use strftime(): first
438 // find the YEAR which is a year in the strftime() range (1970
439 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
440 // of the real year. Then make a copy of the format and
441 // replace all occurrences of YEAR in it with some unique
442 // string not appearing anywhere else in it, then use
443 // strftime() to format the date in year YEAR and then replace
444 // YEAR back by the real year and the unique replacement
445 // string back with YEAR. Notice that "all occurrences of YEAR"
446 // means all occurrences of 4 digit as well as 2 digit form!
448 // the bugs: we assume that neither of %c nor %x contains any
449 // fields which may change between the YEAR and real year. For
450 // example, the week number (%U, %W) and the day number (%j)
451 // will change if one of these years is leap and the other one
454 // find the YEAR: normally, for any year X, Jan 1 of the
455 // year X + 28 is the same weekday as Jan 1 of X (because
456 // the weekday advances by 1 for each normal X and by 2
457 // for each leap X, hence by 5 every 4 years or by 35
458 // which is 0 mod 7 every 28 years) but this rule breaks
459 // down if there are years between X and Y which are
460 // divisible by 4 but not leap (i.e. divisible by 100 but
461 // not 400), hence the correction.
463 int yearReal
= GetYear(tz
);
464 int mod28
= yearReal
% 28;
466 // be careful to not go too far - we risk to leave the
471 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
475 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
478 int nCentury
= year
/ 100,
479 nCenturyReal
= yearReal
/ 100;
481 // need to adjust for the years divisble by 400 which are
482 // not leap but are counted like leap ones if we just take
483 // the number of centuries in between for nLostWeekDays
484 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
485 (nCentury
/ 4 - nCenturyReal
/ 4);
487 // we have to gain back the "lost" weekdays: note that the
488 // effect of this loop is to not do anything to
489 // nLostWeekDays (which we won't use any more), but to
490 // (indirectly) set the year correctly
491 while ( (nLostWeekDays
% 7) != 0 )
493 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
496 // finally move the year below 2000 so that the 2-digit
497 // year number can never match the month or day of the
498 // month when we do the replacements below
502 wxASSERT_MSG( year
>= 1970 && year
< 2000,
503 _T("logic error in wxDateTime::Format") );
506 // use strftime() to format the same date but in supported
509 // NB: we assume that strftime() doesn't check for the
510 // date validity and will happily format the date
511 // corresponding to Feb 29 of a non leap year (which
512 // may happen if yearReal was leap and year is not)
513 struct tm tmAdjusted
;
515 tmAdjusted
.tm_hour
= tm
.hour
;
516 tmAdjusted
.tm_min
= tm
.min
;
517 tmAdjusted
.tm_sec
= tm
.sec
;
518 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
519 tmAdjusted
.tm_yday
= GetDayOfYear();
520 tmAdjusted
.tm_mday
= tm
.mday
;
521 tmAdjusted
.tm_mon
= tm
.mon
;
522 tmAdjusted
.tm_year
= year
- 1900;
523 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
524 wxString str
= CallStrftime(*p
== _T('c') ? _T("%c")
528 // now replace the replacement year with the real year:
529 // notice that we have to replace the 4 digit year with
530 // a unique string not appearing in strftime() output
531 // first to prevent the 2 digit year from matching any
532 // substring of the 4 digit year (but any day, month,
533 // hours or minutes components should be safe because
534 // they are never in 70-99 range)
535 wxString
replacement("|");
536 while ( str
.find(replacement
) != wxString::npos
)
539 str
.Replace(wxString::Format("%d", year
),
541 str
.Replace(wxString::Format("%d", year
% 100),
542 wxString::Format("%d", yearReal
% 100));
543 str
.Replace(replacement
,
544 wxString::Format("%d", yearReal
));
548 #else // !HAVE_STRFTIME
549 // Use "%m/%d/%y %H:%M:%S" format instead
550 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
551 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
552 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
555 case _T('d'): // day of a month (01-31)
556 res
+= wxString::Format(fmt
, tm
.mday
);
559 case _T('H'): // hour in 24h format (00-23)
560 res
+= wxString::Format(fmt
, tm
.hour
);
563 case _T('I'): // hour in 12h format (01-12)
565 // 24h -> 12h, 0h -> 12h too
566 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
567 : tm
.hour
? tm
.hour
: 12;
568 res
+= wxString::Format(fmt
, hour12
);
572 case _T('j'): // day of the year
573 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
576 case _T('l'): // milliseconds (NOT STANDARD)
577 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
580 case _T('m'): // month as a number (01-12)
581 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
584 case _T('M'): // minute as a decimal number (00-59)
585 res
+= wxString::Format(fmt
, tm
.min
);
588 case _T('p'): // AM or PM string
590 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
591 #else // !HAVE_STRFTIME
592 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
593 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
596 case _T('S'): // second as a decimal number (00-61)
597 res
+= wxString::Format(fmt
, tm
.sec
);
600 case _T('U'): // week number in the year (Sunday 1st week day)
601 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
604 case _T('W'): // week number in the year (Monday 1st week day)
605 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
608 case _T('w'): // weekday as a number (0-6), Sunday = 0
609 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
612 // case _T('x'): -- handled with "%c"
614 case _T('X'): // locale default time representation
615 // just use strftime() to format the time for us
617 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
618 #else // !HAVE_STRFTIME
619 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
620 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
623 case _T('y'): // year without century (00-99)
624 res
+= wxString::Format(fmt
, tm
.year
% 100);
627 case _T('Y'): // year with century
628 res
+= wxString::Format(fmt
, tm
.year
);
631 case _T('Z'): // timezone name
633 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
638 // is it the format width?
640 while ( *p
== _T('-') || *p
== _T('+') ||
641 *p
== _T(' ') || wxIsdigit(*p
) )
648 // we've only got the flags and width so far in fmt
649 fmt
.Prepend(_T('%'));
657 // no, it wasn't the width
658 wxFAIL_MSG(_T("unknown format specificator"));
660 // fall through and just copy it nevertheless
662 case _T('%'): // a percent sign
666 case 0: // the end of string
667 wxFAIL_MSG(_T("missing format at the end of string"));
669 // just put the '%' which was the last char in format
679 // this function parses a string in (strict) RFC 822 format: see the section 5
680 // of the RFC for the detailed description, but briefly it's something of the
681 // form "Sat, 18 Dec 1999 00:48:30 +0100"
683 // this function is "strict" by design - it must reject anything except true
684 // RFC822 time specs.
686 wxDateTime::ParseRfc822Date(const wxString
& date
, wxString::const_iterator
*end
)
688 wxString::const_iterator p
= date
.begin();
691 static const int WDAY_LEN
= 3;
692 const wxString::const_iterator endWday
= p
+ WDAY_LEN
;
693 const wxString
wday(p
, endWday
);
694 if ( GetWeekDayFromName(wday
, Name_Abbr
, DateLang_English
) == Inv_WeekDay
)
696 //else: ignore week day for now, we could also check that it really
697 // corresponds to the specified date
701 // 2. separating comma
702 if ( *p
++ != ',' || *p
++ != ' ' )
706 if ( !wxIsdigit(*p
) )
709 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - '0');
713 day
= (wxDateTime_t
)(day
+ (*p
++ - '0'));
720 static const int MONTH_LEN
= 3;
721 const wxString::const_iterator endMonth
= p
+ MONTH_LEN
;
722 const wxString
monName(p
, endMonth
);
723 Month mon
= GetMonthFromName(monName
, Name_Abbr
, DateLang_English
);
724 if ( mon
== Inv_Month
)
733 if ( !wxIsdigit(*p
) )
736 int year
= *p
++ - '0';
737 if ( !wxIsdigit(*p
) ) // should have at least 2 digits in the year
743 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
749 if ( !wxIsdigit(*p
) )
751 // no 3 digit years please
762 // 6. time in hh:mm:ss format with seconds being optional
763 if ( !wxIsdigit(*p
) )
766 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - '0');
768 if ( !wxIsdigit(*p
) )
772 hour
= (wxDateTime_t
)(hour
+ (*p
++ - '0'));
777 if ( !wxIsdigit(*p
) )
780 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - '0');
782 if ( !wxIsdigit(*p
) )
786 min
+= (wxDateTime_t
)(*p
++ - '0');
788 wxDateTime_t sec
= 0;
792 if ( !wxIsdigit(*p
) )
795 sec
= (wxDateTime_t
)(*p
++ - '0');
797 if ( !wxIsdigit(*p
) )
801 sec
+= (wxDateTime_t
)(*p
++ - '0');
807 // 7. now the interesting part: the timezone
808 int offset
wxDUMMY_INITIALIZE(0);
809 if ( *p
== '-' || *p
== '+' )
811 // the explicit offset given: it has the form of hhmm
812 bool plus
= *p
++ == '+';
814 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
819 offset
= MIN_PER_HOUR
*(10*(*p
- '0') + (*(p
+ 1) - '0'));
823 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
827 offset
+= 10*(*p
- '0') + (*(p
+ 1) - '0');
836 // the symbolic timezone given: may be either military timezone or one
837 // of standard abbreviations
840 // military: Z = UTC, J unused, A = -1, ..., Y = +12
841 static const int offsets
[26] =
843 //A B C D E F G H I J K L M
844 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
845 //N O P R Q S T U V W Z Y Z
846 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
849 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
852 offset
= offsets
[*p
++ - 'A'];
857 const wxString
tz(p
, date
.end());
858 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
860 else if ( tz
== _T("AST") )
862 else if ( tz
== _T("ADT") )
864 else if ( tz
== _T("EST") )
866 else if ( tz
== _T("EDT") )
868 else if ( tz
== _T("CST") )
870 else if ( tz
== _T("CDT") )
872 else if ( tz
== _T("MST") )
874 else if ( tz
== _T("MDT") )
876 else if ( tz
== _T("PST") )
878 else if ( tz
== _T("PDT") )
887 offset
*= MIN_PER_HOUR
;
891 // the spec was correct, construct the date from the values we found
892 Set(day
, mon
, year
, hour
, min
, sec
);
893 MakeFromTimezone(TimeZone::Make(offset
*SEC_PER_MIN
));
903 // returns the string containing strftime() format used for short dates in the
904 // current locale or an empty string
905 static wxString
GetLocaleDateFormat()
909 // there is no setlocale() under Windows CE, so just always query the
912 if ( strcmp(setlocale(LC_ALL
, NULL
), "C") != 0 )
915 // The locale was programatically set to non-C. We assume that this was
916 // done using wxLocale, in which case thread's current locale is also
917 // set to correct LCID value and we can use GetLocaleInfo to determine
918 // the correct formatting string:
920 LCID lcid
= LOCALE_USER_DEFAULT
;
922 LCID lcid
= GetThreadLocale();
924 // according to MSDN 80 chars is max allowed for short date format
926 if ( ::GetLocaleInfo(lcid
, LOCALE_SSHORTDATE
, fmt
, WXSIZEOF(fmt
)) )
928 wxChar chLast
= _T('\0');
929 size_t lastCount
= 0;
930 for ( const wxChar
*p
= fmt
; /* NUL handled inside */; p
++ )
940 // these characters come in groups, start counting them
950 // first deal with any special characters we have had
960 // these two are the same as we
961 // don't distinguish between 1 and
975 wxFAIL_MSG( _T("too many 'd's") );
984 // as for 'd' and 'dd' above
997 wxFAIL_MSG( _T("too many 'M's") );
1002 switch ( lastCount
)
1014 wxFAIL_MSG( _T("wrong number of 'y's") );
1019 // strftime() doesn't have era string,
1020 // ignore this format
1021 wxASSERT_MSG( lastCount
<= 2,
1022 _T("too many 'g's") );
1026 wxFAIL_MSG( _T("unreachable") );
1033 // not a special character so must be just a separator,
1035 if ( *p
!= _T('\0') )
1037 if ( *p
== _T('%') )
1039 // this one needs to be escaped
1047 if ( *p
== _T('\0') )
1051 //else: GetLocaleInfo() failed, leave fmtDate value unchanged and
1052 // try our luck with the default formats
1054 //else: default C locale, default formats should work
1059 #endif // __WINDOWS__
1062 wxDateTime::ParseFormat(const wxString
& date
,
1063 const wxString
& format
,
1064 const wxDateTime
& dateDef
,
1065 wxString::const_iterator
*end
)
1067 wxCHECK_MSG( !format
.empty(), false, "format can't be empty" );
1068 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1073 // what fields have we found?
1074 bool haveWDay
= false,
1084 bool hourIsIn12hFormat
= false, // or in 24h one?
1085 isPM
= false; // AM by default
1087 // and the value of the items we have (init them to get rid of warnings)
1088 wxDateTime_t msec
= 0,
1092 WeekDay wday
= Inv_WeekDay
;
1093 wxDateTime_t yday
= 0,
1095 wxDateTime::Month mon
= Inv_Month
;
1098 wxString::const_iterator input
= date
.begin();
1099 for ( wxString::const_iterator fmt
= format
.begin(); fmt
!= format
.end(); ++fmt
)
1101 if ( *fmt
!= _T('%') )
1103 if ( wxIsspace(*fmt
) )
1105 // a white space in the format string matches 0 or more white
1106 // spaces in the input
1107 while ( wxIsspace(*input
) )
1114 // any other character (not whitespace, not '%') must be
1115 // matched by itself in the input
1116 if ( *input
++ != *fmt
)
1123 // done with this format char
1127 // start of a format specification
1129 // parse the optional width
1131 while ( wxIsdigit(*++fmt
) )
1134 width
+= *fmt
- '0';
1137 // the default widths for the various fields
1140 switch ( (*fmt
).GetValue() )
1142 case _T('Y'): // year has 4 digits
1146 case _T('j'): // day of year has 3 digits
1147 case _T('l'): // milliseconds have 3 digits
1151 case _T('w'): // week day as number has only one
1156 // default for all other fields
1161 // then the format itself
1162 switch ( (*fmt
).GetValue() )
1164 case _T('a'): // a weekday name
1167 wday
= GetWeekDayFromName
1169 GetAlphaToken(input
),
1170 *fmt
== 'a' ? Name_Abbr
: Name_Full
,
1173 if ( wday
== Inv_WeekDay
)
1182 case _T('b'): // a month name
1185 mon
= GetMonthFromName
1187 GetAlphaToken(input
),
1188 *fmt
== 'b' ? Name_Abbr
: Name_Full
,
1191 if ( mon
== Inv_Month
)
1200 case _T('c'): // locale default date and time representation
1202 #ifdef HAVE_STRPTIME
1205 // try using strptime() -- it may fail even if the input is
1206 // correct but the date is out of range, so we will fall back
1207 // to our generic code anyhow
1208 if ( CallStrptime(date
, input
, "%c", &tm
) )
1214 year
= 1900 + tm
.tm_year
;
1215 mon
= (Month
)tm
.tm_mon
;
1218 else // strptime() failed; try generic heuristic code
1219 #endif // HAVE_STRPTIME
1222 // try the format which corresponds to ctime() output
1223 // first, then the generic date/time formats
1224 const wxDateTime dt
= ParseFormatAt
1228 wxS("%a %b %d %H:%M:%S %Y"),
1232 if ( !dt
.IsValid() )
1246 haveDay
= haveMon
= haveYear
=
1247 haveHour
= haveMin
= haveSec
= true;
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 false; // 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 if ( !dt
.ParseFormat(wxString(input
, date
.end()),
1359 wxS("%I:%M:%S %p"), &input
) )
1362 haveHour
= haveMin
= haveSec
= true;
1371 case _T('R'): // time as %H:%M
1374 dt
= ParseFormatAt(input
, date
.end(), wxS("%H:%M"));
1375 if ( !dt
.IsValid() )
1387 case _T('S'): // second as a decimal number (00-61)
1388 if ( !GetNumericToken(width
, input
, &num
) || (num
> 61) )
1395 sec
= (wxDateTime_t
)num
;
1398 case _T('T'): // time as %H:%M:%S
1401 dt
= ParseFormatAt(input
, date
.end(), wxS("%H:%M:%S"));
1402 if ( !dt
.IsValid() )
1416 case _T('w'): // weekday as a number (0-6), Sunday = 0
1417 if ( !GetNumericToken(width
, input
, &num
) || (wday
> 6) )
1424 wday
= (WeekDay
)num
;
1427 case _T('x'): // locale default date representation
1428 #ifdef HAVE_STRPTIME
1429 // try using strptime() -- it may fail even if the input is
1430 // correct but the date is out of range, so we will fall back
1431 // to our generic code anyhow
1435 if ( CallStrptime(date
, input
, "%x", &tm
) )
1437 haveDay
= haveMon
= haveYear
= true;
1439 year
= 1900 + tm
.tm_year
;
1440 mon
= (Month
)tm
.tm_mon
;
1446 #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");
1473 dt
= ParseFormatAt(input
, date
.end(),
1474 fmtDate
, fmtDateAlt
);
1475 if ( !dt
.IsValid() )
1491 case _T('X'): // locale default time representation
1492 #ifdef HAVE_STRPTIME
1494 // use strptime() to do it for us (FIXME !Unicode friendly)
1496 if ( !CallStrptime(date
, input
, "%X", &tm
) )
1499 haveHour
= haveMin
= haveSec
= true;
1505 #else // !HAVE_STRPTIME
1506 // TODO under Win32 we can query the LOCALE_ITIME system
1507 // setting which says whether the default time format is
1510 // try to parse what follows as "%H:%M:%S" and, if this
1511 // fails, as "%I:%M:%S %p" - this should catch the most
1514 dt
= ParseFormatAt(input
, date
.end(), "%T", "%r");
1515 if ( !dt
.IsValid() )
1527 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
1530 case _T('y'): // year without century (00-99)
1531 if ( !GetNumericToken(width
, input
, &num
) || (num
> 99) )
1539 // TODO should have an option for roll over date instead of
1540 // hard coding it here
1541 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
1544 case _T('Y'): // year with century
1545 if ( !GetNumericToken(width
, input
, &num
) )
1552 year
= (wxDateTime_t
)num
;
1555 case _T('Z'): // timezone name
1556 wxFAIL_MSG(_T("TODO"));
1559 case _T('%'): // a percent sign
1560 if ( *input
++ != _T('%') )
1567 case 0: // the end of string
1568 wxFAIL_MSG(_T("unexpected format end"));
1572 default: // not a known format spec
1577 // format matched, try to construct a date from what we have now
1579 if ( dateDef
.IsValid() )
1581 // take this date as default
1582 tmDef
= dateDef
.GetTm();
1584 else if ( IsValid() )
1586 // if this date is valid, don't change it
1591 // no default and this date is invalid - fall back to Today()
1592 tmDef
= Today().GetTm();
1608 // TODO we don't check here that the values are consistent, if both year
1609 // day and month/day were found, we just ignore the year day and we
1610 // also always ignore the week day
1613 if ( mday
> GetNumberOfDays(tm
.mon
, tm
.year
) )
1618 else if ( haveYDay
)
1620 if ( yday
> GetNumberOfDays(tm
.year
) )
1623 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
1630 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
1632 // translate to 24hour format
1635 //else: either already in 24h format or no translation needed
1658 // finally check that the week day is consistent -- if we had it
1659 if ( haveWDay
&& GetWeekDay() != wday
)
1668 wxDateTime::ParseDateTime(const wxString
& date
, wxString::const_iterator
*end
)
1670 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1672 // Set to current day and hour, so strings like '14:00' becomes today at
1673 // 14, not some other random date
1674 wxDateTime dtDate
= wxDateTime::Today();
1675 wxDateTime dtTime
= wxDateTime::Today();
1677 wxString::const_iterator
1682 // If we got a date in the beginning, see if there is a time specified
1684 if ( dtDate
.ParseDate(date
, &endDate
) )
1686 // Skip spaces, as the ParseTime() function fails on spaces
1687 while ( endDate
!= date
.end() && wxIsspace(*endDate
) )
1690 const wxString
timestr(endDate
, date
.end());
1691 if ( !dtTime
.ParseTime(timestr
, &endTime
) )
1694 endBoth
= endDate
+ (endTime
- timestr
.begin());
1696 else // no date in the beginning
1698 // check if we have a time followed by a date
1699 if ( !dtTime
.ParseTime(date
, &endTime
) )
1702 while ( endTime
!= date
.end() && wxIsspace(*endTime
) )
1705 const wxString
datestr(endTime
, date
.end());
1706 if ( !dtDate
.ParseDate(datestr
, &endDate
) )
1709 endBoth
= endTime
+ (endDate
- datestr
.begin());
1712 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
1713 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
1714 dtTime
.GetMillisecond());
1722 wxDateTime::ParseDate(const wxString
& date
, wxString::const_iterator
*end
)
1724 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1726 // this is a simplified version of ParseDateTime() which understands only
1727 // "today" (for wxDate compatibility) and digits only otherwise (and not
1728 // all esoteric constructions ParseDateTime() knows about)
1730 const wxString::const_iterator pBegin
= date
.begin();
1732 wxString::const_iterator p
= pBegin
;
1733 while ( wxIsspace(*p
) )
1736 // some special cases
1740 int dayDiffFromToday
;
1743 { wxTRANSLATE("today"), 0 },
1744 { wxTRANSLATE("yesterday"), -1 },
1745 { wxTRANSLATE("tomorrow"), 1 },
1748 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
1750 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
1751 size_t len
= dateStr
.length();
1753 const wxString::const_iterator pEnd
= p
+ len
;
1754 if ( wxString(p
, pEnd
).CmpNoCase(dateStr
) == 0 )
1756 // nothing can follow this, so stop here
1760 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
1762 if ( dayDiffFromToday
)
1764 *this += wxDateSpan::Days(dayDiffFromToday
);
1773 // We try to guess what we have here: for each new (numeric) token, we
1774 // determine if it can be a month, day or a year. Of course, there is an
1775 // ambiguity as some numbers may be days as well as months, so we also
1776 // have the ability to back track.
1779 bool haveDay
= false, // the months day?
1780 haveWDay
= false, // the day of week?
1781 haveMon
= false, // the month?
1782 haveYear
= false; // the year?
1784 // and the value of the items we have (init them to get rid of warnings)
1785 WeekDay wday
= Inv_WeekDay
;
1786 wxDateTime_t day
= 0;
1787 wxDateTime::Month mon
= Inv_Month
;
1790 // tokenize the string
1792 static const wxStringCharType
*dateDelimiters
= wxS(".,/-\t\r\n ");
1793 wxStringTokenizer
tok(wxString(p
, date
.end()), dateDelimiters
);
1794 while ( tok
.HasMoreTokens() )
1796 wxString token
= tok
.GetNextToken();
1802 if ( token
.ToULong(&val
) )
1804 // guess what this number is
1810 if ( !haveMon
&& val
> 0 && val
<= 12 )
1812 // assume it is month
1815 else // not the month
1819 // this can only be the year
1822 else // may be either day or year
1824 // use a leap year if we don't have the year yet to allow
1825 // dates like 2/29/1976 which would be rejected otherwise
1826 wxDateTime_t max_days
= (wxDateTime_t
)(
1828 ? GetNumberOfDays(mon
, haveYear
? year
: 1976)
1833 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
1838 else // yes, suppose it's the day
1852 year
= (wxDateTime_t
)val
;
1861 day
= (wxDateTime_t
)val
;
1867 mon
= (Month
)(val
- 1);
1870 else // not a number
1872 // be careful not to overwrite the current mon value
1873 Month mon2
= GetMonthFromName
1876 Name_Full
| Name_Abbr
,
1877 DateLang_Local
| DateLang_English
1879 if ( mon2
!= Inv_Month
)
1884 // but we already have a month - maybe we guessed wrong?
1887 // no need to check in month range as always < 12, but
1888 // the days are counted from 1 unlike the months
1889 day
= (wxDateTime_t
)(mon
+ 1);
1894 // could possible be the year (doesn't the year come
1895 // before the month in the japanese format?) (FIXME)
1904 else // not a valid month name
1906 WeekDay wday2
= GetWeekDayFromName
1909 Name_Full
| Name_Abbr
,
1910 DateLang_Local
| DateLang_English
1912 if ( wday2
!= Inv_WeekDay
)
1924 else // not a valid weekday name
1927 static const char *ordinals
[] =
1929 wxTRANSLATE("first"),
1930 wxTRANSLATE("second"),
1931 wxTRANSLATE("third"),
1932 wxTRANSLATE("fourth"),
1933 wxTRANSLATE("fifth"),
1934 wxTRANSLATE("sixth"),
1935 wxTRANSLATE("seventh"),
1936 wxTRANSLATE("eighth"),
1937 wxTRANSLATE("ninth"),
1938 wxTRANSLATE("tenth"),
1939 wxTRANSLATE("eleventh"),
1940 wxTRANSLATE("twelfth"),
1941 wxTRANSLATE("thirteenth"),
1942 wxTRANSLATE("fourteenth"),
1943 wxTRANSLATE("fifteenth"),
1944 wxTRANSLATE("sixteenth"),
1945 wxTRANSLATE("seventeenth"),
1946 wxTRANSLATE("eighteenth"),
1947 wxTRANSLATE("nineteenth"),
1948 wxTRANSLATE("twentieth"),
1949 // that's enough - otherwise we'd have problems with
1950 // composite (or not) ordinals
1954 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
1956 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
1962 if ( n
== WXSIZEOF(ordinals
) )
1964 // stop here - something unknown
1971 // don't try anything here (as in case of numeric day
1972 // above) - the symbolic day spec should always
1973 // precede the month/year
1979 day
= (wxDateTime_t
)(n
+ 1);
1984 nPosCur
= tok
.GetPosition();
1987 // either no more tokens or the scan was stopped by something we couldn't
1988 // parse - in any case, see if we can construct a date from what we have
1989 if ( !haveDay
&& !haveWDay
)
1992 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
1993 !(haveDay
&& haveMon
&& haveYear
) )
1995 // without adjectives (which we don't support here) the week day only
1996 // makes sense completely separately or with the full date
1997 // specification (what would "Wed 1999" mean?)
2001 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
2003 // may be we have month and day instead of day and year?
2004 if ( haveDay
&& !haveMon
)
2008 // exchange day and month
2009 mon
= (wxDateTime::Month
)(day
- 1);
2011 // we're in the current year then
2012 if ( (year
> 0) && (year
<= (int)GetNumberOfDays(mon
, Inv_Year
)) )
2014 day
= (wxDateTime_t
)year
;
2019 //else: no, can't exchange, leave haveMon == false
2029 mon
= GetCurrentMonth();
2034 year
= GetCurrentYear();
2039 // normally we check the day above but the check is optimistic in case
2040 // we find the day before its month/year so we have to redo it now
2041 if ( day
> GetNumberOfDays(mon
, year
) )
2044 Set(day
, mon
, year
);
2048 // check that it is really the same
2049 if ( GetWeekDay() != wday
)
2057 SetToWeekDayInSameWeek(wday
);
2060 // return the pointer to the first unparsed char
2062 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
2064 // if we couldn't parse the token after the delimiter, put back the
2065 // delimiter as well
2075 wxDateTime::ParseTime(const wxString
& time
, wxString::const_iterator
*end
)
2077 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
2079 // first try some extra things
2086 { wxTRANSLATE("noon"), 12 },
2087 { wxTRANSLATE("midnight"), 00 },
2091 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
2093 const wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
2094 const wxString::const_iterator p
= time
.begin() + timeString
.length();
2095 if ( timeString
.CmpNoCase(wxString(time
.begin(), p
)) == 0 )
2097 // casts required by DigitalMars
2098 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
2107 // try all time formats we may think about in the order from longest to
2109 static const char *timeFormats
[] =
2111 "%I:%M:%S %p", // 12hour with AM/PM
2112 "%H:%M:%S", // could be the same or 24 hour one so try it too
2113 "%I:%M %p", // 12hour with AM/PM but without seconds
2114 "%H:%M:%S", // and a possibly 24 hour version without seconds
2115 "%X", // possibly something from above or maybe something
2116 // completely different -- try it last
2118 // TODO: parse timezones
2121 for ( size_t nFmt
= 0; nFmt
< WXSIZEOF(timeFormats
); nFmt
++ )
2123 if ( ParseFormat(time
, timeFormats
[nFmt
], end
) )
2130 // ----------------------------------------------------------------------------
2131 // Workdays and holidays support
2132 // ----------------------------------------------------------------------------
2134 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
2136 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
2139 // ============================================================================
2141 // ============================================================================
2143 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
2146 return ds1
.Multiply(n
);
2149 // ============================================================================
2151 // ============================================================================
2153 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
2155 return wxTimeSpan(ts
).Multiply(n
);
2158 // this enum is only used in wxTimeSpan::Format() below but we can't declare
2159 // it locally to the method as it provokes an internal compiler error in egcs
2160 // 2.91.60 when building with -O2
2171 // not all strftime(3) format specifiers make sense here because, for example,
2172 // a time span doesn't have a year nor a timezone
2174 // Here are the ones which are supported (all of them are supported by strftime
2176 // %H hour in 24 hour format
2177 // %M minute (00 - 59)
2178 // %S second (00 - 59)
2181 // Also, for MFC CTimeSpan compatibility, we support
2182 // %D number of days
2184 // And, to be better than MFC :-), we also have
2185 // %E number of wEeks
2186 // %l milliseconds (000 - 999)
2187 wxString
wxTimeSpan::Format(const wxString
& format
) const
2189 // we deal with only positive time spans here and just add the sign in
2190 // front for the negative ones
2193 wxString
str(Negate().Format(format
));
2197 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
2198 _T("NULL format in wxTimeSpan::Format") );
2201 str
.Alloc(format
.length());
2203 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
2205 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
2206 // question is what should ts.Format("%S") do? The code here returns "3273"
2207 // in this case (i.e. the total number of seconds, not just seconds % 60)
2208 // because, for me, this call means "give me entire time interval in
2209 // seconds" and not "give me the seconds part of the time interval"
2211 // If we agree that it should behave like this, it is clear that the
2212 // interpretation of each format specifier depends on the presence of the
2213 // other format specs in the string: if there was "%H" before "%M", we
2214 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
2216 // we remember the most important unit found so far
2217 TimeSpanPart partBiggest
= Part_MSec
;
2219 for ( wxString::const_iterator pch
= format
.begin(); pch
!= format
.end(); ++pch
)
2223 if ( ch
== _T('%') )
2225 // the start of the format specification of the printf() below
2226 wxString
fmtPrefix(_T('%'));
2231 // the number of digits for the format string, 0 if unused
2232 unsigned digits
= 0;
2234 ch
= *++pch
; // get the format spec char
2238 wxFAIL_MSG( _T("invalid format character") );
2244 // skip the part below switch
2249 if ( partBiggest
< Part_Day
)
2255 partBiggest
= Part_Day
;
2260 partBiggest
= Part_Week
;
2266 if ( partBiggest
< Part_Hour
)
2272 partBiggest
= Part_Hour
;
2279 n
= GetMilliseconds().ToLong();
2280 if ( partBiggest
< Part_MSec
)
2284 //else: no need to reset partBiggest to Part_MSec, it is
2285 // the least significant one anyhow
2292 if ( partBiggest
< Part_Min
)
2298 partBiggest
= Part_Min
;
2305 n
= GetSeconds().ToLong();
2306 if ( partBiggest
< Part_Sec
)
2312 partBiggest
= Part_Sec
;
2321 fmtPrefix
<< _T("0") << digits
;
2324 str
+= wxString::Format(fmtPrefix
+ _T("ld"), n
);
2328 // normal character, just copy
2336 #endif // wxUSE_DATETIME