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 const wxString::const_iterator
& end
,
241 unsigned long *number
)
245 while ( p
!= end
&& wxIsdigit(*p
) )
249 if ( len
&& ++n
> len
)
253 return !s
.empty() && s
.ToULong(number
);
256 // scans all alphabetic characters and returns the resulting string
258 GetAlphaToken(wxString::const_iterator
& p
,
259 const wxString::const_iterator
& end
)
262 while ( p
!= end
&& wxIsalpha(*p
) )
270 // parses string starting at given iterator using the specified format and,
271 // optionally, a fall back format (and optionally another one... but it stops
274 // if unsuccessful, returns invalid wxDateTime without changing p; otherwise
275 // advance p to the end of the match and returns wxDateTime containing the
276 // results of the parsing
278 ParseFormatAt(wxString::const_iterator
& p
,
279 const wxString::const_iterator
& end
,
281 // FIXME-VC6: using wxString() instead of wxEmptyString in the
282 // line below results in error C2062: type 'class
283 // wxString (__cdecl *)(void)' unexpected
284 const wxString
& fmtAlt
= wxEmptyString
,
285 const wxString
& fmtAlt2
= wxString())
287 const wxString
str(p
, end
);
288 wxString::const_iterator endParse
;
290 if ( dt
.ParseFormat(str
, fmt
, &endParse
) ||
291 (!fmtAlt
.empty() && dt
.ParseFormat(str
, fmtAlt
, &endParse
)) ||
292 (!fmtAlt2
.empty() && dt
.ParseFormat(str
, fmtAlt2
, &endParse
)) )
294 p
+= endParse
- str
.begin();
296 //else: all formats failed
301 } // anonymous namespace
303 // ----------------------------------------------------------------------------
304 // wxDateTime to/from text representations
305 // ----------------------------------------------------------------------------
307 wxString
wxDateTime::Format(const wxString
& format
, const TimeZone
& tz
) const
309 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
310 _T("NULL format in wxDateTime::Format") );
312 // we have to use our own implementation if the date is out of range of
313 // strftime() or if we use non standard specificators
315 time_t time
= GetTicks();
317 if ( (time
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) )
322 if ( tz
.GetOffset() == -GetTimeZone() )
324 // we are working with local time
325 tm
= wxLocaltime_r(&time
, &tmstruct
);
327 // should never happen
328 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxLocaltime_r() failed") );
332 time
+= (int)tz
.GetOffset();
334 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
335 int time2
= (int) time
;
341 tm
= wxGmtime_r(&time
, &tmstruct
);
343 // should never happen
344 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxGmtime_r() failed") );
348 tm
= (struct tm
*)NULL
;
354 return CallStrftime(format
, tm
);
357 //else: use generic code below
358 #endif // HAVE_STRFTIME
360 // we only parse ANSI C format specifications here, no POSIX 2
361 // complications, no GNU extensions but we do add support for a "%l" format
362 // specifier allowing to get the number of milliseconds
365 // used for calls to strftime() when we only deal with time
366 struct tm tmTimeOnly
;
367 tmTimeOnly
.tm_hour
= tm
.hour
;
368 tmTimeOnly
.tm_min
= tm
.min
;
369 tmTimeOnly
.tm_sec
= tm
.sec
;
370 tmTimeOnly
.tm_wday
= 0;
371 tmTimeOnly
.tm_yday
= 0;
372 tmTimeOnly
.tm_mday
= 1; // any date will do
373 tmTimeOnly
.tm_mon
= 0;
374 tmTimeOnly
.tm_year
= 76;
375 tmTimeOnly
.tm_isdst
= 0; // no DST, we adjust for tz ourselves
377 wxString tmp
, res
, fmt
;
378 for ( wxString::const_iterator p
= format
.begin(); p
!= format
.end(); ++p
)
388 // set the default format
389 switch ( (*++p
).GetValue() )
391 case _T('Y'): // year has 4 digits
395 case _T('j'): // day of year has 3 digits
396 case _T('l'): // milliseconds have 3 digits
400 case _T('w'): // week day as number has only one
405 // it's either another valid format specifier in which case
406 // the format is "%02d" (for all the rest) or we have the
407 // field width preceding the format in which case it will
408 // override the default format anyhow
417 // start of the format specification
418 switch ( (*p
).GetValue() )
420 case _T('a'): // a weekday name
422 // second parameter should be true for abbreviated names
423 res
+= GetWeekDayName(tm
.GetWeekDay(),
424 *p
== _T('a') ? Name_Abbr
: Name_Full
);
427 case _T('b'): // a month name
429 res
+= GetMonthName(tm
.mon
,
430 *p
== _T('b') ? Name_Abbr
: Name_Full
);
433 case _T('c'): // locale default date and time representation
434 case _T('x'): // locale default date representation
437 // the problem: there is no way to know what do these format
438 // specifications correspond to for the current locale.
440 // the solution: use a hack and still use strftime(): first
441 // find the YEAR which is a year in the strftime() range (1970
442 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
443 // of the real year. Then make a copy of the format and
444 // replace all occurrences of YEAR in it with some unique
445 // string not appearing anywhere else in it, then use
446 // strftime() to format the date in year YEAR and then replace
447 // YEAR back by the real year and the unique replacement
448 // string back with YEAR. Notice that "all occurrences of YEAR"
449 // means all occurrences of 4 digit as well as 2 digit form!
451 // the bugs: we assume that neither of %c nor %x contains any
452 // fields which may change between the YEAR and real year. For
453 // example, the week number (%U, %W) and the day number (%j)
454 // will change if one of these years is leap and the other one
457 // find the YEAR: normally, for any year X, Jan 1 of the
458 // year X + 28 is the same weekday as Jan 1 of X (because
459 // the weekday advances by 1 for each normal X and by 2
460 // for each leap X, hence by 5 every 4 years or by 35
461 // which is 0 mod 7 every 28 years) but this rule breaks
462 // down if there are years between X and Y which are
463 // divisible by 4 but not leap (i.e. divisible by 100 but
464 // not 400), hence the correction.
466 int yearReal
= GetYear(tz
);
467 int mod28
= yearReal
% 28;
469 // be careful to not go too far - we risk to leave the
474 year
= 1988 + mod28
; // 1988 == 0 (mod 28)
478 year
= 1970 + mod28
- 10; // 1970 == 10 (mod 28)
481 int nCentury
= year
/ 100,
482 nCenturyReal
= yearReal
/ 100;
484 // need to adjust for the years divisble by 400 which are
485 // not leap but are counted like leap ones if we just take
486 // the number of centuries in between for nLostWeekDays
487 int nLostWeekDays
= (nCentury
- nCenturyReal
) -
488 (nCentury
/ 4 - nCenturyReal
/ 4);
490 // we have to gain back the "lost" weekdays: note that the
491 // effect of this loop is to not do anything to
492 // nLostWeekDays (which we won't use any more), but to
493 // (indirectly) set the year correctly
494 while ( (nLostWeekDays
% 7) != 0 )
496 nLostWeekDays
+= year
++ % 4 ? 1 : 2;
499 // finally move the year below 2000 so that the 2-digit
500 // year number can never match the month or day of the
501 // month when we do the replacements below
505 wxASSERT_MSG( year
>= 1970 && year
< 2000,
506 _T("logic error in wxDateTime::Format") );
509 // use strftime() to format the same date but in supported
512 // NB: we assume that strftime() doesn't check for the
513 // date validity and will happily format the date
514 // corresponding to Feb 29 of a non leap year (which
515 // may happen if yearReal was leap and year is not)
516 struct tm tmAdjusted
;
518 tmAdjusted
.tm_hour
= tm
.hour
;
519 tmAdjusted
.tm_min
= tm
.min
;
520 tmAdjusted
.tm_sec
= tm
.sec
;
521 tmAdjusted
.tm_wday
= tm
.GetWeekDay();
522 tmAdjusted
.tm_yday
= GetDayOfYear();
523 tmAdjusted
.tm_mday
= tm
.mday
;
524 tmAdjusted
.tm_mon
= tm
.mon
;
525 tmAdjusted
.tm_year
= year
- 1900;
526 tmAdjusted
.tm_isdst
= 0; // no DST, already adjusted
527 wxString str
= CallStrftime(*p
== _T('c') ? _T("%c")
531 // now replace the replacement year with the real year:
532 // notice that we have to replace the 4 digit year with
533 // a unique string not appearing in strftime() output
534 // first to prevent the 2 digit year from matching any
535 // substring of the 4 digit year (but any day, month,
536 // hours or minutes components should be safe because
537 // they are never in 70-99 range)
538 wxString
replacement("|");
539 while ( str
.find(replacement
) != wxString::npos
)
542 str
.Replace(wxString::Format("%d", year
),
544 str
.Replace(wxString::Format("%d", year
% 100),
545 wxString::Format("%d", yearReal
% 100));
546 str
.Replace(replacement
,
547 wxString::Format("%d", yearReal
));
551 #else // !HAVE_STRFTIME
552 // Use "%m/%d/%y %H:%M:%S" format instead
553 res
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
554 tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
);
555 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
558 case _T('d'): // day of a month (01-31)
559 res
+= wxString::Format(fmt
, tm
.mday
);
562 case _T('H'): // hour in 24h format (00-23)
563 res
+= wxString::Format(fmt
, tm
.hour
);
566 case _T('I'): // hour in 12h format (01-12)
568 // 24h -> 12h, 0h -> 12h too
569 int hour12
= tm
.hour
> 12 ? tm
.hour
- 12
570 : tm
.hour
? tm
.hour
: 12;
571 res
+= wxString::Format(fmt
, hour12
);
575 case _T('j'): // day of the year
576 res
+= wxString::Format(fmt
, GetDayOfYear(tz
));
579 case _T('l'): // milliseconds (NOT STANDARD)
580 res
+= wxString::Format(fmt
, GetMillisecond(tz
));
583 case _T('m'): // month as a number (01-12)
584 res
+= wxString::Format(fmt
, tm
.mon
+ 1);
587 case _T('M'): // minute as a decimal number (00-59)
588 res
+= wxString::Format(fmt
, tm
.min
);
591 case _T('p'): // AM or PM string
593 res
+= CallStrftime(_T("%p"), &tmTimeOnly
);
594 #else // !HAVE_STRFTIME
595 res
+= (tmTimeOnly
.tm_hour
> 12) ? wxT("pm") : wxT("am");
596 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
599 case _T('S'): // second as a decimal number (00-61)
600 res
+= wxString::Format(fmt
, tm
.sec
);
603 case _T('U'): // week number in the year (Sunday 1st week day)
604 res
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
));
607 case _T('W'): // week number in the year (Monday 1st week day)
608 res
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
));
611 case _T('w'): // weekday as a number (0-6), Sunday = 0
612 res
+= wxString::Format(fmt
, tm
.GetWeekDay());
615 // case _T('x'): -- handled with "%c"
617 case _T('X'): // locale default time representation
618 // just use strftime() to format the time for us
620 res
+= CallStrftime(_T("%X"), &tmTimeOnly
);
621 #else // !HAVE_STRFTIME
622 res
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
);
623 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
626 case _T('y'): // year without century (00-99)
627 res
+= wxString::Format(fmt
, tm
.year
% 100);
630 case _T('Y'): // year with century
631 res
+= wxString::Format(fmt
, tm
.year
);
634 case _T('Z'): // timezone name
636 res
+= CallStrftime(_T("%Z"), &tmTimeOnly
);
641 // is it the format width?
643 while ( *p
== _T('-') || *p
== _T('+') ||
644 *p
== _T(' ') || wxIsdigit(*p
) )
651 // we've only got the flags and width so far in fmt
652 fmt
.Prepend(_T('%'));
660 // no, it wasn't the width
661 wxFAIL_MSG(_T("unknown format specificator"));
663 // fall through and just copy it nevertheless
665 case _T('%'): // a percent sign
669 case 0: // the end of string
670 wxFAIL_MSG(_T("missing format at the end of string"));
672 // just put the '%' which was the last char in format
682 // this function parses a string in (strict) RFC 822 format: see the section 5
683 // of the RFC for the detailed description, but briefly it's something of the
684 // form "Sat, 18 Dec 1999 00:48:30 +0100"
686 // this function is "strict" by design - it must reject anything except true
687 // RFC822 time specs.
689 wxDateTime::ParseRfc822Date(const wxString
& date
, wxString::const_iterator
*end
)
691 wxString::const_iterator p
= date
.begin();
694 static const int WDAY_LEN
= 3;
695 const wxString::const_iterator endWday
= p
+ WDAY_LEN
;
696 const wxString
wday(p
, endWday
);
697 if ( GetWeekDayFromName(wday
, Name_Abbr
, DateLang_English
) == Inv_WeekDay
)
699 //else: ignore week day for now, we could also check that it really
700 // corresponds to the specified date
704 // 2. separating comma
705 if ( *p
++ != ',' || *p
++ != ' ' )
709 if ( !wxIsdigit(*p
) )
712 wxDateTime_t day
= (wxDateTime_t
)(*p
++ - '0');
716 day
= (wxDateTime_t
)(day
+ (*p
++ - '0'));
723 static const int MONTH_LEN
= 3;
724 const wxString::const_iterator endMonth
= p
+ MONTH_LEN
;
725 const wxString
monName(p
, endMonth
);
726 Month mon
= GetMonthFromName(monName
, Name_Abbr
, DateLang_English
);
727 if ( mon
== Inv_Month
)
736 if ( !wxIsdigit(*p
) )
739 int year
= *p
++ - '0';
740 if ( !wxIsdigit(*p
) ) // should have at least 2 digits in the year
746 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
752 if ( !wxIsdigit(*p
) )
754 // no 3 digit years please
765 // 6. time in hh:mm:ss format with seconds being optional
766 if ( !wxIsdigit(*p
) )
769 wxDateTime_t hour
= (wxDateTime_t
)(*p
++ - '0');
771 if ( !wxIsdigit(*p
) )
775 hour
= (wxDateTime_t
)(hour
+ (*p
++ - '0'));
780 if ( !wxIsdigit(*p
) )
783 wxDateTime_t min
= (wxDateTime_t
)(*p
++ - '0');
785 if ( !wxIsdigit(*p
) )
789 min
+= (wxDateTime_t
)(*p
++ - '0');
791 wxDateTime_t sec
= 0;
795 if ( !wxIsdigit(*p
) )
798 sec
= (wxDateTime_t
)(*p
++ - '0');
800 if ( !wxIsdigit(*p
) )
804 sec
+= (wxDateTime_t
)(*p
++ - '0');
810 // 7. now the interesting part: the timezone
811 int offset
wxDUMMY_INITIALIZE(0);
812 if ( *p
== '-' || *p
== '+' )
814 // the explicit offset given: it has the form of hhmm
815 bool plus
= *p
++ == '+';
817 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
822 offset
= MIN_PER_HOUR
*(10*(*p
- '0') + (*(p
+ 1) - '0'));
826 if ( !wxIsdigit(*p
) || !wxIsdigit(*(p
+ 1)) )
830 offset
+= 10*(*p
- '0') + (*(p
+ 1) - '0');
839 // the symbolic timezone given: may be either military timezone or one
840 // of standard abbreviations
843 // military: Z = UTC, J unused, A = -1, ..., Y = +12
844 static const int offsets
[26] =
846 //A B C D E F G H I J K L M
847 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
848 //N O P R Q S T U V W Z Y Z
849 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
852 if ( *p
< _T('A') || *p
> _T('Z') || *p
== _T('J') )
855 offset
= offsets
[*p
++ - 'A'];
860 const wxString
tz(p
, date
.end());
861 if ( tz
== _T("UT") || tz
== _T("UTC") || tz
== _T("GMT") )
863 else if ( tz
== _T("AST") )
865 else if ( tz
== _T("ADT") )
867 else if ( tz
== _T("EST") )
869 else if ( tz
== _T("EDT") )
871 else if ( tz
== _T("CST") )
873 else if ( tz
== _T("CDT") )
875 else if ( tz
== _T("MST") )
877 else if ( tz
== _T("MDT") )
879 else if ( tz
== _T("PST") )
881 else if ( tz
== _T("PDT") )
890 offset
*= MIN_PER_HOUR
;
894 // the spec was correct, construct the date from the values we found
895 Set(day
, mon
, year
, hour
, min
, sec
);
896 MakeFromTimezone(TimeZone::Make(offset
*SEC_PER_MIN
));
906 // returns the string containing strftime() format used for short dates in the
907 // current locale or an empty string
908 static wxString
GetLocaleDateFormat()
912 // there is no setlocale() under Windows CE, so just always query the
915 if ( strcmp(setlocale(LC_ALL
, NULL
), "C") != 0 )
918 // The locale was programatically set to non-C. We assume that this was
919 // done using wxLocale, in which case thread's current locale is also
920 // set to correct LCID value and we can use GetLocaleInfo to determine
921 // the correct formatting string:
923 LCID lcid
= LOCALE_USER_DEFAULT
;
925 LCID lcid
= GetThreadLocale();
927 // according to MSDN 80 chars is max allowed for short date format
929 if ( ::GetLocaleInfo(lcid
, LOCALE_SSHORTDATE
, fmt
, WXSIZEOF(fmt
)) )
931 wxChar chLast
= _T('\0');
932 size_t lastCount
= 0;
933 for ( const wxChar
*p
= fmt
; /* NUL handled inside */; p
++ )
943 // these characters come in groups, start counting them
953 // first deal with any special characters we have had
963 // these two are the same as we
964 // don't distinguish between 1 and
978 wxFAIL_MSG( _T("too many 'd's") );
987 // as for 'd' and 'dd' above
1000 wxFAIL_MSG( _T("too many 'M's") );
1005 switch ( lastCount
)
1017 wxFAIL_MSG( _T("wrong number of 'y's") );
1022 // strftime() doesn't have era string,
1023 // ignore this format
1024 wxASSERT_MSG( lastCount
<= 2,
1025 _T("too many 'g's") );
1029 wxFAIL_MSG( _T("unreachable") );
1036 // not a special character so must be just a separator,
1038 if ( *p
!= _T('\0') )
1040 if ( *p
== _T('%') )
1042 // this one needs to be escaped
1050 if ( *p
== _T('\0') )
1054 //else: GetLocaleInfo() failed, leave fmtDate value unchanged and
1055 // try our luck with the default formats
1057 //else: default C locale, default formats should work
1062 #endif // __WINDOWS__
1066 #include "wx/osx/private.h"
1068 // under OSX locale formats are defined using
1069 // http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns
1071 // so we need a translation function, bluntly copied from the windows
1072 // version above and enhanced with the additional elements needed
1074 static wxString
TranslateFromUnicodeFormat(const wxString
& fmt
)
1078 wxChar chLast
= _T('\0');
1079 size_t lastCount
= 0;
1080 for ( wxString::const_iterator p
= fmt
.begin(); /* end handled inside */; ++p
)
1082 if ( p
== fmt
.end() || *p
== chLast
)
1085 if ( p
== fmt
.end() )
1091 switch ( (*p
).GetValue() )
1093 // these characters come in groups, start counting them
1107 // first deal with any special characters we have had
1113 switch ( lastCount
)
1117 // these two are the same as we
1118 // don't distinguish between 1 and
1119 // 2 digits for days
1132 wxFAIL_MSG( _T("too many 'd's") );
1137 switch ( lastCount
)
1141 // as for 'd' and 'dd' above
1154 wxFAIL_MSG( _T("too many 'M's") );
1159 switch ( lastCount
)
1171 wxFAIL_MSG( _T("wrong number of 'y's") );
1176 switch ( lastCount
)
1184 wxFAIL_MSG( _T("wrong number of 'H's") );
1189 switch ( lastCount
)
1197 wxFAIL_MSG( _T("wrong number of 'h's") );
1202 switch ( lastCount
)
1210 wxFAIL_MSG( _T("wrong number of 'm's") );
1215 switch ( lastCount
)
1223 wxFAIL_MSG( _T("wrong number of 's's") );
1228 // strftime() doesn't have era string,
1229 // ignore this format
1230 wxASSERT_MSG( lastCount
<= 2,
1231 _T("too many 'g's") );
1235 wxFAIL_MSG( _T("unreachable") );
1242 // not a special character so must be just a separator,
1244 if ( *p
== _T('%') )
1246 // this one needs to be escaped
1257 static wxString
GetLocaleDateFormat()
1259 wxCFRef
<CFLocaleRef
> currentLocale( CFLocaleCopyCurrent() );
1261 wxCFRef
<CFDateFormatterRef
> dateFormatter( CFDateFormatterCreate
1262 (NULL
, currentLocale
, kCFDateFormatterShortStyle
, kCFDateFormatterNoStyle
));
1263 wxCFStringRef cfs
= wxCFRetain( CFDateFormatterGetFormat(dateFormatter
));
1264 return TranslateFromUnicodeFormat(cfs
.AsString());
1267 static wxString
GetLocaleFullDateFormat()
1269 wxCFRef
<CFLocaleRef
> currentLocale( CFLocaleCopyCurrent() );
1271 wxCFRef
<CFDateFormatterRef
> dateFormatter( CFDateFormatterCreate
1272 (NULL
, currentLocale
, kCFDateFormatterLongStyle
, kCFDateFormatterMediumStyle
));
1273 wxCFStringRef cfs
= wxCFRetain( CFDateFormatterGetFormat(dateFormatter
));
1274 return TranslateFromUnicodeFormat(cfs
.AsString());
1282 wxDateTime::ParseFormat(const wxString
& date
,
1283 const wxString
& format
,
1284 const wxDateTime
& dateDef
,
1285 wxString::const_iterator
*endParse
)
1287 wxCHECK_MSG( !format
.empty(), false, "format can't be empty" );
1288 wxCHECK_MSG( endParse
, false, "end iterator pointer must be specified" );
1293 // what fields have we found?
1294 bool haveWDay
= false,
1304 bool hourIsIn12hFormat
= false, // or in 24h one?
1305 isPM
= false; // AM by default
1307 // and the value of the items we have (init them to get rid of warnings)
1308 wxDateTime_t msec
= 0,
1312 WeekDay wday
= Inv_WeekDay
;
1313 wxDateTime_t yday
= 0,
1315 wxDateTime::Month mon
= Inv_Month
;
1318 wxString::const_iterator input
= date
.begin();
1319 const wxString::const_iterator end
= date
.end();
1320 for ( wxString::const_iterator fmt
= format
.begin(); fmt
!= format
.end(); ++fmt
)
1322 if ( *fmt
!= _T('%') )
1324 if ( wxIsspace(*fmt
) )
1326 // a white space in the format string matches 0 or more white
1327 // spaces in the input
1328 while ( wxIsspace(*input
) )
1335 // any other character (not whitespace, not '%') must be
1336 // matched by itself in the input
1337 if ( *input
++ != *fmt
)
1344 // done with this format char
1348 // start of a format specification
1350 // parse the optional width
1352 while ( wxIsdigit(*++fmt
) )
1355 width
+= *fmt
- '0';
1358 // the default widths for the various fields
1361 switch ( (*fmt
).GetValue() )
1363 case _T('Y'): // year has 4 digits
1367 case _T('j'): // day of year has 3 digits
1368 case _T('l'): // milliseconds have 3 digits
1372 case _T('w'): // week day as number has only one
1377 // default for all other fields
1382 // then the format itself
1383 switch ( (*fmt
).GetValue() )
1385 case _T('a'): // a weekday name
1388 wday
= GetWeekDayFromName
1390 GetAlphaToken(input
, end
),
1391 *fmt
== 'a' ? Name_Abbr
: Name_Full
,
1394 if ( wday
== Inv_WeekDay
)
1403 case _T('b'): // a month name
1406 mon
= GetMonthFromName
1408 GetAlphaToken(input
, end
),
1409 *fmt
== 'b' ? Name_Abbr
: Name_Full
,
1412 if ( mon
== Inv_Month
)
1421 case _T('c'): // locale default date and time representation
1423 #ifdef HAVE_STRPTIME
1426 // try using strptime() -- it may fail even if the input is
1427 // correct but the date is out of range, so we will fall back
1428 // to our generic code anyhow
1429 if ( CallStrptime(date
, input
, "%c", &tm
) )
1435 year
= 1900 + tm
.tm_year
;
1436 mon
= (Month
)tm
.tm_mon
;
1439 else // strptime() failed; try generic heuristic code
1440 #endif // HAVE_STRPTIME
1444 bool hasValidDate
= false;
1445 wxString fmtDate
= GetLocaleFullDateFormat();
1446 if ( !fmtDate
.empty() )
1448 const wxDateTime dt
= ParseFormatAt
1457 hasValidDate
= true;
1461 if ( !hasValidDate
)
1464 // try the format which corresponds to ctime() output
1465 // first, then the generic date/time formats
1466 const wxDateTime dt
= ParseFormatAt
1470 wxS("%a %b %d %H:%M:%S %Y"),
1474 if ( !dt
.IsValid() )
1489 haveDay
= haveMon
= haveYear
=
1490 haveHour
= haveMin
= haveSec
= true;
1494 case _T('d'): // day of a month (01-31)
1495 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1496 (num
> 31) || (num
< 1) )
1502 // we can't check whether the day range is correct yet, will
1503 // do it later - assume ok for now
1505 mday
= (wxDateTime_t
)num
;
1508 case _T('H'): // hour in 24h format (00-23)
1509 if ( !GetNumericToken(width
, input
, end
, &num
) || (num
> 23) )
1516 hour
= (wxDateTime_t
)num
;
1519 case _T('I'): // hour in 12h format (01-12)
1520 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1521 !num
|| (num
> 12) )
1528 hourIsIn12hFormat
= true;
1529 hour
= (wxDateTime_t
)(num
% 12); // 12 should be 0
1532 case _T('j'): // day of the year
1533 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1534 !num
|| (num
> 366) )
1541 yday
= (wxDateTime_t
)num
;
1544 case _T('l'): // milliseconds (0-999)
1545 if ( !GetNumericToken(width
, input
, end
, &num
) )
1549 msec
= (wxDateTime_t
)num
;
1552 case _T('m'): // month as a number (01-12)
1553 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1554 !num
|| (num
> 12) )
1561 mon
= (Month
)(num
- 1);
1564 case _T('M'): // minute as a decimal number (00-59)
1565 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1573 min
= (wxDateTime_t
)num
;
1576 case _T('p'): // AM or PM string
1578 wxString am
, pm
, token
= GetAlphaToken(input
, end
);
1580 // some locales have empty AM/PM tokens and thus when formatting
1581 // dates with the %p specifier nothing is generated; when trying to
1582 // parse them back, we get an empty token here... but that's not
1587 GetAmPmStrings(&am
, &pm
);
1588 if (am
.empty() && pm
.empty())
1589 return false; // no am/pm strings defined
1590 if ( token
.CmpNoCase(pm
) == 0 )
1594 else if ( token
.CmpNoCase(am
) != 0 )
1602 case _T('r'): // time as %I:%M:%S %p
1605 if ( !dt
.ParseFormat(wxString(input
, end
),
1606 wxS("%I:%M:%S %p"), &input
) )
1609 haveHour
= haveMin
= haveSec
= true;
1618 case _T('R'): // time as %H:%M
1621 dt
= ParseFormatAt(input
, end
, wxS("%H:%M"));
1622 if ( !dt
.IsValid() )
1634 case _T('S'): // second as a decimal number (00-61)
1635 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1643 sec
= (wxDateTime_t
)num
;
1646 case _T('T'): // time as %H:%M:%S
1649 dt
= ParseFormatAt(input
, end
, wxS("%H:%M:%S"));
1650 if ( !dt
.IsValid() )
1664 case _T('w'): // weekday as a number (0-6), Sunday = 0
1665 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1673 wday
= (WeekDay
)num
;
1676 case _T('x'): // locale default date representation
1677 #ifdef HAVE_STRPTIME
1678 // try using strptime() -- it may fail even if the input is
1679 // correct but the date is out of range, so we will fall back
1680 // to our generic code anyhow
1684 if ( CallStrptime(date
, input
, "%x", &tm
) )
1686 haveDay
= haveMon
= haveYear
= true;
1688 year
= 1900 + tm
.tm_year
;
1689 mon
= (Month
)tm
.tm_mon
;
1695 #endif // HAVE_STRPTIME
1701 #if defined( __WINDOWS__ ) || defined( __WXOSX__ )
1702 // The above doesn't work for all locales, try to query
1703 // the OS for the right way of formatting the date:
1704 fmtDate
= GetLocaleDateFormat();
1705 if ( fmtDate
.empty() )
1706 #endif // __WINDOWS__
1708 if ( IsWestEuropeanCountry(GetCountry()) ||
1709 GetCountry() == Russia
)
1711 fmtDate
= _T("%d/%m/%y");
1712 fmtDateAlt
= _T("%m/%d/%y");
1716 fmtDate
= _T("%m/%d/%y");
1717 fmtDateAlt
= _T("%d/%m/%y");
1722 dt
= ParseFormatAt(input
, end
,
1723 fmtDate
, fmtDateAlt
);
1726 if ( !dt
.IsValid() )
1728 wxString fmtDateLong
= fmtDate
;
1729 wxString fmtDateLongAlt
= fmtDateAlt
;
1732 if ( !fmtDateLong
.empty() )
1734 fmtDateLong
.Replace("%y","%Y");
1735 fmtDateLongAlt
.Replace("%y","%Y");
1736 const wxDateTime dtLong
= ParseFormatAt(input
, end
,
1737 fmtDateLong
, fmtDateLongAlt
);
1738 if ( !dtLong
.IsValid() )
1741 tm
= dtLong
.GetTm();
1760 case _T('X'): // locale default time representation
1761 #ifdef HAVE_STRPTIME
1763 // use strptime() to do it for us (FIXME !Unicode friendly)
1765 if ( !CallStrptime(date
, input
, "%X", &tm
) )
1768 haveHour
= haveMin
= haveSec
= true;
1774 #else // !HAVE_STRPTIME
1775 // TODO under Win32 we can query the LOCALE_ITIME system
1776 // setting which says whether the default time format is
1779 // try to parse what follows as "%H:%M:%S" and, if this
1780 // fails, as "%I:%M:%S %p" - this should catch the most
1783 dt
= ParseFormatAt(input
, end
, "%T", "%r");
1784 if ( !dt
.IsValid() )
1796 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
1799 case _T('y'): // year without century (00-99)
1800 if ( !GetNumericToken(width
, input
, end
, &num
) ||
1809 // TODO should have an option for roll over date instead of
1810 // hard coding it here
1811 year
= (num
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
;
1814 case _T('Y'): // year with century
1815 if ( !GetNumericToken(width
, input
, end
, &num
) )
1822 year
= (wxDateTime_t
)num
;
1825 case _T('Z'): // timezone name
1826 wxFAIL_MSG(_T("TODO"));
1829 case _T('%'): // a percent sign
1830 if ( *input
++ != _T('%') )
1837 case 0: // the end of string
1838 wxFAIL_MSG(_T("unexpected format end"));
1842 default: // not a known format spec
1847 // format matched, try to construct a date from what we have now
1849 if ( dateDef
.IsValid() )
1851 // take this date as default
1852 tmDef
= dateDef
.GetTm();
1854 else if ( IsValid() )
1856 // if this date is valid, don't change it
1861 // no default and this date is invalid - fall back to Today()
1862 tmDef
= Today().GetTm();
1878 // TODO we don't check here that the values are consistent, if both year
1879 // day and month/day were found, we just ignore the year day and we
1880 // also always ignore the week day
1883 if ( mday
> GetNumberOfDays(tm
.mon
, tm
.year
) )
1888 else if ( haveYDay
)
1890 if ( yday
> GetNumberOfDays(tm
.year
) )
1893 Tm tm2
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm();
1900 if ( haveHour
&& hourIsIn12hFormat
&& isPM
)
1902 // translate to 24hour format
1905 //else: either already in 24h format or no translation needed
1928 // finally check that the week day is consistent -- if we had it
1929 if ( haveWDay
&& GetWeekDay() != wday
)
1938 wxDateTime::ParseDateTime(const wxString
& date
, wxString::const_iterator
*end
)
1940 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1942 // Set to current day and hour, so strings like '14:00' becomes today at
1943 // 14, not some other random date
1944 wxDateTime dtDate
= wxDateTime::Today();
1945 wxDateTime dtTime
= wxDateTime::Today();
1947 wxString::const_iterator
1952 // If we got a date in the beginning, see if there is a time specified
1954 if ( dtDate
.ParseDate(date
, &endDate
) )
1956 // Skip spaces, as the ParseTime() function fails on spaces
1957 while ( endDate
!= date
.end() && wxIsspace(*endDate
) )
1960 const wxString
timestr(endDate
, date
.end());
1961 if ( !dtTime
.ParseTime(timestr
, &endTime
) )
1964 endBoth
= endDate
+ (endTime
- timestr
.begin());
1966 else // no date in the beginning
1968 // check if we have a time followed by a date
1969 if ( !dtTime
.ParseTime(date
, &endTime
) )
1972 while ( endTime
!= date
.end() && wxIsspace(*endTime
) )
1975 const wxString
datestr(endTime
, date
.end());
1976 if ( !dtDate
.ParseDate(datestr
, &endDate
) )
1979 endBoth
= endTime
+ (endDate
- datestr
.begin());
1982 Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(),
1983 dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(),
1984 dtTime
.GetMillisecond());
1992 wxDateTime::ParseDate(const wxString
& date
, wxString::const_iterator
*end
)
1994 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
1996 // this is a simplified version of ParseDateTime() which understands only
1997 // "today" (for wxDate compatibility) and digits only otherwise (and not
1998 // all esoteric constructions ParseDateTime() knows about)
2000 const wxString::const_iterator pBegin
= date
.begin();
2002 wxString::const_iterator p
= pBegin
;
2003 while ( wxIsspace(*p
) )
2006 // some special cases
2010 int dayDiffFromToday
;
2013 { wxTRANSLATE("today"), 0 },
2014 { wxTRANSLATE("yesterday"), -1 },
2015 { wxTRANSLATE("tomorrow"), 1 },
2018 const size_t lenRest
= date
.end() - p
;
2019 for ( size_t n
= 0; n
< WXSIZEOF(literalDates
); n
++ )
2021 const wxString dateStr
= wxGetTranslation(literalDates
[n
].str
);
2022 size_t len
= dateStr
.length();
2024 if ( len
> lenRest
)
2027 const wxString::const_iterator pEnd
= p
+ len
;
2028 if ( wxString(p
, pEnd
).CmpNoCase(dateStr
) == 0 )
2030 // nothing can follow this, so stop here
2034 int dayDiffFromToday
= literalDates
[n
].dayDiffFromToday
;
2036 if ( dayDiffFromToday
)
2038 *this += wxDateSpan::Days(dayDiffFromToday
);
2047 // We try to guess what we have here: for each new (numeric) token, we
2048 // determine if it can be a month, day or a year. Of course, there is an
2049 // ambiguity as some numbers may be days as well as months, so we also
2050 // have the ability to back track.
2053 bool haveDay
= false, // the months day?
2054 haveWDay
= false, // the day of week?
2055 haveMon
= false, // the month?
2056 haveYear
= false; // the year?
2058 // and the value of the items we have (init them to get rid of warnings)
2059 WeekDay wday
= Inv_WeekDay
;
2060 wxDateTime_t day
= 0;
2061 wxDateTime::Month mon
= Inv_Month
;
2064 // tokenize the string
2066 static const wxStringCharType
*dateDelimiters
= wxS(".,/-\t\r\n ");
2067 wxStringTokenizer
tok(wxString(p
, date
.end()), dateDelimiters
);
2068 while ( tok
.HasMoreTokens() )
2070 wxString token
= tok
.GetNextToken();
2076 if ( token
.ToULong(&val
) )
2078 // guess what this number is
2084 if ( !haveMon
&& val
> 0 && val
<= 12 )
2086 // assume it is month
2089 else // not the month
2093 // this can only be the year
2096 else // may be either day or year
2098 // use a leap year if we don't have the year yet to allow
2099 // dates like 2/29/1976 which would be rejected otherwise
2100 wxDateTime_t max_days
= (wxDateTime_t
)(
2102 ? GetNumberOfDays(mon
, haveYear
? year
: 1976)
2107 if ( (val
== 0) || (val
> (unsigned long)max_days
) )
2112 else // yes, suppose it's the day
2126 year
= (wxDateTime_t
)val
;
2135 day
= (wxDateTime_t
)val
;
2141 mon
= (Month
)(val
- 1);
2144 else // not a number
2146 // be careful not to overwrite the current mon value
2147 Month mon2
= GetMonthFromName
2150 Name_Full
| Name_Abbr
,
2151 DateLang_Local
| DateLang_English
2153 if ( mon2
!= Inv_Month
)
2158 // but we already have a month - maybe we guessed wrong?
2161 // no need to check in month range as always < 12, but
2162 // the days are counted from 1 unlike the months
2163 day
= (wxDateTime_t
)(mon
+ 1);
2168 // could possible be the year (doesn't the year come
2169 // before the month in the japanese format?) (FIXME)
2178 else // not a valid month name
2180 WeekDay wday2
= GetWeekDayFromName
2183 Name_Full
| Name_Abbr
,
2184 DateLang_Local
| DateLang_English
2186 if ( wday2
!= Inv_WeekDay
)
2198 else // not a valid weekday name
2201 static const char *ordinals
[] =
2203 wxTRANSLATE("first"),
2204 wxTRANSLATE("second"),
2205 wxTRANSLATE("third"),
2206 wxTRANSLATE("fourth"),
2207 wxTRANSLATE("fifth"),
2208 wxTRANSLATE("sixth"),
2209 wxTRANSLATE("seventh"),
2210 wxTRANSLATE("eighth"),
2211 wxTRANSLATE("ninth"),
2212 wxTRANSLATE("tenth"),
2213 wxTRANSLATE("eleventh"),
2214 wxTRANSLATE("twelfth"),
2215 wxTRANSLATE("thirteenth"),
2216 wxTRANSLATE("fourteenth"),
2217 wxTRANSLATE("fifteenth"),
2218 wxTRANSLATE("sixteenth"),
2219 wxTRANSLATE("seventeenth"),
2220 wxTRANSLATE("eighteenth"),
2221 wxTRANSLATE("nineteenth"),
2222 wxTRANSLATE("twentieth"),
2223 // that's enough - otherwise we'd have problems with
2224 // composite (or not) ordinals
2228 for ( n
= 0; n
< WXSIZEOF(ordinals
); n
++ )
2230 if ( token
.CmpNoCase(ordinals
[n
]) == 0 )
2236 if ( n
== WXSIZEOF(ordinals
) )
2238 // stop here - something unknown
2245 // don't try anything here (as in case of numeric day
2246 // above) - the symbolic day spec should always
2247 // precede the month/year
2253 day
= (wxDateTime_t
)(n
+ 1);
2258 nPosCur
= tok
.GetPosition();
2261 // either no more tokens or the scan was stopped by something we couldn't
2262 // parse - in any case, see if we can construct a date from what we have
2263 if ( !haveDay
&& !haveWDay
)
2266 if ( haveWDay
&& (haveMon
|| haveYear
|| haveDay
) &&
2267 !(haveDay
&& haveMon
&& haveYear
) )
2269 // without adjectives (which we don't support here) the week day only
2270 // makes sense completely separately or with the full date
2271 // specification (what would "Wed 1999" mean?)
2275 if ( !haveWDay
&& haveYear
&& !(haveDay
&& haveMon
) )
2277 // may be we have month and day instead of day and year?
2278 if ( haveDay
&& !haveMon
)
2282 // exchange day and month
2283 mon
= (wxDateTime::Month
)(day
- 1);
2285 // we're in the current year then
2286 if ( (year
> 0) && (year
<= (int)GetNumberOfDays(mon
, Inv_Year
)) )
2288 day
= (wxDateTime_t
)year
;
2293 //else: no, can't exchange, leave haveMon == false
2303 mon
= GetCurrentMonth();
2308 year
= GetCurrentYear();
2313 // normally we check the day above but the check is optimistic in case
2314 // we find the day before its month/year so we have to redo it now
2315 if ( day
> GetNumberOfDays(mon
, year
) )
2318 Set(day
, mon
, year
);
2322 // check that it is really the same
2323 if ( GetWeekDay() != wday
)
2331 SetToWeekDayInSameWeek(wday
);
2334 // return the pointer to the first unparsed char
2336 if ( nPosCur
&& wxStrchr(dateDelimiters
, *(p
- 1)) )
2338 // if we couldn't parse the token after the delimiter, put back the
2339 // delimiter as well
2349 wxDateTime::ParseTime(const wxString
& time
, wxString::const_iterator
*end
)
2351 wxCHECK_MSG( end
, false, "end iterator pointer must be specified" );
2353 // first try some extra things
2360 { wxTRANSLATE("noon"), 12 },
2361 { wxTRANSLATE("midnight"), 00 },
2365 for ( size_t n
= 0; n
< WXSIZEOF(stdTimes
); n
++ )
2367 const wxString timeString
= wxGetTranslation(stdTimes
[n
].name
);
2368 const wxString::const_iterator p
= time
.begin() + timeString
.length();
2369 if ( timeString
.CmpNoCase(wxString(time
.begin(), p
)) == 0 )
2371 // casts required by DigitalMars
2372 Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0));
2381 // try all time formats we may think about in the order from longest to
2383 static const char *timeFormats
[] =
2385 "%I:%M:%S %p", // 12hour with AM/PM
2386 "%H:%M:%S", // could be the same or 24 hour one so try it too
2387 "%I:%M %p", // 12hour with AM/PM but without seconds
2388 "%H:%M:%S", // and a possibly 24 hour version without seconds
2389 "%X", // possibly something from above or maybe something
2390 // completely different -- try it last
2392 // TODO: parse timezones
2395 for ( size_t nFmt
= 0; nFmt
< WXSIZEOF(timeFormats
); nFmt
++ )
2397 if ( ParseFormat(time
, timeFormats
[nFmt
], end
) )
2404 // ----------------------------------------------------------------------------
2405 // Workdays and holidays support
2406 // ----------------------------------------------------------------------------
2408 bool wxDateTime::IsWorkDay(Country
WXUNUSED(country
)) const
2410 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
2413 // ============================================================================
2415 // ============================================================================
2417 wxDateSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxDateSpan
& ds
)
2420 return ds1
.Multiply(n
);
2423 // ============================================================================
2425 // ============================================================================
2427 wxTimeSpan WXDLLIMPEXP_BASE
operator*(int n
, const wxTimeSpan
& ts
)
2429 return wxTimeSpan(ts
).Multiply(n
);
2432 // this enum is only used in wxTimeSpan::Format() below but we can't declare
2433 // it locally to the method as it provokes an internal compiler error in egcs
2434 // 2.91.60 when building with -O2
2445 // not all strftime(3) format specifiers make sense here because, for example,
2446 // a time span doesn't have a year nor a timezone
2448 // Here are the ones which are supported (all of them are supported by strftime
2450 // %H hour in 24 hour format
2451 // %M minute (00 - 59)
2452 // %S second (00 - 59)
2455 // Also, for MFC CTimeSpan compatibility, we support
2456 // %D number of days
2458 // And, to be better than MFC :-), we also have
2459 // %E number of wEeks
2460 // %l milliseconds (000 - 999)
2461 wxString
wxTimeSpan::Format(const wxString
& format
) const
2463 // we deal with only positive time spans here and just add the sign in
2464 // front for the negative ones
2467 wxString
str(Negate().Format(format
));
2471 wxCHECK_MSG( !format
.empty(), wxEmptyString
,
2472 _T("NULL format in wxTimeSpan::Format") );
2475 str
.Alloc(format
.length());
2477 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
2479 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
2480 // question is what should ts.Format("%S") do? The code here returns "3273"
2481 // in this case (i.e. the total number of seconds, not just seconds % 60)
2482 // because, for me, this call means "give me entire time interval in
2483 // seconds" and not "give me the seconds part of the time interval"
2485 // If we agree that it should behave like this, it is clear that the
2486 // interpretation of each format specifier depends on the presence of the
2487 // other format specs in the string: if there was "%H" before "%M", we
2488 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
2490 // we remember the most important unit found so far
2491 TimeSpanPart partBiggest
= Part_MSec
;
2493 for ( wxString::const_iterator pch
= format
.begin(); pch
!= format
.end(); ++pch
)
2497 if ( ch
== _T('%') )
2499 // the start of the format specification of the printf() below
2500 wxString
fmtPrefix(_T('%'));
2505 // the number of digits for the format string, 0 if unused
2506 unsigned digits
= 0;
2508 ch
= *++pch
; // get the format spec char
2512 wxFAIL_MSG( _T("invalid format character") );
2518 // skip the part below switch
2523 if ( partBiggest
< Part_Day
)
2529 partBiggest
= Part_Day
;
2534 partBiggest
= Part_Week
;
2540 if ( partBiggest
< Part_Hour
)
2546 partBiggest
= Part_Hour
;
2553 n
= GetMilliseconds().ToLong();
2554 if ( partBiggest
< Part_MSec
)
2558 //else: no need to reset partBiggest to Part_MSec, it is
2559 // the least significant one anyhow
2566 if ( partBiggest
< Part_Min
)
2572 partBiggest
= Part_Min
;
2579 n
= GetSeconds().ToLong();
2580 if ( partBiggest
< Part_Sec
)
2586 partBiggest
= Part_Sec
;
2595 fmtPrefix
<< _T("0") << digits
;
2598 str
+= wxString::Format(fmtPrefix
+ _T("ld"), n
);
2602 // normal character, just copy
2610 #endif // wxUSE_DATETIME