abandon attempts to make wxAnyStrPtr behave as bool: user-defined logical operators...
[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 unsigned long *number)
241 {
242 size_t n = 1;
243 wxString s;
244 while ( wxIsdigit(*p) )
245 {
246 s += *p++;
247
248 if ( len && ++n > len )
249 break;
250 }
251
252 return !s.empty() && s.ToULong(number);
253 }
254
255 // scans all alphabetic characters and returns the resulting string
256 wxString GetAlphaToken(wxString::const_iterator& p)
257 {
258 wxString s;
259 while ( wxIsalpha(*p) )
260 {
261 s += *p++;
262 }
263
264 return s;
265 }
266
267 // parses string starting at given iterator using the specified format and,
268 // optionally, a fall back format (and optionally another one... but it stops
269 // there, really)
270 //
271 // if unsuccessful, returns invalid wxDateTime without changing p; otherwise
272 // advance p to the end of the match and returns wxDateTime containing the
273 // results of the parsing
274 wxDateTime
275 ParseFormatAt(wxString::const_iterator& p,
276 const wxString::const_iterator& end,
277 const wxString& fmt,
278 // FIXME-VC6: using wxString() instead of wxEmptyString in the
279 // line below results in error C2062: type 'class
280 // wxString (__cdecl *)(void)' unexpected
281 const wxString& fmtAlt = wxEmptyString,
282 const wxString& fmtAlt2 = wxString())
283 {
284 const wxString str(p, end);
285 wxString::const_iterator endParse;
286 wxDateTime dt;
287 if ( dt.ParseFormat(str, fmt, &endParse) ||
288 (!fmtAlt.empty() && dt.ParseFormat(str, fmtAlt, &endParse)) ||
289 (!fmtAlt2.empty() && dt.ParseFormat(str, fmtAlt2, &endParse)) )
290 {
291 p += endParse - str.begin();
292 }
293 //else: all formats failed
294
295 return dt;
296 }
297
298 } // anonymous namespace
299
300 // ----------------------------------------------------------------------------
301 // wxDateTime to/from text representations
302 // ----------------------------------------------------------------------------
303
304 wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const
305 {
306 wxCHECK_MSG( !format.empty(), wxEmptyString,
307 _T("NULL format in wxDateTime::Format") );
308
309 // we have to use our own implementation if the date is out of range of
310 // strftime() or if we use non standard specificators
311 #ifdef HAVE_STRFTIME
312 time_t time = GetTicks();
313
314 if ( (time != (time_t)-1) && !wxStrstr(format, _T("%l")) )
315 {
316 // use strftime()
317 struct tm tmstruct;
318 struct tm *tm;
319 if ( tz.GetOffset() == -GetTimeZone() )
320 {
321 // we are working with local time
322 tm = wxLocaltime_r(&time, &tmstruct);
323
324 // should never happen
325 wxCHECK_MSG( tm, wxEmptyString, _T("wxLocaltime_r() failed") );
326 }
327 else
328 {
329 time += (int)tz.GetOffset();
330
331 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
332 int time2 = (int) time;
333 if ( time2 >= 0 )
334 #else
335 if ( time >= 0 )
336 #endif
337 {
338 tm = wxGmtime_r(&time, &tmstruct);
339
340 // should never happen
341 wxCHECK_MSG( tm, wxEmptyString, _T("wxGmtime_r() failed") );
342 }
343 else
344 {
345 tm = (struct tm *)NULL;
346 }
347 }
348
349 if ( tm )
350 {
351 return CallStrftime(format, tm);
352 }
353 }
354 //else: use generic code below
355 #endif // HAVE_STRFTIME
356
357 // we only parse ANSI C format specifications here, no POSIX 2
358 // complications, no GNU extensions but we do add support for a "%l" format
359 // specifier allowing to get the number of milliseconds
360 Tm tm = GetTm(tz);
361
362 // used for calls to strftime() when we only deal with time
363 struct tm tmTimeOnly;
364 tmTimeOnly.tm_hour = tm.hour;
365 tmTimeOnly.tm_min = tm.min;
366 tmTimeOnly.tm_sec = tm.sec;
367 tmTimeOnly.tm_wday = 0;
368 tmTimeOnly.tm_yday = 0;
369 tmTimeOnly.tm_mday = 1; // any date will do
370 tmTimeOnly.tm_mon = 0;
371 tmTimeOnly.tm_year = 76;
372 tmTimeOnly.tm_isdst = 0; // no DST, we adjust for tz ourselves
373
374 wxString tmp, res, fmt;
375 for ( wxString::const_iterator p = format.begin(); p != format.end(); ++p )
376 {
377 if ( *p != _T('%') )
378 {
379 // copy as is
380 res += *p;
381
382 continue;
383 }
384
385 // set the default format
386 switch ( (*++p).GetValue() )
387 {
388 case _T('Y'): // year has 4 digits
389 fmt = _T("%04d");
390 break;
391
392 case _T('j'): // day of year has 3 digits
393 case _T('l'): // milliseconds have 3 digits
394 fmt = _T("%03d");
395 break;
396
397 case _T('w'): // week day as number has only one
398 fmt = _T("%d");
399 break;
400
401 default:
402 // it's either another valid format specifier in which case
403 // the format is "%02d" (for all the rest) or we have the
404 // field width preceding the format in which case it will
405 // override the default format anyhow
406 fmt = _T("%02d");
407 }
408
409 bool restart = true;
410 while ( restart )
411 {
412 restart = false;
413
414 // start of the format specification
415 switch ( (*p).GetValue() )
416 {
417 case _T('a'): // a weekday name
418 case _T('A'):
419 // second parameter should be true for abbreviated names
420 res += GetWeekDayName(tm.GetWeekDay(),
421 *p == _T('a') ? Name_Abbr : Name_Full);
422 break;
423
424 case _T('b'): // a month name
425 case _T('B'):
426 res += GetMonthName(tm.mon,
427 *p == _T('b') ? Name_Abbr : Name_Full);
428 break;
429
430 case _T('c'): // locale default date and time representation
431 case _T('x'): // locale default date representation
432 #ifdef HAVE_STRFTIME
433 //
434 // the problem: there is no way to know what do these format
435 // specifications correspond to for the current locale.
436 //
437 // the solution: use a hack and still use strftime(): first
438 // find the YEAR which is a year in the strftime() range (1970
439 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
440 // of the real year. Then make a copy of the format and
441 // replace all occurrences of YEAR in it with some unique
442 // string not appearing anywhere else in it, then use
443 // strftime() to format the date in year YEAR and then replace
444 // YEAR back by the real year and the unique replacement
445 // string back with YEAR. Notice that "all occurrences of YEAR"
446 // means all occurrences of 4 digit as well as 2 digit form!
447 //
448 // the bugs: we assume that neither of %c nor %x contains any
449 // fields which may change between the YEAR and real year. For
450 // example, the week number (%U, %W) and the day number (%j)
451 // will change if one of these years is leap and the other one
452 // is not!
453 {
454 // find the YEAR: normally, for any year X, Jan 1 of the
455 // year X + 28 is the same weekday as Jan 1 of X (because
456 // the weekday advances by 1 for each normal X and by 2
457 // for each leap X, hence by 5 every 4 years or by 35
458 // which is 0 mod 7 every 28 years) but this rule breaks
459 // down if there are years between X and Y which are
460 // divisible by 4 but not leap (i.e. divisible by 100 but
461 // not 400), hence the correction.
462
463 int yearReal = GetYear(tz);
464 int mod28 = yearReal % 28;
465
466 // be careful to not go too far - we risk to leave the
467 // supported range
468 int year;
469 if ( mod28 < 10 )
470 {
471 year = 1988 + mod28; // 1988 == 0 (mod 28)
472 }
473 else
474 {
475 year = 1970 + mod28 - 10; // 1970 == 10 (mod 28)
476 }
477
478 int nCentury = year / 100,
479 nCenturyReal = yearReal / 100;
480
481 // need to adjust for the years divisble by 400 which are
482 // not leap but are counted like leap ones if we just take
483 // the number of centuries in between for nLostWeekDays
484 int nLostWeekDays = (nCentury - nCenturyReal) -
485 (nCentury / 4 - nCenturyReal / 4);
486
487 // we have to gain back the "lost" weekdays: note that the
488 // effect of this loop is to not do anything to
489 // nLostWeekDays (which we won't use any more), but to
490 // (indirectly) set the year correctly
491 while ( (nLostWeekDays % 7) != 0 )
492 {
493 nLostWeekDays += year++ % 4 ? 1 : 2;
494 }
495
496 // finally move the year below 2000 so that the 2-digit
497 // year number can never match the month or day of the
498 // month when we do the replacements below
499 if ( year >= 2000 )
500 year -= 28;
501
502 wxASSERT_MSG( year >= 1970 && year < 2000,
503 _T("logic error in wxDateTime::Format") );
504
505
506 // use strftime() to format the same date but in supported
507 // year
508 //
509 // NB: we assume that strftime() doesn't check for the
510 // date validity and will happily format the date
511 // corresponding to Feb 29 of a non leap year (which
512 // may happen if yearReal was leap and year is not)
513 struct tm tmAdjusted;
514 InitTm(tmAdjusted);
515 tmAdjusted.tm_hour = tm.hour;
516 tmAdjusted.tm_min = tm.min;
517 tmAdjusted.tm_sec = tm.sec;
518 tmAdjusted.tm_wday = tm.GetWeekDay();
519 tmAdjusted.tm_yday = GetDayOfYear();
520 tmAdjusted.tm_mday = tm.mday;
521 tmAdjusted.tm_mon = tm.mon;
522 tmAdjusted.tm_year = year - 1900;
523 tmAdjusted.tm_isdst = 0; // no DST, already adjusted
524 wxString str = CallStrftime(*p == _T('c') ? _T("%c")
525 : _T("%x"),
526 &tmAdjusted);
527
528 // now replace the replacement year with the real year:
529 // notice that we have to replace the 4 digit year with
530 // a unique string not appearing in strftime() output
531 // first to prevent the 2 digit year from matching any
532 // substring of the 4 digit year (but any day, month,
533 // hours or minutes components should be safe because
534 // they are never in 70-99 range)
535 wxString replacement("|");
536 while ( str.find(replacement) != wxString::npos )
537 replacement += '|';
538
539 str.Replace(wxString::Format("%d", year),
540 replacement);
541 str.Replace(wxString::Format("%d", year % 100),
542 wxString::Format("%d", yearReal % 100));
543 str.Replace(replacement,
544 wxString::Format("%d", yearReal));
545
546 res += str;
547 }
548 #else // !HAVE_STRFTIME
549 // Use "%m/%d/%y %H:%M:%S" format instead
550 res += wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
551 tm.mon+1,tm.mday, tm.year, tm.hour, tm.min, tm.sec);
552 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
553 break;
554
555 case _T('d'): // day of a month (01-31)
556 res += wxString::Format(fmt, tm.mday);
557 break;
558
559 case _T('H'): // hour in 24h format (00-23)
560 res += wxString::Format(fmt, tm.hour);
561 break;
562
563 case _T('I'): // hour in 12h format (01-12)
564 {
565 // 24h -> 12h, 0h -> 12h too
566 int hour12 = tm.hour > 12 ? tm.hour - 12
567 : tm.hour ? tm.hour : 12;
568 res += wxString::Format(fmt, hour12);
569 }
570 break;
571
572 case _T('j'): // day of the year
573 res += wxString::Format(fmt, GetDayOfYear(tz));
574 break;
575
576 case _T('l'): // milliseconds (NOT STANDARD)
577 res += wxString::Format(fmt, GetMillisecond(tz));
578 break;
579
580 case _T('m'): // month as a number (01-12)
581 res += wxString::Format(fmt, tm.mon + 1);
582 break;
583
584 case _T('M'): // minute as a decimal number (00-59)
585 res += wxString::Format(fmt, tm.min);
586 break;
587
588 case _T('p'): // AM or PM string
589 #ifdef HAVE_STRFTIME
590 res += CallStrftime(_T("%p"), &tmTimeOnly);
591 #else // !HAVE_STRFTIME
592 res += (tmTimeOnly.tm_hour > 12) ? wxT("pm") : wxT("am");
593 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
594 break;
595
596 case _T('S'): // second as a decimal number (00-61)
597 res += wxString::Format(fmt, tm.sec);
598 break;
599
600 case _T('U'): // week number in the year (Sunday 1st week day)
601 res += wxString::Format(fmt, GetWeekOfYear(Sunday_First, tz));
602 break;
603
604 case _T('W'): // week number in the year (Monday 1st week day)
605 res += wxString::Format(fmt, GetWeekOfYear(Monday_First, tz));
606 break;
607
608 case _T('w'): // weekday as a number (0-6), Sunday = 0
609 res += wxString::Format(fmt, tm.GetWeekDay());
610 break;
611
612 // case _T('x'): -- handled with "%c"
613
614 case _T('X'): // locale default time representation
615 // just use strftime() to format the time for us
616 #ifdef HAVE_STRFTIME
617 res += CallStrftime(_T("%X"), &tmTimeOnly);
618 #else // !HAVE_STRFTIME
619 res += wxString::Format(wxT("%02d:%02d:%02d"),tm.hour, tm.min, tm.sec);
620 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
621 break;
622
623 case _T('y'): // year without century (00-99)
624 res += wxString::Format(fmt, tm.year % 100);
625 break;
626
627 case _T('Y'): // year with century
628 res += wxString::Format(fmt, tm.year);
629 break;
630
631 case _T('Z'): // timezone name
632 #ifdef HAVE_STRFTIME
633 res += CallStrftime(_T("%Z"), &tmTimeOnly);
634 #endif
635 break;
636
637 default:
638 // is it the format width?
639 fmt.Empty();
640 while ( *p == _T('-') || *p == _T('+') ||
641 *p == _T(' ') || wxIsdigit(*p) )
642 {
643 fmt += *p;
644 }
645
646 if ( !fmt.empty() )
647 {
648 // we've only got the flags and width so far in fmt
649 fmt.Prepend(_T('%'));
650 fmt.Append(_T('d'));
651
652 restart = true;
653
654 break;
655 }
656
657 // no, it wasn't the width
658 wxFAIL_MSG(_T("unknown format specificator"));
659
660 // fall through and just copy it nevertheless
661
662 case _T('%'): // a percent sign
663 res += *p;
664 break;
665
666 case 0: // the end of string
667 wxFAIL_MSG(_T("missing format at the end of string"));
668
669 // just put the '%' which was the last char in format
670 res += _T('%');
671 break;
672 }
673 }
674 }
675
676 return res;
677 }
678
679 // this function parses a string in (strict) RFC 822 format: see the section 5
680 // of the RFC for the detailed description, but briefly it's something of the
681 // form "Sat, 18 Dec 1999 00:48:30 +0100"
682 //
683 // this function is "strict" by design - it must reject anything except true
684 // RFC822 time specs.
685 bool
686 wxDateTime::ParseRfc822Date(const wxString& date, wxString::const_iterator *end)
687 {
688 wxString::const_iterator p = date.begin();
689
690 // 1. week day
691 static const int WDAY_LEN = 3;
692 const wxString::const_iterator endWday = p + WDAY_LEN;
693 const wxString wday(p, endWday);
694 if ( GetWeekDayFromName(wday, Name_Abbr, DateLang_English) == Inv_WeekDay )
695 return false;
696 //else: ignore week day for now, we could also check that it really
697 // corresponds to the specified date
698
699 p = endWday;
700
701 // 2. separating comma
702 if ( *p++ != ',' || *p++ != ' ' )
703 return false;
704
705 // 3. day number
706 if ( !wxIsdigit(*p) )
707 return false;
708
709 wxDateTime_t day = (wxDateTime_t)(*p++ - '0');
710 if ( wxIsdigit(*p) )
711 {
712 day *= 10;
713 day = (wxDateTime_t)(day + (*p++ - '0'));
714 }
715
716 if ( *p++ != ' ' )
717 return false;
718
719 // 4. month name
720 static const int MONTH_LEN = 3;
721 const wxString::const_iterator endMonth = p + MONTH_LEN;
722 const wxString monName(p, endMonth);
723 Month mon = GetMonthFromName(monName, Name_Abbr, DateLang_English);
724 if ( mon == Inv_Month )
725 return false;
726
727 p = endMonth;
728
729 if ( *p++ != ' ' )
730 return false;
731
732 // 5. year
733 if ( !wxIsdigit(*p) )
734 return false;
735
736 int year = *p++ - '0';
737 if ( !wxIsdigit(*p) ) // should have at least 2 digits in the year
738 return false;
739
740 year *= 10;
741 year += *p++ - '0';
742
743 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
744 if ( wxIsdigit(*p) )
745 {
746 year *= 10;
747 year += *p++ - '0';
748
749 if ( !wxIsdigit(*p) )
750 {
751 // no 3 digit years please
752 return false;
753 }
754
755 year *= 10;
756 year += *p++ - '0';
757 }
758
759 if ( *p++ != ' ' )
760 return false;
761
762 // 6. time in hh:mm:ss format with seconds being optional
763 if ( !wxIsdigit(*p) )
764 return false;
765
766 wxDateTime_t hour = (wxDateTime_t)(*p++ - '0');
767
768 if ( !wxIsdigit(*p) )
769 return false;
770
771 hour *= 10;
772 hour = (wxDateTime_t)(hour + (*p++ - '0'));
773
774 if ( *p++ != ':' )
775 return false;
776
777 if ( !wxIsdigit(*p) )
778 return false;
779
780 wxDateTime_t min = (wxDateTime_t)(*p++ - '0');
781
782 if ( !wxIsdigit(*p) )
783 return false;
784
785 min *= 10;
786 min += (wxDateTime_t)(*p++ - '0');
787
788 wxDateTime_t sec = 0;
789 if ( *p == ':' )
790 {
791 p++;
792 if ( !wxIsdigit(*p) )
793 return false;
794
795 sec = (wxDateTime_t)(*p++ - '0');
796
797 if ( !wxIsdigit(*p) )
798 return false;
799
800 sec *= 10;
801 sec += (wxDateTime_t)(*p++ - '0');
802 }
803
804 if ( *p++ != ' ' )
805 return false;
806
807 // 7. now the interesting part: the timezone
808 int offset wxDUMMY_INITIALIZE(0);
809 if ( *p == '-' || *p == '+' )
810 {
811 // the explicit offset given: it has the form of hhmm
812 bool plus = *p++ == '+';
813
814 if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) )
815 return false;
816
817
818 // hours
819 offset = MIN_PER_HOUR*(10*(*p - '0') + (*(p + 1) - '0'));
820
821 p += 2;
822
823 if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) )
824 return false;
825
826 // minutes
827 offset += 10*(*p - '0') + (*(p + 1) - '0');
828
829 if ( !plus )
830 offset = -offset;
831
832 p += 2;
833 }
834 else // not numeric
835 {
836 // the symbolic timezone given: may be either military timezone or one
837 // of standard abbreviations
838 if ( !*(p + 1) )
839 {
840 // military: Z = UTC, J unused, A = -1, ..., Y = +12
841 static const int offsets[26] =
842 {
843 //A B C D E F G H I J K L M
844 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
845 //N O P R Q S T U V W Z Y Z
846 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
847 };
848
849 if ( *p < _T('A') || *p > _T('Z') || *p == _T('J') )
850 return false;
851
852 offset = offsets[*p++ - 'A'];
853 }
854 else
855 {
856 // abbreviation
857 const wxString tz(p, date.end());
858 if ( tz == _T("UT") || tz == _T("UTC") || tz == _T("GMT") )
859 offset = 0;
860 else if ( tz == _T("AST") )
861 offset = AST - GMT0;
862 else if ( tz == _T("ADT") )
863 offset = ADT - GMT0;
864 else if ( tz == _T("EST") )
865 offset = EST - GMT0;
866 else if ( tz == _T("EDT") )
867 offset = EDT - GMT0;
868 else if ( tz == _T("CST") )
869 offset = CST - GMT0;
870 else if ( tz == _T("CDT") )
871 offset = CDT - GMT0;
872 else if ( tz == _T("MST") )
873 offset = MST - GMT0;
874 else if ( tz == _T("MDT") )
875 offset = MDT - GMT0;
876 else if ( tz == _T("PST") )
877 offset = PST - GMT0;
878 else if ( tz == _T("PDT") )
879 offset = PDT - GMT0;
880 else
881 return false;
882
883 p += tz.length();
884 }
885
886 // make it minutes
887 offset *= MIN_PER_HOUR;
888 }
889
890
891 // the spec was correct, construct the date from the values we found
892 Set(day, mon, year, hour, min, sec);
893 MakeFromTimezone(TimeZone::Make(offset*SEC_PER_MIN));
894
895 if ( end )
896 *end = p;
897
898 return true;
899 }
900
901 #ifdef __WINDOWS__
902
903 // returns the string containing strftime() format used for short dates in the
904 // current locale or an empty string
905 static wxString GetLocaleDateFormat()
906 {
907 wxString fmtWX;
908
909 // there is no setlocale() under Windows CE, so just always query the
910 // system there
911 #ifndef __WXWINCE__
912 if ( strcmp(setlocale(LC_ALL, NULL), "C") != 0 )
913 #endif
914 {
915 // The locale was programatically set to non-C. We assume that this was
916 // done using wxLocale, in which case thread's current locale is also
917 // set to correct LCID value and we can use GetLocaleInfo to determine
918 // the correct formatting string:
919 #ifdef __WXWINCE__
920 LCID lcid = LOCALE_USER_DEFAULT;
921 #else
922 LCID lcid = GetThreadLocale();
923 #endif
924 // according to MSDN 80 chars is max allowed for short date format
925 wxChar fmt[81];
926 if ( ::GetLocaleInfo(lcid, LOCALE_SSHORTDATE, fmt, WXSIZEOF(fmt)) )
927 {
928 wxChar chLast = _T('\0');
929 size_t lastCount = 0;
930 for ( const wxChar *p = fmt; /* NUL handled inside */; p++ )
931 {
932 if ( *p == chLast )
933 {
934 lastCount++;
935 continue;
936 }
937
938 switch ( *p )
939 {
940 // these characters come in groups, start counting them
941 case _T('d'):
942 case _T('M'):
943 case _T('y'):
944 case _T('g'):
945 chLast = *p;
946 lastCount = 1;
947 break;
948
949 default:
950 // first deal with any special characters we have had
951 if ( lastCount )
952 {
953 switch ( chLast )
954 {
955 case _T('d'):
956 switch ( lastCount )
957 {
958 case 1: // d
959 case 2: // dd
960 // these two are the same as we
961 // don't distinguish between 1 and
962 // 2 digits for days
963 fmtWX += _T("%d");
964 break;
965
966 case 3: // ddd
967 fmtWX += _T("%a");
968 break;
969
970 case 4: // dddd
971 fmtWX += _T("%A");
972 break;
973
974 default:
975 wxFAIL_MSG( _T("too many 'd's") );
976 }
977 break;
978
979 case _T('M'):
980 switch ( lastCount )
981 {
982 case 1: // M
983 case 2: // MM
984 // as for 'd' and 'dd' above
985 fmtWX += _T("%m");
986 break;
987
988 case 3:
989 fmtWX += _T("%b");
990 break;
991
992 case 4:
993 fmtWX += _T("%B");
994 break;
995
996 default:
997 wxFAIL_MSG( _T("too many 'M's") );
998 }
999 break;
1000
1001 case _T('y'):
1002 switch ( lastCount )
1003 {
1004 case 1: // y
1005 case 2: // yy
1006 fmtWX += _T("%y");
1007 break;
1008
1009 case 4: // yyyy
1010 fmtWX += _T("%Y");
1011 break;
1012
1013 default:
1014 wxFAIL_MSG( _T("wrong number of 'y's") );
1015 }
1016 break;
1017
1018 case _T('g'):
1019 // strftime() doesn't have era string,
1020 // ignore this format
1021 wxASSERT_MSG( lastCount <= 2,
1022 _T("too many 'g's") );
1023 break;
1024
1025 default:
1026 wxFAIL_MSG( _T("unreachable") );
1027 }
1028
1029 chLast = _T('\0');
1030 lastCount = 0;
1031 }
1032
1033 // not a special character so must be just a separator,
1034 // treat as is
1035 if ( *p != _T('\0') )
1036 {
1037 if ( *p == _T('%') )
1038 {
1039 // this one needs to be escaped
1040 fmtWX += _T('%');
1041 }
1042
1043 fmtWX += *p;
1044 }
1045 }
1046
1047 if ( *p == _T('\0') )
1048 break;
1049 }
1050 }
1051 //else: GetLocaleInfo() failed, leave fmtDate value unchanged and
1052 // try our luck with the default formats
1053 }
1054 //else: default C locale, default formats should work
1055
1056 return fmtWX;
1057 }
1058
1059 #endif // __WINDOWS__
1060
1061 bool
1062 wxDateTime::ParseFormat(const wxString& date,
1063 const wxString& format,
1064 const wxDateTime& dateDef,
1065 wxString::const_iterator *end)
1066 {
1067 wxCHECK_MSG( !format.empty(), false, "format can't be empty" );
1068 wxCHECK_MSG( end, false, "end iterator pointer must be specified" );
1069
1070 wxString str;
1071 unsigned long num;
1072
1073 // what fields have we found?
1074 bool haveWDay = false,
1075 haveYDay = false,
1076 haveDay = false,
1077 haveMon = false,
1078 haveYear = false,
1079 haveHour = false,
1080 haveMin = false,
1081 haveSec = false,
1082 haveMsec = false;
1083
1084 bool hourIsIn12hFormat = false, // or in 24h one?
1085 isPM = false; // AM by default
1086
1087 // and the value of the items we have (init them to get rid of warnings)
1088 wxDateTime_t msec = 0,
1089 sec = 0,
1090 min = 0,
1091 hour = 0;
1092 WeekDay wday = Inv_WeekDay;
1093 wxDateTime_t yday = 0,
1094 mday = 0;
1095 wxDateTime::Month mon = Inv_Month;
1096 int year = 0;
1097
1098 wxString::const_iterator input = date.begin();
1099 for ( wxString::const_iterator fmt = format.begin(); fmt != format.end(); ++fmt )
1100 {
1101 if ( *fmt != _T('%') )
1102 {
1103 if ( wxIsspace(*fmt) )
1104 {
1105 // a white space in the format string matches 0 or more white
1106 // spaces in the input
1107 while ( wxIsspace(*input) )
1108 {
1109 input++;
1110 }
1111 }
1112 else // !space
1113 {
1114 // any other character (not whitespace, not '%') must be
1115 // matched by itself in the input
1116 if ( *input++ != *fmt )
1117 {
1118 // no match
1119 return false;
1120 }
1121 }
1122
1123 // done with this format char
1124 continue;
1125 }
1126
1127 // start of a format specification
1128
1129 // parse the optional width
1130 size_t width = 0;
1131 while ( wxIsdigit(*++fmt) )
1132 {
1133 width *= 10;
1134 width += *fmt - '0';
1135 }
1136
1137 // the default widths for the various fields
1138 if ( !width )
1139 {
1140 switch ( (*fmt).GetValue() )
1141 {
1142 case _T('Y'): // year has 4 digits
1143 width = 4;
1144 break;
1145
1146 case _T('j'): // day of year has 3 digits
1147 case _T('l'): // milliseconds have 3 digits
1148 width = 3;
1149 break;
1150
1151 case _T('w'): // week day as number has only one
1152 width = 1;
1153 break;
1154
1155 default:
1156 // default for all other fields
1157 width = 2;
1158 }
1159 }
1160
1161 // then the format itself
1162 switch ( (*fmt).GetValue() )
1163 {
1164 case _T('a'): // a weekday name
1165 case _T('A'):
1166 {
1167 wday = GetWeekDayFromName
1168 (
1169 GetAlphaToken(input),
1170 *fmt == 'a' ? Name_Abbr : Name_Full,
1171 DateLang_Local
1172 );
1173 if ( wday == Inv_WeekDay )
1174 {
1175 // no match
1176 return false;
1177 }
1178 }
1179 haveWDay = true;
1180 break;
1181
1182 case _T('b'): // a month name
1183 case _T('B'):
1184 {
1185 mon = GetMonthFromName
1186 (
1187 GetAlphaToken(input),
1188 *fmt == 'b' ? Name_Abbr : Name_Full,
1189 DateLang_Local
1190 );
1191 if ( mon == Inv_Month )
1192 {
1193 // no match
1194 return false;
1195 }
1196 }
1197 haveMon = true;
1198 break;
1199
1200 case _T('c'): // locale default date and time representation
1201 {
1202 #ifdef HAVE_STRPTIME
1203 struct tm tm;
1204
1205 // try using strptime() -- it may fail even if the input is
1206 // correct but the date is out of range, so we will fall back
1207 // to our generic code anyhow
1208 if ( CallStrptime(date, input, "%c", &tm) )
1209 {
1210 hour = tm.tm_hour;
1211 min = tm.tm_min;
1212 sec = tm.tm_sec;
1213
1214 year = 1900 + tm.tm_year;
1215 mon = (Month)tm.tm_mon;
1216 mday = tm.tm_mday;
1217 }
1218 else // strptime() failed; try generic heuristic code
1219 #endif // HAVE_STRPTIME
1220 {
1221
1222 // try the format which corresponds to ctime() output
1223 // first, then the generic date/time formats
1224 const wxDateTime dt = ParseFormatAt
1225 (
1226 input,
1227 date.end(),
1228 wxS("%a %b %d %H:%M:%S %Y"),
1229 wxS("%x %X"),
1230 wxS("%X %x")
1231 );
1232 if ( !dt.IsValid() )
1233 return false;
1234
1235 Tm tm = dt.GetTm();
1236
1237 hour = tm.hour;
1238 min = tm.min;
1239 sec = tm.sec;
1240
1241 year = tm.year;
1242 mon = tm.mon;
1243 mday = tm.mday;
1244 }
1245
1246 haveDay = haveMon = haveYear =
1247 haveHour = haveMin = haveSec = true;
1248 }
1249 break;
1250
1251 case _T('d'): // day of a month (01-31)
1252 if ( !GetNumericToken(width, input, &num) ||
1253 (num > 31) || (num < 1) )
1254 {
1255 // no match
1256 return false;
1257 }
1258
1259 // we can't check whether the day range is correct yet, will
1260 // do it later - assume ok for now
1261 haveDay = true;
1262 mday = (wxDateTime_t)num;
1263 break;
1264
1265 case _T('H'): // hour in 24h format (00-23)
1266 if ( !GetNumericToken(width, input, &num) || (num > 23) )
1267 {
1268 // no match
1269 return false;
1270 }
1271
1272 haveHour = true;
1273 hour = (wxDateTime_t)num;
1274 break;
1275
1276 case _T('I'): // hour in 12h format (01-12)
1277 if ( !GetNumericToken(width, input, &num) || !num || (num > 12) )
1278 {
1279 // no match
1280 return false;
1281 }
1282
1283 haveHour = true;
1284 hourIsIn12hFormat = true;
1285 hour = (wxDateTime_t)(num % 12); // 12 should be 0
1286 break;
1287
1288 case _T('j'): // day of the year
1289 if ( !GetNumericToken(width, input, &num) || !num || (num > 366) )
1290 {
1291 // no match
1292 return false;
1293 }
1294
1295 haveYDay = true;
1296 yday = (wxDateTime_t)num;
1297 break;
1298
1299 case _T('l'): // milliseconds (0-999)
1300 if ( !GetNumericToken(width, input, &num) )
1301 return false;
1302
1303 haveMsec = true;
1304 msec = (wxDateTime_t)num;
1305 break;
1306
1307 case _T('m'): // month as a number (01-12)
1308 if ( !GetNumericToken(width, input, &num) || !num || (num > 12) )
1309 {
1310 // no match
1311 return false;
1312 }
1313
1314 haveMon = true;
1315 mon = (Month)(num - 1);
1316 break;
1317
1318 case _T('M'): // minute as a decimal number (00-59)
1319 if ( !GetNumericToken(width, input, &num) || (num > 59) )
1320 {
1321 // no match
1322 return false;
1323 }
1324
1325 haveMin = true;
1326 min = (wxDateTime_t)num;
1327 break;
1328
1329 case _T('p'): // AM or PM string
1330 {
1331 wxString am, pm, token = GetAlphaToken(input);
1332
1333 // some locales have empty AM/PM tokens and thus when formatting
1334 // dates with the %p specifier nothing is generated; when trying to
1335 // parse them back, we get an empty token here... but that's not
1336 // an error.
1337 if (token.empty())
1338 break;
1339
1340 GetAmPmStrings(&am, &pm);
1341 if (am.empty() && pm.empty())
1342 return false; // no am/pm strings defined
1343 if ( token.CmpNoCase(pm) == 0 )
1344 {
1345 isPM = true;
1346 }
1347 else if ( token.CmpNoCase(am) != 0 )
1348 {
1349 // no match
1350 return false;
1351 }
1352 }
1353 break;
1354
1355 case _T('r'): // time as %I:%M:%S %p
1356 {
1357 wxDateTime dt;
1358 if ( !dt.ParseFormat(wxString(input, date.end()),
1359 wxS("%I:%M:%S %p"), &input) )
1360 return false;
1361
1362 haveHour = haveMin = haveSec = true;
1363
1364 Tm tm = dt.GetTm();
1365 hour = tm.hour;
1366 min = tm.min;
1367 sec = tm.sec;
1368 }
1369 break;
1370
1371 case _T('R'): // time as %H:%M
1372 {
1373 const wxDateTime
1374 dt = ParseFormatAt(input, date.end(), wxS("%H:%M"));
1375 if ( !dt.IsValid() )
1376 return false;
1377
1378 haveHour =
1379 haveMin = true;
1380
1381 Tm tm = dt.GetTm();
1382 hour = tm.hour;
1383 min = tm.min;
1384 }
1385 break;
1386
1387 case _T('S'): // second as a decimal number (00-61)
1388 if ( !GetNumericToken(width, input, &num) || (num > 61) )
1389 {
1390 // no match
1391 return false;
1392 }
1393
1394 haveSec = true;
1395 sec = (wxDateTime_t)num;
1396 break;
1397
1398 case _T('T'): // time as %H:%M:%S
1399 {
1400 const wxDateTime
1401 dt = ParseFormatAt(input, date.end(), wxS("%H:%M:%S"));
1402 if ( !dt.IsValid() )
1403 return false;
1404
1405 haveHour =
1406 haveMin =
1407 haveSec = true;
1408
1409 Tm tm = dt.GetTm();
1410 hour = tm.hour;
1411 min = tm.min;
1412 sec = tm.sec;
1413 }
1414 break;
1415
1416 case _T('w'): // weekday as a number (0-6), Sunday = 0
1417 if ( !GetNumericToken(width, input, &num) || (wday > 6) )
1418 {
1419 // no match
1420 return false;
1421 }
1422
1423 haveWDay = true;
1424 wday = (WeekDay)num;
1425 break;
1426
1427 case _T('x'): // locale default date representation
1428 #ifdef HAVE_STRPTIME
1429 // try using strptime() -- it may fail even if the input is
1430 // correct but the date is out of range, so we will fall back
1431 // to our generic code anyhow
1432 {
1433 struct tm tm;
1434
1435 if ( CallStrptime(date, input, "%x", &tm) )
1436 {
1437 haveDay = haveMon = haveYear = true;
1438
1439 year = 1900 + tm.tm_year;
1440 mon = (Month)tm.tm_mon;
1441 mday = tm.tm_mday;
1442
1443 break;
1444 }
1445 }
1446 #endif // HAVE_STRPTIME
1447
1448 {
1449 wxString fmtDate,
1450 fmtDateAlt;
1451
1452 #ifdef __WINDOWS__
1453 // The above doesn't work for all locales, try to query
1454 // Windows for the right way of formatting the date:
1455 fmtDate = GetLocaleDateFormat();
1456 if ( fmtDate.empty() )
1457 #endif // __WINDOWS__
1458 {
1459 if ( IsWestEuropeanCountry(GetCountry()) ||
1460 GetCountry() == Russia )
1461 {
1462 fmtDate = _T("%d/%m/%y");
1463 fmtDateAlt = _T("%m/%d/%y");
1464 }
1465 else // assume USA
1466 {
1467 fmtDate = _T("%m/%d/%y");
1468 fmtDateAlt = _T("%d/%m/%y");
1469 }
1470 }
1471
1472 const wxDateTime
1473 dt = ParseFormatAt(input, date.end(),
1474 fmtDate, fmtDateAlt);
1475 if ( !dt.IsValid() )
1476 return false;
1477
1478 Tm tm = dt.GetTm();
1479
1480 haveDay =
1481 haveMon =
1482 haveYear = true;
1483
1484 year = tm.year;
1485 mon = tm.mon;
1486 mday = tm.mday;
1487 }
1488
1489 break;
1490
1491 case _T('X'): // locale default time representation
1492 #ifdef HAVE_STRPTIME
1493 {
1494 // use strptime() to do it for us (FIXME !Unicode friendly)
1495 struct tm tm;
1496 if ( !CallStrptime(date, input, "%X", &tm) )
1497 return false;
1498
1499 haveHour = haveMin = haveSec = true;
1500
1501 hour = tm.tm_hour;
1502 min = tm.tm_min;
1503 sec = tm.tm_sec;
1504 }
1505 #else // !HAVE_STRPTIME
1506 // TODO under Win32 we can query the LOCALE_ITIME system
1507 // setting which says whether the default time format is
1508 // 24 or 12 hour
1509 {
1510 // try to parse what follows as "%H:%M:%S" and, if this
1511 // fails, as "%I:%M:%S %p" - this should catch the most
1512 // common cases
1513 const wxDateTime
1514 dt = ParseFormatAt(input, date.end(), "%T", "%r");
1515 if ( !dt.IsValid() )
1516 return false;
1517
1518 haveHour =
1519 haveMin =
1520 haveSec = true;
1521
1522 Tm tm = dt.GetTm();
1523 hour = tm.hour;
1524 min = tm.min;
1525 sec = tm.sec;
1526 }
1527 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
1528 break;
1529
1530 case _T('y'): // year without century (00-99)
1531 if ( !GetNumericToken(width, input, &num) || (num > 99) )
1532 {
1533 // no match
1534 return false;
1535 }
1536
1537 haveYear = true;
1538
1539 // TODO should have an option for roll over date instead of
1540 // hard coding it here
1541 year = (num > 30 ? 1900 : 2000) + (wxDateTime_t)num;
1542 break;
1543
1544 case _T('Y'): // year with century
1545 if ( !GetNumericToken(width, input, &num) )
1546 {
1547 // no match
1548 return false;
1549 }
1550
1551 haveYear = true;
1552 year = (wxDateTime_t)num;
1553 break;
1554
1555 case _T('Z'): // timezone name
1556 wxFAIL_MSG(_T("TODO"));
1557 break;
1558
1559 case _T('%'): // a percent sign
1560 if ( *input++ != _T('%') )
1561 {
1562 // no match
1563 return false;
1564 }
1565 break;
1566
1567 case 0: // the end of string
1568 wxFAIL_MSG(_T("unexpected format end"));
1569
1570 // fall through
1571
1572 default: // not a known format spec
1573 return false;
1574 }
1575 }
1576
1577 // format matched, try to construct a date from what we have now
1578 Tm tmDef;
1579 if ( dateDef.IsValid() )
1580 {
1581 // take this date as default
1582 tmDef = dateDef.GetTm();
1583 }
1584 else if ( IsValid() )
1585 {
1586 // if this date is valid, don't change it
1587 tmDef = GetTm();
1588 }
1589 else
1590 {
1591 // no default and this date is invalid - fall back to Today()
1592 tmDef = Today().GetTm();
1593 }
1594
1595 Tm tm = tmDef;
1596
1597 // set the date
1598 if ( haveMon )
1599 {
1600 tm.mon = mon;
1601 }
1602
1603 if ( haveYear )
1604 {
1605 tm.year = year;
1606 }
1607
1608 // TODO we don't check here that the values are consistent, if both year
1609 // day and month/day were found, we just ignore the year day and we
1610 // also always ignore the week day
1611 if ( haveDay )
1612 {
1613 if ( mday > GetNumberOfDays(tm.mon, tm.year) )
1614 return false;
1615
1616 tm.mday = mday;
1617 }
1618 else if ( haveYDay )
1619 {
1620 if ( yday > GetNumberOfDays(tm.year) )
1621 return false;
1622
1623 Tm tm2 = wxDateTime(1, Jan, tm.year).SetToYearDay(yday).GetTm();
1624
1625 tm.mon = tm2.mon;
1626 tm.mday = tm2.mday;
1627 }
1628
1629 // deal with AM/PM
1630 if ( haveHour && hourIsIn12hFormat && isPM )
1631 {
1632 // translate to 24hour format
1633 hour += 12;
1634 }
1635 //else: either already in 24h format or no translation needed
1636
1637 // set the time
1638 if ( haveHour )
1639 {
1640 tm.hour = hour;
1641 }
1642
1643 if ( haveMin )
1644 {
1645 tm.min = min;
1646 }
1647
1648 if ( haveSec )
1649 {
1650 tm.sec = sec;
1651 }
1652
1653 if ( haveMsec )
1654 tm.msec = msec;
1655
1656 Set(tm);
1657
1658 // finally check that the week day is consistent -- if we had it
1659 if ( haveWDay && GetWeekDay() != wday )
1660 return false;
1661
1662 *end = input;
1663
1664 return true;
1665 }
1666
1667 bool
1668 wxDateTime::ParseDateTime(const wxString& date, wxString::const_iterator *end)
1669 {
1670 wxCHECK_MSG( end, false, "end iterator pointer must be specified" );
1671
1672 // Set to current day and hour, so strings like '14:00' becomes today at
1673 // 14, not some other random date
1674 wxDateTime dtDate = wxDateTime::Today();
1675 wxDateTime dtTime = wxDateTime::Today();
1676
1677 wxString::const_iterator
1678 endTime,
1679 endDate,
1680 endBoth;
1681
1682 // If we got a date in the beginning, see if there is a time specified
1683 // after the date
1684 if ( dtDate.ParseDate(date, &endDate) )
1685 {
1686 // Skip spaces, as the ParseTime() function fails on spaces
1687 while ( endDate != date.end() && wxIsspace(*endDate) )
1688 ++endDate;
1689
1690 const wxString timestr(endDate, date.end());
1691 if ( !dtTime.ParseTime(timestr, &endTime) )
1692 return false;
1693
1694 endBoth = endDate + (endTime - timestr.begin());
1695 }
1696 else // no date in the beginning
1697 {
1698 // check if we have a time followed by a date
1699 if ( !dtTime.ParseTime(date, &endTime) )
1700 return false;
1701
1702 while ( endTime != date.end() && wxIsspace(*endTime) )
1703 ++endTime;
1704
1705 const wxString datestr(endTime, date.end());
1706 if ( !dtDate.ParseDate(datestr, &endDate) )
1707 return false;
1708
1709 endBoth = endTime + (endDate - datestr.begin());
1710 }
1711
1712 Set(dtDate.GetDay(), dtDate.GetMonth(), dtDate.GetYear(),
1713 dtTime.GetHour(), dtTime.GetMinute(), dtTime.GetSecond(),
1714 dtTime.GetMillisecond());
1715
1716 *end = endBoth;
1717
1718 return true;
1719 }
1720
1721 bool
1722 wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
1723 {
1724 wxCHECK_MSG( end, false, "end iterator pointer must be specified" );
1725
1726 // this is a simplified version of ParseDateTime() which understands only
1727 // "today" (for wxDate compatibility) and digits only otherwise (and not
1728 // all esoteric constructions ParseDateTime() knows about)
1729
1730 const wxString::const_iterator pBegin = date.begin();
1731
1732 wxString::const_iterator p = pBegin;
1733 while ( wxIsspace(*p) )
1734 p++;
1735
1736 // some special cases
1737 static struct
1738 {
1739 const char *str;
1740 int dayDiffFromToday;
1741 } literalDates[] =
1742 {
1743 { wxTRANSLATE("today"), 0 },
1744 { wxTRANSLATE("yesterday"), -1 },
1745 { wxTRANSLATE("tomorrow"), 1 },
1746 };
1747
1748 for ( size_t n = 0; n < WXSIZEOF(literalDates); n++ )
1749 {
1750 const wxString dateStr = wxGetTranslation(literalDates[n].str);
1751 size_t len = dateStr.length();
1752
1753 const wxString::const_iterator pEnd = p + len;
1754 if ( wxString(p, pEnd).CmpNoCase(dateStr) == 0 )
1755 {
1756 // nothing can follow this, so stop here
1757
1758 p = pEnd;
1759
1760 int dayDiffFromToday = literalDates[n].dayDiffFromToday;
1761 *this = Today();
1762 if ( dayDiffFromToday )
1763 {
1764 *this += wxDateSpan::Days(dayDiffFromToday);
1765 }
1766
1767 *end = pEnd;
1768
1769 return true;
1770 }
1771 }
1772
1773 // We try to guess what we have here: for each new (numeric) token, we
1774 // determine if it can be a month, day or a year. Of course, there is an
1775 // ambiguity as some numbers may be days as well as months, so we also
1776 // have the ability to back track.
1777
1778 // what do we have?
1779 bool haveDay = false, // the months day?
1780 haveWDay = false, // the day of week?
1781 haveMon = false, // the month?
1782 haveYear = false; // the year?
1783
1784 // and the value of the items we have (init them to get rid of warnings)
1785 WeekDay wday = Inv_WeekDay;
1786 wxDateTime_t day = 0;
1787 wxDateTime::Month mon = Inv_Month;
1788 int year = 0;
1789
1790 // tokenize the string
1791 size_t nPosCur = 0;
1792 static const wxStringCharType *dateDelimiters = wxS(".,/-\t\r\n ");
1793 wxStringTokenizer tok(wxString(p, date.end()), dateDelimiters);
1794 while ( tok.HasMoreTokens() )
1795 {
1796 wxString token = tok.GetNextToken();
1797 if ( !token )
1798 continue;
1799
1800 // is it a number?
1801 unsigned long val;
1802 if ( token.ToULong(&val) )
1803 {
1804 // guess what this number is
1805
1806 bool isDay = false,
1807 isMonth = false,
1808 isYear = false;
1809
1810 if ( !haveMon && val > 0 && val <= 12 )
1811 {
1812 // assume it is month
1813 isMonth = true;
1814 }
1815 else // not the month
1816 {
1817 if ( haveDay )
1818 {
1819 // this can only be the year
1820 isYear = true;
1821 }
1822 else // may be either day or year
1823 {
1824 // use a leap year if we don't have the year yet to allow
1825 // dates like 2/29/1976 which would be rejected otherwise
1826 wxDateTime_t max_days = (wxDateTime_t)(
1827 haveMon
1828 ? GetNumberOfDays(mon, haveYear ? year : 1976)
1829 : 31
1830 );
1831
1832 // can it be day?
1833 if ( (val == 0) || (val > (unsigned long)max_days) )
1834 {
1835 // no
1836 isYear = true;
1837 }
1838 else // yes, suppose it's the day
1839 {
1840 isDay = true;
1841 }
1842 }
1843 }
1844
1845 if ( isYear )
1846 {
1847 if ( haveYear )
1848 break;
1849
1850 haveYear = true;
1851
1852 year = (wxDateTime_t)val;
1853 }
1854 else if ( isDay )
1855 {
1856 if ( haveDay )
1857 break;
1858
1859 haveDay = true;
1860
1861 day = (wxDateTime_t)val;
1862 }
1863 else if ( isMonth )
1864 {
1865 haveMon = true;
1866
1867 mon = (Month)(val - 1);
1868 }
1869 }
1870 else // not a number
1871 {
1872 // be careful not to overwrite the current mon value
1873 Month mon2 = GetMonthFromName
1874 (
1875 token,
1876 Name_Full | Name_Abbr,
1877 DateLang_Local | DateLang_English
1878 );
1879 if ( mon2 != Inv_Month )
1880 {
1881 // it's a month
1882 if ( haveMon )
1883 {
1884 // but we already have a month - maybe we guessed wrong?
1885 if ( !haveDay )
1886 {
1887 // no need to check in month range as always < 12, but
1888 // the days are counted from 1 unlike the months
1889 day = (wxDateTime_t)(mon + 1);
1890 haveDay = true;
1891 }
1892 else
1893 {
1894 // could possible be the year (doesn't the year come
1895 // before the month in the japanese format?) (FIXME)
1896 break;
1897 }
1898 }
1899
1900 mon = mon2;
1901
1902 haveMon = true;
1903 }
1904 else // not a valid month name
1905 {
1906 WeekDay wday2 = GetWeekDayFromName
1907 (
1908 token,
1909 Name_Full | Name_Abbr,
1910 DateLang_Local | DateLang_English
1911 );
1912 if ( wday2 != Inv_WeekDay )
1913 {
1914 // a week day
1915 if ( haveWDay )
1916 {
1917 break;
1918 }
1919
1920 wday = wday2;
1921
1922 haveWDay = true;
1923 }
1924 else // not a valid weekday name
1925 {
1926 // try the ordinals
1927 static const char *ordinals[] =
1928 {
1929 wxTRANSLATE("first"),
1930 wxTRANSLATE("second"),
1931 wxTRANSLATE("third"),
1932 wxTRANSLATE("fourth"),
1933 wxTRANSLATE("fifth"),
1934 wxTRANSLATE("sixth"),
1935 wxTRANSLATE("seventh"),
1936 wxTRANSLATE("eighth"),
1937 wxTRANSLATE("ninth"),
1938 wxTRANSLATE("tenth"),
1939 wxTRANSLATE("eleventh"),
1940 wxTRANSLATE("twelfth"),
1941 wxTRANSLATE("thirteenth"),
1942 wxTRANSLATE("fourteenth"),
1943 wxTRANSLATE("fifteenth"),
1944 wxTRANSLATE("sixteenth"),
1945 wxTRANSLATE("seventeenth"),
1946 wxTRANSLATE("eighteenth"),
1947 wxTRANSLATE("nineteenth"),
1948 wxTRANSLATE("twentieth"),
1949 // that's enough - otherwise we'd have problems with
1950 // composite (or not) ordinals
1951 };
1952
1953 size_t n;
1954 for ( n = 0; n < WXSIZEOF(ordinals); n++ )
1955 {
1956 if ( token.CmpNoCase(ordinals[n]) == 0 )
1957 {
1958 break;
1959 }
1960 }
1961
1962 if ( n == WXSIZEOF(ordinals) )
1963 {
1964 // stop here - something unknown
1965 break;
1966 }
1967
1968 // it's a day
1969 if ( haveDay )
1970 {
1971 // don't try anything here (as in case of numeric day
1972 // above) - the symbolic day spec should always
1973 // precede the month/year
1974 break;
1975 }
1976
1977 haveDay = true;
1978
1979 day = (wxDateTime_t)(n + 1);
1980 }
1981 }
1982 }
1983
1984 nPosCur = tok.GetPosition();
1985 }
1986
1987 // either no more tokens or the scan was stopped by something we couldn't
1988 // parse - in any case, see if we can construct a date from what we have
1989 if ( !haveDay && !haveWDay )
1990 return false;
1991
1992 if ( haveWDay && (haveMon || haveYear || haveDay) &&
1993 !(haveDay && haveMon && haveYear) )
1994 {
1995 // without adjectives (which we don't support here) the week day only
1996 // makes sense completely separately or with the full date
1997 // specification (what would "Wed 1999" mean?)
1998 return false;
1999 }
2000
2001 if ( !haveWDay && haveYear && !(haveDay && haveMon) )
2002 {
2003 // may be we have month and day instead of day and year?
2004 if ( haveDay && !haveMon )
2005 {
2006 if ( day <= 12 )
2007 {
2008 // exchange day and month
2009 mon = (wxDateTime::Month)(day - 1);
2010
2011 // we're in the current year then
2012 if ( (year > 0) && (year <= (int)GetNumberOfDays(mon, Inv_Year)) )
2013 {
2014 day = (wxDateTime_t)year;
2015
2016 haveMon = true;
2017 haveYear = false;
2018 }
2019 //else: no, can't exchange, leave haveMon == false
2020 }
2021 }
2022
2023 if ( !haveMon )
2024 return false;
2025 }
2026
2027 if ( !haveMon )
2028 {
2029 mon = GetCurrentMonth();
2030 }
2031
2032 if ( !haveYear )
2033 {
2034 year = GetCurrentYear();
2035 }
2036
2037 if ( haveDay )
2038 {
2039 // normally we check the day above but the check is optimistic in case
2040 // we find the day before its month/year so we have to redo it now
2041 if ( day > GetNumberOfDays(mon, year) )
2042 return false;
2043
2044 Set(day, mon, year);
2045
2046 if ( haveWDay )
2047 {
2048 // check that it is really the same
2049 if ( GetWeekDay() != wday )
2050 return false;
2051 }
2052 }
2053 else // haveWDay
2054 {
2055 *this = Today();
2056
2057 SetToWeekDayInSameWeek(wday);
2058 }
2059
2060 // return the pointer to the first unparsed char
2061 p += nPosCur;
2062 if ( nPosCur && wxStrchr(dateDelimiters, *(p - 1)) )
2063 {
2064 // if we couldn't parse the token after the delimiter, put back the
2065 // delimiter as well
2066 p--;
2067 }
2068
2069 *end = p;
2070
2071 return true;
2072 }
2073
2074 bool
2075 wxDateTime::ParseTime(const wxString& time, wxString::const_iterator *end)
2076 {
2077 wxCHECK_MSG( end, false, "end iterator pointer must be specified" );
2078
2079 // first try some extra things
2080 static const struct
2081 {
2082 const char *name;
2083 wxDateTime_t hour;
2084 } stdTimes[] =
2085 {
2086 { wxTRANSLATE("noon"), 12 },
2087 { wxTRANSLATE("midnight"), 00 },
2088 // anything else?
2089 };
2090
2091 for ( size_t n = 0; n < WXSIZEOF(stdTimes); n++ )
2092 {
2093 const wxString timeString = wxGetTranslation(stdTimes[n].name);
2094 const wxString::const_iterator p = time.begin() + timeString.length();
2095 if ( timeString.CmpNoCase(wxString(time.begin(), p)) == 0 )
2096 {
2097 // casts required by DigitalMars
2098 Set(stdTimes[n].hour, wxDateTime_t(0), wxDateTime_t(0));
2099
2100 if ( end )
2101 *end = p;
2102
2103 return true;
2104 }
2105 }
2106
2107 // try all time formats we may think about in the order from longest to
2108 // shortest
2109 static const char *timeFormats[] =
2110 {
2111 "%I:%M:%S %p", // 12hour with AM/PM
2112 "%H:%M:%S", // could be the same or 24 hour one so try it too
2113 "%I:%M %p", // 12hour with AM/PM but without seconds
2114 "%H:%M:%S", // and a possibly 24 hour version without seconds
2115 "%X", // possibly something from above or maybe something
2116 // completely different -- try it last
2117
2118 // TODO: parse timezones
2119 };
2120
2121 for ( size_t nFmt = 0; nFmt < WXSIZEOF(timeFormats); nFmt++ )
2122 {
2123 if ( ParseFormat(time, timeFormats[nFmt], end) )
2124 return true;
2125 }
2126
2127 return false;
2128 }
2129
2130 // ----------------------------------------------------------------------------
2131 // Workdays and holidays support
2132 // ----------------------------------------------------------------------------
2133
2134 bool wxDateTime::IsWorkDay(Country WXUNUSED(country)) const
2135 {
2136 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
2137 }
2138
2139 // ============================================================================
2140 // wxDateSpan
2141 // ============================================================================
2142
2143 wxDateSpan WXDLLIMPEXP_BASE operator*(int n, const wxDateSpan& ds)
2144 {
2145 wxDateSpan ds1(ds);
2146 return ds1.Multiply(n);
2147 }
2148
2149 // ============================================================================
2150 // wxTimeSpan
2151 // ============================================================================
2152
2153 wxTimeSpan WXDLLIMPEXP_BASE operator*(int n, const wxTimeSpan& ts)
2154 {
2155 return wxTimeSpan(ts).Multiply(n);
2156 }
2157
2158 // this enum is only used in wxTimeSpan::Format() below but we can't declare
2159 // it locally to the method as it provokes an internal compiler error in egcs
2160 // 2.91.60 when building with -O2
2161 enum TimeSpanPart
2162 {
2163 Part_Week,
2164 Part_Day,
2165 Part_Hour,
2166 Part_Min,
2167 Part_Sec,
2168 Part_MSec
2169 };
2170
2171 // not all strftime(3) format specifiers make sense here because, for example,
2172 // a time span doesn't have a year nor a timezone
2173 //
2174 // Here are the ones which are supported (all of them are supported by strftime
2175 // as well):
2176 // %H hour in 24 hour format
2177 // %M minute (00 - 59)
2178 // %S second (00 - 59)
2179 // %% percent sign
2180 //
2181 // Also, for MFC CTimeSpan compatibility, we support
2182 // %D number of days
2183 //
2184 // And, to be better than MFC :-), we also have
2185 // %E number of wEeks
2186 // %l milliseconds (000 - 999)
2187 wxString wxTimeSpan::Format(const wxString& format) const
2188 {
2189 // we deal with only positive time spans here and just add the sign in
2190 // front for the negative ones
2191 if ( IsNegative() )
2192 {
2193 wxString str(Negate().Format(format));
2194 return "-" + str;
2195 }
2196
2197 wxCHECK_MSG( !format.empty(), wxEmptyString,
2198 _T("NULL format in wxTimeSpan::Format") );
2199
2200 wxString str;
2201 str.Alloc(format.length());
2202
2203 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
2204 //
2205 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
2206 // question is what should ts.Format("%S") do? The code here returns "3273"
2207 // in this case (i.e. the total number of seconds, not just seconds % 60)
2208 // because, for me, this call means "give me entire time interval in
2209 // seconds" and not "give me the seconds part of the time interval"
2210 //
2211 // If we agree that it should behave like this, it is clear that the
2212 // interpretation of each format specifier depends on the presence of the
2213 // other format specs in the string: if there was "%H" before "%M", we
2214 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
2215
2216 // we remember the most important unit found so far
2217 TimeSpanPart partBiggest = Part_MSec;
2218
2219 for ( wxString::const_iterator pch = format.begin(); pch != format.end(); ++pch )
2220 {
2221 wxChar ch = *pch;
2222
2223 if ( ch == _T('%') )
2224 {
2225 // the start of the format specification of the printf() below
2226 wxString fmtPrefix(_T('%'));
2227
2228 // the number
2229 long n;
2230
2231 // the number of digits for the format string, 0 if unused
2232 unsigned digits = 0;
2233
2234 ch = *++pch; // get the format spec char
2235 switch ( ch )
2236 {
2237 default:
2238 wxFAIL_MSG( _T("invalid format character") );
2239 // fall through
2240
2241 case _T('%'):
2242 str += ch;
2243
2244 // skip the part below switch
2245 continue;
2246
2247 case _T('D'):
2248 n = GetDays();
2249 if ( partBiggest < Part_Day )
2250 {
2251 n %= DAYS_PER_WEEK;
2252 }
2253 else
2254 {
2255 partBiggest = Part_Day;
2256 }
2257 break;
2258
2259 case _T('E'):
2260 partBiggest = Part_Week;
2261 n = GetWeeks();
2262 break;
2263
2264 case _T('H'):
2265 n = GetHours();
2266 if ( partBiggest < Part_Hour )
2267 {
2268 n %= HOURS_PER_DAY;
2269 }
2270 else
2271 {
2272 partBiggest = Part_Hour;
2273 }
2274
2275 digits = 2;
2276 break;
2277
2278 case _T('l'):
2279 n = GetMilliseconds().ToLong();
2280 if ( partBiggest < Part_MSec )
2281 {
2282 n %= 1000;
2283 }
2284 //else: no need to reset partBiggest to Part_MSec, it is
2285 // the least significant one anyhow
2286
2287 digits = 3;
2288 break;
2289
2290 case _T('M'):
2291 n = GetMinutes();
2292 if ( partBiggest < Part_Min )
2293 {
2294 n %= MIN_PER_HOUR;
2295 }
2296 else
2297 {
2298 partBiggest = Part_Min;
2299 }
2300
2301 digits = 2;
2302 break;
2303
2304 case _T('S'):
2305 n = GetSeconds().ToLong();
2306 if ( partBiggest < Part_Sec )
2307 {
2308 n %= SEC_PER_MIN;
2309 }
2310 else
2311 {
2312 partBiggest = Part_Sec;
2313 }
2314
2315 digits = 2;
2316 break;
2317 }
2318
2319 if ( digits )
2320 {
2321 fmtPrefix << _T("0") << digits;
2322 }
2323
2324 str += wxString::Format(fmtPrefix + _T("ld"), n);
2325 }
2326 else
2327 {
2328 // normal character, just copy
2329 str += ch;
2330 }
2331 }
2332
2333 return str;
2334 }
2335
2336 #endif // wxUSE_DATETIME