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