1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/datetimefmt.cpp
3 // Purpose: wxDateTime formatting & parsing code
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // parts of code taken from sndcal library by Scott E. Lee:
11 // Copyright 1993-1995, Scott E. Lee, all rights reserved.
12 // Permission granted to use, copy, modify, distribute and sell
13 // so long as the above copyright and this permission statement
14 // are retained in all copies.
16 // Licence: wxWindows licence
17 ///////////////////////////////////////////////////////////////////////////////
19 // ============================================================================
21 // ============================================================================
23 // ----------------------------------------------------------------------------
25 // ----------------------------------------------------------------------------
27 // For compilers that support precompilation, includes "wx.h".
28 #include "wx/wxprec.h"
34 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
38 #include "wx/msw/wrapwin.h"
40 #include "wx/string.h"
43 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
44 #include "wx/module.h"
48 #include "wx/thread.h"
59 #include "wx/datetime.h"
62 // ============================================================================
63 // implementation of wxDateTime
64 // ============================================================================
66 // ----------------------------------------------------------------------------
67 // helpers shared between datetime.cpp and datetimefmt.cpp
68 // ----------------------------------------------------------------------------
70 extern void InitTm(struct tm
& tm
);
72 extern wxString
CallStrftime(const wxString
& format
, const tm
* tm
);
74 // ----------------------------------------------------------------------------
75 // constants (see also datetime.cpp)
76 // ----------------------------------------------------------------------------
78 static const int DAYS_PER_WEEK
= 7;
80 static const int HOURS_PER_DAY
= 24;
82 static const int SEC_PER_MIN
= 60;
84 static const int MIN_PER_HOUR
= 60;
86 // ----------------------------------------------------------------------------
88 // ----------------------------------------------------------------------------
93 // all the functions below taking non-const wxString::const_iterator p advance
94 // it until the end of the match
96 // scans all digits (but no more than len) and returns the resulting number
97 bool GetNumericToken(size_t len
,
98 wxString::const_iterator
& p
,
99 const wxString::const_iterator
& end
,
100 unsigned long *number
)
104 while ( p
!= end
&& wxIsdigit(*p
) )
108 if ( len
&& ++n
> len
)
112 return !s
.empty() && s
.ToULong(number
);
115 // scans all alphabetic characters and returns the resulting string
117 GetAlphaToken(wxString::const_iterator
& p
,
118 const wxString::const_iterator
& end
)
121 while ( p
!= end
&& wxIsalpha(*p
) )
131 DateLang_English
= 1,
135 // return the month if the string is a month name or Inv_Month otherwise
137 // flags can contain wxDateTime::Name_Abbr/Name_Full or both of them and lang
138 // can be either DateLang_Local (default) to interpret string as a localized
139 // month name or DateLang_English to parse it as a standard English name or
140 // their combination to interpret it in any way
142 GetMonthFromName(wxString::const_iterator
& p
,
143 const wxString::const_iterator
& end
,
147 const wxString::const_iterator pOrig
= p
;
148 const wxString name
= GetAlphaToken(p
, end
);
150 return wxDateTime::Inv_Month
;
152 wxDateTime::Month mon
;
153 for ( mon
= wxDateTime::Jan
; mon
< wxDateTime::Inv_Month
; wxNextMonth(mon
) )
155 // case-insensitive comparison either one of or with both abbreviated
157 if ( flags
& wxDateTime::Name_Full
)
159 if ( lang
& DateLang_English
)
161 if ( name
.CmpNoCase(wxDateTime::GetEnglishMonthName(mon
,
162 wxDateTime::Name_Full
)) == 0 )
166 if ( lang
& DateLang_Local
)
168 if ( name
.CmpNoCase(wxDateTime::GetMonthName(mon
,
169 wxDateTime::Name_Full
)) == 0 )
174 if ( flags
& wxDateTime::Name_Abbr
)
176 if ( lang
& DateLang_English
)
178 if ( name
.CmpNoCase(wxDateTime::GetEnglishMonthName(mon
,
179 wxDateTime::Name_Abbr
)) == 0 )
183 if ( lang
& DateLang_Local
)
185 // some locales (e.g. French one) use periods for the
186 // abbreviated month names but it's never part of name so
187 // compare it specially
188 wxString nameAbbr
= wxDateTime::GetMonthName(mon
,
189 wxDateTime::Name_Abbr
);
190 const bool hasPeriod
= *nameAbbr
.rbegin() == '.';
192 nameAbbr
.erase(nameAbbr
.end() - 1);
194 if ( name
.CmpNoCase(nameAbbr
) == 0 )
198 // skip trailing period if it was part of the match
201 else // no match as no matching period
211 if ( mon
== wxDateTime::Inv_Month
)
217 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
219 // flags and lang parameters have the same meaning as for GetMonthFromName()
222 GetWeekDayFromName(wxString::const_iterator
& p
,
223 const wxString::const_iterator
& end
,
226 const wxString::const_iterator pOrig
= p
;
227 const wxString name
= GetAlphaToken(p
, end
);
229 return wxDateTime::Inv_WeekDay
;
231 wxDateTime::WeekDay wd
;
232 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
234 if ( flags
& wxDateTime::Name_Full
)
236 if ( lang
& DateLang_English
)
238 if ( name
.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd
,
239 wxDateTime::Name_Full
)) == 0 )
243 if ( lang
& DateLang_Local
)
245 if ( name
.CmpNoCase(wxDateTime::GetWeekDayName(wd
,
246 wxDateTime::Name_Full
)) == 0 )
251 if ( flags
& wxDateTime::Name_Abbr
)
253 if ( lang
& DateLang_English
)
255 if ( name
.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd
,
256 wxDateTime::Name_Abbr
)) == 0 )
260 if ( lang
& DateLang_Local
)
262 if ( name
.CmpNoCase(wxDateTime::GetWeekDayName(wd
,
263 wxDateTime::Name_Abbr
)) == 0 )
269 if ( wd
== wxDateTime::Inv_WeekDay
)
275 // parses string starting at given iterator using the specified format and,
276 // optionally, a fall back format (and optionally another one... but it stops
279 // if unsuccessful, returns invalid wxDateTime without changing p; otherwise
280 // advance p to the end of the match and returns wxDateTime containing the
281 // results of the parsing
283 ParseFormatAt(wxString::const_iterator
& p
,
284 const wxString::const_iterator
& end
,
286 // FIXME-VC6: using wxString() instead of wxEmptyString in the
287 // line below results in error C2062: type 'class
288 // wxString (__cdecl *)(void)' unexpected
289 const wxString
& fmtAlt
= wxEmptyString
)
291 const wxString
str(p
, end
);
292 wxString::const_iterator endParse
;
295 // Use a default date outside of the DST period to avoid problems with
296 // parsing the time differently depending on the today's date (which is used
297 // as the fall back date if none is explicitly specified).
298 static const wxDateTime
dtDef(1, wxDateTime::Jan
, 2012);
300 if ( dt
.ParseFormat(str
, fmt
, dtDef
, &endParse
) ||
301 (!fmtAlt
.empty() && dt
.ParseFormat(str
, fmtAlt
, dtDef
, &endParse
)) )
303 p
+= endParse
- str
.begin();
305 //else: all formats failed
310 } // anonymous namespace
312 // ----------------------------------------------------------------------------
313 // wxDateTime to/from text representations
314 // ----------------------------------------------------------------------------
316 wxString
wxDateTime::Format(const wxString
& formatp
, const TimeZone
& tz
) const
318 wxCHECK_MSG( !formatp
.empty(), wxEmptyString
,
319 wxT("NULL format in wxDateTime::Format") );
321 wxString format
= formatp
;
323 format
.Replace("%c",wxLocale::GetInfo(wxLOCALE_DATE_TIME_FMT
));
324 format
.Replace("%x",wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT
));
325 format
.Replace("%X",wxLocale::GetInfo(wxLOCALE_TIME_FMT
));
327 // we have to use our own implementation if the date is out of range of
328 // strftime() or if we use non standard specifiers (notice that "%z" is
329 // special because it is de facto standard under Unix but is not supported
331 #ifdef wxHAS_STRFTIME
332 time_t time
= GetTicks();
334 if ( (time
!= (time_t)-1) && !wxStrstr(format
, wxT("%l"))
336 && !wxStrstr(format
, wxT("%z"))
343 if ( tz
.GetOffset() == -wxGetTimeZone() )
345 // we are working with local time
346 tm
= wxLocaltime_r(&time
, &tmstruct
);
348 // should never happen
349 wxCHECK_MSG( tm
, wxEmptyString
, wxT("wxLocaltime_r() failed") );
353 time
+= (int)tz
.GetOffset();
355 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
356 int time2
= (int) time
;
362 tm
= wxGmtime_r(&time
, &tmstruct
);
364 // should never happen
365 wxCHECK_MSG( tm
, wxEmptyString
, wxT("wxGmtime_r() failed") );
369 tm
= (struct tm
*)NULL
;
375 return CallStrftime(format
, tm
);
378 //else: use generic code below
379 #endif // wxHAS_STRFTIME
381 // we only parse ANSI C format specifications here, no POSIX 2
382 // complications, no GNU extensions but we do add support for a "%l" format
383 // specifier allowing to get the number of milliseconds
386 // used for calls to strftime() when we only deal with time
387 struct tm tmTimeOnly
;
388 memset(&tmTimeOnly
, 0, sizeof(tmTimeOnly
));
389 tmTimeOnly
.tm_hour
= tm
.hour
;
390 tmTimeOnly
.tm_min
= tm
.min
;
391 tmTimeOnly
.tm_sec
= tm
.sec
;
392 tmTimeOnly
.tm_mday
= 1; // any date will do, use 1976-01-01
393 tmTimeOnly
.tm_mon
= 0;
394 tmTimeOnly
.tm_year
= 76;
395 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
397 wxString tmp
, res
, fmt
;
398 for ( wxString::const_iterator p
= format
.begin(); p
!= format
.end(); ++p
)
400 if ( *p
!= wxT('%') )
408 // set the default format
409 switch ( (*++p
).GetValue() )
411 case wxT('Y'): // year has 4 digits
412 case wxT('z'): // time zone as well
416 case wxT('j'): // day of year has 3 digits
417 case wxT('l'): // milliseconds have 3 digits
421 case wxT('w'): // week day as number has only one
426 // it's either another valid format specifier in which case
427 // the format is "%02d" (for all the rest) or we have the
428 // field width preceding the format in which case it will
429 // override the default format anyhow
438 // start of the format specification
439 switch ( (*p
).GetValue() )
441 case wxT('a'): // a weekday name
443 // second parameter should be true for abbreviated names
444 res
+= GetWeekDayName(tm
.GetWeekDay(),
445 *p
== wxT('a') ? Name_Abbr
: Name_Full
);
448 case wxT('b'): // a month name
450 res
+= GetMonthName(tm
.mon
,
451 *p
== wxT('b') ? Name_Abbr
: Name_Full
);
454 case wxT('c'): // locale default date and time representation
455 case wxT('x'): // locale default date representation
456 #ifdef wxHAS_STRFTIME
458 // the problem: there is no way to know what do these format
459 // specifications correspond to for the current locale.
461 // the solution: use a hack and still use strftime(): first
462 // find the YEAR which is a year in the strftime() range (1970
463 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
464 // of the real year. Then make a copy of the format and
465 // replace all occurrences of YEAR in it with some unique
466 // string not appearing anywhere else in it, then use
467 // strftime() to format the date in year YEAR and then replace
468 // YEAR back by the real year and the unique replacement
469 // string back with YEAR. Notice that "all occurrences of YEAR"
470 // means all occurrences of 4 digit as well as 2 digit form!
472 // the bugs: we assume that neither of %c nor %x contains any
473 // fields which may change between the YEAR and real year. For
474 // example, the week number (%U, %W) and the day number (%j)
475 // will change if one of these years is leap and the other one
478 // find the YEAR: normally, for any year X, Jan 1 of the
479 // year X + 28 is the same weekday as Jan 1 of X (because
480 // the weekday advances by 1 for each normal X and by 2
481 // for each leap X, hence by 5 every 4 years or by 35
482 // which is 0 mod 7 every 28 years) but this rule breaks
483 // down if there are years between X and Y which are
484 // divisible by 4 but not leap (i.e. divisible by 100 but
485 // not 400), hence the correction.
487 int yearReal
= GetYear(tz
);
488 int mod28
= yearReal
% 28;
490 // be careful to not go too far - we risk to leave the
495 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
499 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
502 int nCentury
= year
/ 100,
503 nCenturyReal
= yearReal
/ 100;
505 // need to adjust for the years divisble by 400 which are
506 // not leap but are counted like leap ones if we just take
507 // the number of centuries in between for nLostWeekDays
508 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
509 (nCentury
/ 4 - nCenturyReal
/ 4);
511 // we have to gain back the "lost" weekdays: note that the
512 // effect of this loop is to not do anything to
513 // nLostWeekDays (which we won't use any more), but to
514 // (indirectly) set the year correctly
515 while ( (nLostWeekDays
% 7) != 0 )
517 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
520 // finally move the year below 2000 so that the 2-digit
521 // year number can never match the month or day of the
522 // month when we do the replacements below
526 wxASSERT_MSG( year
>= 1970 && year
< 2000,
527 wxT("logic error in wxDateTime::Format") );
530 // use strftime() to format the same date but in supported
533 // NB: we assume that strftime() doesn't check for the
534 // date validity and will happily format the date
535 // corresponding to Feb 29 of a non leap year (which
536 // may happen if yearReal was leap and year is not)
537 struct tm tmAdjusted
;
539 tmAdjusted
.tm_hour
= tm
.hour
;
540 tmAdjusted
.tm_min
= tm
.min
;
541 tmAdjusted
.tm_sec
= tm
.sec
;
542 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
543 tmAdjusted
.tm_yday
= GetDayOfYear();
544 tmAdjusted
.tm_mday
= tm
.mday
;
545 tmAdjusted
.tm_mon
= tm
.mon
;
546 tmAdjusted
.tm_year
= year
- 1900;
547 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
548 wxString str
= CallStrftime(*p
== wxT('c') ? wxT("%c")
552 // now replace the replacement year with the real year:
553 // notice that we have to replace the 4 digit year with
554 // a unique string not appearing in strftime() output
555 // first to prevent the 2 digit year from matching any
556 // substring of the 4 digit year (but any day, month,
557 // hours or minutes components should be safe because
558 // they are never in 70-99 range)
559 wxString
replacement("|");
560 while ( str
.find(replacement
) != wxString::npos
)
563 str
.Replace(wxString::Format("%d", year
),
565 str
.Replace(wxString::Format("%d", year
% 100),
566 wxString::Format("%d", yearReal
% 100));
567 str
.Replace(replacement
,
568 wxString::Format("%d", yearReal
));
572 #else // !wxHAS_STRFTIME
573 // Use "%m/%d/%y %H:%M:%S" format instead
574 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
575 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
576 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
579 case wxT('d'): // day of a month (01-31)
580 res
+= wxString::Format(fmt
, tm
.mday
);
583 case wxT('H'): // hour in 24h format (00-23)
584 res
+= wxString::Format(fmt
, tm
.hour
);
587 case wxT('I'): // hour in 12h format (01-12)
589 // 24h -> 12h, 0h -> 12h too
590 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
591 : tm
.hour
? tm
.hour
: 12;
592 res
+= wxString::Format(fmt
, hour12
);
596 case wxT('j'): // day of the year
597 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
600 case wxT('l'): // milliseconds (NOT STANDARD)
601 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
604 case wxT('m'): // month as a number (01-12)
605 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
608 case wxT('M'): // minute as a decimal number (00-59)
609 res
+= wxString::Format(fmt
, tm
.min
);
612 case wxT('p'): // AM or PM string
613 #ifdef wxHAS_STRFTIME
614 res
+= CallStrftime(wxT("%p"), &tmTimeOnly
);
615 #else // !wxHAS_STRFTIME
616 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
617 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
620 case wxT('S'): // second as a decimal number (00-61)
621 res
+= wxString::Format(fmt
, tm
.sec
);
624 case wxT('U'): // week number in the year (Sunday 1st week day)
625 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
628 case wxT('W'): // week number in the year (Monday 1st week day)
629 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
632 case wxT('w'): // weekday as a number (0-6), Sunday = 0
633 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
636 // case wxT('x'): -- handled with "%c"
638 case wxT('X'): // locale default time representation
639 // just use strftime() to format the time for us
640 #ifdef wxHAS_STRFTIME
641 res
+= CallStrftime(wxT("%X"), &tmTimeOnly
);
642 #else // !wxHAS_STRFTIME
643 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
644 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
647 case wxT('y'): // year without century (00-99)
648 res
+= wxString::Format(fmt
, tm
.year
% 100);
651 case wxT('Y'): // year with century
652 res
+= wxString::Format(fmt
, tm
.year
);
655 case wxT('z'): // time zone as [-+]HHMM
657 int ofs
= tz
.GetOffset();
668 // Converts seconds to HHMM representation.
669 res
+= wxString::Format(fmt
,
670 100*(ofs
/3600) + (ofs
/60)%60
);
674 case wxT('Z'): // timezone name
675 #ifdef wxHAS_STRFTIME
676 res
+= CallStrftime(wxT("%Z"), &tmTimeOnly
);
681 // is it the format width?
683 *p
== wxT('-') || *p
== wxT('+') ||
684 *p
== wxT(' ') || wxIsdigit(*p
);
692 // we've only got the flags and width so far in fmt
693 fmt
.Prepend(wxT('%'));
694 fmt
.Append(wxT('d'));
701 // no, it wasn't the width
702 wxFAIL_MSG(wxT("unknown format specifier"));
704 // fall through and just copy it nevertheless
706 case wxT('%'): // a percent sign
710 case 0: // the end of string
711 wxFAIL_MSG(wxT("missing format at the end of string"));
713 // just put the '%' which was the last char in format
723 // this function parses a string in (strict) RFC 822 format: see the section 5
724 // of the RFC for the detailed description, but briefly it's something of the
725 // form "Sat, 18 Dec 1999 00:48:30 +0100"
727 // this function is "strict" by design - it must reject anything except true
728 // RFC822 time specs.
730 wxDateTime::ParseRfc822Date(const wxString
& date
, wxString::const_iterator
*end
)
732 const wxString::const_iterator pEnd
= date
.end();
733 wxString::const_iterator p
= date
.begin();
736 const wxDateTime::WeekDay
737 wd
= GetWeekDayFromName(p
, pEnd
, Name_Abbr
, DateLang_English
);
738 if ( wd
== Inv_WeekDay
)
740 //else: ignore week day for now, we could also check that it really
741 // corresponds to the specified date
743 // 2. separating comma
744 if ( *p
++ != ',' || *p
++ != ' ' )
748 if ( !wxIsdigit(*p
) )
751 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - '0');
755 day
= (wxDateTime_t
)(day
+ (*p
++ - '0'));
762 const Month mon
= GetMonthFromName(p
, pEnd
, Name_Abbr
, DateLang_English
);
763 if ( mon
== Inv_Month
)
770 if ( !wxIsdigit(*p
) )
773 int year
= *p
++ - '0';
774 if ( !wxIsdigit(*p
) ) // should have at least 2 digits in the year
780 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
786 if ( !wxIsdigit(*p
) )
788 // no 3 digit years please
799 // 6. time in hh:mm:ss format with seconds being optional
800 if ( !wxIsdigit(*p
) )
803 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - '0');
805 if ( !wxIsdigit(*p
) )
809 hour
= (wxDateTime_t
)(hour
+ (*p
++ - '0'));
814 if ( !wxIsdigit(*p
) )
817 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - '0');
819 if ( !wxIsdigit(*p
) )
823 min
+= (wxDateTime_t
)(*p
++ - '0');
825 wxDateTime_t sec
= 0;
829 if ( !wxIsdigit(*p
) )
832 sec
= (wxDateTime_t
)(*p
++ - '0');
834 if ( !wxIsdigit(*p
) )
838 sec
+= (wxDateTime_t
)(*p
++ - '0');
844 // 7. now the interesting part: the timezone
845 int offset
= 0; // just to suppress warnings
846 if ( *p
== '-' || *p
== '+' )
848 // the explicit offset given: it has the form of hhmm
849 bool plus
= *p
++ == '+';
851 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
856 offset
= MIN_PER_HOUR
*(10*(*p
- '0') + (*(p
+ 1) - '0'));
860 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
864 offset
+= 10*(*p
- '0') + (*(p
+ 1) - '0');
873 // the symbolic timezone given: may be either military timezone or one
874 // of standard abbreviations
877 // military: Z = UTC, J unused, A = -1, ..., Y = +12
878 static const int offsets
[26] =
880 //A B C D E F G H I J K L M
881 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
882 //N O P R Q S T U V W Z Y Z
883 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
886 if ( *p
< wxT('A') || *p
> wxT('Z') || *p
== wxT('J') )
889 offset
= offsets
[*p
++ - 'A'];
894 const wxString
tz(p
, date
.end());
895 if ( tz
== wxT("UT") || tz
== wxT("UTC") || tz
== wxT("GMT") )
897 else if ( tz
== wxT("AST") )
899 else if ( tz
== wxT("ADT") )
901 else if ( tz
== wxT("EST") )
903 else if ( tz
== wxT("EDT") )
905 else if ( tz
== wxT("CST") )
907 else if ( tz
== wxT("CDT") )
909 else if ( tz
== wxT("MST") )
911 else if ( tz
== wxT("MDT") )
913 else if ( tz
== wxT("PST") )
915 else if ( tz
== wxT("PDT") )
924 offset
*= MIN_PER_HOUR
;
928 // the spec was correct, construct the date from the values we found
929 Set(day
, mon
, year
, hour
, min
, sec
);
930 MakeFromTimezone(TimeZone::Make(offset
*SEC_PER_MIN
));
938 const char* wxDateTime::ParseRfc822Date(const char* date
)
940 wxString::const_iterator end
;
941 wxString
dateStr(date
);
942 if ( !ParseRfc822Date(dateStr
, &end
) )
945 return date
+ dateStr
.IterOffsetInMBStr(end
);
948 const wchar_t* wxDateTime::ParseRfc822Date(const wchar_t* date
)
950 wxString::const_iterator end
;
951 wxString
dateStr(date
);
952 if ( !ParseRfc822Date(dateStr
, &end
) )
955 return date
+ (end
- dateStr
.begin());
959 wxDateTime::ParseFormat(const wxString
& date
,
960 const wxString
& format
,
961 const wxDateTime
& dateDef
,
962 wxString::const_iterator
*endParse
)
964 wxCHECK_MSG( !format
.empty(), false, "format can't be empty" );
965 wxCHECK_MSG( endParse
, false, "end iterator pointer must be specified" );
970 // what fields have we found?
971 bool haveWDay
= false,
981 bool hourIsIn12hFormat
= false, // or in 24h one?
982 isPM
= false; // AM by default
984 bool haveTimeZone
= false;
986 // and the value of the items we have (init them to get rid of warnings)
987 wxDateTime_t msec
= 0,
991 WeekDay wday
= Inv_WeekDay
;
992 wxDateTime_t yday
= 0,
994 wxDateTime::Month mon
= Inv_Month
;
996 long timeZone
= 0; // time zone in seconds as expected in Tm structure
998 wxString::const_iterator input
= date
.begin();
999 const wxString::const_iterator end
= date
.end();
1000 for ( wxString::const_iterator fmt
= format
.begin(); fmt
!= format
.end(); ++fmt
)
1002 if ( *fmt
!= wxT('%') )
1004 if ( wxIsspace(*fmt
) )
1006 // a white space in the format string matches 0 or more white
1007 // spaces in the input
1008 while ( input
!= end
&& wxIsspace(*input
) )
1015 // any other character (not whitespace, not '%') must be
1016 // matched by itself in the input
1017 if ( input
== end
|| *input
++ != *fmt
)
1024 // done with this format char
1028 // start of a format specification
1030 // parse the optional width
1032 while ( wxIsdigit(*++fmt
) )
1035 width
+= *fmt
- '0';
1038 // the default widths for the various fields
1041 switch ( (*fmt
).GetValue() )
1043 case wxT('Y'): // year has 4 digits
1047 case wxT('j'): // day of year has 3 digits
1048 case wxT('l'): // milliseconds have 3 digits
1052 case wxT('w'): // week day as number has only one
1057 // default for all other fields
1062 // then the format itself
1063 switch ( (*fmt
).GetValue() )
1065 case wxT('a'): // a weekday name
1068 wday
= GetWeekDayFromName
1071 *fmt
== 'a' ? Name_Abbr
: Name_Full
,
1074 if ( wday
== Inv_WeekDay
)
1083 case wxT('b'): // a month name
1086 mon
= GetMonthFromName
1089 *fmt
== 'b' ? Name_Abbr
: Name_Full
,
1092 if ( mon
== Inv_Month
)
1101 case wxT('c'): // locale default date and time representation
1107 fmtDateTime
= wxLocale::GetInfo(wxLOCALE_DATE_TIME_FMT
);
1108 if ( !fmtDateTime
.empty() )
1109 dt
= ParseFormatAt(input
, end
, fmtDateTime
);
1110 #endif // wxUSE_INTL
1111 if ( !dt
.IsValid() )
1113 // also try the format which corresponds to ctime()
1114 // output (i.e. the "C" locale default)
1115 dt
= ParseFormatAt(input
, end
, wxS("%a %b %d %H:%M:%S %Y"));
1118 if ( !dt
.IsValid() )
1120 // and finally also the two generic date/time formats
1121 dt
= ParseFormatAt(input
, end
, wxS("%x %X"), wxS("%X %x"));
1124 if ( !dt
.IsValid() )
1127 const Tm tm
= dt
.GetTm();
1137 haveDay
= haveMon
= haveYear
=
1138 haveHour
= haveMin
= haveSec
= true;
1142 case wxT('d'): // day of a month (01-31)
1143 case 'e': // day of a month (1-31) (GNU extension)
1144 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1145 (num
> 31) || (num
< 1) )
1151 // we can't check whether the day range is correct yet, will
1152 // do it later - assume ok for now
1154 mday
= (wxDateTime_t
)num
;
1157 case wxT('H'): // hour in 24h format (00-23)
1158 if ( !GetNumericToken(width
, input
, end
, &num
) || (num
> 23) )
1165 hour
= (wxDateTime_t
)num
;
1168 case wxT('I'): // hour in 12h format (01-12)
1169 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1170 !num
|| (num
> 12) )
1177 hourIsIn12hFormat
= true;
1178 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
1181 case wxT('j'): // day of the year
1182 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1183 !num
|| (num
> 366) )
1190 yday
= (wxDateTime_t
)num
;
1193 case wxT('l'): // milliseconds (0-999)
1194 if ( !GetNumericToken(width
, input
, end
, &num
) )
1198 msec
= (wxDateTime_t
)num
;
1201 case wxT('m'): // month as a number (01-12)
1202 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1203 !num
|| (num
> 12) )
1210 mon
= (Month
)(num
- 1);
1213 case wxT('M'): // minute as a decimal number (00-59)
1214 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1222 min
= (wxDateTime_t
)num
;
1225 case wxT('p'): // AM or PM string
1228 GetAmPmStrings(&am
, &pm
);
1230 // we can never match %p in locales which don't use AM/PM
1231 if ( am
.empty() || pm
.empty() )
1234 const size_t pos
= input
- date
.begin();
1235 if ( date
.compare(pos
, pm
.length(), pm
) == 0 )
1238 input
+= pm
.length();
1240 else if ( date
.compare(pos
, am
.length(), am
) == 0 )
1242 input
+= am
.length();
1251 case wxT('r'): // time as %I:%M:%S %p
1254 if ( !dt
.ParseFormat(wxString(input
, end
),
1255 wxS("%I:%M:%S %p"), &input
) )
1258 haveHour
= haveMin
= haveSec
= true;
1260 const Tm tm
= dt
.GetTm();
1267 case wxT('R'): // time as %H:%M
1270 dt
= ParseFormatAt(input
, end
, wxS("%H:%M"));
1271 if ( !dt
.IsValid() )
1277 const Tm tm
= dt
.GetTm();
1283 case wxT('S'): // second as a decimal number (00-61)
1284 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1292 sec
= (wxDateTime_t
)num
;
1295 case wxT('T'): // time as %H:%M:%S
1298 dt
= ParseFormatAt(input
, end
, wxS("%H:%M:%S"));
1299 if ( !dt
.IsValid() )
1306 const Tm tm
= dt
.GetTm();
1313 case wxT('w'): // weekday as a number (0-6), Sunday = 0
1314 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1322 wday
= (WeekDay
)num
;
1325 case wxT('x'): // locale default date representation
1329 fmtDate
= wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT
),
1330 fmtDateAlt
= wxLocale::GetInfo(wxLOCALE_LONG_DATE_FMT
);
1331 #else // !wxUSE_INTL
1332 wxString fmtDate
, fmtDateAlt
;
1333 #endif // wxUSE_INTL/!wxUSE_INTL
1334 if ( fmtDate
.empty() )
1336 if ( IsWestEuropeanCountry(GetCountry()) ||
1337 GetCountry() == Russia
)
1339 fmtDate
= wxS("%d/%m/%Y");
1340 fmtDateAlt
= wxS("%m/%d/%Y");
1344 fmtDate
= wxS("%m/%d/%Y");
1345 fmtDateAlt
= wxS("%d/%m/%Y");
1350 dt
= ParseFormatAt(input
, end
, fmtDate
, fmtDateAlt
);
1352 if ( !dt
.IsValid() )
1354 // try with short years too
1355 fmtDate
.Replace("%Y","%y");
1356 fmtDateAlt
.Replace("%Y","%y");
1357 dt
= ParseFormatAt(input
, end
, fmtDate
, fmtDateAlt
);
1359 if ( !dt
.IsValid() )
1363 const Tm tm
= dt
.GetTm();
1376 case wxT('X'): // locale default time representation
1379 wxString fmtTime
= wxLocale::GetInfo(wxLOCALE_TIME_FMT
),
1381 #else // !wxUSE_INTL
1382 wxString fmtTime
, fmtTimeAlt
;
1383 #endif // wxUSE_INTL/!wxUSE_INTL
1384 if ( fmtTime
.empty() )
1386 // try to parse what follows as "%H:%M:%S" and, if this
1387 // fails, as "%I:%M:%S %p" - this should catch the most
1394 dt
= ParseFormatAt(input
, end
, fmtTime
, fmtTimeAlt
);
1395 if ( !dt
.IsValid() )
1402 const Tm tm
= dt
.GetTm();
1409 case wxT('y'): // year without century (00-99)
1410 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1419 // TODO should have an option for roll over date instead of
1420 // hard coding it here
1421 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
1424 case wxT('Y'): // year with century
1425 if ( !GetNumericToken(width
, input
, end
, &num
) )
1432 year
= (wxDateTime_t
)num
;
1437 // check that we have something here at all
1441 // and then check that it's either plus or minus sign
1443 if ( *input
== wxT('-') )
1445 else if ( *input
== wxT('+') )
1448 return false; // no match
1450 // here should follow 4 digits HHMM
1452 unsigned long tzHourMin
;
1453 if ( !GetNumericToken(4, input
, end
, &tzHourMin
) )
1454 return false; // no match
1456 const unsigned hours
= tzHourMin
/ 100;
1457 const unsigned minutes
= tzHourMin
% 100;
1459 if ( hours
> 12 || minutes
> 59 )
1460 return false; // bad format
1462 timeZone
= 3600*hours
+ 60*minutes
;
1464 timeZone
= -timeZone
;
1466 haveTimeZone
= true;
1470 case wxT('Z'): // timezone name
1471 // FIXME: currently we just ignore everything that looks like a
1473 GetAlphaToken(input
, end
);
1476 case wxT('%'): // a percent sign
1477 if ( input
== end
|| *input
++ != wxT('%') )
1484 case 0: // the end of string
1485 wxFAIL_MSG(wxT("unexpected format end"));
1489 default: // not a known format spec
1494 // format matched, try to construct a date from what we have now
1496 if ( dateDef
.IsValid() )
1498 // take this date as default
1499 tmDef
= dateDef
.GetTm();
1501 else if ( IsValid() )
1503 // if this date is valid, don't change it
1508 // no default and this date is invalid - fall back to Today()
1509 tmDef
= Today().GetTm();
1525 // TODO we don't check here that the values are consistent, if both year
1526 // day and month/day were found, we just ignore the year day and we
1527 // also always ignore the week day
1530 if ( mday
> GetNumberOfDays(tm
.mon
, tm
.year
) )
1535 else if ( haveYDay
)
1537 if ( yday
> GetNumberOfDays(tm
.year
) )
1540 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
1547 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
1549 // translate to 24hour format
1552 //else: either already in 24h format or no translation needed
1575 // If a time zone was specified and it is not the local time zone, we need
1576 // to shift the time accordingly.
1578 // Note that avoiding the call to MakeFromTimeZone is necessary to avoid
1580 if ( haveTimeZone
&& timeZone
!= -wxGetTimeZone() )
1581 MakeFromTimezone(timeZone
);
1583 // finally check that the week day is consistent -- if we had it
1584 if ( haveWDay
&& GetWeekDay() != wday
)
1593 wxDateTime::ParseFormat(const char* date
,
1594 const wxString
& format
,
1595 const wxDateTime
& dateDef
)
1597 wxString::const_iterator end
;
1598 wxString
dateStr(date
);
1599 if ( !ParseFormat(dateStr
, format
, dateDef
, &end
) )
1602 return date
+ dateStr
.IterOffsetInMBStr(end
);
1606 wxDateTime::ParseFormat(const wchar_t* date
,
1607 const wxString
& format
,
1608 const wxDateTime
& dateDef
)
1610 wxString::const_iterator end
;
1611 wxString
dateStr(date
);
1612 if ( !ParseFormat(dateStr
, format
, dateDef
, &end
) )
1615 return date
+ (end
- dateStr
.begin());
1619 wxDateTime::ParseDateTime(const wxString
& date
, wxString::const_iterator
*end
)
1621 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1627 wxString::const_iterator
1632 // If we got a date in the beginning, see if there is a time specified
1634 if ( dtDate
.ParseDate(date
, &endDate
) )
1636 // Skip spaces, as the ParseTime() function fails on spaces
1637 while ( endDate
!= date
.end() && wxIsspace(*endDate
) )
1640 const wxString
timestr(endDate
, date
.end());
1641 if ( !dtTime
.ParseTime(timestr
, &endTime
) )
1644 endBoth
= endDate
+ (endTime
- timestr
.begin());
1646 else // no date in the beginning
1648 // check if we have a time followed by a date
1649 if ( !dtTime
.ParseTime(date
, &endTime
) )
1652 while ( endTime
!= date
.end() && wxIsspace(*endTime
) )
1655 const wxString
datestr(endTime
, date
.end());
1656 if ( !dtDate
.ParseDate(datestr
, &endDate
) )
1659 endBoth
= endTime
+ (endDate
- datestr
.begin());
1662 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
1663 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
1664 dtTime
.GetMillisecond());
1671 const char* wxDateTime::ParseDateTime(const char* date
)
1673 wxString::const_iterator end
;
1674 wxString
dateStr(date
);
1675 if ( !ParseDateTime(dateStr
, &end
) )
1678 return date
+ dateStr
.IterOffsetInMBStr(end
);
1681 const wchar_t* wxDateTime::ParseDateTime(const wchar_t* date
)
1683 wxString::const_iterator end
;
1684 wxString
dateStr(date
);
1685 if ( !ParseDateTime(dateStr
, &end
) )
1688 return date
+ (end
- dateStr
.begin());
1692 wxDateTime::ParseDate(const wxString
& date
, wxString::const_iterator
*end
)
1694 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1696 // this is a simplified version of ParseDateTime() which understands only
1697 // "today" (for wxDate compatibility) and digits only otherwise (and not
1698 // all esoteric constructions ParseDateTime() knows about)
1700 const wxString::const_iterator pBegin
= date
.begin();
1701 const wxString::const_iterator pEnd
= date
.end();
1703 wxString::const_iterator p
= pBegin
;
1704 while ( p
!= pEnd
&& wxIsspace(*p
) )
1707 // some special cases
1711 int dayDiffFromToday
;
1714 { wxTRANSLATE("today"), 0 },
1715 { wxTRANSLATE("yesterday"), -1 },
1716 { wxTRANSLATE("tomorrow"), 1 },
1719 const size_t lenRest
= pEnd
- p
;
1720 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
1722 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
1723 size_t len
= dateStr
.length();
1725 if ( len
> lenRest
)
1728 const wxString::const_iterator pEnd
= p
+ len
;
1729 if ( wxString(p
, pEnd
).CmpNoCase(dateStr
) == 0 )
1731 // nothing can follow this, so stop here
1735 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
1737 if ( dayDiffFromToday
)
1739 *this += wxDateSpan::Days(dayDiffFromToday
);
1748 // We try to guess what we have here: for each new (numeric) token, we
1749 // determine if it can be a month, day or a year. Of course, there is an
1750 // ambiguity as some numbers may be days as well as months, so we also
1751 // have the ability to back track.
1754 bool haveDay
= false, // the months day?
1755 haveWDay
= false, // the day of week?
1756 haveMon
= false, // the month?
1757 haveYear
= false; // the year?
1759 bool monWasNumeric
= false; // was month specified as a number?
1761 // and the value of the items we have (init them to get rid of warnings)
1762 WeekDay wday
= Inv_WeekDay
;
1763 wxDateTime_t day
= 0;
1764 wxDateTime::Month mon
= Inv_Month
;
1767 // tokenize the string
1770 // skip white space and date delimiters
1771 if ( wxStrchr(".,/-\t\r\n ", *p
) )
1777 // modify copy of the iterator as we're not sure if the next token is
1778 // still part of the date at all
1779 wxString::const_iterator pCopy
= p
;
1781 // we can have either alphabetic or numeric token, start by testing if
1784 if ( GetNumericToken(10 /* max length */, pCopy
, pEnd
, &val
) )
1786 // guess what this number is
1792 if ( !haveMon
&& val
> 0 && val
<= 12 )
1794 // assume it is month
1797 else // not the month
1801 // this can only be the year
1804 else // may be either day or year
1806 // use a leap year if we don't have the year yet to allow
1807 // dates like 2/29/1976 which would be rejected otherwise
1808 wxDateTime_t max_days
= (wxDateTime_t
)(
1810 ? GetNumberOfDays(mon
, haveYear
? year
: 1976)
1815 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
1820 else // yes, suppose it's the day
1834 year
= (wxDateTime_t
)val
;
1843 day
= (wxDateTime_t
)val
;
1848 monWasNumeric
= true;
1850 mon
= (Month
)(val
- 1);
1853 else // not a number
1855 // be careful not to overwrite the current mon value
1856 Month mon2
= GetMonthFromName
1859 Name_Full
| Name_Abbr
,
1860 DateLang_Local
| DateLang_English
1862 if ( mon2
!= Inv_Month
)
1867 // but we already have a month - maybe we guessed wrong
1868 // when we had interpreted that numeric value as a month
1869 // and it was the day number instead?
1870 if ( haveDay
|| !monWasNumeric
)
1873 // assume we did and change our mind: reinterpret the month
1874 // value as a day (notice that there is no need to check
1875 // that it is valid as month values are always < 12, but
1876 // the days are counted from 1 unlike the months)
1877 day
= (wxDateTime_t
)(mon
+ 1);
1885 else // not a valid month name
1887 WeekDay wday2
= GetWeekDayFromName
1890 Name_Full
| Name_Abbr
,
1891 DateLang_Local
| DateLang_English
1893 if ( wday2
!= Inv_WeekDay
)
1903 else // not a valid weekday name
1906 static const char *const ordinals
[] =
1908 wxTRANSLATE("first"),
1909 wxTRANSLATE("second"),
1910 wxTRANSLATE("third"),
1911 wxTRANSLATE("fourth"),
1912 wxTRANSLATE("fifth"),
1913 wxTRANSLATE("sixth"),
1914 wxTRANSLATE("seventh"),
1915 wxTRANSLATE("eighth"),
1916 wxTRANSLATE("ninth"),
1917 wxTRANSLATE("tenth"),
1918 wxTRANSLATE("eleventh"),
1919 wxTRANSLATE("twelfth"),
1920 wxTRANSLATE("thirteenth"),
1921 wxTRANSLATE("fourteenth"),
1922 wxTRANSLATE("fifteenth"),
1923 wxTRANSLATE("sixteenth"),
1924 wxTRANSLATE("seventeenth"),
1925 wxTRANSLATE("eighteenth"),
1926 wxTRANSLATE("nineteenth"),
1927 wxTRANSLATE("twentieth"),
1928 // that's enough - otherwise we'd have problems with
1929 // composite (or not) ordinals
1933 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
1935 const wxString ord
= wxGetTranslation(ordinals
[n
]);
1936 const size_t len
= ord
.length();
1937 if ( date
.compare(p
- pBegin
, len
, ord
) == 0 )
1944 if ( n
== WXSIZEOF(ordinals
) )
1946 // stop here - something unknown
1953 // don't try anything here (as in case of numeric day
1954 // above) - the symbolic day spec should always
1955 // precede the month/year
1961 day
= (wxDateTime_t
)(n
+ 1);
1966 // advance iterator past a successfully parsed token
1970 // either no more tokens or the scan was stopped by something we couldn't
1971 // parse - in any case, see if we can construct a date from what we have
1972 if ( !haveDay
&& !haveWDay
)
1975 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
1976 !(haveDay
&& haveMon
&& haveYear
) )
1978 // without adjectives (which we don't support here) the week day only
1979 // makes sense completely separately or with the full date
1980 // specification (what would "Wed 1999" mean?)
1984 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
1986 // may be we have month and day instead of day and year?
1987 if ( haveDay
&& !haveMon
)
1991 // exchange day and month
1992 mon
= (wxDateTime::Month
)(day
- 1);
1994 // we're in the current year then
1995 if ( (year
> 0) && (year
<= (int)GetNumberOfDays(mon
, Inv_Year
)) )
1997 day
= (wxDateTime_t
)year
;
2002 //else: no, can't exchange, leave haveMon == false
2012 mon
= GetCurrentMonth();
2017 year
= GetCurrentYear();
2022 // normally we check the day above but the check is optimistic in case
2023 // we find the day before its month/year so we have to redo it now
2024 if ( day
> GetNumberOfDays(mon
, year
) )
2027 Set(day
, mon
, year
);
2031 // check that it is really the same
2032 if ( GetWeekDay() != wday
)
2040 SetToWeekDayInSameWeek(wday
);
2048 const char* wxDateTime::ParseDate(const char* date
)
2050 wxString::const_iterator end
;
2051 wxString
dateStr(date
);
2052 if ( !ParseDate(dateStr
, &end
) )
2055 return date
+ dateStr
.IterOffsetInMBStr(end
);
2058 const wchar_t* wxDateTime::ParseDate(const wchar_t* date
)
2060 wxString::const_iterator end
;
2061 wxString
dateStr(date
);
2062 if ( !ParseDate(dateStr
, &end
) )
2065 return date
+ (end
- dateStr
.begin());
2069 wxDateTime::ParseTime(const wxString
& time
, wxString::const_iterator
*end
)
2071 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
2073 // first try some extra things
2080 { wxTRANSLATE("noon"), 12 },
2081 { wxTRANSLATE("midnight"), 00 },
2085 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
2087 const wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
2088 if ( timeString
.CmpNoCase(wxString(time
, timeString
.length())) == 0 )
2090 // casts required by DigitalMars
2091 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
2094 *end
= time
.begin() + timeString
.length();
2100 // try all time formats we may think about in the order from longest to
2102 static const char *const timeFormats
[] =
2104 "%I:%M:%S %p", // 12hour with AM/PM
2105 "%H:%M:%S", // could be the same or 24 hour one so try it too
2106 "%I:%M %p", // 12hour with AM/PM but without seconds
2107 "%H:%M", // and a possibly 24 hour version without seconds
2108 "%X", // possibly something from above or maybe something
2109 // completely different -- try it last
2111 // TODO: parse timezones
2114 for ( size_t nFmt
= 0; nFmt
< WXSIZEOF(timeFormats
); nFmt
++ )
2116 if ( ParseFormat(time
, timeFormats
[nFmt
], end
) )
2123 const char* wxDateTime::ParseTime(const char* date
)
2125 wxString::const_iterator end
;
2126 wxString
dateStr(date
);
2127 if ( !ParseTime(dateStr
, &end
) )
2130 return date
+ dateStr
.IterOffsetInMBStr(end
);
2133 const wchar_t* wxDateTime::ParseTime(const wchar_t* date
)
2135 wxString::const_iterator end
;
2136 wxString
dateStr(date
);
2137 if ( !ParseTime(dateStr
, &end
) )
2140 return date
+ (end
- dateStr
.begin());
2143 // ----------------------------------------------------------------------------
2144 // Workdays and holidays support
2145 // ----------------------------------------------------------------------------
2147 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
2149 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
2152 // ============================================================================
2154 // ============================================================================
2156 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
2159 return ds1
.Multiply(n
);
2162 // ============================================================================
2164 // ============================================================================
2166 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
2168 return wxTimeSpan(ts
).Multiply(n
);
2171 // this enum is only used in wxTimeSpan::Format() below but we can't declare
2172 // it locally to the method as it provokes an internal compiler error in egcs
2173 // 2.91.60 when building with -O2
2184 // not all strftime(3) format specifiers make sense here because, for example,
2185 // a time span doesn't have a year nor a timezone
2187 // Here are the ones which are supported (all of them are supported by strftime
2189 // %H hour in 24 hour format
2190 // %M minute (00 - 59)
2191 // %S second (00 - 59)
2194 // Also, for MFC CTimeSpan compatibility, we support
2195 // %D number of days
2197 // And, to be better than MFC :-), we also have
2198 // %E number of wEeks
2199 // %l milliseconds (000 - 999)
2200 wxString
wxTimeSpan::Format(const wxString
& format
) const
2202 // we deal with only positive time spans here and just add the sign in
2203 // front for the negative ones
2206 wxString
str(Negate().Format(format
));
2210 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
2211 wxT("NULL format in wxTimeSpan::Format") );
2214 str
.Alloc(format
.length());
2216 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
2218 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
2219 // question is what should ts.Format("%S") do? The code here returns "3273"
2220 // in this case (i.e. the total number of seconds, not just seconds % 60)
2221 // because, for me, this call means "give me entire time interval in
2222 // seconds" and not "give me the seconds part of the time interval"
2224 // If we agree that it should behave like this, it is clear that the
2225 // interpretation of each format specifier depends on the presence of the
2226 // other format specs in the string: if there was "%H" before "%M", we
2227 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
2229 // we remember the most important unit found so far
2230 TimeSpanPart partBiggest
= Part_MSec
;
2232 for ( wxString::const_iterator pch
= format
.begin(); pch
!= format
.end(); ++pch
)
2236 if ( ch
== wxT('%') )
2238 // the start of the format specification of the printf() below
2239 wxString
fmtPrefix(wxT('%'));
2244 // the number of digits for the format string, 0 if unused
2245 unsigned digits
= 0;
2247 ch
= *++pch
; // get the format spec char
2251 wxFAIL_MSG( wxT("invalid format character") );
2257 // skip the part below switch
2262 if ( partBiggest
< Part_Day
)
2268 partBiggest
= Part_Day
;
2273 partBiggest
= Part_Week
;
2279 if ( partBiggest
< Part_Hour
)
2285 partBiggest
= Part_Hour
;
2292 n
= GetMilliseconds().ToLong();
2293 if ( partBiggest
< Part_MSec
)
2297 //else: no need to reset partBiggest to Part_MSec, it is
2298 // the least significant one anyhow
2305 if ( partBiggest
< Part_Min
)
2311 partBiggest
= Part_Min
;
2318 n
= GetSeconds().ToLong();
2319 if ( partBiggest
< Part_Sec
)
2325 partBiggest
= Part_Sec
;
2334 fmtPrefix
<< wxT("0") << digits
;
2337 str
+= wxString::Format(fmtPrefix
+ wxT("ld"), n
);
2341 // normal character, just copy
2349 #endif // wxUSE_DATETIME