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