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