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