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