]> git.saurik.com Git - wxWidgets.git/blob - src/common/datetimefmt.cpp
minor corrections to TranslateFromUnicodeFormat()
[wxWidgets.git] / src / common / datetimefmt.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/datetimefmt.cpp
3 // Purpose: wxDateTime formatting & parsing code
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 11.05.99
7 // RCS-ID: $Id$
8 // Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // parts of code taken from sndcal library by Scott E. Lee:
10 //
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.
15 //
16 // Licence: wxWindows licence
17 ///////////////////////////////////////////////////////////////////////////////
18
19 // ============================================================================
20 // declarations
21 // ============================================================================
22
23 // ----------------------------------------------------------------------------
24 // headers
25 // ----------------------------------------------------------------------------
26
27 // For compilers that support precompilation, includes "wx.h".
28 #include "wx/wxprec.h"
29
30 #ifdef __BORLANDC__
31 #pragma hdrstop
32 #endif
33
34 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
35
36 #ifndef WX_PRECOMP
37 #ifdef __WXMSW__
38 #include "wx/msw/wrapwin.h"
39 #endif
40 #include "wx/string.h"
41 #include "wx/log.h"
42 #include "wx/intl.h"
43 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
44 #include "wx/module.h"
45 #include "wx/crt.h"
46 #endif // WX_PRECOMP
47
48 #include "wx/thread.h"
49 #include "wx/tokenzr.h"
50
51 #include <ctype.h>
52
53 #ifdef __WINDOWS__
54 #include <winnls.h>
55 #ifndef __WXWINCE__
56 #include <locale.h>
57 #endif
58 #endif
59
60 #include "wx/datetime.h"
61
62 // ============================================================================
63 // implementation of wxDateTime
64 // ============================================================================
65
66 // ----------------------------------------------------------------------------
67 // helpers shared between datetime.cpp and datetimefmt.cpp
68 // ----------------------------------------------------------------------------
69
70 extern void InitTm(struct tm& tm);
71
72 extern int GetTimeZone();
73
74 extern wxString CallStrftime(const wxString& format, const tm* tm);
75
76 // ----------------------------------------------------------------------------
77 // constants (see also datetime.cpp)
78 // ----------------------------------------------------------------------------
79
80 static const int DAYS_PER_WEEK = 7;
81
82 static const int HOURS_PER_DAY = 24;
83
84 static const int SEC_PER_MIN = 60;
85
86 static const int MIN_PER_HOUR = 60;
87
88 // ----------------------------------------------------------------------------
89 // parsing helpers
90 // ----------------------------------------------------------------------------
91
92 namespace
93 {
94
95 #ifdef HAVE_STRPTIME
96
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 *);
101 #endif
102
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
107 bool
108 CallStrptime(const wxString& str,
109 wxString::const_iterator& p,
110 const char *fmt,
111 tm *tm)
112 {
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());
116
117 const char * const end = strptime(start, fmt, tm);
118 if ( !end )
119 return false;
120
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();
125
126 return true;
127 }
128
129 #endif // HAVE_STRPTIME
130
131 enum
132 {
133 DateLang_English = 1,
134 DateLang_Local = 2
135 };
136
137 // return the month if the string is a month name or Inv_Month otherwise
138 //
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
143 wxDateTime::Month
144 GetMonthFromName(const wxString& name, int flags, int lang)
145 {
146 wxDateTime::Month mon;
147 for ( mon = wxDateTime::Jan; mon < wxDateTime::Inv_Month; wxNextMonth(mon) )
148 {
149 // case-insensitive comparison either one of or with both abbreviated
150 // and not versions
151 if ( flags & wxDateTime::Name_Full )
152 {
153 if ( lang & DateLang_English )
154 {
155 if ( name.CmpNoCase(wxDateTime::GetEnglishMonthName(mon,
156 wxDateTime::Name_Full)) == 0 )
157 break;
158 }
159
160 if ( lang & DateLang_Local )
161 {
162 if ( name.CmpNoCase(wxDateTime::GetMonthName(mon,
163 wxDateTime::Name_Full)) == 0 )
164 break;
165 }
166 }
167
168 if ( flags & wxDateTime::Name_Abbr )
169 {
170 if ( lang & DateLang_English )
171 {
172 if ( name.CmpNoCase(wxDateTime::GetEnglishMonthName(mon,
173 wxDateTime::Name_Abbr)) == 0 )
174 break;
175 }
176
177 if ( lang & DateLang_Local )
178 {
179 if ( name.CmpNoCase(wxDateTime::GetMonthName(mon,
180 wxDateTime::Name_Abbr)) == 0 )
181 break;
182 }
183 }
184 }
185
186 return mon;
187 }
188
189 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
190 //
191 // flags and lang parameters have the same meaning as for GetMonthFromName()
192 // above
193 wxDateTime::WeekDay
194 GetWeekDayFromName(const wxString& name, int flags, int lang)
195 {
196 wxDateTime::WeekDay wd;
197 for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) )
198 {
199 if ( flags & wxDateTime::Name_Full )
200 {
201 if ( lang & DateLang_English )
202 {
203 if ( name.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd,
204 wxDateTime::Name_Full)) == 0 )
205 break;
206 }
207
208 if ( lang & DateLang_Local )
209 {
210 if ( name.CmpNoCase(wxDateTime::GetWeekDayName(wd,
211 wxDateTime::Name_Full)) == 0 )
212 break;
213 }
214 }
215
216 if ( flags & wxDateTime::Name_Abbr )
217 {
218 if ( lang & DateLang_English )
219 {
220 if ( name.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd,
221 wxDateTime::Name_Abbr)) == 0 )
222 break;
223 }
224
225 if ( lang & DateLang_Local )
226 {
227 if ( name.CmpNoCase(wxDateTime::GetWeekDayName(wd,
228 wxDateTime::Name_Abbr)) == 0 )
229 break;
230 }
231 }
232 }
233
234 return wd;
235 }
236
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)
242 {
243 size_t n = 1;
244 wxString s;
245 while ( p != end && wxIsdigit(*p) )
246 {
247 s += *p++;
248
249 if ( len && ++n > len )
250 break;
251 }
252
253 return !s.empty() && s.ToULong(number);
254 }
255
256 // scans all alphabetic characters and returns the resulting string
257 wxString
258 GetAlphaToken(wxString::const_iterator& p,
259 const wxString::const_iterator& end)
260 {
261 wxString s;
262 while ( p != end && wxIsalpha(*p) )
263 {
264 s += *p++;
265 }
266
267 return s;
268 }
269
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
272 // there, really)
273 //
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
277 wxDateTime
278 ParseFormatAt(wxString::const_iterator& p,
279 const wxString::const_iterator& end,
280 const wxString& fmt,
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())
286 {
287 const wxString str(p, end);
288 wxString::const_iterator endParse;
289 wxDateTime dt;
290 if ( dt.ParseFormat(str, fmt, &endParse) ||
291 (!fmtAlt.empty() && dt.ParseFormat(str, fmtAlt, &endParse)) ||
292 (!fmtAlt2.empty() && dt.ParseFormat(str, fmtAlt2, &endParse)) )
293 {
294 p += endParse - str.begin();
295 }
296 //else: all formats failed
297
298 return dt;
299 }
300
301 } // anonymous namespace
302
303 // ----------------------------------------------------------------------------
304 // wxDateTime to/from text representations
305 // ----------------------------------------------------------------------------
306
307 wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const
308 {
309 wxCHECK_MSG( !format.empty(), wxEmptyString,
310 _T("NULL format in wxDateTime::Format") );
311
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
314 #ifdef HAVE_STRFTIME
315 time_t time = GetTicks();
316
317 if ( (time != (time_t)-1) && !wxStrstr(format, _T("%l")) )
318 {
319 // use strftime()
320 struct tm tmstruct;
321 struct tm *tm;
322 if ( tz.GetOffset() == -GetTimeZone() )
323 {
324 // we are working with local time
325 tm = wxLocaltime_r(&time, &tmstruct);
326
327 // should never happen
328 wxCHECK_MSG( tm, wxEmptyString, _T("wxLocaltime_r() failed") );
329 }
330 else
331 {
332 time += (int)tz.GetOffset();
333
334 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
335 int time2 = (int) time;
336 if ( time2 >= 0 )
337 #else
338 if ( time >= 0 )
339 #endif
340 {
341 tm = wxGmtime_r(&time, &tmstruct);
342
343 // should never happen
344 wxCHECK_MSG( tm, wxEmptyString, _T("wxGmtime_r() failed") );
345 }
346 else
347 {
348 tm = (struct tm *)NULL;
349 }
350 }
351
352 if ( tm )
353 {
354 return CallStrftime(format, tm);
355 }
356 }
357 //else: use generic code below
358 #endif // HAVE_STRFTIME
359
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
363 Tm tm = GetTm(tz);
364
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
376
377 wxString tmp, res, fmt;
378 for ( wxString::const_iterator p = format.begin(); p != format.end(); ++p )
379 {
380 if ( *p != _T('%') )
381 {
382 // copy as is
383 res += *p;
384
385 continue;
386 }
387
388 // set the default format
389 switch ( (*++p).GetValue() )
390 {
391 case _T('Y'): // year has 4 digits
392 fmt = _T("%04d");
393 break;
394
395 case _T('j'): // day of year has 3 digits
396 case _T('l'): // milliseconds have 3 digits
397 fmt = _T("%03d");
398 break;
399
400 case _T('w'): // week day as number has only one
401 fmt = _T("%d");
402 break;
403
404 default:
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
409 fmt = _T("%02d");
410 }
411
412 bool restart = true;
413 while ( restart )
414 {
415 restart = false;
416
417 // start of the format specification
418 switch ( (*p).GetValue() )
419 {
420 case _T('a'): // a weekday name
421 case _T('A'):
422 // second parameter should be true for abbreviated names
423 res += GetWeekDayName(tm.GetWeekDay(),
424 *p == _T('a') ? Name_Abbr : Name_Full);
425 break;
426
427 case _T('b'): // a month name
428 case _T('B'):
429 res += GetMonthName(tm.mon,
430 *p == _T('b') ? Name_Abbr : Name_Full);
431 break;
432
433 case _T('c'): // locale default date and time representation
434 case _T('x'): // locale default date representation
435 #ifdef HAVE_STRFTIME
436 //
437 // the problem: there is no way to know what do these format
438 // specifications correspond to for the current locale.
439 //
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!
450 //
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
455 // is not!
456 {
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.
465
466 int yearReal = GetYear(tz);
467 int mod28 = yearReal % 28;
468
469 // be careful to not go too far - we risk to leave the
470 // supported range
471 int year;
472 if ( mod28 < 10 )
473 {
474 year = 1988 + mod28; // 1988 == 0 (mod 28)
475 }
476 else
477 {
478 year = 1970 + mod28 - 10; // 1970 == 10 (mod 28)
479 }
480
481 int nCentury = year / 100,
482 nCenturyReal = yearReal / 100;
483
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);
489
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 )
495 {
496 nLostWeekDays += year++ % 4 ? 1 : 2;
497 }
498
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
502 if ( year >= 2000 )
503 year -= 28;
504
505 wxASSERT_MSG( year >= 1970 && year < 2000,
506 _T("logic error in wxDateTime::Format") );
507
508
509 // use strftime() to format the same date but in supported
510 // year
511 //
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;
517 InitTm(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")
528 : _T("%x"),
529 &tmAdjusted);
530
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 )
540 replacement += '|';
541
542 str.Replace(wxString::Format("%d", year),
543 replacement);
544 str.Replace(wxString::Format("%d", year % 100),
545 wxString::Format("%d", yearReal % 100));
546 str.Replace(replacement,
547 wxString::Format("%d", yearReal));
548
549 res += str;
550 }
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
556 break;
557
558 case _T('d'): // day of a month (01-31)
559 res += wxString::Format(fmt, tm.mday);
560 break;
561
562 case _T('H'): // hour in 24h format (00-23)
563 res += wxString::Format(fmt, tm.hour);
564 break;
565
566 case _T('I'): // hour in 12h format (01-12)
567 {
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);
572 }
573 break;
574
575 case _T('j'): // day of the year
576 res += wxString::Format(fmt, GetDayOfYear(tz));
577 break;
578
579 case _T('l'): // milliseconds (NOT STANDARD)
580 res += wxString::Format(fmt, GetMillisecond(tz));
581 break;
582
583 case _T('m'): // month as a number (01-12)
584 res += wxString::Format(fmt, tm.mon + 1);
585 break;
586
587 case _T('M'): // minute as a decimal number (00-59)
588 res += wxString::Format(fmt, tm.min);
589 break;
590
591 case _T('p'): // AM or PM string
592 #ifdef HAVE_STRFTIME
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
597 break;
598
599 case _T('S'): // second as a decimal number (00-61)
600 res += wxString::Format(fmt, tm.sec);
601 break;
602
603 case _T('U'): // week number in the year (Sunday 1st week day)
604 res += wxString::Format(fmt, GetWeekOfYear(Sunday_First, tz));
605 break;
606
607 case _T('W'): // week number in the year (Monday 1st week day)
608 res += wxString::Format(fmt, GetWeekOfYear(Monday_First, tz));
609 break;
610
611 case _T('w'): // weekday as a number (0-6), Sunday = 0
612 res += wxString::Format(fmt, tm.GetWeekDay());
613 break;
614
615 // case _T('x'): -- handled with "%c"
616
617 case _T('X'): // locale default time representation
618 // just use strftime() to format the time for us
619 #ifdef HAVE_STRFTIME
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
624 break;
625
626 case _T('y'): // year without century (00-99)
627 res += wxString::Format(fmt, tm.year % 100);
628 break;
629
630 case _T('Y'): // year with century
631 res += wxString::Format(fmt, tm.year);
632 break;
633
634 case _T('Z'): // timezone name
635 #ifdef HAVE_STRFTIME
636 res += CallStrftime(_T("%Z"), &tmTimeOnly);
637 #endif
638 break;
639
640 default:
641 // is it the format width?
642 fmt.Empty();
643 while ( *p == _T('-') || *p == _T('+') ||
644 *p == _T(' ') || wxIsdigit(*p) )
645 {
646 fmt += *p;
647 }
648
649 if ( !fmt.empty() )
650 {
651 // we've only got the flags and width so far in fmt
652 fmt.Prepend(_T('%'));
653 fmt.Append(_T('d'));
654
655 restart = true;
656
657 break;
658 }
659
660 // no, it wasn't the width
661 wxFAIL_MSG(_T("unknown format specificator"));
662
663 // fall through and just copy it nevertheless
664
665 case _T('%'): // a percent sign
666 res += *p;
667 break;
668
669 case 0: // the end of string
670 wxFAIL_MSG(_T("missing format at the end of string"));
671
672 // just put the '%' which was the last char in format
673 res += _T('%');
674 break;
675 }
676 }
677 }
678
679 return res;
680 }
681
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"
685 //
686 // this function is "strict" by design - it must reject anything except true
687 // RFC822 time specs.
688 bool
689 wxDateTime::ParseRfc822Date(const wxString& date, wxString::const_iterator *end)
690 {
691 wxString::const_iterator p = date.begin();
692
693 // 1. week day
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 )
698 return false;
699 //else: ignore week day for now, we could also check that it really
700 // corresponds to the specified date
701
702 p = endWday;
703
704 // 2. separating comma
705 if ( *p++ != ',' || *p++ != ' ' )
706 return false;
707
708 // 3. day number
709 if ( !wxIsdigit(*p) )
710 return false;
711
712 wxDateTime_t day = (wxDateTime_t)(*p++ - '0');
713 if ( wxIsdigit(*p) )
714 {
715 day *= 10;
716 day = (wxDateTime_t)(day + (*p++ - '0'));
717 }
718
719 if ( *p++ != ' ' )
720 return false;
721
722 // 4. month name
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 )
728 return false;
729
730 p = endMonth;
731
732 if ( *p++ != ' ' )
733 return false;
734
735 // 5. year
736 if ( !wxIsdigit(*p) )
737 return false;
738
739 int year = *p++ - '0';
740 if ( !wxIsdigit(*p) ) // should have at least 2 digits in the year
741 return false;
742
743 year *= 10;
744 year += *p++ - '0';
745
746 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
747 if ( wxIsdigit(*p) )
748 {
749 year *= 10;
750 year += *p++ - '0';
751
752 if ( !wxIsdigit(*p) )
753 {
754 // no 3 digit years please
755 return false;
756 }
757
758 year *= 10;
759 year += *p++ - '0';
760 }
761
762 if ( *p++ != ' ' )
763 return false;
764
765 // 6. time in hh:mm:ss format with seconds being optional
766 if ( !wxIsdigit(*p) )
767 return false;
768
769 wxDateTime_t hour = (wxDateTime_t)(*p++ - '0');
770
771 if ( !wxIsdigit(*p) )
772 return false;
773
774 hour *= 10;
775 hour = (wxDateTime_t)(hour + (*p++ - '0'));
776
777 if ( *p++ != ':' )
778 return false;
779
780 if ( !wxIsdigit(*p) )
781 return false;
782
783 wxDateTime_t min = (wxDateTime_t)(*p++ - '0');
784
785 if ( !wxIsdigit(*p) )
786 return false;
787
788 min *= 10;
789 min += (wxDateTime_t)(*p++ - '0');
790
791 wxDateTime_t sec = 0;
792 if ( *p == ':' )
793 {
794 p++;
795 if ( !wxIsdigit(*p) )
796 return false;
797
798 sec = (wxDateTime_t)(*p++ - '0');
799
800 if ( !wxIsdigit(*p) )
801 return false;
802
803 sec *= 10;
804 sec += (wxDateTime_t)(*p++ - '0');
805 }
806
807 if ( *p++ != ' ' )
808 return false;
809
810 // 7. now the interesting part: the timezone
811 int offset wxDUMMY_INITIALIZE(0);
812 if ( *p == '-' || *p == '+' )
813 {
814 // the explicit offset given: it has the form of hhmm
815 bool plus = *p++ == '+';
816
817 if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) )
818 return false;
819
820
821 // hours
822 offset = MIN_PER_HOUR*(10*(*p - '0') + (*(p + 1) - '0'));
823
824 p += 2;
825
826 if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) )
827 return false;
828
829 // minutes
830 offset += 10*(*p - '0') + (*(p + 1) - '0');
831
832 if ( !plus )
833 offset = -offset;
834
835 p += 2;
836 }
837 else // not numeric
838 {
839 // the symbolic timezone given: may be either military timezone or one
840 // of standard abbreviations
841 if ( !*(p + 1) )
842 {
843 // military: Z = UTC, J unused, A = -1, ..., Y = +12
844 static const int offsets[26] =
845 {
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
850 };
851
852 if ( *p < _T('A') || *p > _T('Z') || *p == _T('J') )
853 return false;
854
855 offset = offsets[*p++ - 'A'];
856 }
857 else
858 {
859 // abbreviation
860 const wxString tz(p, date.end());
861 if ( tz == _T("UT") || tz == _T("UTC") || tz == _T("GMT") )
862 offset = 0;
863 else if ( tz == _T("AST") )
864 offset = AST - GMT0;
865 else if ( tz == _T("ADT") )
866 offset = ADT - GMT0;
867 else if ( tz == _T("EST") )
868 offset = EST - GMT0;
869 else if ( tz == _T("EDT") )
870 offset = EDT - GMT0;
871 else if ( tz == _T("CST") )
872 offset = CST - GMT0;
873 else if ( tz == _T("CDT") )
874 offset = CDT - GMT0;
875 else if ( tz == _T("MST") )
876 offset = MST - GMT0;
877 else if ( tz == _T("MDT") )
878 offset = MDT - GMT0;
879 else if ( tz == _T("PST") )
880 offset = PST - GMT0;
881 else if ( tz == _T("PDT") )
882 offset = PDT - GMT0;
883 else
884 return false;
885
886 p += tz.length();
887 }
888
889 // make it minutes
890 offset *= MIN_PER_HOUR;
891 }
892
893
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));
897
898 if ( end )
899 *end = p;
900
901 return true;
902 }
903
904 #ifdef __WINDOWS__
905
906 // returns the string containing strftime() format used for short dates in the
907 // current locale or an empty string
908 static wxString GetLocaleDateFormat()
909 {
910 wxString fmtWX;
911
912 // there is no setlocale() under Windows CE, so just always query the
913 // system there
914 #ifndef __WXWINCE__
915 if ( strcmp(setlocale(LC_ALL, NULL), "C") != 0 )
916 #endif
917 {
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:
922 #ifdef __WXWINCE__
923 LCID lcid = LOCALE_USER_DEFAULT;
924 #else
925 LCID lcid = GetThreadLocale();
926 #endif
927 // according to MSDN 80 chars is max allowed for short date format
928 wxChar fmt[81];
929 if ( ::GetLocaleInfo(lcid, LOCALE_SSHORTDATE, fmt, WXSIZEOF(fmt)) )
930 {
931 wxChar chLast = _T('\0');
932 size_t lastCount = 0;
933 for ( const wxChar *p = fmt; /* NUL handled inside */; p++ )
934 {
935 if ( *p == chLast )
936 {
937 lastCount++;
938 continue;
939 }
940
941 switch ( *p )
942 {
943 // these characters come in groups, start counting them
944 case _T('d'):
945 case _T('M'):
946 case _T('y'):
947 case _T('g'):
948 chLast = *p;
949 lastCount = 1;
950 break;
951
952 default:
953 // first deal with any special characters we have had
954 if ( lastCount )
955 {
956 switch ( chLast )
957 {
958 case _T('d'):
959 switch ( lastCount )
960 {
961 case 1: // d
962 case 2: // dd
963 // these two are the same as we
964 // don't distinguish between 1 and
965 // 2 digits for days
966 fmtWX += _T("%d");
967 break;
968
969 case 3: // ddd
970 fmtWX += _T("%a");
971 break;
972
973 case 4: // dddd
974 fmtWX += _T("%A");
975 break;
976
977 default:
978 wxFAIL_MSG( _T("too many 'd's") );
979 }
980 break;
981
982 case _T('M'):
983 switch ( lastCount )
984 {
985 case 1: // M
986 case 2: // MM
987 // as for 'd' and 'dd' above
988 fmtWX += _T("%m");
989 break;
990
991 case 3:
992 fmtWX += _T("%b");
993 break;
994
995 case 4:
996 fmtWX += _T("%B");
997 break;
998
999 default:
1000 wxFAIL_MSG( _T("too many 'M's") );
1001 }
1002 break;
1003
1004 case _T('y'):
1005 switch ( lastCount )
1006 {
1007 case 1: // y
1008 case 2: // yy
1009 fmtWX += _T("%y");
1010 break;
1011
1012 case 4: // yyyy
1013 fmtWX += _T("%Y");
1014 break;
1015
1016 default:
1017 wxFAIL_MSG( _T("wrong number of 'y's") );
1018 }
1019 break;
1020
1021 case _T('g'):
1022 // strftime() doesn't have era string,
1023 // ignore this format
1024 wxASSERT_MSG( lastCount <= 2,
1025 _T("too many 'g's") );
1026 break;
1027
1028 default:
1029 wxFAIL_MSG( _T("unreachable") );
1030 }
1031
1032 chLast = _T('\0');
1033 lastCount = 0;
1034 }
1035
1036 // not a special character so must be just a separator,
1037 // treat as is
1038 if ( *p != _T('\0') )
1039 {
1040 if ( *p == _T('%') )
1041 {
1042 // this one needs to be escaped
1043 fmtWX += _T('%');
1044 }
1045
1046 fmtWX += *p;
1047 }
1048 }
1049
1050 if ( *p == _T('\0') )
1051 break;
1052 }
1053 }
1054 //else: GetLocaleInfo() failed, leave fmtDate value unchanged and
1055 // try our luck with the default formats
1056 }
1057 //else: default C locale, default formats should work
1058
1059 return fmtWX;
1060 }
1061
1062 #endif // __WINDOWS__
1063
1064 #ifdef __WXOSX__
1065
1066 #include "wx/osx/private.h"
1067
1068 // under OSX locale formats are defined using
1069 // http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns
1070 //
1071 // so we need a translation function, bluntly copied from the windows
1072 // version above and enhanced with the additional elements needed
1073
1074 static wxString TranslateFromUnicodeFormat(const wxString& fmt)
1075 {
1076 wxString fmtWX;
1077
1078 wxChar chLast = _T('\0');
1079 size_t lastCount = 0;
1080 for ( wxString::const_iterator p = fmt.begin(); /* end handled inside */; ++p )
1081 {
1082 if ( p == fmt.end() || *p == chLast )
1083 {
1084 lastCount++;
1085 if ( p == fmt.end() )
1086 break;
1087
1088 continue;
1089 }
1090
1091 switch ( (*p).GetValue() )
1092 {
1093 // these characters come in groups, start counting them
1094 case _T('d'):
1095 case _T('M'):
1096 case _T('y'):
1097 case _T('g'):
1098 case _T('h'):
1099 case _T('H'):
1100 case _T('m'):
1101 case _T('s'):
1102 chLast = *p;
1103 lastCount = 1;
1104 break;
1105
1106 default:
1107 // first deal with any special characters we have had
1108 if ( lastCount )
1109 {
1110 switch ( chLast )
1111 {
1112 case _T('d'):
1113 switch ( lastCount )
1114 {
1115 case 1: // d
1116 case 2: // dd
1117 // these two are the same as we
1118 // don't distinguish between 1 and
1119 // 2 digits for days
1120 fmtWX += _T("%d");
1121 break;
1122
1123 case 3: // ddd
1124 fmtWX += _T("%a");
1125 break;
1126
1127 case 4: // dddd
1128 fmtWX += _T("%A");
1129 break;
1130
1131 default:
1132 wxFAIL_MSG( _T("too many 'd's") );
1133 }
1134 break;
1135
1136 case _T('M'):
1137 switch ( lastCount )
1138 {
1139 case 1: // M
1140 case 2: // MM
1141 // as for 'd' and 'dd' above
1142 fmtWX += _T("%m");
1143 break;
1144
1145 case 3:
1146 fmtWX += _T("%b");
1147 break;
1148
1149 case 4:
1150 fmtWX += _T("%B");
1151 break;
1152
1153 default:
1154 wxFAIL_MSG( _T("too many 'M's") );
1155 }
1156 break;
1157
1158 case _T('y'):
1159 switch ( lastCount )
1160 {
1161 case 1: // y
1162 case 2: // yy
1163 fmtWX += _T("%y");
1164 break;
1165
1166 case 4: // yyyy
1167 fmtWX += _T("%Y");
1168 break;
1169
1170 default:
1171 wxFAIL_MSG( _T("wrong number of 'y's") );
1172 }
1173 break;
1174
1175 case _T('H'):
1176 switch ( lastCount )
1177 {
1178 case 1: // H
1179 case 2: // HH
1180 fmtWX += _T("%H");
1181 break;
1182
1183 default:
1184 wxFAIL_MSG( _T("wrong number of 'H's") );
1185 }
1186 break;
1187
1188 case _T('h'):
1189 switch ( lastCount )
1190 {
1191 case 1: // h
1192 case 2: // hh
1193 fmtWX += _T("%h");
1194 break;
1195
1196 default:
1197 wxFAIL_MSG( _T("wrong number of 'h's") );
1198 }
1199 break;
1200
1201 case _T('m'):
1202 switch ( lastCount )
1203 {
1204 case 1: // m
1205 case 2: // mm
1206 fmtWX += _T("%M");
1207 break;
1208
1209 default:
1210 wxFAIL_MSG( _T("wrong number of 'm's") );
1211 }
1212 break;
1213
1214 case _T('s'):
1215 switch ( lastCount )
1216 {
1217 case 1: // s
1218 case 2: // ss
1219 fmtWX += _T("%S");
1220 break;
1221
1222 default:
1223 wxFAIL_MSG( _T("wrong number of 's's") );
1224 }
1225 break;
1226
1227 case _T('g'):
1228 // strftime() doesn't have era string,
1229 // ignore this format
1230 wxASSERT_MSG( lastCount <= 2,
1231 _T("too many 'g's") );
1232 break;
1233
1234 default:
1235 wxFAIL_MSG( _T("unreachable") );
1236 }
1237
1238 chLast = _T('\0');
1239 lastCount = 0;
1240 }
1241
1242 // not a special character so must be just a separator,
1243 // treat as is
1244 if ( *p == _T('%') )
1245 {
1246 // this one needs to be escaped
1247 fmtWX += _T('%');
1248 }
1249
1250 fmtWX += *p;
1251 }
1252 }
1253
1254 return fmtWX;
1255 }
1256
1257 static wxString GetLocaleDateFormat()
1258 {
1259 wxCFRef<CFLocaleRef> currentLocale( CFLocaleCopyCurrent() );
1260
1261 wxCFRef<CFDateFormatterRef> dateFormatter( CFDateFormatterCreate
1262 (NULL, currentLocale, kCFDateFormatterShortStyle, kCFDateFormatterNoStyle));
1263 wxCFStringRef cfs = wxCFRetain( CFDateFormatterGetFormat(dateFormatter ));
1264 return TranslateFromUnicodeFormat(cfs.AsString());
1265 }
1266
1267 static wxString GetLocaleFullDateFormat()
1268 {
1269 wxCFRef<CFLocaleRef> currentLocale( CFLocaleCopyCurrent() );
1270
1271 wxCFRef<CFDateFormatterRef> dateFormatter( CFDateFormatterCreate
1272 (NULL, currentLocale, kCFDateFormatterLongStyle, kCFDateFormatterMediumStyle));
1273 wxCFStringRef cfs = wxCFRetain( CFDateFormatterGetFormat(dateFormatter ));
1274 return TranslateFromUnicodeFormat(cfs.AsString());
1275 }
1276
1277
1278
1279 #endif // __WXOSX__
1280
1281 bool
1282 wxDateTime::ParseFormat(const wxString& date,
1283 const wxString& format,
1284 const wxDateTime& dateDef,
1285 wxString::const_iterator *endParse)
1286 {
1287 wxCHECK_MSG( !format.empty(), false, "format can't be empty" );
1288 wxCHECK_MSG( endParse, false, "end iterator pointer must be specified" );
1289
1290 wxString str;
1291 unsigned long num;
1292
1293 // what fields have we found?
1294 bool haveWDay = false,
1295 haveYDay = false,
1296 haveDay = false,
1297 haveMon = false,
1298 haveYear = false,
1299 haveHour = false,
1300 haveMin = false,
1301 haveSec = false,
1302 haveMsec = false;
1303
1304 bool hourIsIn12hFormat = false, // or in 24h one?
1305 isPM = false; // AM by default
1306
1307 // and the value of the items we have (init them to get rid of warnings)
1308 wxDateTime_t msec = 0,
1309 sec = 0,
1310 min = 0,
1311 hour = 0;
1312 WeekDay wday = Inv_WeekDay;
1313 wxDateTime_t yday = 0,
1314 mday = 0;
1315 wxDateTime::Month mon = Inv_Month;
1316 int year = 0;
1317
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 )
1321 {
1322 if ( *fmt != _T('%') )
1323 {
1324 if ( wxIsspace(*fmt) )
1325 {
1326 // a white space in the format string matches 0 or more white
1327 // spaces in the input
1328 while ( wxIsspace(*input) )
1329 {
1330 input++;
1331 }
1332 }
1333 else // !space
1334 {
1335 // any other character (not whitespace, not '%') must be
1336 // matched by itself in the input
1337 if ( *input++ != *fmt )
1338 {
1339 // no match
1340 return false;
1341 }
1342 }
1343
1344 // done with this format char
1345 continue;
1346 }
1347
1348 // start of a format specification
1349
1350 // parse the optional width
1351 size_t width = 0;
1352 while ( wxIsdigit(*++fmt) )
1353 {
1354 width *= 10;
1355 width += *fmt - '0';
1356 }
1357
1358 // the default widths for the various fields
1359 if ( !width )
1360 {
1361 switch ( (*fmt).GetValue() )
1362 {
1363 case _T('Y'): // year has 4 digits
1364 width = 4;
1365 break;
1366
1367 case _T('j'): // day of year has 3 digits
1368 case _T('l'): // milliseconds have 3 digits
1369 width = 3;
1370 break;
1371
1372 case _T('w'): // week day as number has only one
1373 width = 1;
1374 break;
1375
1376 default:
1377 // default for all other fields
1378 width = 2;
1379 }
1380 }
1381
1382 // then the format itself
1383 switch ( (*fmt).GetValue() )
1384 {
1385 case _T('a'): // a weekday name
1386 case _T('A'):
1387 {
1388 wday = GetWeekDayFromName
1389 (
1390 GetAlphaToken(input, end),
1391 *fmt == 'a' ? Name_Abbr : Name_Full,
1392 DateLang_Local
1393 );
1394 if ( wday == Inv_WeekDay )
1395 {
1396 // no match
1397 return false;
1398 }
1399 }
1400 haveWDay = true;
1401 break;
1402
1403 case _T('b'): // a month name
1404 case _T('B'):
1405 {
1406 mon = GetMonthFromName
1407 (
1408 GetAlphaToken(input, end),
1409 *fmt == 'b' ? Name_Abbr : Name_Full,
1410 DateLang_Local
1411 );
1412 if ( mon == Inv_Month )
1413 {
1414 // no match
1415 return false;
1416 }
1417 }
1418 haveMon = true;
1419 break;
1420
1421 case _T('c'): // locale default date and time representation
1422 {
1423 #ifdef HAVE_STRPTIME
1424 struct tm tm;
1425
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) )
1430 {
1431 hour = tm.tm_hour;
1432 min = tm.tm_min;
1433 sec = tm.tm_sec;
1434
1435 year = 1900 + tm.tm_year;
1436 mon = (Month)tm.tm_mon;
1437 mday = tm.tm_mday;
1438 }
1439 else // strptime() failed; try generic heuristic code
1440 #endif // HAVE_STRPTIME
1441 {
1442 Tm tm;
1443 #ifdef __WXOSX__
1444 bool hasValidDate = false;
1445 wxString fmtDate = GetLocaleFullDateFormat();
1446 if ( !fmtDate.empty() )
1447 {
1448 const wxDateTime dt = ParseFormatAt
1449 (
1450 input,
1451 end,
1452 fmtDate
1453 );
1454 if ( dt.IsValid() )
1455 {
1456 tm = dt.GetTm();
1457 hasValidDate = true;
1458 }
1459 }
1460
1461 if ( !hasValidDate )
1462 #endif // __WXOSX__
1463 {
1464 // try the format which corresponds to ctime() output
1465 // first, then the generic date/time formats
1466 const wxDateTime dt = ParseFormatAt
1467 (
1468 input,
1469 end,
1470 wxS("%a %b %d %H:%M:%S %Y"),
1471 wxS("%x %X"),
1472 wxS("%X %x")
1473 );
1474 if ( !dt.IsValid() )
1475 return false;
1476 tm = dt.GetTm();
1477 }
1478
1479
1480 hour = tm.hour;
1481 min = tm.min;
1482 sec = tm.sec;
1483
1484 year = tm.year;
1485 mon = tm.mon;
1486 mday = tm.mday;
1487 }
1488
1489 haveDay = haveMon = haveYear =
1490 haveHour = haveMin = haveSec = true;
1491 }
1492 break;
1493
1494 case _T('d'): // day of a month (01-31)
1495 if ( !GetNumericToken(width, input, end, &num) ||
1496 (num > 31) || (num < 1) )
1497 {
1498 // no match
1499 return false;
1500 }
1501
1502 // we can't check whether the day range is correct yet, will
1503 // do it later - assume ok for now
1504 haveDay = true;
1505 mday = (wxDateTime_t)num;
1506 break;
1507
1508 case _T('H'): // hour in 24h format (00-23)
1509 if ( !GetNumericToken(width, input, end, &num) || (num > 23) )
1510 {
1511 // no match
1512 return false;
1513 }
1514
1515 haveHour = true;
1516 hour = (wxDateTime_t)num;
1517 break;
1518
1519 case _T('I'): // hour in 12h format (01-12)
1520 if ( !GetNumericToken(width, input, end, &num) ||
1521 !num || (num > 12) )
1522 {
1523 // no match
1524 return false;
1525 }
1526
1527 haveHour = true;
1528 hourIsIn12hFormat = true;
1529 hour = (wxDateTime_t)(num % 12); // 12 should be 0
1530 break;
1531
1532 case _T('j'): // day of the year
1533 if ( !GetNumericToken(width, input, end, &num) ||
1534 !num || (num > 366) )
1535 {
1536 // no match
1537 return false;
1538 }
1539
1540 haveYDay = true;
1541 yday = (wxDateTime_t)num;
1542 break;
1543
1544 case _T('l'): // milliseconds (0-999)
1545 if ( !GetNumericToken(width, input, end, &num) )
1546 return false;
1547
1548 haveMsec = true;
1549 msec = (wxDateTime_t)num;
1550 break;
1551
1552 case _T('m'): // month as a number (01-12)
1553 if ( !GetNumericToken(width, input, end, &num) ||
1554 !num || (num > 12) )
1555 {
1556 // no match
1557 return false;
1558 }
1559
1560 haveMon = true;
1561 mon = (Month)(num - 1);
1562 break;
1563
1564 case _T('M'): // minute as a decimal number (00-59)
1565 if ( !GetNumericToken(width, input, end, &num) ||
1566 (num > 59) )
1567 {
1568 // no match
1569 return false;
1570 }
1571
1572 haveMin = true;
1573 min = (wxDateTime_t)num;
1574 break;
1575
1576 case _T('p'): // AM or PM string
1577 {
1578 wxString am, pm, token = GetAlphaToken(input, end);
1579
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
1583 // an error.
1584 if (token.empty())
1585 break;
1586
1587 GetAmPmStrings(&am, &pm);
1588 if (am.empty() && pm.empty())
1589 return false; // no am/pm strings defined
1590 if ( token.CmpNoCase(pm) == 0 )
1591 {
1592 isPM = true;
1593 }
1594 else if ( token.CmpNoCase(am) != 0 )
1595 {
1596 // no match
1597 return false;
1598 }
1599 }
1600 break;
1601
1602 case _T('r'): // time as %I:%M:%S %p
1603 {
1604 wxDateTime dt;
1605 if ( !dt.ParseFormat(wxString(input, end),
1606 wxS("%I:%M:%S %p"), &input) )
1607 return false;
1608
1609 haveHour = haveMin = haveSec = true;
1610
1611 Tm tm = dt.GetTm();
1612 hour = tm.hour;
1613 min = tm.min;
1614 sec = tm.sec;
1615 }
1616 break;
1617
1618 case _T('R'): // time as %H:%M
1619 {
1620 const wxDateTime
1621 dt = ParseFormatAt(input, end, wxS("%H:%M"));
1622 if ( !dt.IsValid() )
1623 return false;
1624
1625 haveHour =
1626 haveMin = true;
1627
1628 Tm tm = dt.GetTm();
1629 hour = tm.hour;
1630 min = tm.min;
1631 }
1632 break;
1633
1634 case _T('S'): // second as a decimal number (00-61)
1635 if ( !GetNumericToken(width, input, end, &num) ||
1636 (num > 61) )
1637 {
1638 // no match
1639 return false;
1640 }
1641
1642 haveSec = true;
1643 sec = (wxDateTime_t)num;
1644 break;
1645
1646 case _T('T'): // time as %H:%M:%S
1647 {
1648 const wxDateTime
1649 dt = ParseFormatAt(input, end, wxS("%H:%M:%S"));
1650 if ( !dt.IsValid() )
1651 return false;
1652
1653 haveHour =
1654 haveMin =
1655 haveSec = true;
1656
1657 Tm tm = dt.GetTm();
1658 hour = tm.hour;
1659 min = tm.min;
1660 sec = tm.sec;
1661 }
1662 break;
1663
1664 case _T('w'): // weekday as a number (0-6), Sunday = 0
1665 if ( !GetNumericToken(width, input, end, &num) ||
1666 (wday > 6) )
1667 {
1668 // no match
1669 return false;
1670 }
1671
1672 haveWDay = true;
1673 wday = (WeekDay)num;
1674 break;
1675
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
1681 {
1682 struct tm tm;
1683
1684 if ( CallStrptime(date, input, "%x", &tm) )
1685 {
1686 haveDay = haveMon = haveYear = true;
1687
1688 year = 1900 + tm.tm_year;
1689 mon = (Month)tm.tm_mon;
1690 mday = tm.tm_mday;
1691
1692 break;
1693 }
1694 }
1695 #endif // HAVE_STRPTIME
1696
1697 {
1698 wxString fmtDate,
1699 fmtDateAlt;
1700
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__
1707 {
1708 if ( IsWestEuropeanCountry(GetCountry()) ||
1709 GetCountry() == Russia )
1710 {
1711 fmtDate = _T("%d/%m/%y");
1712 fmtDateAlt = _T("%m/%d/%y");
1713 }
1714 else // assume USA
1715 {
1716 fmtDate = _T("%m/%d/%y");
1717 fmtDateAlt = _T("%d/%m/%y");
1718 }
1719 }
1720
1721 const wxDateTime
1722 dt = ParseFormatAt(input, end,
1723 fmtDate, fmtDateAlt);
1724 Tm tm;
1725
1726 if ( !dt.IsValid() )
1727 {
1728 wxString fmtDateLong = fmtDate;
1729 wxString fmtDateLongAlt = fmtDateAlt;
1730
1731
1732 if ( !fmtDateLong.empty() )
1733 {
1734 fmtDateLong.Replace("%y","%Y");
1735 fmtDateLongAlt.Replace("%y","%Y");
1736 const wxDateTime dtLong = ParseFormatAt(input, end,
1737 fmtDateLong, fmtDateLongAlt);
1738 if ( !dtLong.IsValid() )
1739 return false;
1740
1741 tm = dtLong.GetTm();
1742 }
1743 else
1744 return false;
1745 }
1746 else
1747 tm = dt.GetTm();
1748
1749 haveDay =
1750 haveMon =
1751 haveYear = true;
1752
1753 year = tm.year;
1754 mon = tm.mon;
1755 mday = tm.mday;
1756 }
1757
1758 break;
1759
1760 case _T('X'): // locale default time representation
1761 #ifdef HAVE_STRPTIME
1762 {
1763 // use strptime() to do it for us (FIXME !Unicode friendly)
1764 struct tm tm;
1765 if ( !CallStrptime(date, input, "%X", &tm) )
1766 return false;
1767
1768 haveHour = haveMin = haveSec = true;
1769
1770 hour = tm.tm_hour;
1771 min = tm.tm_min;
1772 sec = tm.tm_sec;
1773 }
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
1777 // 24 or 12 hour
1778 {
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
1781 // common cases
1782 const wxDateTime
1783 dt = ParseFormatAt(input, end, "%T", "%r");
1784 if ( !dt.IsValid() )
1785 return false;
1786
1787 haveHour =
1788 haveMin =
1789 haveSec = true;
1790
1791 Tm tm = dt.GetTm();
1792 hour = tm.hour;
1793 min = tm.min;
1794 sec = tm.sec;
1795 }
1796 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
1797 break;
1798
1799 case _T('y'): // year without century (00-99)
1800 if ( !GetNumericToken(width, input, end, &num) ||
1801 (num > 99) )
1802 {
1803 // no match
1804 return false;
1805 }
1806
1807 haveYear = true;
1808
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;
1812 break;
1813
1814 case _T('Y'): // year with century
1815 if ( !GetNumericToken(width, input, end, &num) )
1816 {
1817 // no match
1818 return false;
1819 }
1820
1821 haveYear = true;
1822 year = (wxDateTime_t)num;
1823 break;
1824
1825 case _T('Z'): // timezone name
1826 wxFAIL_MSG(_T("TODO"));
1827 break;
1828
1829 case _T('%'): // a percent sign
1830 if ( *input++ != _T('%') )
1831 {
1832 // no match
1833 return false;
1834 }
1835 break;
1836
1837 case 0: // the end of string
1838 wxFAIL_MSG(_T("unexpected format end"));
1839
1840 // fall through
1841
1842 default: // not a known format spec
1843 return false;
1844 }
1845 }
1846
1847 // format matched, try to construct a date from what we have now
1848 Tm tmDef;
1849 if ( dateDef.IsValid() )
1850 {
1851 // take this date as default
1852 tmDef = dateDef.GetTm();
1853 }
1854 else if ( IsValid() )
1855 {
1856 // if this date is valid, don't change it
1857 tmDef = GetTm();
1858 }
1859 else
1860 {
1861 // no default and this date is invalid - fall back to Today()
1862 tmDef = Today().GetTm();
1863 }
1864
1865 Tm tm = tmDef;
1866
1867 // set the date
1868 if ( haveMon )
1869 {
1870 tm.mon = mon;
1871 }
1872
1873 if ( haveYear )
1874 {
1875 tm.year = year;
1876 }
1877
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
1881 if ( haveDay )
1882 {
1883 if ( mday > GetNumberOfDays(tm.mon, tm.year) )
1884 return false;
1885
1886 tm.mday = mday;
1887 }
1888 else if ( haveYDay )
1889 {
1890 if ( yday > GetNumberOfDays(tm.year) )
1891 return false;
1892
1893 Tm tm2 = wxDateTime(1, Jan, tm.year).SetToYearDay(yday).GetTm();
1894
1895 tm.mon = tm2.mon;
1896 tm.mday = tm2.mday;
1897 }
1898
1899 // deal with AM/PM
1900 if ( haveHour && hourIsIn12hFormat && isPM )
1901 {
1902 // translate to 24hour format
1903 hour += 12;
1904 }
1905 //else: either already in 24h format or no translation needed
1906
1907 // set the time
1908 if ( haveHour )
1909 {
1910 tm.hour = hour;
1911 }
1912
1913 if ( haveMin )
1914 {
1915 tm.min = min;
1916 }
1917
1918 if ( haveSec )
1919 {
1920 tm.sec = sec;
1921 }
1922
1923 if ( haveMsec )
1924 tm.msec = msec;
1925
1926 Set(tm);
1927
1928 // finally check that the week day is consistent -- if we had it
1929 if ( haveWDay && GetWeekDay() != wday )
1930 return false;
1931
1932 *endParse = input;
1933
1934 return true;
1935 }
1936
1937 bool
1938 wxDateTime::ParseDateTime(const wxString& date, wxString::const_iterator *end)
1939 {
1940 wxCHECK_MSG( end, false, "end iterator pointer must be specified" );
1941
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();
1946
1947 wxString::const_iterator
1948 endTime,
1949 endDate,
1950 endBoth;
1951
1952 // If we got a date in the beginning, see if there is a time specified
1953 // after the date
1954 if ( dtDate.ParseDate(date, &endDate) )
1955 {
1956 // Skip spaces, as the ParseTime() function fails on spaces
1957 while ( endDate != date.end() && wxIsspace(*endDate) )
1958 ++endDate;
1959
1960 const wxString timestr(endDate, date.end());
1961 if ( !dtTime.ParseTime(timestr, &endTime) )
1962 return false;
1963
1964 endBoth = endDate + (endTime - timestr.begin());
1965 }
1966 else // no date in the beginning
1967 {
1968 // check if we have a time followed by a date
1969 if ( !dtTime.ParseTime(date, &endTime) )
1970 return false;
1971
1972 while ( endTime != date.end() && wxIsspace(*endTime) )
1973 ++endTime;
1974
1975 const wxString datestr(endTime, date.end());
1976 if ( !dtDate.ParseDate(datestr, &endDate) )
1977 return false;
1978
1979 endBoth = endTime + (endDate - datestr.begin());
1980 }
1981
1982 Set(dtDate.GetDay(), dtDate.GetMonth(), dtDate.GetYear(),
1983 dtTime.GetHour(), dtTime.GetMinute(), dtTime.GetSecond(),
1984 dtTime.GetMillisecond());
1985
1986 *end = endBoth;
1987
1988 return true;
1989 }
1990
1991 bool
1992 wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
1993 {
1994 wxCHECK_MSG( end, false, "end iterator pointer must be specified" );
1995
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)
1999
2000 const wxString::const_iterator pBegin = date.begin();
2001
2002 wxString::const_iterator p = pBegin;
2003 while ( wxIsspace(*p) )
2004 p++;
2005
2006 // some special cases
2007 static struct
2008 {
2009 const char *str;
2010 int dayDiffFromToday;
2011 } literalDates[] =
2012 {
2013 { wxTRANSLATE("today"), 0 },
2014 { wxTRANSLATE("yesterday"), -1 },
2015 { wxTRANSLATE("tomorrow"), 1 },
2016 };
2017
2018 const size_t lenRest = date.end() - p;
2019 for ( size_t n = 0; n < WXSIZEOF(literalDates); n++ )
2020 {
2021 const wxString dateStr = wxGetTranslation(literalDates[n].str);
2022 size_t len = dateStr.length();
2023
2024 if ( len > lenRest )
2025 continue;
2026
2027 const wxString::const_iterator pEnd = p + len;
2028 if ( wxString(p, pEnd).CmpNoCase(dateStr) == 0 )
2029 {
2030 // nothing can follow this, so stop here
2031
2032 p = pEnd;
2033
2034 int dayDiffFromToday = literalDates[n].dayDiffFromToday;
2035 *this = Today();
2036 if ( dayDiffFromToday )
2037 {
2038 *this += wxDateSpan::Days(dayDiffFromToday);
2039 }
2040
2041 *end = pEnd;
2042
2043 return true;
2044 }
2045 }
2046
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.
2051
2052 // what do we have?
2053 bool haveDay = false, // the months day?
2054 haveWDay = false, // the day of week?
2055 haveMon = false, // the month?
2056 haveYear = false; // the year?
2057
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;
2062 int year = 0;
2063
2064 // tokenize the string
2065 size_t nPosCur = 0;
2066 static const wxStringCharType *dateDelimiters = wxS(".,/-\t\r\n ");
2067 wxStringTokenizer tok(wxString(p, date.end()), dateDelimiters);
2068 while ( tok.HasMoreTokens() )
2069 {
2070 wxString token = tok.GetNextToken();
2071 if ( !token )
2072 continue;
2073
2074 // is it a number?
2075 unsigned long val;
2076 if ( token.ToULong(&val) )
2077 {
2078 // guess what this number is
2079
2080 bool isDay = false,
2081 isMonth = false,
2082 isYear = false;
2083
2084 if ( !haveMon && val > 0 && val <= 12 )
2085 {
2086 // assume it is month
2087 isMonth = true;
2088 }
2089 else // not the month
2090 {
2091 if ( haveDay )
2092 {
2093 // this can only be the year
2094 isYear = true;
2095 }
2096 else // may be either day or year
2097 {
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)(
2101 haveMon
2102 ? GetNumberOfDays(mon, haveYear ? year : 1976)
2103 : 31
2104 );
2105
2106 // can it be day?
2107 if ( (val == 0) || (val > (unsigned long)max_days) )
2108 {
2109 // no
2110 isYear = true;
2111 }
2112 else // yes, suppose it's the day
2113 {
2114 isDay = true;
2115 }
2116 }
2117 }
2118
2119 if ( isYear )
2120 {
2121 if ( haveYear )
2122 break;
2123
2124 haveYear = true;
2125
2126 year = (wxDateTime_t)val;
2127 }
2128 else if ( isDay )
2129 {
2130 if ( haveDay )
2131 break;
2132
2133 haveDay = true;
2134
2135 day = (wxDateTime_t)val;
2136 }
2137 else if ( isMonth )
2138 {
2139 haveMon = true;
2140
2141 mon = (Month)(val - 1);
2142 }
2143 }
2144 else // not a number
2145 {
2146 // be careful not to overwrite the current mon value
2147 Month mon2 = GetMonthFromName
2148 (
2149 token,
2150 Name_Full | Name_Abbr,
2151 DateLang_Local | DateLang_English
2152 );
2153 if ( mon2 != Inv_Month )
2154 {
2155 // it's a month
2156 if ( haveMon )
2157 {
2158 // but we already have a month - maybe we guessed wrong?
2159 if ( !haveDay )
2160 {
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);
2164 haveDay = true;
2165 }
2166 else
2167 {
2168 // could possible be the year (doesn't the year come
2169 // before the month in the japanese format?) (FIXME)
2170 break;
2171 }
2172 }
2173
2174 mon = mon2;
2175
2176 haveMon = true;
2177 }
2178 else // not a valid month name
2179 {
2180 WeekDay wday2 = GetWeekDayFromName
2181 (
2182 token,
2183 Name_Full | Name_Abbr,
2184 DateLang_Local | DateLang_English
2185 );
2186 if ( wday2 != Inv_WeekDay )
2187 {
2188 // a week day
2189 if ( haveWDay )
2190 {
2191 break;
2192 }
2193
2194 wday = wday2;
2195
2196 haveWDay = true;
2197 }
2198 else // not a valid weekday name
2199 {
2200 // try the ordinals
2201 static const char *ordinals[] =
2202 {
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
2225 };
2226
2227 size_t n;
2228 for ( n = 0; n < WXSIZEOF(ordinals); n++ )
2229 {
2230 if ( token.CmpNoCase(ordinals[n]) == 0 )
2231 {
2232 break;
2233 }
2234 }
2235
2236 if ( n == WXSIZEOF(ordinals) )
2237 {
2238 // stop here - something unknown
2239 break;
2240 }
2241
2242 // it's a day
2243 if ( haveDay )
2244 {
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
2248 break;
2249 }
2250
2251 haveDay = true;
2252
2253 day = (wxDateTime_t)(n + 1);
2254 }
2255 }
2256 }
2257
2258 nPosCur = tok.GetPosition();
2259 }
2260
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 )
2264 return false;
2265
2266 if ( haveWDay && (haveMon || haveYear || haveDay) &&
2267 !(haveDay && haveMon && haveYear) )
2268 {
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?)
2272 return false;
2273 }
2274
2275 if ( !haveWDay && haveYear && !(haveDay && haveMon) )
2276 {
2277 // may be we have month and day instead of day and year?
2278 if ( haveDay && !haveMon )
2279 {
2280 if ( day <= 12 )
2281 {
2282 // exchange day and month
2283 mon = (wxDateTime::Month)(day - 1);
2284
2285 // we're in the current year then
2286 if ( (year > 0) && (year <= (int)GetNumberOfDays(mon, Inv_Year)) )
2287 {
2288 day = (wxDateTime_t)year;
2289
2290 haveMon = true;
2291 haveYear = false;
2292 }
2293 //else: no, can't exchange, leave haveMon == false
2294 }
2295 }
2296
2297 if ( !haveMon )
2298 return false;
2299 }
2300
2301 if ( !haveMon )
2302 {
2303 mon = GetCurrentMonth();
2304 }
2305
2306 if ( !haveYear )
2307 {
2308 year = GetCurrentYear();
2309 }
2310
2311 if ( haveDay )
2312 {
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) )
2316 return false;
2317
2318 Set(day, mon, year);
2319
2320 if ( haveWDay )
2321 {
2322 // check that it is really the same
2323 if ( GetWeekDay() != wday )
2324 return false;
2325 }
2326 }
2327 else // haveWDay
2328 {
2329 *this = Today();
2330
2331 SetToWeekDayInSameWeek(wday);
2332 }
2333
2334 // return the pointer to the first unparsed char
2335 p += nPosCur;
2336 if ( nPosCur && wxStrchr(dateDelimiters, *(p - 1)) )
2337 {
2338 // if we couldn't parse the token after the delimiter, put back the
2339 // delimiter as well
2340 p--;
2341 }
2342
2343 *end = p;
2344
2345 return true;
2346 }
2347
2348 bool
2349 wxDateTime::ParseTime(const wxString& time, wxString::const_iterator *end)
2350 {
2351 wxCHECK_MSG( end, false, "end iterator pointer must be specified" );
2352
2353 // first try some extra things
2354 static const struct
2355 {
2356 const char *name;
2357 wxDateTime_t hour;
2358 } stdTimes[] =
2359 {
2360 { wxTRANSLATE("noon"), 12 },
2361 { wxTRANSLATE("midnight"), 00 },
2362 // anything else?
2363 };
2364
2365 for ( size_t n = 0; n < WXSIZEOF(stdTimes); n++ )
2366 {
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 )
2370 {
2371 // casts required by DigitalMars
2372 Set(stdTimes[n].hour, wxDateTime_t(0), wxDateTime_t(0));
2373
2374 if ( end )
2375 *end = p;
2376
2377 return true;
2378 }
2379 }
2380
2381 // try all time formats we may think about in the order from longest to
2382 // shortest
2383 static const char *timeFormats[] =
2384 {
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
2391
2392 // TODO: parse timezones
2393 };
2394
2395 for ( size_t nFmt = 0; nFmt < WXSIZEOF(timeFormats); nFmt++ )
2396 {
2397 if ( ParseFormat(time, timeFormats[nFmt], end) )
2398 return true;
2399 }
2400
2401 return false;
2402 }
2403
2404 // ----------------------------------------------------------------------------
2405 // Workdays and holidays support
2406 // ----------------------------------------------------------------------------
2407
2408 bool wxDateTime::IsWorkDay(Country WXUNUSED(country)) const
2409 {
2410 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
2411 }
2412
2413 // ============================================================================
2414 // wxDateSpan
2415 // ============================================================================
2416
2417 wxDateSpan WXDLLIMPEXP_BASE operator*(int n, const wxDateSpan& ds)
2418 {
2419 wxDateSpan ds1(ds);
2420 return ds1.Multiply(n);
2421 }
2422
2423 // ============================================================================
2424 // wxTimeSpan
2425 // ============================================================================
2426
2427 wxTimeSpan WXDLLIMPEXP_BASE operator*(int n, const wxTimeSpan& ts)
2428 {
2429 return wxTimeSpan(ts).Multiply(n);
2430 }
2431
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
2435 enum TimeSpanPart
2436 {
2437 Part_Week,
2438 Part_Day,
2439 Part_Hour,
2440 Part_Min,
2441 Part_Sec,
2442 Part_MSec
2443 };
2444
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
2447 //
2448 // Here are the ones which are supported (all of them are supported by strftime
2449 // as well):
2450 // %H hour in 24 hour format
2451 // %M minute (00 - 59)
2452 // %S second (00 - 59)
2453 // %% percent sign
2454 //
2455 // Also, for MFC CTimeSpan compatibility, we support
2456 // %D number of days
2457 //
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
2462 {
2463 // we deal with only positive time spans here and just add the sign in
2464 // front for the negative ones
2465 if ( IsNegative() )
2466 {
2467 wxString str(Negate().Format(format));
2468 return "-" + str;
2469 }
2470
2471 wxCHECK_MSG( !format.empty(), wxEmptyString,
2472 _T("NULL format in wxTimeSpan::Format") );
2473
2474 wxString str;
2475 str.Alloc(format.length());
2476
2477 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
2478 //
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"
2484 //
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
2489
2490 // we remember the most important unit found so far
2491 TimeSpanPart partBiggest = Part_MSec;
2492
2493 for ( wxString::const_iterator pch = format.begin(); pch != format.end(); ++pch )
2494 {
2495 wxChar ch = *pch;
2496
2497 if ( ch == _T('%') )
2498 {
2499 // the start of the format specification of the printf() below
2500 wxString fmtPrefix(_T('%'));
2501
2502 // the number
2503 long n;
2504
2505 // the number of digits for the format string, 0 if unused
2506 unsigned digits = 0;
2507
2508 ch = *++pch; // get the format spec char
2509 switch ( ch )
2510 {
2511 default:
2512 wxFAIL_MSG( _T("invalid format character") );
2513 // fall through
2514
2515 case _T('%'):
2516 str += ch;
2517
2518 // skip the part below switch
2519 continue;
2520
2521 case _T('D'):
2522 n = GetDays();
2523 if ( partBiggest < Part_Day )
2524 {
2525 n %= DAYS_PER_WEEK;
2526 }
2527 else
2528 {
2529 partBiggest = Part_Day;
2530 }
2531 break;
2532
2533 case _T('E'):
2534 partBiggest = Part_Week;
2535 n = GetWeeks();
2536 break;
2537
2538 case _T('H'):
2539 n = GetHours();
2540 if ( partBiggest < Part_Hour )
2541 {
2542 n %= HOURS_PER_DAY;
2543 }
2544 else
2545 {
2546 partBiggest = Part_Hour;
2547 }
2548
2549 digits = 2;
2550 break;
2551
2552 case _T('l'):
2553 n = GetMilliseconds().ToLong();
2554 if ( partBiggest < Part_MSec )
2555 {
2556 n %= 1000;
2557 }
2558 //else: no need to reset partBiggest to Part_MSec, it is
2559 // the least significant one anyhow
2560
2561 digits = 3;
2562 break;
2563
2564 case _T('M'):
2565 n = GetMinutes();
2566 if ( partBiggest < Part_Min )
2567 {
2568 n %= MIN_PER_HOUR;
2569 }
2570 else
2571 {
2572 partBiggest = Part_Min;
2573 }
2574
2575 digits = 2;
2576 break;
2577
2578 case _T('S'):
2579 n = GetSeconds().ToLong();
2580 if ( partBiggest < Part_Sec )
2581 {
2582 n %= SEC_PER_MIN;
2583 }
2584 else
2585 {
2586 partBiggest = Part_Sec;
2587 }
2588
2589 digits = 2;
2590 break;
2591 }
2592
2593 if ( digits )
2594 {
2595 fmtPrefix << _T("0") << digits;
2596 }
2597
2598 str += wxString::Format(fmtPrefix + _T("ld"), n);
2599 }
2600 else
2601 {
2602 // normal character, just copy
2603 str += ch;
2604 }
2605 }
2606
2607 return str;
2608 }
2609
2610 #endif // wxUSE_DATETIME