]> git.saurik.com Git - wxWidgets.git/blob - src/common/datetime.cpp
added wxCAL_MONDAY/SUNDAY_FIRST flags and Ctrl-Home/Right/Left and Home/End keys
[wxWidgets.git] / src / common / datetime.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: wx/datetime.h
3 // Purpose: implementation of time/date related classes
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 license
17 ///////////////////////////////////////////////////////////////////////////////
18
19 /*
20 * Implementation notes:
21 *
22 * 1. the time is stored as a 64bit integer containing the signed number of
23 * milliseconds since Jan 1. 1970 (the Unix Epoch) - so it is always
24 * expressed in GMT.
25 *
26 * 2. the range is thus something about 580 million years, but due to current
27 * algorithms limitations, only dates from Nov 24, 4714BC are handled
28 *
29 * 3. standard ANSI C functions are used to do time calculations whenever
30 * possible, i.e. when the date is in the range Jan 1, 1970 to 2038
31 *
32 * 4. otherwise, the calculations are done by converting the date to/from JDN
33 * first (the range limitation mentioned above comes from here: the
34 * algorithm used by Scott E. Lee's code only works for positive JDNs, more
35 * or less)
36 *
37 * 5. the object constructed for the given DD-MM-YYYY HH:MM:SS corresponds to
38 * this moment in local time and may be converted to the object
39 * corresponding to the same date/time in another time zone by using
40 * ToTimezone()
41 *
42 * 6. the conversions to the current (or any other) timezone are done when the
43 * internal time representation is converted to the broken-down one in
44 * wxDateTime::Tm.
45 */
46
47 // ============================================================================
48 // declarations
49 // ============================================================================
50
51 // ----------------------------------------------------------------------------
52 // headers
53 // ----------------------------------------------------------------------------
54
55 #ifdef __GNUG__
56 #pragma implementation "datetime.h"
57 #endif
58
59 // For compilers that support precompilation, includes "wx.h".
60 #include "wx/wxprec.h"
61
62 #ifdef __BORLANDC__
63 #pragma hdrstop
64 #endif
65
66 #ifndef WX_PRECOMP
67 #include "wx/string.h"
68 #include "wx/intl.h"
69 #include "wx/log.h"
70 #endif // WX_PRECOMP
71
72 #include "wx/thread.h"
73 #include "wx/tokenzr.h"
74
75 #define wxDEFINE_TIME_CONSTANTS // before including datetime.h
76
77 #include "wx/datetime.h"
78
79 // ----------------------------------------------------------------------------
80 // conditional compilation
81 // ----------------------------------------------------------------------------
82
83 #if defined(HAVE_STRPTIME) && defined(__LINUX__)
84 // glibc 2.0.7 strptime() is broken - the following snippet causes it to
85 // crash (instead of just failing):
86 //
87 // strncpy(buf, "Tue Dec 21 20:25:40 1999", 128);
88 // strptime(buf, "%x", &tm);
89 //
90 // so don't use it
91 #undef HAVE_STRPTIME
92 #endif // broken strptime()
93
94 #ifndef WX_TIMEZONE
95 #if defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
96 #define WX_TIMEZONE _timezone
97 #else // unknown platform - try timezone
98 #define WX_TIMEZONE timezone
99 #endif
100 #endif // !WX_TIMEZONE
101
102 // ----------------------------------------------------------------------------
103 // constants
104 // ----------------------------------------------------------------------------
105
106 // some trivial ones
107 static const int MONTHS_IN_YEAR = 12;
108
109 static const int SECONDS_IN_MINUTE = 60;
110
111 static const long SECONDS_PER_DAY = 86400l;
112
113 static const long MILLISECONDS_PER_DAY = 86400000l;
114
115 // this is the integral part of JDN of the midnight of Jan 1, 1970
116 // (i.e. JDN(Jan 1, 1970) = 2440587.5)
117 static const long EPOCH_JDN = 2440587l;
118
119 // the date of JDN -0.5 (as we don't work with fractional parts, this is the
120 // reference date for us) is Nov 24, 4714BC
121 static const int JDN_0_YEAR = -4713;
122 static const int JDN_0_MONTH = wxDateTime::Nov;
123 static const int JDN_0_DAY = 24;
124
125 // the constants used for JDN calculations
126 static const long JDN_OFFSET = 32046l;
127 static const long DAYS_PER_5_MONTHS = 153l;
128 static const long DAYS_PER_4_YEARS = 1461l;
129 static const long DAYS_PER_400_YEARS = 146097l;
130
131 // this array contains the cumulated number of days in all previous months for
132 // normal and leap years
133 static const wxDateTime::wxDateTime_t gs_cumulatedDays[2][MONTHS_IN_YEAR] =
134 {
135 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
136 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
137 };
138
139 // ----------------------------------------------------------------------------
140 // global data
141 // ----------------------------------------------------------------------------
142
143 static wxDateTime gs_dtDefault;
144
145 wxDateTime& wxDefaultDateTime = gs_dtDefault;
146
147 wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown;
148
149 // ----------------------------------------------------------------------------
150 // private globals
151 // ----------------------------------------------------------------------------
152
153 // a critical section is needed to protect GetTimeZone() static
154 // variable in MT case
155 #if wxUSE_THREADS
156 static wxCriticalSection gs_critsectTimezone;
157 #endif // wxUSE_THREADS
158
159 // ----------------------------------------------------------------------------
160 // private functions
161 // ----------------------------------------------------------------------------
162
163 // get the number of days in the given month of the given year
164 static inline
165 wxDateTime::wxDateTime_t GetNumOfDaysInMonth(int year, wxDateTime::Month month)
166 {
167 // the number of days in month in Julian/Gregorian calendar: the first line
168 // is for normal years, the second one is for the leap ones
169 static wxDateTime::wxDateTime_t daysInMonth[2][MONTHS_IN_YEAR] =
170 {
171 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
172 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
173 };
174
175 return daysInMonth[wxDateTime::IsLeapYear(year)][month];
176 }
177
178 // ensure that the timezone variable is set by calling localtime
179 static int GetTimeZone()
180 {
181 // set to TRUE when the timezone is set
182 static bool s_timezoneSet = FALSE;
183
184 wxCRIT_SECT_LOCKER(lock, gs_critsectTimezone);
185
186 if ( !s_timezoneSet )
187 {
188 // just call localtime() instead of figuring out whether this system
189 // supports tzset(), _tzset() or something else
190 time_t t;
191 (void)localtime(&t);
192
193 s_timezoneSet = TRUE;
194 }
195
196 return (int)WX_TIMEZONE;
197 }
198
199 // return the integral part of the JDN for the midnight of the given date (to
200 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
201 // noon of the previous day)
202 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day,
203 wxDateTime::Month mon,
204 int year)
205 {
206 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
207
208 // check the date validity
209 wxASSERT_MSG(
210 (year > JDN_0_YEAR) ||
211 ((year == JDN_0_YEAR) && (mon > JDN_0_MONTH)) ||
212 ((year == JDN_0_YEAR) && (mon == JDN_0_MONTH) && (day >= JDN_0_DAY)),
213 _T("date out of range - can't convert to JDN")
214 );
215
216 // make the year positive to avoid problems with negative numbers division
217 year += 4800;
218
219 // months are counted from March here
220 int month;
221 if ( mon >= wxDateTime::Mar )
222 {
223 month = mon - 2;
224 }
225 else
226 {
227 month = mon + 10;
228 year--;
229 }
230
231 // now we can simply add all the contributions together
232 return ((year / 100) * DAYS_PER_400_YEARS) / 4
233 + ((year % 100) * DAYS_PER_4_YEARS) / 4
234 + (month * DAYS_PER_5_MONTHS + 2) / 5
235 + day
236 - JDN_OFFSET;
237 }
238
239 // this function is a wrapper around strftime(3)
240 static wxString CallStrftime(const wxChar *format, const tm* tm)
241 {
242 wxChar buf[4096];
243 if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) )
244 {
245 // buffer is too small?
246 wxFAIL_MSG(_T("strftime() failed"));
247 }
248
249 return wxString(buf);
250 }
251
252 // if year and/or month have invalid values, replace them with the current ones
253 static void ReplaceDefaultYearMonthWithCurrent(int *year,
254 wxDateTime::Month *month)
255 {
256 struct tm *tmNow = NULL;
257
258 if ( *year == wxDateTime::Inv_Year )
259 {
260 tmNow = wxDateTime::GetTmNow();
261
262 *year = 1900 + tmNow->tm_year;
263 }
264
265 if ( *month == wxDateTime::Inv_Month )
266 {
267 if ( !tmNow )
268 tmNow = wxDateTime::GetTmNow();
269
270 *month = (wxDateTime::Month)tmNow->tm_mon;
271 }
272 }
273
274 // fll the struct tm with default values
275 static void InitTm(struct tm& tm)
276 {
277 // struct tm may have etxra fields (undocumented and with unportable
278 // names) which, nevertheless, must be set to 0
279 memset(&tm, 0, sizeof(struct tm));
280
281 tm.tm_mday = 1; // mday 0 is invalid
282 tm.tm_year = 76; // any valid year
283 tm.tm_isdst = -1; // auto determine
284 }
285
286 // parsing helpers
287 // ---------------
288
289 // return the month if the string is a month name or Inv_Month otherwise
290 static wxDateTime::Month GetMonthFromName(const wxString& name, int flags)
291 {
292 wxDateTime::Month mon;
293 for ( mon = wxDateTime::Jan; mon < wxDateTime::Inv_Month; wxNextMonth(mon) )
294 {
295 // case-insensitive comparison either one of or with both abbreviated
296 // and not versions
297 if ( flags & wxDateTime::Name_Full )
298 {
299 if ( name.CmpNoCase(wxDateTime::
300 GetMonthName(mon, wxDateTime::Name_Full)) == 0 )
301 {
302 break;
303 }
304 }
305
306 if ( flags & wxDateTime::Name_Abbr )
307 {
308 if ( name.CmpNoCase(wxDateTime::
309 GetMonthName(mon, wxDateTime::Name_Abbr)) == 0 )
310 {
311 break;
312 }
313 }
314 }
315
316 return mon;
317 }
318
319 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
320 static wxDateTime::WeekDay GetWeekDayFromName(const wxString& name, int flags)
321 {
322 wxDateTime::WeekDay wd;
323 for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) )
324 {
325 // case-insensitive comparison either one of or with both abbreviated
326 // and not versions
327 if ( flags & wxDateTime::Name_Full )
328 {
329 if ( name.CmpNoCase(wxDateTime::
330 GetWeekDayName(wd, wxDateTime::Name_Full)) == 0 )
331 {
332 break;
333 }
334 }
335
336 if ( flags & wxDateTime::Name_Abbr )
337 {
338 if ( name.CmpNoCase(wxDateTime::
339 GetWeekDayName(wd, wxDateTime::Name_Abbr)) == 0 )
340 {
341 break;
342 }
343 }
344 }
345
346 return wd;
347 }
348
349 // scans all digits and returns the resulting number
350 static bool GetNumericToken(const wxChar*& p, unsigned long *number)
351 {
352 wxString s;
353 while ( wxIsdigit(*p) )
354 {
355 s += *p++;
356 }
357
358 return !!s && s.ToULong(number);
359 }
360
361 // scans all alphabetic characters and returns the resulting string
362 static wxString GetAlphaToken(const wxChar*& p)
363 {
364 wxString s;
365 while ( wxIsalpha(*p) )
366 {
367 s += *p++;
368 }
369
370 return s;
371 }
372
373 // ============================================================================
374 // implementation of wxDateTime
375 // ============================================================================
376
377 // ----------------------------------------------------------------------------
378 // struct Tm
379 // ----------------------------------------------------------------------------
380
381 wxDateTime::Tm::Tm()
382 {
383 year = (wxDateTime_t)wxDateTime::Inv_Year;
384 mon = wxDateTime::Inv_Month;
385 mday = 0;
386 hour = min = sec = msec = 0;
387 wday = wxDateTime::Inv_WeekDay;
388 }
389
390 wxDateTime::Tm::Tm(const struct tm& tm, const TimeZone& tz)
391 : m_tz(tz)
392 {
393 msec = 0;
394 sec = tm.tm_sec;
395 min = tm.tm_min;
396 hour = tm.tm_hour;
397 mday = tm.tm_mday;
398 mon = (wxDateTime::Month)tm.tm_mon;
399 year = 1900 + tm.tm_year;
400 wday = tm.tm_wday;
401 yday = tm.tm_yday;
402 }
403
404 bool wxDateTime::Tm::IsValid() const
405 {
406 // we allow for the leap seconds, although we don't use them (yet)
407 return (year != wxDateTime::Inv_Year) && (mon != wxDateTime::Inv_Month) &&
408 (mday <= GetNumOfDaysInMonth(year, mon)) &&
409 (hour < 24) && (min < 60) && (sec < 62) && (msec < 1000);
410 }
411
412 void wxDateTime::Tm::ComputeWeekDay()
413 {
414 // compute the week day from day/month/year: we use the dumbest algorithm
415 // possible: just compute our JDN and then use the (simple to derive)
416 // formula: weekday = (JDN + 1.5) % 7
417 wday = (wxDateTime::WeekDay)(GetTruncatedJDN(mday, mon, year) + 2) % 7;
418 }
419
420 void wxDateTime::Tm::AddMonths(int monDiff)
421 {
422 // normalize the months field
423 while ( monDiff < -mon )
424 {
425 year--;
426
427 monDiff += MONTHS_IN_YEAR;
428 }
429
430 while ( monDiff + mon >= MONTHS_IN_YEAR )
431 {
432 year++;
433
434 monDiff -= MONTHS_IN_YEAR;
435 }
436
437 mon = (wxDateTime::Month)(mon + monDiff);
438
439 wxASSERT_MSG( mon >= 0 && mon < MONTHS_IN_YEAR, _T("logic error") );
440 }
441
442 void wxDateTime::Tm::AddDays(int dayDiff)
443 {
444 // normalize the days field
445 while ( dayDiff + mday < 1 )
446 {
447 AddMonths(-1);
448
449 dayDiff += GetNumOfDaysInMonth(year, mon);
450 }
451
452 mday += dayDiff;
453 while ( mday > GetNumOfDaysInMonth(year, mon) )
454 {
455 mday -= GetNumOfDaysInMonth(year, mon);
456
457 AddMonths(1);
458 }
459
460 wxASSERT_MSG( mday > 0 && mday <= GetNumOfDaysInMonth(year, mon),
461 _T("logic error") );
462 }
463
464 // ----------------------------------------------------------------------------
465 // class TimeZone
466 // ----------------------------------------------------------------------------
467
468 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz)
469 {
470 switch ( tz )
471 {
472 case wxDateTime::Local:
473 // get the offset from C RTL: it returns the difference GMT-local
474 // while we want to have the offset _from_ GMT, hence the '-'
475 m_offset = -GetTimeZone();
476 break;
477
478 case wxDateTime::GMT_12:
479 case wxDateTime::GMT_11:
480 case wxDateTime::GMT_10:
481 case wxDateTime::GMT_9:
482 case wxDateTime::GMT_8:
483 case wxDateTime::GMT_7:
484 case wxDateTime::GMT_6:
485 case wxDateTime::GMT_5:
486 case wxDateTime::GMT_4:
487 case wxDateTime::GMT_3:
488 case wxDateTime::GMT_2:
489 case wxDateTime::GMT_1:
490 m_offset = -3600*(wxDateTime::GMT0 - tz);
491 break;
492
493 case wxDateTime::GMT0:
494 case wxDateTime::GMT1:
495 case wxDateTime::GMT2:
496 case wxDateTime::GMT3:
497 case wxDateTime::GMT4:
498 case wxDateTime::GMT5:
499 case wxDateTime::GMT6:
500 case wxDateTime::GMT7:
501 case wxDateTime::GMT8:
502 case wxDateTime::GMT9:
503 case wxDateTime::GMT10:
504 case wxDateTime::GMT11:
505 case wxDateTime::GMT12:
506 m_offset = 3600*(tz - wxDateTime::GMT0);
507 break;
508
509 case wxDateTime::A_CST:
510 // Central Standard Time in use in Australia = UTC + 9.5
511 m_offset = 60l*(9*60 + 30);
512 break;
513
514 default:
515 wxFAIL_MSG( _T("unknown time zone") );
516 }
517 }
518
519 // ----------------------------------------------------------------------------
520 // static functions
521 // ----------------------------------------------------------------------------
522
523 /* static */
524 bool wxDateTime::IsLeapYear(int year, wxDateTime::Calendar cal)
525 {
526 if ( year == Inv_Year )
527 year = GetCurrentYear();
528
529 if ( cal == Gregorian )
530 {
531 // in Gregorian calendar leap years are those divisible by 4 except
532 // those divisible by 100 unless they're also divisible by 400
533 // (in some countries, like Russia and Greece, additional corrections
534 // exist, but they won't manifest themselves until 2700)
535 return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
536 }
537 else if ( cal == Julian )
538 {
539 // in Julian calendar the rule is simpler
540 return year % 4 == 0;
541 }
542 else
543 {
544 wxFAIL_MSG(_T("unknown calendar"));
545
546 return FALSE;
547 }
548 }
549
550 /* static */
551 int wxDateTime::GetCentury(int year)
552 {
553 return year > 0 ? year / 100 : year / 100 - 1;
554 }
555
556 /* static */
557 int wxDateTime::ConvertYearToBC(int year)
558 {
559 // year 0 is BC 1
560 return year > 0 ? year : year - 1;
561 }
562
563 /* static */
564 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal)
565 {
566 switch ( cal )
567 {
568 case Gregorian:
569 return Now().GetYear();
570
571 case Julian:
572 wxFAIL_MSG(_T("TODO"));
573 break;
574
575 default:
576 wxFAIL_MSG(_T("unsupported calendar"));
577 break;
578 }
579
580 return Inv_Year;
581 }
582
583 /* static */
584 wxDateTime::Month wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal)
585 {
586 switch ( cal )
587 {
588 case Gregorian:
589 return Now().GetMonth();
590
591 case Julian:
592 wxFAIL_MSG(_T("TODO"));
593 break;
594
595 default:
596 wxFAIL_MSG(_T("unsupported calendar"));
597 break;
598 }
599
600 return Inv_Month;
601 }
602
603 /* static */
604 wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(int year, Calendar cal)
605 {
606 if ( year == Inv_Year )
607 {
608 // take the current year if none given
609 year = GetCurrentYear();
610 }
611
612 switch ( cal )
613 {
614 case Gregorian:
615 case Julian:
616 return IsLeapYear(year) ? 366 : 365;
617
618 default:
619 wxFAIL_MSG(_T("unsupported calendar"));
620 break;
621 }
622
623 return 0;
624 }
625
626 /* static */
627 wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(wxDateTime::Month month,
628 int year,
629 wxDateTime::Calendar cal)
630 {
631 wxCHECK_MSG( month < MONTHS_IN_YEAR, 0, _T("invalid month") );
632
633 if ( cal == Gregorian || cal == Julian )
634 {
635 if ( year == Inv_Year )
636 {
637 // take the current year if none given
638 year = GetCurrentYear();
639 }
640
641 return GetNumOfDaysInMonth(year, month);
642 }
643 else
644 {
645 wxFAIL_MSG(_T("unsupported calendar"));
646
647 return 0;
648 }
649 }
650
651 /* static */
652 wxString wxDateTime::GetMonthName(wxDateTime::Month month,
653 wxDateTime::NameFlags flags)
654 {
655 wxCHECK_MSG( month != Inv_Month, _T(""), _T("invalid month") );
656
657 // notice that we must set all the fields to avoid confusing libc (GNU one
658 // gets confused to a crash if we don't do this)
659 tm tm;
660 InitTm(tm);
661 tm.tm_mon = month;
662
663 return CallStrftime(flags == Name_Abbr ? _T("%b") : _T("%B"), &tm);
664 }
665
666 /* static */
667 wxString wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday,
668 wxDateTime::NameFlags flags)
669 {
670 wxCHECK_MSG( wday != Inv_WeekDay, _T(""), _T("invalid weekday") );
671
672 // take some arbitrary Sunday
673 tm tm;
674 InitTm(tm);
675 tm.tm_mday = 28;
676 tm.tm_mon = Nov;
677 tm.tm_year = 99;
678
679 // and offset it by the number of days needed to get the correct wday
680 tm.tm_mday += wday;
681
682 // call mktime() to normalize it...
683 (void)mktime(&tm);
684
685 // ... and call strftime()
686 return CallStrftime(flags == Name_Abbr ? _T("%a") : _T("%A"), &tm);
687 }
688
689 /* static */
690 void wxDateTime::GetAmPmStrings(wxString *am, wxString *pm)
691 {
692 tm tm;
693 InitTm(tm);
694 if ( am )
695 {
696 *am = CallStrftime(_T("%p"), &tm);
697 }
698 if ( pm )
699 {
700 tm.tm_hour = 13;
701 *pm = CallStrftime(_T("%p"), &tm);
702 }
703 }
704
705 // ----------------------------------------------------------------------------
706 // Country stuff: date calculations depend on the country (DST, work days,
707 // ...), so we need to know which rules to follow.
708 // ----------------------------------------------------------------------------
709
710 /* static */
711 wxDateTime::Country wxDateTime::GetCountry()
712 {
713 // TODO use LOCALE_ICOUNTRY setting under Win32
714
715 if ( ms_country == Country_Unknown )
716 {
717 // try to guess from the time zone name
718 time_t t = time(NULL);
719 struct tm *tm = localtime(&t);
720
721 wxString tz = CallStrftime(_T("%Z"), tm);
722 if ( tz == _T("WET") || tz == _T("WEST") )
723 {
724 ms_country = UK;
725 }
726 else if ( tz == _T("CET") || tz == _T("CEST") )
727 {
728 ms_country = Country_EEC;
729 }
730 else if ( tz == _T("MSK") || tz == _T("MSD") )
731 {
732 ms_country = Russia;
733 }
734 else if ( tz == _T("AST") || tz == _T("ADT") ||
735 tz == _T("EST") || tz == _T("EDT") ||
736 tz == _T("CST") || tz == _T("CDT") ||
737 tz == _T("MST") || tz == _T("MDT") ||
738 tz == _T("PST") || tz == _T("PDT") )
739 {
740 ms_country = USA;
741 }
742 else
743 {
744 // well, choose a default one
745 ms_country = USA;
746 }
747 }
748
749 return ms_country;
750 }
751
752 /* static */
753 void wxDateTime::SetCountry(wxDateTime::Country country)
754 {
755 ms_country = country;
756 }
757
758 /* static */
759 bool wxDateTime::IsWestEuropeanCountry(Country country)
760 {
761 if ( country == Country_Default )
762 {
763 country = GetCountry();
764 }
765
766 return (Country_WesternEurope_Start <= country) &&
767 (country <= Country_WesternEurope_End);
768 }
769
770 // ----------------------------------------------------------------------------
771 // DST calculations: we use 3 different rules for the West European countries,
772 // USA and for the rest of the world. This is undoubtedly false for many
773 // countries, but I lack the necessary info (and the time to gather it),
774 // please add the other rules here!
775 // ----------------------------------------------------------------------------
776
777 /* static */
778 bool wxDateTime::IsDSTApplicable(int year, Country country)
779 {
780 if ( year == Inv_Year )
781 {
782 // take the current year if none given
783 year = GetCurrentYear();
784 }
785
786 if ( country == Country_Default )
787 {
788 country = GetCountry();
789 }
790
791 switch ( country )
792 {
793 case USA:
794 case UK:
795 // DST was first observed in the US and UK during WWI, reused
796 // during WWII and used again since 1966
797 return year >= 1966 ||
798 (year >= 1942 && year <= 1945) ||
799 (year == 1918 || year == 1919);
800
801 default:
802 // assume that it started after WWII
803 return year > 1950;
804 }
805 }
806
807 /* static */
808 wxDateTime wxDateTime::GetBeginDST(int year, Country country)
809 {
810 if ( year == Inv_Year )
811 {
812 // take the current year if none given
813 year = GetCurrentYear();
814 }
815
816 if ( country == Country_Default )
817 {
818 country = GetCountry();
819 }
820
821 if ( !IsDSTApplicable(year, country) )
822 {
823 return wxInvalidDateTime;
824 }
825
826 wxDateTime dt;
827
828 if ( IsWestEuropeanCountry(country) || (country == Russia) )
829 {
830 // DST begins at 1 a.m. GMT on the last Sunday of March
831 if ( !dt.SetToLastWeekDay(Sun, Mar, year) )
832 {
833 // weird...
834 wxFAIL_MSG( _T("no last Sunday in March?") );
835 }
836
837 dt += wxTimeSpan::Hours(1);
838
839 // disable DST tests because it could result in an infinite recursion!
840 dt.MakeGMT(TRUE);
841 }
842 else switch ( country )
843 {
844 case USA:
845 switch ( year )
846 {
847 case 1918:
848 case 1919:
849 // don't know for sure - assume it was in effect all year
850
851 case 1943:
852 case 1944:
853 case 1945:
854 dt.Set(1, Jan, year);
855 break;
856
857 case 1942:
858 // DST was installed Feb 2, 1942 by the Congress
859 dt.Set(2, Feb, year);
860 break;
861
862 // Oil embargo changed the DST period in the US
863 case 1974:
864 dt.Set(6, Jan, 1974);
865 break;
866
867 case 1975:
868 dt.Set(23, Feb, 1975);
869 break;
870
871 default:
872 // before 1986, DST begun on the last Sunday of April, but
873 // in 1986 Reagan changed it to begin at 2 a.m. of the
874 // first Sunday in April
875 if ( year < 1986 )
876 {
877 if ( !dt.SetToLastWeekDay(Sun, Apr, year) )
878 {
879 // weird...
880 wxFAIL_MSG( _T("no first Sunday in April?") );
881 }
882 }
883 else
884 {
885 if ( !dt.SetToWeekDay(Sun, 1, Apr, year) )
886 {
887 // weird...
888 wxFAIL_MSG( _T("no first Sunday in April?") );
889 }
890 }
891
892 dt += wxTimeSpan::Hours(2);
893
894 // TODO what about timezone??
895 }
896
897 break;
898
899 default:
900 // assume Mar 30 as the start of the DST for the rest of the world
901 // - totally bogus, of course
902 dt.Set(30, Mar, year);
903 }
904
905 return dt;
906 }
907
908 /* static */
909 wxDateTime wxDateTime::GetEndDST(int year, Country country)
910 {
911 if ( year == Inv_Year )
912 {
913 // take the current year if none given
914 year = GetCurrentYear();
915 }
916
917 if ( country == Country_Default )
918 {
919 country = GetCountry();
920 }
921
922 if ( !IsDSTApplicable(year, country) )
923 {
924 return wxInvalidDateTime;
925 }
926
927 wxDateTime dt;
928
929 if ( IsWestEuropeanCountry(country) || (country == Russia) )
930 {
931 // DST ends at 1 a.m. GMT on the last Sunday of October
932 if ( !dt.SetToLastWeekDay(Sun, Oct, year) )
933 {
934 // weirder and weirder...
935 wxFAIL_MSG( _T("no last Sunday in October?") );
936 }
937
938 dt += wxTimeSpan::Hours(1);
939
940 // disable DST tests because it could result in an infinite recursion!
941 dt.MakeGMT(TRUE);
942 }
943 else switch ( country )
944 {
945 case USA:
946 switch ( year )
947 {
948 case 1918:
949 case 1919:
950 // don't know for sure - assume it was in effect all year
951
952 case 1943:
953 case 1944:
954 dt.Set(31, Dec, year);
955 break;
956
957 case 1945:
958 // the time was reset after the end of the WWII
959 dt.Set(30, Sep, year);
960 break;
961
962 default:
963 // DST ends at 2 a.m. on the last Sunday of October
964 if ( !dt.SetToLastWeekDay(Sun, Oct, year) )
965 {
966 // weirder and weirder...
967 wxFAIL_MSG( _T("no last Sunday in October?") );
968 }
969
970 dt += wxTimeSpan::Hours(2);
971
972 // TODO what about timezone??
973 }
974 break;
975
976 default:
977 // assume October 26th as the end of the DST - totally bogus too
978 dt.Set(26, Oct, year);
979 }
980
981 return dt;
982 }
983
984 // ----------------------------------------------------------------------------
985 // constructors and assignment operators
986 // ----------------------------------------------------------------------------
987
988 // the values in the tm structure contain the local time
989 wxDateTime& wxDateTime::Set(const struct tm& tm)
990 {
991 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
992
993 struct tm tm2(tm);
994 time_t timet = mktime(&tm2);
995
996 if ( timet == (time_t)-1 )
997 {
998 // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
999 // less than timezone - try to make it work for this case
1000 if ( tm2.tm_year == 70 && tm2.tm_mon == 0 && tm2.tm_mday == 1 )
1001 {
1002 // add timezone to make sure that date is in range
1003 tm2.tm_sec -= GetTimeZone();
1004
1005 timet = mktime(&tm2);
1006 if ( timet != (time_t)-1 )
1007 {
1008 timet += GetTimeZone();
1009
1010 return Set(timet);
1011 }
1012 }
1013
1014 wxFAIL_MSG( _T("mktime() failed") );
1015
1016 return wxInvalidDateTime;
1017 }
1018 else
1019 {
1020 return Set(timet);
1021 }
1022 }
1023
1024 wxDateTime& wxDateTime::Set(wxDateTime_t hour,
1025 wxDateTime_t minute,
1026 wxDateTime_t second,
1027 wxDateTime_t millisec)
1028 {
1029 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1030
1031 // we allow seconds to be 61 to account for the leap seconds, even if we
1032 // don't use them really
1033 wxCHECK_MSG( hour < 24 && second < 62 && minute < 60 && millisec < 1000,
1034 wxInvalidDateTime,
1035 _T("Invalid time in wxDateTime::Set()") );
1036
1037 // get the current date from system
1038 struct tm *tm = GetTmNow();
1039
1040 wxCHECK_MSG( tm, wxInvalidDateTime, _T("localtime() failed") );
1041
1042 // adjust the time
1043 tm->tm_hour = hour;
1044 tm->tm_min = minute;
1045 tm->tm_sec = second;
1046
1047 (void)Set(*tm);
1048
1049 // and finally adjust milliseconds
1050 return SetMillisecond(millisec);
1051 }
1052
1053 wxDateTime& wxDateTime::Set(wxDateTime_t day,
1054 Month month,
1055 int year,
1056 wxDateTime_t hour,
1057 wxDateTime_t minute,
1058 wxDateTime_t second,
1059 wxDateTime_t millisec)
1060 {
1061 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1062
1063 wxCHECK_MSG( hour < 24 && second < 62 && minute < 60 && millisec < 1000,
1064 wxInvalidDateTime,
1065 _T("Invalid time in wxDateTime::Set()") );
1066
1067 ReplaceDefaultYearMonthWithCurrent(&year, &month);
1068
1069 wxCHECK_MSG( (0 < day) && (day <= GetNumberOfDays(month, year)),
1070 wxInvalidDateTime,
1071 _T("Invalid date in wxDateTime::Set()") );
1072
1073 // the range of time_t type (inclusive)
1074 static const int yearMinInRange = 1970;
1075 static const int yearMaxInRange = 2037;
1076
1077 // test only the year instead of testing for the exact end of the Unix
1078 // time_t range - it doesn't bring anything to do more precise checks
1079 if ( year >= yearMinInRange && year <= yearMaxInRange )
1080 {
1081 // use the standard library version if the date is in range - this is
1082 // probably more efficient than our code
1083 struct tm tm;
1084 tm.tm_year = year - 1900;
1085 tm.tm_mon = month;
1086 tm.tm_mday = day;
1087 tm.tm_hour = hour;
1088 tm.tm_min = minute;
1089 tm.tm_sec = second;
1090 tm.tm_isdst = -1; // mktime() will guess it
1091
1092 (void)Set(tm);
1093
1094 // and finally adjust milliseconds
1095 return SetMillisecond(millisec);
1096 }
1097 else
1098 {
1099 // do time calculations ourselves: we want to calculate the number of
1100 // milliseconds between the given date and the epoch
1101
1102 // get the JDN for the midnight of this day
1103 m_time = GetTruncatedJDN(day, month, year);
1104 m_time -= EPOCH_JDN;
1105 m_time *= SECONDS_PER_DAY * TIME_T_FACTOR;
1106
1107 // JDN corresponds to GMT, we take localtime
1108 Add(wxTimeSpan(hour, minute, second + GetTimeZone(), millisec));
1109 }
1110
1111 return *this;
1112 }
1113
1114 wxDateTime& wxDateTime::Set(double jdn)
1115 {
1116 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1117 // EPOCH_JDN + 0.5
1118 jdn -= EPOCH_JDN + 0.5;
1119
1120 jdn *= MILLISECONDS_PER_DAY;
1121
1122 m_time.Assign(jdn);
1123
1124 return *this;
1125 }
1126
1127 wxDateTime& wxDateTime::ResetTime()
1128 {
1129 Tm tm = GetTm();
1130
1131 if ( tm.hour || tm.min || tm.sec || tm.msec )
1132 {
1133 tm.msec =
1134 tm.sec =
1135 tm.min =
1136 tm.hour = 0;
1137
1138 Set(tm);
1139 }
1140
1141 return *this;
1142 }
1143
1144 // ----------------------------------------------------------------------------
1145 // time_t <-> broken down time conversions
1146 // ----------------------------------------------------------------------------
1147
1148 wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
1149 {
1150 #ifdef __VMS__
1151 int time2;
1152 #endif
1153 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1154
1155 time_t time = GetTicks();
1156 if ( time != (time_t)-1 )
1157 {
1158 // use C RTL functions
1159 tm *tm;
1160 if ( tz.GetOffset() == -GetTimeZone() )
1161 {
1162 // we are working with local time
1163 tm = localtime(&time);
1164
1165 // should never happen
1166 wxCHECK_MSG( tm, Tm(), _T("gmtime() failed") );
1167 }
1168 else
1169 {
1170 time += tz.GetOffset();
1171 #ifdef __VMS__ // time is unsigned so avoid warning
1172 time2 = (int) time;
1173 if ( time2 >= 0 )
1174 #else
1175 if ( time >= 0 )
1176 #endif
1177 {
1178 tm = gmtime(&time);
1179
1180 // should never happen
1181 wxCHECK_MSG( tm, Tm(), _T("gmtime() failed") );
1182 }
1183 else
1184 {
1185 tm = (struct tm *)NULL;
1186 }
1187 }
1188
1189 if ( tm )
1190 {
1191 return Tm(*tm, tz);
1192 }
1193 //else: use generic code below
1194 }
1195
1196 // remember the time and do the calculations with the date only - this
1197 // eliminates rounding errors of the floating point arithmetics
1198
1199 wxLongLong timeMidnight = m_time + tz.GetOffset() * 1000;
1200
1201 long timeOnly = (timeMidnight % MILLISECONDS_PER_DAY).ToLong();
1202
1203 // we want to always have positive time and timeMidnight to be really
1204 // the midnight before it
1205 if ( timeOnly < 0 )
1206 {
1207 timeOnly = MILLISECONDS_PER_DAY + timeOnly;
1208 }
1209
1210 timeMidnight -= timeOnly;
1211
1212 // calculate the Gregorian date from JDN for the midnight of our date:
1213 // this will yield day, month (in 1..12 range) and year
1214
1215 // actually, this is the JDN for the noon of the previous day
1216 long jdn = (timeMidnight / MILLISECONDS_PER_DAY).ToLong() + EPOCH_JDN;
1217
1218 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1219
1220 wxASSERT_MSG( jdn > -2, _T("JDN out of range") );
1221
1222 // calculate the century
1223 int temp = (jdn + JDN_OFFSET) * 4 - 1;
1224 int century = temp / DAYS_PER_400_YEARS;
1225
1226 // then the year and day of year (1 <= dayOfYear <= 366)
1227 temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3;
1228 int year = (century * 100) + (temp / DAYS_PER_4_YEARS);
1229 int dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
1230
1231 // and finally the month and day of the month
1232 temp = dayOfYear * 5 - 3;
1233 int month = temp / DAYS_PER_5_MONTHS;
1234 int day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
1235
1236 // month is counted from March - convert to normal
1237 if ( month < 10 )
1238 {
1239 month += 3;
1240 }
1241 else
1242 {
1243 year += 1;
1244 month -= 9;
1245 }
1246
1247 // year is offset by 4800
1248 year -= 4800;
1249
1250 // check that the algorithm gave us something reasonable
1251 wxASSERT_MSG( (0 < month) && (month <= 12), _T("invalid month") );
1252 wxASSERT_MSG( (1 <= day) && (day < 32), _T("invalid day") );
1253 wxASSERT_MSG( (INT_MIN <= year) && (year <= INT_MAX),
1254 _T("year range overflow") );
1255
1256 // construct Tm from these values
1257 Tm tm;
1258 tm.year = (int)year;
1259 tm.mon = (Month)(month - 1); // algorithm yields 1 for January, not 0
1260 tm.mday = (wxDateTime_t)day;
1261 tm.msec = timeOnly % 1000;
1262 timeOnly -= tm.msec;
1263 timeOnly /= 1000; // now we have time in seconds
1264
1265 tm.sec = timeOnly % 60;
1266 timeOnly -= tm.sec;
1267 timeOnly /= 60; // now we have time in minutes
1268
1269 tm.min = timeOnly % 60;
1270 timeOnly -= tm.min;
1271
1272 tm.hour = timeOnly / 60;
1273
1274 return tm;
1275 }
1276
1277 wxDateTime& wxDateTime::SetYear(int year)
1278 {
1279 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1280
1281 Tm tm(GetTm());
1282 tm.year = year;
1283 Set(tm);
1284
1285 return *this;
1286 }
1287
1288 wxDateTime& wxDateTime::SetMonth(Month month)
1289 {
1290 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1291
1292 Tm tm(GetTm());
1293 tm.mon = month;
1294 Set(tm);
1295
1296 return *this;
1297 }
1298
1299 wxDateTime& wxDateTime::SetDay(wxDateTime_t mday)
1300 {
1301 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1302
1303 Tm tm(GetTm());
1304 tm.mday = mday;
1305 Set(tm);
1306
1307 return *this;
1308 }
1309
1310 wxDateTime& wxDateTime::SetHour(wxDateTime_t hour)
1311 {
1312 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1313
1314 Tm tm(GetTm());
1315 tm.hour = hour;
1316 Set(tm);
1317
1318 return *this;
1319 }
1320
1321 wxDateTime& wxDateTime::SetMinute(wxDateTime_t min)
1322 {
1323 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1324
1325 Tm tm(GetTm());
1326 tm.min = min;
1327 Set(tm);
1328
1329 return *this;
1330 }
1331
1332 wxDateTime& wxDateTime::SetSecond(wxDateTime_t sec)
1333 {
1334 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1335
1336 Tm tm(GetTm());
1337 tm.sec = sec;
1338 Set(tm);
1339
1340 return *this;
1341 }
1342
1343 wxDateTime& wxDateTime::SetMillisecond(wxDateTime_t millisecond)
1344 {
1345 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1346
1347 // we don't need to use GetTm() for this one
1348 m_time -= m_time % 1000l;
1349 m_time += millisecond;
1350
1351 return *this;
1352 }
1353
1354 // ----------------------------------------------------------------------------
1355 // wxDateTime arithmetics
1356 // ----------------------------------------------------------------------------
1357
1358 wxDateTime& wxDateTime::Add(const wxDateSpan& diff)
1359 {
1360 Tm tm(GetTm());
1361
1362 tm.year += diff.GetYears();
1363 tm.AddMonths(diff.GetMonths());
1364 tm.AddDays(diff.GetTotalDays());
1365
1366 Set(tm);
1367
1368 wxASSERT_MSG( IsSameTime(tm),
1369 _T("Add(wxDateSpan) shouldn't modify time") );
1370
1371 return *this;
1372 }
1373
1374 // ----------------------------------------------------------------------------
1375 // Weekday and monthday stuff
1376 // ----------------------------------------------------------------------------
1377
1378 wxDateTime& wxDateTime::SetToLastMonthDay(Month month,
1379 int year)
1380 {
1381 // take the current month/year if none specified
1382 if ( year == Inv_Year )
1383 year = GetYear();
1384 if ( month == Inv_Month )
1385 month = GetMonth();
1386
1387 return Set(GetNumOfDaysInMonth(year, month), month, year);
1388 }
1389
1390 wxDateTime& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday)
1391 {
1392 wxCHECK_MSG( weekday != Inv_WeekDay, wxInvalidDateTime, _T("invalid weekday") );
1393
1394 WeekDay wdayThis = GetWeekDay();
1395 if ( weekday == wdayThis )
1396 {
1397 // nothing to do
1398 return *this;
1399 }
1400 else if ( weekday < wdayThis )
1401 {
1402 return Substract(wxTimeSpan::Days(wdayThis - weekday));
1403 }
1404 else // weekday > wdayThis
1405 {
1406 return Add(wxTimeSpan::Days(weekday - wdayThis));
1407 }
1408 }
1409
1410 wxDateTime& wxDateTime::SetToNextWeekDay(WeekDay weekday)
1411 {
1412 wxCHECK_MSG( weekday != Inv_WeekDay, wxInvalidDateTime, _T("invalid weekday") );
1413
1414 int diff;
1415 WeekDay wdayThis = GetWeekDay();
1416 if ( weekday == wdayThis )
1417 {
1418 // nothing to do
1419 return *this;
1420 }
1421 else if ( weekday < wdayThis )
1422 {
1423 // need to advance a week
1424 diff = 7 - (wdayThis - weekday);
1425 }
1426 else // weekday > wdayThis
1427 {
1428 diff = weekday - wdayThis;
1429 }
1430
1431 return Add(wxTimeSpan::Days(diff));
1432 }
1433
1434 wxDateTime& wxDateTime::SetToPrevWeekDay(WeekDay weekday)
1435 {
1436 wxCHECK_MSG( weekday != Inv_WeekDay, wxInvalidDateTime, _T("invalid weekday") );
1437
1438 int diff;
1439 WeekDay wdayThis = GetWeekDay();
1440 if ( weekday == wdayThis )
1441 {
1442 // nothing to do
1443 return *this;
1444 }
1445 else if ( weekday > wdayThis )
1446 {
1447 // need to go to previous week
1448 diff = 7 - (weekday - wdayThis);
1449 }
1450 else // weekday < wdayThis
1451 {
1452 diff = wdayThis - weekday;
1453 }
1454
1455 return Substract(wxTimeSpan::Days(diff));
1456 }
1457
1458 bool wxDateTime::SetToWeekDay(WeekDay weekday,
1459 int n,
1460 Month month,
1461 int year)
1462 {
1463 wxCHECK_MSG( weekday != Inv_WeekDay, FALSE, _T("invalid weekday") );
1464
1465 // we don't check explicitly that -5 <= n <= 5 because we will return FALSE
1466 // anyhow in such case - but may be should still give an assert for it?
1467
1468 // take the current month/year if none specified
1469 ReplaceDefaultYearMonthWithCurrent(&year, &month);
1470
1471 wxDateTime dt;
1472
1473 // TODO this probably could be optimised somehow...
1474
1475 if ( n > 0 )
1476 {
1477 // get the first day of the month
1478 dt.Set(1, month, year);
1479
1480 // get its wday
1481 WeekDay wdayFirst = dt.GetWeekDay();
1482
1483 // go to the first weekday of the month
1484 int diff = weekday - wdayFirst;
1485 if ( diff < 0 )
1486 diff += 7;
1487
1488 // add advance n-1 weeks more
1489 diff += 7*(n - 1);
1490
1491 dt += wxDateSpan::Days(diff);
1492 }
1493 else // count from the end of the month
1494 {
1495 // get the last day of the month
1496 dt.SetToLastMonthDay(month, year);
1497
1498 // get its wday
1499 WeekDay wdayLast = dt.GetWeekDay();
1500
1501 // go to the last weekday of the month
1502 int diff = wdayLast - weekday;
1503 if ( diff < 0 )
1504 diff += 7;
1505
1506 // and rewind n-1 weeks from there
1507 diff += 7*(-n - 1);
1508
1509 dt -= wxDateSpan::Days(diff);
1510 }
1511
1512 // check that it is still in the same month
1513 if ( dt.GetMonth() == month )
1514 {
1515 *this = dt;
1516
1517 return TRUE;
1518 }
1519 else
1520 {
1521 // no such day in this month
1522 return FALSE;
1523 }
1524 }
1525
1526 wxDateTime::wxDateTime_t wxDateTime::GetDayOfYear(const TimeZone& tz) const
1527 {
1528 Tm tm(GetTm(tz));
1529
1530 return gs_cumulatedDays[IsLeapYear(tm.year)][tm.mon] + tm.mday;
1531 }
1532
1533 wxDateTime::wxDateTime_t wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags,
1534 const TimeZone& tz) const
1535 {
1536 if ( flags == Default_First )
1537 {
1538 flags = GetCountry() == USA ? Sunday_First : Monday_First;
1539 }
1540
1541 wxDateTime_t nDayInYear = GetDayOfYear(tz);
1542 wxDateTime_t week;
1543
1544 WeekDay wd = GetWeekDay(tz);
1545 if ( flags == Sunday_First )
1546 {
1547 week = (nDayInYear - wd + 7) / 7;
1548 }
1549 else
1550 {
1551 // have to shift the week days values
1552 week = (nDayInYear - (wd - 1 + 7) % 7 + 7) / 7;
1553 }
1554
1555 // FIXME some more elegant way??
1556 WeekDay wdYearStart = wxDateTime(1, Jan, GetYear()).GetWeekDay();
1557 if ( wdYearStart == Wed || wdYearStart == Thu )
1558 {
1559 week++;
1560 }
1561
1562 return week;
1563 }
1564
1565 wxDateTime::wxDateTime_t wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags,
1566 const TimeZone& tz) const
1567 {
1568 Tm tm = GetTm(tz);
1569 wxDateTime dtMonthStart = wxDateTime(1, tm.mon, tm.year);
1570 size_t nWeek = GetWeekOfYear(flags) - dtMonthStart.GetWeekOfYear(flags) + 1;
1571 if ( nWeek < 0 )
1572 {
1573 // this may happen for January when Jan, 1 is the last week of the
1574 // previous year
1575 nWeek += IsLeapYear(tm.year - 1) ? 53 : 52;
1576 }
1577
1578 return nWeek;
1579 }
1580
1581 wxDateTime& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday)
1582 {
1583 int year = GetYear();
1584 wxCHECK_MSG( (0 < yday) && (yday <= GetNumberOfDays(year)),
1585 wxInvalidDateTime, _T("invalid year day") );
1586
1587 bool isLeap = IsLeapYear(year);
1588 for ( Month mon = Jan; mon < Inv_Month; wxNextMonth(mon) )
1589 {
1590 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
1591 // don't need it neither - because of the CHECK above we know that
1592 // yday lies in December then
1593 if ( (mon == Dec) || (yday < gs_cumulatedDays[isLeap][mon + 1]) )
1594 {
1595 Set(yday - gs_cumulatedDays[isLeap][mon], mon, year);
1596
1597 break;
1598 }
1599 }
1600
1601 return *this;
1602 }
1603
1604 // ----------------------------------------------------------------------------
1605 // Julian day number conversion and related stuff
1606 // ----------------------------------------------------------------------------
1607
1608 double wxDateTime::GetJulianDayNumber() const
1609 {
1610 // JDN are always expressed for the GMT dates
1611 Tm tm(ToTimezone(GMT0).GetTm(GMT0));
1612
1613 double result = GetTruncatedJDN(tm.mday, tm.mon, tm.year);
1614
1615 // add the part GetTruncatedJDN() neglected
1616 result += 0.5;
1617
1618 // and now add the time: 86400 sec = 1 JDN
1619 return result + ((double)(60*(60*tm.hour + tm.min) + tm.sec)) / 86400;
1620 }
1621
1622 double wxDateTime::GetRataDie() const
1623 {
1624 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
1625 return GetJulianDayNumber() - 1721119.5 - 306;
1626 }
1627
1628 // ----------------------------------------------------------------------------
1629 // timezone and DST stuff
1630 // ----------------------------------------------------------------------------
1631
1632 int wxDateTime::IsDST(wxDateTime::Country country) const
1633 {
1634 wxCHECK_MSG( country == Country_Default, -1,
1635 _T("country support not implemented") );
1636
1637 // use the C RTL for the dates in the standard range
1638 time_t timet = GetTicks();
1639 if ( timet != (time_t)-1 )
1640 {
1641 tm *tm = localtime(&timet);
1642
1643 wxCHECK_MSG( tm, -1, _T("localtime() failed") );
1644
1645 return tm->tm_isdst;
1646 }
1647 else
1648 {
1649 int year = GetYear();
1650
1651 if ( !IsDSTApplicable(year, country) )
1652 {
1653 // no DST time in this year in this country
1654 return -1;
1655 }
1656
1657 return IsBetween(GetBeginDST(year, country), GetEndDST(year, country));
1658 }
1659 }
1660
1661 wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST)
1662 {
1663 int secDiff = GetTimeZone() + tz.GetOffset();
1664
1665 // we need to know whether DST is or not in effect for this date unless
1666 // the test disabled by the caller
1667 if ( !noDST && (IsDST() == 1) )
1668 {
1669 // FIXME we assume that the DST is always shifted by 1 hour
1670 secDiff -= 3600;
1671 }
1672
1673 return Substract(wxTimeSpan::Seconds(secDiff));
1674 }
1675
1676 // ----------------------------------------------------------------------------
1677 // wxDateTime to/from text representations
1678 // ----------------------------------------------------------------------------
1679
1680 wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
1681 {
1682 #ifdef __VMS__
1683 int time2;
1684 #endif
1685 wxCHECK_MSG( format, _T(""), _T("NULL format in wxDateTime::Format") );
1686
1687 time_t time = GetTicks();
1688 if ( time != (time_t)-1 )
1689 {
1690 // use strftime()
1691 tm *tm;
1692 if ( tz.GetOffset() == -GetTimeZone() )
1693 {
1694 // we are working with local time
1695 tm = localtime(&time);
1696
1697 // should never happen
1698 wxCHECK_MSG( tm, wxEmptyString, _T("localtime() failed") );
1699 }
1700 else
1701 {
1702 time += tz.GetOffset();
1703
1704 #ifdef __VMS__ /* time is unsigned so VMS gives a warning on the original */
1705 time2 = (int) time;
1706 if ( time2 >= 0 )
1707 #else
1708 if ( time >= 0 )
1709 #endif
1710 {
1711 tm = gmtime(&time);
1712
1713 // should never happen
1714 wxCHECK_MSG( tm, wxEmptyString, _T("gmtime() failed") );
1715 }
1716 else
1717 {
1718 tm = (struct tm *)NULL;
1719 }
1720 }
1721
1722 if ( tm )
1723 {
1724 return CallStrftime(format, tm);
1725 }
1726 //else: use generic code below
1727 }
1728
1729 // we only parse ANSI C format specifications here, no POSIX 2
1730 // complications, no GNU extensions
1731 Tm tm = GetTm(tz);
1732
1733 // used for calls to strftime() when we only deal with time
1734 struct tm tmTimeOnly;
1735 tmTimeOnly.tm_hour = tm.hour;
1736 tmTimeOnly.tm_min = tm.min;
1737 tmTimeOnly.tm_sec = tm.sec;
1738 tmTimeOnly.tm_wday = 0;
1739 tmTimeOnly.tm_yday = 0;
1740 tmTimeOnly.tm_mday = 1; // any date will do
1741 tmTimeOnly.tm_mon = 0;
1742 tmTimeOnly.tm_year = 76;
1743 tmTimeOnly.tm_isdst = 0; // no DST, we adjust for tz ourselves
1744
1745 wxString tmp, res, fmt;
1746 for ( const wxChar *p = format; *p; p++ )
1747 {
1748 if ( *p != _T('%') )
1749 {
1750 // copy as is
1751 res += *p;
1752
1753 continue;
1754 }
1755
1756 // set the default format
1757 switch ( *++p )
1758 {
1759 case _T('Y'): // year has 4 digits
1760 fmt = _T("%04d");
1761 break;
1762
1763 case _T('j'): // day of year has 3 digits
1764 fmt = _T("%03d");
1765 break;
1766
1767 default:
1768 // it's either another valid format specifier in which case
1769 // the format is "%02d" (for all the rest) or we have the
1770 // field width preceding the format in which case it will
1771 // override the default format anyhow
1772 fmt = _T("%02d");
1773 }
1774
1775 restart:
1776 // start of the format specification
1777 switch ( *p )
1778 {
1779 case _T('a'): // a weekday name
1780 case _T('A'):
1781 // second parameter should be TRUE for abbreviated names
1782 res += GetWeekDayName(tm.GetWeekDay(),
1783 *p == _T('a') ? Name_Abbr : Name_Full);
1784 break;
1785
1786 case _T('b'): // a month name
1787 case _T('B'):
1788 res += GetMonthName(tm.mon,
1789 *p == _T('b') ? Name_Abbr : Name_Full);
1790 break;
1791
1792 case _T('c'): // locale default date and time representation
1793 case _T('x'): // locale default date representation
1794 //
1795 // the problem: there is no way to know what do these format
1796 // specifications correspond to for the current locale.
1797 //
1798 // the solution: use a hack and still use strftime(): first
1799 // find the YEAR which is a year in the strftime() range (1970
1800 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
1801 // of the real year. Then make a copy of the format and
1802 // replace all occurences of YEAR in it with some unique
1803 // string not appearing anywhere else in it, then use
1804 // strftime() to format the date in year YEAR and then replace
1805 // YEAR back by the real year and the unique replacement
1806 // string back with YEAR. Notice that "all occurences of YEAR"
1807 // means all occurences of 4 digit as well as 2 digit form!
1808 //
1809 // the bugs: we assume that neither of %c nor %x contains any
1810 // fields which may change between the YEAR and real year. For
1811 // example, the week number (%U, %W) and the day number (%j)
1812 // will change if one of these years is leap and the other one
1813 // is not!
1814 {
1815 // find the YEAR: normally, for any year X, Jan 1 or the
1816 // year X + 28 is the same weekday as Jan 1 of X (because
1817 // the weekday advances by 1 for each normal X and by 2
1818 // for each leap X, hence by 5 every 4 years or by 35
1819 // which is 0 mod 7 every 28 years) but this rule breaks
1820 // down if there are years between X and Y which are
1821 // divisible by 4 but not leap (i.e. divisible by 100 but
1822 // not 400), hence the correction.
1823
1824 int yearReal = GetYear(tz);
1825 int mod28 = yearReal % 28;
1826
1827 // be careful to not go too far - we risk to leave the
1828 // supported range
1829 int year;
1830 if ( mod28 < 10 )
1831 {
1832 year = 1988 + mod28; // 1988 == 0 (mod 28)
1833 }
1834 else
1835 {
1836 year = 1970 + mod28 - 10; // 1970 == 10 (mod 28)
1837 }
1838
1839 int nCentury = year / 100,
1840 nCenturyReal = yearReal / 100;
1841
1842 // need to adjust for the years divisble by 400 which are
1843 // not leap but are counted like leap ones if we just take
1844 // the number of centuries in between for nLostWeekDays
1845 int nLostWeekDays = (nCentury - nCenturyReal) -
1846 (nCentury / 4 - nCenturyReal / 4);
1847
1848 // we have to gain back the "lost" weekdays: note that the
1849 // effect of this loop is to not do anything to
1850 // nLostWeekDays (which we won't use any more), but to
1851 // (indirectly) set the year correctly
1852 while ( (nLostWeekDays % 7) != 0 )
1853 {
1854 nLostWeekDays += year++ % 4 ? 1 : 2;
1855 }
1856
1857 // at any rate, we couldn't go further than 1988 + 9 + 28!
1858 wxASSERT_MSG( year < 2030,
1859 _T("logic error in wxDateTime::Format") );
1860
1861 wxString strYear, strYear2;
1862 strYear.Printf(_T("%d"), year);
1863 strYear2.Printf(_T("%d"), year % 100);
1864
1865 // find two strings not occuring in format (this is surely
1866 // not optimal way of doing it... improvements welcome!)
1867 wxString fmt = format;
1868 wxString replacement = (wxChar)-1;
1869 while ( fmt.Find(replacement) != wxNOT_FOUND )
1870 {
1871 replacement << (wxChar)-1;
1872 }
1873
1874 wxString replacement2 = (wxChar)-2;
1875 while ( fmt.Find(replacement) != wxNOT_FOUND )
1876 {
1877 replacement << (wxChar)-2;
1878 }
1879
1880 // replace all occurences of year with it
1881 bool wasReplaced = fmt.Replace(strYear, replacement) > 0;
1882 if ( !wasReplaced )
1883 wasReplaced = fmt.Replace(strYear2, replacement2) > 0;
1884
1885 // use strftime() to format the same date but in supported
1886 // year
1887 //
1888 // NB: we assume that strftime() doesn't check for the
1889 // date validity and will happily format the date
1890 // corresponding to Feb 29 of a non leap year (which
1891 // may happen if yearReal was leap and year is not)
1892 struct tm tmAdjusted;
1893 InitTm(tmAdjusted);
1894 tmAdjusted.tm_hour = tm.hour;
1895 tmAdjusted.tm_min = tm.min;
1896 tmAdjusted.tm_sec = tm.sec;
1897 tmAdjusted.tm_wday = tm.GetWeekDay();
1898 tmAdjusted.tm_yday = GetDayOfYear();
1899 tmAdjusted.tm_mday = tm.mday;
1900 tmAdjusted.tm_mon = tm.mon;
1901 tmAdjusted.tm_year = year - 1900;
1902 tmAdjusted.tm_isdst = 0; // no DST, already adjusted
1903 wxString str = CallStrftime(*p == _T('c') ? _T("%c")
1904 : _T("%x"),
1905 &tmAdjusted);
1906
1907 // now replace the occurence of 1999 with the real year
1908 wxString strYearReal, strYearReal2;
1909 strYearReal.Printf(_T("%04d"), yearReal);
1910 strYearReal2.Printf(_T("%02d"), yearReal % 100);
1911 str.Replace(strYear, strYearReal);
1912 str.Replace(strYear2, strYearReal2);
1913
1914 // and replace back all occurences of replacement string
1915 if ( wasReplaced )
1916 {
1917 str.Replace(replacement2, strYear2);
1918 str.Replace(replacement, strYear);
1919 }
1920
1921 res += str;
1922 }
1923 break;
1924
1925 case _T('d'): // day of a month (01-31)
1926 res += wxString::Format(fmt, tm.mday);
1927 break;
1928
1929 case _T('H'): // hour in 24h format (00-23)
1930 res += wxString::Format(fmt, tm.hour);
1931 break;
1932
1933 case _T('I'): // hour in 12h format (01-12)
1934 {
1935 // 24h -> 12h, 0h -> 12h too
1936 int hour12 = tm.hour > 12 ? tm.hour - 12
1937 : tm.hour ? tm.hour : 12;
1938 res += wxString::Format(fmt, hour12);
1939 }
1940 break;
1941
1942 case _T('j'): // day of the year
1943 res += wxString::Format(fmt, GetDayOfYear(tz));
1944 break;
1945
1946 case _T('m'): // month as a number (01-12)
1947 res += wxString::Format(fmt, tm.mon + 1);
1948 break;
1949
1950 case _T('M'): // minute as a decimal number (00-59)
1951 res += wxString::Format(fmt, tm.min);
1952 break;
1953
1954 case _T('p'): // AM or PM string
1955 res += CallStrftime(_T("%p"), &tmTimeOnly);
1956 break;
1957
1958 case _T('S'): // second as a decimal number (00-61)
1959 res += wxString::Format(fmt, tm.sec);
1960 break;
1961
1962 case _T('U'): // week number in the year (Sunday 1st week day)
1963 res += wxString::Format(fmt, GetWeekOfYear(Sunday_First, tz));
1964 break;
1965
1966 case _T('W'): // week number in the year (Monday 1st week day)
1967 res += wxString::Format(fmt, GetWeekOfYear(Monday_First, tz));
1968 break;
1969
1970 case _T('w'): // weekday as a number (0-6), Sunday = 0
1971 res += wxString::Format(fmt, tm.GetWeekDay());
1972 break;
1973
1974 // case _T('x'): -- handled with "%c"
1975
1976 case _T('X'): // locale default time representation
1977 // just use strftime() to format the time for us
1978 res += CallStrftime(_T("%X"), &tmTimeOnly);
1979 break;
1980
1981 case _T('y'): // year without century (00-99)
1982 res += wxString::Format(fmt, tm.year % 100);
1983 break;
1984
1985 case _T('Y'): // year with century
1986 res += wxString::Format(fmt, tm.year);
1987 break;
1988
1989 case _T('Z'): // timezone name
1990 res += CallStrftime(_T("%Z"), &tmTimeOnly);
1991 break;
1992
1993 default:
1994 // is it the format width?
1995 fmt.Empty();
1996 while ( *p == _T('-') || *p == _T('+') ||
1997 *p == _T(' ') || wxIsdigit(*p) )
1998 {
1999 fmt += *p;
2000 }
2001
2002 if ( !fmt.IsEmpty() )
2003 {
2004 // we've only got the flags and width so far in fmt
2005 fmt.Prepend(_T('%'));
2006 fmt.Append(_T('d'));
2007
2008 goto restart;
2009 }
2010
2011 // no, it wasn't the width
2012 wxFAIL_MSG(_T("unknown format specificator"));
2013
2014 // fall through and just copy it nevertheless
2015
2016 case _T('%'): // a percent sign
2017 res += *p;
2018 break;
2019
2020 case 0: // the end of string
2021 wxFAIL_MSG(_T("missing format at the end of string"));
2022
2023 // just put the '%' which was the last char in format
2024 res += _T('%');
2025 break;
2026 }
2027 }
2028
2029 return res;
2030 }
2031
2032 // this function parses a string in (strict) RFC 822 format: see the section 5
2033 // of the RFC for the detailed description, but briefly it's something of the
2034 // form "Sat, 18 Dec 1999 00:48:30 +0100"
2035 //
2036 // this function is "strict" by design - it must reject anything except true
2037 // RFC822 time specs.
2038 //
2039 // TODO a great candidate for using reg exps
2040 const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date)
2041 {
2042 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
2043
2044 const wxChar *p = date;
2045 const wxChar *comma = wxStrchr(p, _T(','));
2046 if ( comma )
2047 {
2048 // the part before comma is the weekday
2049
2050 // skip it for now - we don't use but might check that it really
2051 // corresponds to the specfied date
2052 p = comma + 1;
2053
2054 if ( *p != _T(' ') )
2055 {
2056 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
2057
2058 return (wxChar *)NULL;
2059 }
2060
2061 p++; // skip space
2062 }
2063
2064 // the following 1 or 2 digits are the day number
2065 if ( !wxIsdigit(*p) )
2066 {
2067 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
2068
2069 return (wxChar *)NULL;
2070 }
2071
2072 wxDateTime_t day = *p++ - _T('0');
2073 if ( wxIsdigit(*p) )
2074 {
2075 day *= 10;
2076 day += *p++ - _T('0');
2077 }
2078
2079 if ( *p++ != _T(' ') )
2080 {
2081 return (wxChar *)NULL;
2082 }
2083
2084 // the following 3 letters specify the month
2085 wxString monName(p, 3);
2086 Month mon;
2087 if ( monName == _T("Jan") )
2088 mon = Jan;
2089 else if ( monName == _T("Feb") )
2090 mon = Feb;
2091 else if ( monName == _T("Mar") )
2092 mon = Mar;
2093 else if ( monName == _T("Apr") )
2094 mon = Apr;
2095 else if ( monName == _T("May") )
2096 mon = May;
2097 else if ( monName == _T("Jun") )
2098 mon = Jun;
2099 else if ( monName == _T("Jul") )
2100 mon = Jul;
2101 else if ( monName == _T("Aug") )
2102 mon = Aug;
2103 else if ( monName == _T("Sep") )
2104 mon = Sep;
2105 else if ( monName == _T("Oct") )
2106 mon = Oct;
2107 else if ( monName == _T("Nov") )
2108 mon = Nov;
2109 else if ( monName == _T("Dec") )
2110 mon = Dec;
2111 else
2112 {
2113 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName.c_str());
2114
2115 return (wxChar *)NULL;
2116 }
2117
2118 p += 3;
2119
2120 if ( *p++ != _T(' ') )
2121 {
2122 return (wxChar *)NULL;
2123 }
2124
2125 // next is the year
2126 if ( !wxIsdigit(*p) )
2127 {
2128 // no year?
2129 return (wxChar *)NULL;
2130 }
2131
2132 int year = *p++ - _T('0');
2133
2134 if ( !wxIsdigit(*p) )
2135 {
2136 // should have at least 2 digits in the year
2137 return (wxChar *)NULL;
2138 }
2139
2140 year *= 10;
2141 year += *p++ - _T('0');
2142
2143 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
2144 if ( wxIsdigit(*p) )
2145 {
2146 year *= 10;
2147 year += *p++ - _T('0');
2148
2149 if ( !wxIsdigit(*p) )
2150 {
2151 // no 3 digit years please
2152 return (wxChar *)NULL;
2153 }
2154
2155 year *= 10;
2156 year += *p++ - _T('0');
2157 }
2158
2159 if ( *p++ != _T(' ') )
2160 {
2161 return (wxChar *)NULL;
2162 }
2163
2164 // time is in the format hh:mm:ss and seconds are optional
2165 if ( !wxIsdigit(*p) )
2166 {
2167 return (wxChar *)NULL;
2168 }
2169
2170 wxDateTime_t hour = *p++ - _T('0');
2171
2172 if ( !wxIsdigit(*p) )
2173 {
2174 return (wxChar *)NULL;
2175 }
2176
2177 hour *= 10;
2178 hour += *p++ - _T('0');
2179
2180 if ( *p++ != _T(':') )
2181 {
2182 return (wxChar *)NULL;
2183 }
2184
2185 if ( !wxIsdigit(*p) )
2186 {
2187 return (wxChar *)NULL;
2188 }
2189
2190 wxDateTime_t min = *p++ - _T('0');
2191
2192 if ( !wxIsdigit(*p) )
2193 {
2194 return (wxChar *)NULL;
2195 }
2196
2197 min *= 10;
2198 min += *p++ - _T('0');
2199
2200 wxDateTime_t sec = 0;
2201 if ( *p++ == _T(':') )
2202 {
2203 if ( !wxIsdigit(*p) )
2204 {
2205 return (wxChar *)NULL;
2206 }
2207
2208 sec = *p++ - _T('0');
2209
2210 if ( !wxIsdigit(*p) )
2211 {
2212 return (wxChar *)NULL;
2213 }
2214
2215 sec *= 10;
2216 sec += *p++ - _T('0');
2217 }
2218
2219 if ( *p++ != _T(' ') )
2220 {
2221 return (wxChar *)NULL;
2222 }
2223
2224 // and now the interesting part: the timezone
2225 int offset;
2226 if ( *p == _T('-') || *p == _T('+') )
2227 {
2228 // the explicit offset given: it has the form of hhmm
2229 bool plus = *p++ == _T('+');
2230
2231 if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) )
2232 {
2233 return (wxChar *)NULL;
2234 }
2235
2236 // hours
2237 offset = 60*(10*(*p - _T('0')) + (*(p + 1) - _T('0')));
2238
2239 p += 2;
2240
2241 if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) )
2242 {
2243 return (wxChar *)NULL;
2244 }
2245
2246 // minutes
2247 offset += 10*(*p - _T('0')) + (*(p + 1) - _T('0'));
2248
2249 if ( !plus )
2250 {
2251 offset = -offset;
2252 }
2253
2254 p += 2;
2255 }
2256 else
2257 {
2258 // the symbolic timezone given: may be either military timezone or one
2259 // of standard abbreviations
2260 if ( !*(p + 1) )
2261 {
2262 // military: Z = UTC, J unused, A = -1, ..., Y = +12
2263 static const int offsets[26] =
2264 {
2265 //A B C D E F G H I J K L M
2266 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
2267 //N O P R Q S T U V W Z Y Z
2268 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
2269 };
2270
2271 if ( *p < _T('A') || *p > _T('Z') || *p == _T('J') )
2272 {
2273 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p);
2274
2275 return (wxChar *)NULL;
2276 }
2277
2278 offset = offsets[*p++ - _T('A')];
2279 }
2280 else
2281 {
2282 // abbreviation
2283 wxString tz = p;
2284 if ( tz == _T("UT") || tz == _T("UTC") || tz == _T("GMT") )
2285 offset = 0;
2286 else if ( tz == _T("AST") )
2287 offset = AST - GMT0;
2288 else if ( tz == _T("ADT") )
2289 offset = ADT - GMT0;
2290 else if ( tz == _T("EST") )
2291 offset = EST - GMT0;
2292 else if ( tz == _T("EDT") )
2293 offset = EDT - GMT0;
2294 else if ( tz == _T("CST") )
2295 offset = CST - GMT0;
2296 else if ( tz == _T("CDT") )
2297 offset = CDT - GMT0;
2298 else if ( tz == _T("MST") )
2299 offset = MST - GMT0;
2300 else if ( tz == _T("MDT") )
2301 offset = MDT - GMT0;
2302 else if ( tz == _T("PST") )
2303 offset = PST - GMT0;
2304 else if ( tz == _T("PDT") )
2305 offset = PDT - GMT0;
2306 else
2307 {
2308 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p);
2309
2310 return (wxChar *)NULL;
2311 }
2312
2313 p += tz.length();
2314 }
2315
2316 // make it minutes
2317 offset *= 60;
2318 }
2319
2320 // the spec was correct
2321 Set(day, mon, year, hour, min, sec);
2322 MakeTimezone((wxDateTime_t)(60*offset));
2323
2324 return p;
2325 }
2326
2327 const wxChar *wxDateTime::ParseFormat(const wxChar *date,
2328 const wxChar *format,
2329 const wxDateTime& dateDef)
2330 {
2331 wxCHECK_MSG( date && format, (wxChar *)NULL,
2332 _T("NULL pointer in wxDateTime::ParseFormat()") );
2333
2334 wxString str;
2335 unsigned long num;
2336
2337 // what fields have we found?
2338 bool haveWDay = FALSE,
2339 haveYDay = FALSE,
2340 haveDay = FALSE,
2341 haveMon = FALSE,
2342 haveYear = FALSE,
2343 haveHour = FALSE,
2344 haveMin = FALSE,
2345 haveSec = FALSE;
2346
2347 bool hourIsIn12hFormat = FALSE, // or in 24h one?
2348 isPM = FALSE; // AM by default
2349
2350 // and the value of the items we have (init them to get rid of warnings)
2351 wxDateTime_t sec = 0,
2352 min = 0,
2353 hour = 0;
2354 WeekDay wday = Inv_WeekDay;
2355 wxDateTime_t yday = 0,
2356 mday = 0;
2357 wxDateTime::Month mon = Inv_Month;
2358 int year = 0;
2359
2360 const wxChar *input = date;
2361 for ( const wxChar *fmt = format; *fmt; fmt++ )
2362 {
2363 if ( *fmt != _T('%') )
2364 {
2365 if ( wxIsspace(*fmt) )
2366 {
2367 // a white space in the format string matches 0 or more white
2368 // spaces in the input
2369 while ( wxIsspace(*input) )
2370 {
2371 input++;
2372 }
2373 }
2374 else // !space
2375 {
2376 // any other character (not whitespace, not '%') must be
2377 // matched by itself in the input
2378 if ( *input++ != *fmt )
2379 {
2380 // no match
2381 return (wxChar *)NULL;
2382 }
2383 }
2384
2385 // done with this format char
2386 continue;
2387 }
2388
2389 // start of a format specification
2390 switch ( *++fmt )
2391 {
2392 case _T('a'): // a weekday name
2393 case _T('A'):
2394 {
2395 int flag = *fmt == _T('a') ? Name_Abbr : Name_Full;
2396 wday = GetWeekDayFromName(GetAlphaToken(input), flag);
2397 if ( wday == Inv_WeekDay )
2398 {
2399 // no match
2400 return (wxChar *)NULL;
2401 }
2402 }
2403 haveWDay = TRUE;
2404 break;
2405
2406 case _T('b'): // a month name
2407 case _T('B'):
2408 {
2409 int flag = *fmt == _T('b') ? Name_Abbr : Name_Full;
2410 mon = GetMonthFromName(GetAlphaToken(input), flag);
2411 if ( mon == Inv_Month )
2412 {
2413 // no match
2414 return (wxChar *)NULL;
2415 }
2416 }
2417 haveMon = TRUE;
2418 break;
2419
2420 case _T('c'): // locale default date and time representation
2421 {
2422 wxDateTime dt;
2423
2424 // this is the format which corresponds to ctime() output
2425 // and strptime("%c") should parse it, so try it first
2426 static const wxChar *fmtCtime = _T("%a %b %d %H:%M:%S %Y");
2427
2428 const wxChar *result = dt.ParseFormat(input, fmtCtime);
2429 if ( !result )
2430 {
2431 result = dt.ParseFormat(input, _T("%x %X"));
2432 }
2433
2434 if ( !result )
2435 {
2436 result = dt.ParseFormat(input, _T("%X %x"));
2437 }
2438
2439 if ( !result )
2440 {
2441 // we've tried everything and still no match
2442 return (wxChar *)NULL;
2443 }
2444
2445 Tm tm = dt.GetTm();
2446
2447 haveDay = haveMon = haveYear =
2448 haveHour = haveMin = haveSec = TRUE;
2449
2450 hour = tm.hour;
2451 min = tm.min;
2452 sec = tm.sec;
2453
2454 year = tm.year;
2455 mon = tm.mon;
2456 mday = tm.mday;
2457
2458 input = result;
2459 }
2460 break;
2461
2462 case _T('d'): // day of a month (01-31)
2463 if ( !GetNumericToken(input, &num) || (num > 31) )
2464 {
2465 // no match
2466 return (wxChar *)NULL;
2467 }
2468
2469 // we can't check whether the day range is correct yet, will
2470 // do it later - assume ok for now
2471 haveDay = TRUE;
2472 mday = (wxDateTime_t)num;
2473 break;
2474
2475 case _T('H'): // hour in 24h format (00-23)
2476 if ( !GetNumericToken(input, &num) || (num > 23) )
2477 {
2478 // no match
2479 return (wxChar *)NULL;
2480 }
2481
2482 haveHour = TRUE;
2483 hour = (wxDateTime_t)num;
2484 break;
2485
2486 case _T('I'): // hour in 12h format (01-12)
2487 if ( !GetNumericToken(input, &num) || !num || (num > 12) )
2488 {
2489 // no match
2490 return (wxChar *)NULL;
2491 }
2492
2493 haveHour = TRUE;
2494 hourIsIn12hFormat = TRUE;
2495 hour = num % 12; // 12 should be 0
2496 break;
2497
2498 case _T('j'): // day of the year
2499 if ( !GetNumericToken(input, &num) || !num || (num > 366) )
2500 {
2501 // no match
2502 return (wxChar *)NULL;
2503 }
2504
2505 haveYDay = TRUE;
2506 yday = (wxDateTime_t)num;
2507 break;
2508
2509 case _T('m'): // month as a number (01-12)
2510 if ( !GetNumericToken(input, &num) || !num || (num > 12) )
2511 {
2512 // no match
2513 return (wxChar *)NULL;
2514 }
2515
2516 haveMon = TRUE;
2517 mon = (Month)(num - 1);
2518 break;
2519
2520 case _T('M'): // minute as a decimal number (00-59)
2521 if ( !GetNumericToken(input, &num) || (num > 59) )
2522 {
2523 // no match
2524 return (wxChar *)NULL;
2525 }
2526
2527 haveMin = TRUE;
2528 min = (wxDateTime_t)num;
2529 break;
2530
2531 case _T('p'): // AM or PM string
2532 {
2533 wxString am, pm, token = GetAlphaToken(input);
2534
2535 GetAmPmStrings(&am, &pm);
2536 if ( token.CmpNoCase(pm) == 0 )
2537 {
2538 isPM = TRUE;
2539 }
2540 else if ( token.CmpNoCase(am) != 0 )
2541 {
2542 // no match
2543 return (wxChar *)NULL;
2544 }
2545 }
2546 break;
2547
2548 case _T('r'): // time as %I:%M:%S %p
2549 {
2550 wxDateTime dt;
2551 input = dt.ParseFormat(input, _T("%I:%M:%S %p"));
2552 if ( !input )
2553 {
2554 // no match
2555 return (wxChar *)NULL;
2556 }
2557
2558 haveHour = haveMin = haveSec = TRUE;
2559
2560 Tm tm = dt.GetTm();
2561 hour = tm.hour;
2562 min = tm.min;
2563 sec = tm.sec;
2564 }
2565 break;
2566
2567 case _T('R'): // time as %H:%M
2568 {
2569 wxDateTime dt;
2570 input = dt.ParseFormat(input, _T("%H:%M"));
2571 if ( !input )
2572 {
2573 // no match
2574 return (wxChar *)NULL;
2575 }
2576
2577 haveHour = haveMin = TRUE;
2578
2579 Tm tm = dt.GetTm();
2580 hour = tm.hour;
2581 min = tm.min;
2582 }
2583
2584 case _T('S'): // second as a decimal number (00-61)
2585 if ( !GetNumericToken(input, &num) || (num > 61) )
2586 {
2587 // no match
2588 return (wxChar *)NULL;
2589 }
2590
2591 haveSec = TRUE;
2592 sec = (wxDateTime_t)num;
2593 break;
2594
2595 case _T('T'): // time as %H:%M:%S
2596 {
2597 wxDateTime dt;
2598 input = dt.ParseFormat(input, _T("%H:%M:%S"));
2599 if ( !input )
2600 {
2601 // no match
2602 return (wxChar *)NULL;
2603 }
2604
2605 haveHour = haveMin = haveSec = TRUE;
2606
2607 Tm tm = dt.GetTm();
2608 hour = tm.hour;
2609 min = tm.min;
2610 sec = tm.sec;
2611 }
2612 break;
2613
2614 case _T('w'): // weekday as a number (0-6), Sunday = 0
2615 if ( !GetNumericToken(input, &num) || (wday > 6) )
2616 {
2617 // no match
2618 return (wxChar *)NULL;
2619 }
2620
2621 haveWDay = TRUE;
2622 wday = (WeekDay)num;
2623 break;
2624
2625 case _T('x'): // locale default date representation
2626 #ifdef HAVE_STRPTIME
2627 // try using strptime() - it may fail even if the input is
2628 // correct but the date is out of range, so we will fall back
2629 // to our generic code anyhow (FIXME !Unicode friendly)
2630 {
2631 struct tm tm;
2632 const wxChar *result = strptime(input, "%x", &tm);
2633 if ( result )
2634 {
2635 input = result;
2636
2637 haveDay = haveMon = haveYear = TRUE;
2638
2639 year = 1900 + tm.tm_year;
2640 mon = (Month)tm.tm_mon;
2641 mday = tm.tm_mday;
2642
2643 break;
2644 }
2645 }
2646 #endif // HAVE_STRPTIME
2647
2648 // TODO query the LOCALE_IDATE setting under Win32
2649 {
2650 wxDateTime dt;
2651
2652 wxString fmtDate, fmtDateAlt;
2653 if ( IsWestEuropeanCountry(GetCountry()) ||
2654 GetCountry() == Russia )
2655 {
2656 fmtDate = _T("%d/%m/%y");
2657 fmtDateAlt = _T("%m/%d/%y");
2658 }
2659 else // assume USA
2660 {
2661 fmtDate = _T("%m/%d/%y");
2662 fmtDateAlt = _T("%d/%m/%y");
2663 }
2664
2665 const wxChar *result = dt.ParseFormat(input, fmtDate);
2666
2667 if ( !result )
2668 {
2669 // ok, be nice and try another one
2670 result = dt.ParseFormat(input, fmtDateAlt);
2671 }
2672
2673 if ( !result )
2674 {
2675 // bad luck
2676 return (wxChar *)NULL;
2677 }
2678
2679 Tm tm = dt.GetTm();
2680
2681 haveDay = haveMon = haveYear = TRUE;
2682
2683 year = tm.year;
2684 mon = tm.mon;
2685 mday = tm.mday;
2686
2687 input = result;
2688 }
2689
2690 break;
2691
2692 case _T('X'): // locale default time representation
2693 #ifdef HAVE_STRPTIME
2694 {
2695 // use strptime() to do it for us (FIXME !Unicode friendly)
2696 struct tm tm;
2697 input = strptime(input, "%X", &tm);
2698 if ( !input )
2699 {
2700 return (wxChar *)NULL;
2701 }
2702
2703 haveHour = haveMin = haveSec = TRUE;
2704
2705 hour = tm.tm_hour;
2706 min = tm.tm_min;
2707 sec = tm.tm_sec;
2708 }
2709 #else // !HAVE_STRPTIME
2710 // TODO under Win32 we can query the LOCALE_ITIME system
2711 // setting which says whether the default time format is
2712 // 24 or 12 hour
2713 {
2714 // try to parse what follows as "%H:%M:%S" and, if this
2715 // fails, as "%I:%M:%S %p" - this should catch the most
2716 // common cases
2717 wxDateTime dt;
2718
2719 const wxChar *result = dt.ParseFormat(input, _T("%T"));
2720 if ( !result )
2721 {
2722 result = dt.ParseFormat(input, _T("%r"));
2723 }
2724
2725 if ( !result )
2726 {
2727 // no match
2728 return (wxChar *)NULL;
2729 }
2730
2731 haveHour = haveMin = haveSec = TRUE;
2732
2733 Tm tm = dt.GetTm();
2734 hour = tm.hour;
2735 min = tm.min;
2736 sec = tm.sec;
2737
2738 input = result;
2739 }
2740 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
2741 break;
2742
2743 case _T('y'): // year without century (00-99)
2744 if ( !GetNumericToken(input, &num) || (num > 99) )
2745 {
2746 // no match
2747 return (wxChar *)NULL;
2748 }
2749
2750 haveYear = TRUE;
2751 year = 1900 + num;
2752 break;
2753
2754 case _T('Y'): // year with century
2755 if ( !GetNumericToken(input, &num) )
2756 {
2757 // no match
2758 return (wxChar *)NULL;
2759 }
2760
2761 haveYear = TRUE;
2762 year = (wxDateTime_t)num;
2763 break;
2764
2765 case _T('Z'): // timezone name
2766 wxFAIL_MSG(_T("TODO"));
2767 break;
2768
2769 case _T('%'): // a percent sign
2770 if ( *input++ != _T('%') )
2771 {
2772 // no match
2773 return (wxChar *)NULL;
2774 }
2775 break;
2776
2777 case 0: // the end of string
2778 wxFAIL_MSG(_T("unexpected format end"));
2779
2780 // fall through
2781
2782 default: // not a known format spec
2783 return (wxChar *)NULL;
2784 }
2785 }
2786
2787 // format matched, try to construct a date from what we have now
2788 Tm tmDef;
2789 if ( dateDef.IsValid() )
2790 {
2791 // take this date as default
2792 tmDef = dateDef.GetTm();
2793 }
2794 else if ( m_time != 0 )
2795 {
2796 // if this date is valid, don't change it
2797 tmDef = GetTm();
2798 }
2799 else
2800 {
2801 // no default and this date is invalid - fall back to Today()
2802 tmDef = Today().GetTm();
2803 }
2804
2805 Tm tm = tmDef;
2806
2807 // set the date
2808 if ( haveYear )
2809 {
2810 tm.year = year;
2811 }
2812
2813 // TODO we don't check here that the values are consistent, if both year
2814 // day and month/day were found, we just ignore the year day and we
2815 // also always ignore the week day
2816 if ( haveMon && haveDay )
2817 {
2818 if ( mday > GetNumOfDaysInMonth(tm.year, mon) )
2819 {
2820 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
2821
2822 return (wxChar *)NULL;
2823 }
2824
2825 tm.mon = mon;
2826 tm.mday = mday;
2827 }
2828 else if ( haveYDay )
2829 {
2830 if ( yday > GetNumberOfDays(tm.year) )
2831 {
2832 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
2833
2834 return (wxChar *)NULL;
2835 }
2836
2837 Tm tm2 = wxDateTime(1, Jan, tm.year).SetToYearDay(yday).GetTm();
2838
2839 tm.mon = tm2.mon;
2840 tm.mday = tm2.mday;
2841 }
2842
2843 // deal with AM/PM
2844 if ( haveHour && hourIsIn12hFormat && isPM )
2845 {
2846 // translate to 24hour format
2847 hour += 12;
2848 }
2849 //else: either already in 24h format or no translation needed
2850
2851 // set the time
2852 if ( haveHour )
2853 {
2854 tm.hour = hour;
2855 }
2856
2857 if ( haveMin )
2858 {
2859 tm.min = min;
2860 }
2861
2862 if ( haveSec )
2863 {
2864 tm.sec = sec;
2865 }
2866
2867 Set(tm);
2868
2869 return input;
2870 }
2871
2872 const wxChar *wxDateTime::ParseDateTime(const wxChar *date)
2873 {
2874 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
2875
2876 // there is a public domain version of getdate.y, but it only works for
2877 // English...
2878 wxFAIL_MSG(_T("TODO"));
2879
2880 return (wxChar *)NULL;
2881 }
2882
2883 const wxChar *wxDateTime::ParseDate(const wxChar *date)
2884 {
2885 // this is a simplified version of ParseDateTime() which understands only
2886 // "today" (for wxDate compatibility) and digits only otherwise (and not
2887 // all esoteric constructions ParseDateTime() knows about)
2888
2889 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
2890
2891 const wxChar *p = date;
2892 while ( wxIsspace(*p) )
2893 p++;
2894
2895 wxString today = _T("today");
2896 size_t len = today.length();
2897 if ( wxString(p, len).CmpNoCase(today) == 0 )
2898 {
2899 // nothing can follow this, so stop here
2900 p += len;
2901
2902 *this = Today();
2903
2904 return p;
2905 }
2906
2907 // what do we have?
2908 bool haveDay = FALSE, // the months day?
2909 haveWDay = FALSE, // the day of week?
2910 haveMon = FALSE, // the month?
2911 haveYear = FALSE; // the year?
2912
2913 // and the value of the items we have (init them to get rid of warnings)
2914 WeekDay wday = Inv_WeekDay;
2915 wxDateTime_t day = 0;
2916 wxDateTime::Month mon = Inv_Month;
2917 int year = 0;
2918
2919 // tokenize the string
2920 wxStringTokenizer tok(p, _T(",/-\t\n "));
2921 while ( tok.HasMoreTokens() )
2922 {
2923 wxString token = tok.GetNextToken();
2924
2925 // is it a number?
2926 unsigned long val;
2927 if ( token.ToULong(&val) )
2928 {
2929 // guess what this number is
2930
2931 bool isDay = FALSE,
2932 isMonth = FALSE,
2933 // only years are counted from 0
2934 isYear = (val == 0) || (val > 31);
2935 if ( !isYear )
2936 {
2937 // may be the month or month day or the year, assume the
2938 // month day by default and fallback to month if the range
2939 // allow it or to the year if our assumption doesn't work
2940 if ( haveDay )
2941 {
2942 // we already have the day, so may only be a month or year
2943 if ( val > 12 )
2944 {
2945 isYear = TRUE;
2946 }
2947 else
2948 {
2949 isMonth = TRUE;
2950 }
2951 }
2952 else // it may be day
2953 {
2954 isDay = TRUE;
2955
2956 // check the range
2957 if ( haveMon )
2958 {
2959 if ( val > GetNumOfDaysInMonth(haveYear ? year
2960 : Inv_Year,
2961 mon) )
2962 {
2963 // oops, it can't be a day finally
2964 isDay = FALSE;
2965
2966 if ( val > 12 )
2967 {
2968 isYear = TRUE;
2969 }
2970 else
2971 {
2972 isMonth = TRUE;
2973 }
2974 }
2975 }
2976 }
2977 }
2978
2979 // remember that we have this and stop the scan if it's the second
2980 // time we find this, except for the day logic (see there)
2981 if ( isYear )
2982 {
2983 if ( haveYear )
2984 {
2985 break;
2986 }
2987
2988 haveYear = TRUE;
2989
2990 // no roll over - 99 means 99, not 1999 for us
2991 year = val;
2992 }
2993 else if ( isMonth )
2994 {
2995 if ( haveMon )
2996 {
2997 break;
2998 }
2999
3000 haveMon = TRUE;
3001
3002 mon = (wxDateTime::Month)val;
3003 }
3004 else
3005 {
3006 wxASSERT_MSG( isDay, _T("logic error") );
3007
3008 if ( haveDay )
3009 {
3010 // may be were mistaken when we found it for the first
3011 // time? may be it was a month or year instead?
3012 //
3013 // this ability to "backtrack" allows us to correctly parse
3014 // both things like 01/13 and 13/01 - but, of course, we
3015 // still can't resolve the ambiguity in 01/02 (it will be
3016 // Feb 1 for us, not Jan 2 as americans might expect!)
3017 if ( (day <= 12) && !haveMon )
3018 {
3019 // exchange day and month
3020 mon = (wxDateTime::Month)day;
3021
3022 haveMon = TRUE;
3023 }
3024 else if ( !haveYear )
3025 {
3026 // exchange day and year
3027 year = day;
3028
3029 haveYear = TRUE;
3030 }
3031 }
3032
3033 haveDay = TRUE;
3034
3035 day = val;
3036 }
3037 }
3038 else // not a number
3039 {
3040 mon = GetMonthFromName(token, Name_Full | Name_Abbr);
3041 if ( mon != Inv_Month )
3042 {
3043 // it's a month
3044 if ( haveMon )
3045 {
3046 break;
3047 }
3048
3049 haveMon = TRUE;
3050 }
3051 else
3052 {
3053 wday = GetWeekDayFromName(token, Name_Full | Name_Abbr);
3054 if ( wday != Inv_WeekDay )
3055 {
3056 // a week day
3057 if ( haveWDay )
3058 {
3059 break;
3060 }
3061
3062 haveWDay = TRUE;
3063 }
3064 else
3065 {
3066 // try the ordinals
3067 static const wxChar *ordinals[] =
3068 {
3069 wxTRANSLATE("first"),
3070 wxTRANSLATE("second"),
3071 wxTRANSLATE("third"),
3072 wxTRANSLATE("fourth"),
3073 wxTRANSLATE("fifth"),
3074 wxTRANSLATE("sixth"),
3075 wxTRANSLATE("seventh"),
3076 wxTRANSLATE("eighth"),
3077 wxTRANSLATE("ninth"),
3078 wxTRANSLATE("tenth"),
3079 wxTRANSLATE("eleventh"),
3080 wxTRANSLATE("twelfth"),
3081 wxTRANSLATE("thirteenth"),
3082 wxTRANSLATE("fourteenth"),
3083 wxTRANSLATE("fifteenth"),
3084 wxTRANSLATE("sixteenth"),
3085 wxTRANSLATE("seventeenth"),
3086 wxTRANSLATE("eighteenth"),
3087 wxTRANSLATE("nineteenth"),
3088 wxTRANSLATE("twentieth"),
3089 // that's enough - otherwise we'd have problems with
3090 // composite (or not) ordinals otherwise
3091 };
3092
3093 size_t n;
3094 for ( n = 0; n < WXSIZEOF(ordinals); n++ )
3095 {
3096 if ( token.CmpNoCase(ordinals[n]) == 0 )
3097 {
3098 break;
3099 }
3100 }
3101
3102 if ( n == WXSIZEOF(ordinals) )
3103 {
3104 // stop here - something unknown
3105 break;
3106 }
3107
3108 // it's a day
3109 if ( haveDay )
3110 {
3111 // don't try anything here (as in case of numeric day
3112 // above) - the symbolic day spec should always
3113 // precede the month/year
3114 break;
3115 }
3116
3117 haveDay = TRUE;
3118
3119 day = n + 1;
3120 }
3121 }
3122 }
3123 }
3124
3125 // either no more tokens or the scan was stopped by something we couldn't
3126 // parse - in any case, see if we can construct a date from what we have
3127 if ( !haveDay && !haveWDay )
3128 {
3129 wxLogDebug(_T("no day, no weekday hence no date."));
3130
3131 return (wxChar *)NULL;
3132 }
3133
3134 if ( haveWDay && (haveMon || haveYear || haveDay) &&
3135 !(haveMon && haveMon && haveYear) )
3136 {
3137 // without adjectives (which we don't support here) the week day only
3138 // makes sense completely separately or with the full date
3139 // specification (what would "Wed 1999" mean?)
3140 return (wxChar *)NULL;
3141 }
3142
3143 if ( !haveMon )
3144 {
3145 mon = GetCurrentMonth();
3146 }
3147
3148 if ( !haveYear )
3149 {
3150 year = GetCurrentYear();
3151 }
3152
3153 if ( haveDay )
3154 {
3155 Set(day, mon, year);
3156
3157 if ( haveWDay )
3158 {
3159 // check that it is really the same
3160 if ( GetWeekDay() != wday )
3161 {
3162 // inconsistency detected
3163 return (wxChar *)NULL;
3164 }
3165 }
3166 }
3167 else // haveWDay
3168 {
3169 *this = Today();
3170
3171 SetToWeekDayInSameWeek(wday);
3172 }
3173
3174 // return the pointer to the next char
3175 return p + wxStrlen(p) - wxStrlen(tok.GetString());
3176 }
3177
3178 const wxChar *wxDateTime::ParseTime(const wxChar *time)
3179 {
3180 wxCHECK_MSG( time, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
3181
3182 // first try some extra things
3183 static const struct
3184 {
3185 const wxChar *name;
3186 wxDateTime_t hour;
3187 } stdTimes[] =
3188 {
3189 { wxTRANSLATE("noon"), 12 },
3190 { wxTRANSLATE("midnight"), 00 },
3191 // anything else?
3192 };
3193
3194 for ( size_t n = 0; n < WXSIZEOF(stdTimes); n++ )
3195 {
3196 wxString timeString = wxGetTranslation(stdTimes[n].name);
3197 size_t len = timeString.length();
3198 if ( timeString.CmpNoCase(wxString(time, len)) == 0 )
3199 {
3200 Set(stdTimes[n].hour, 0, 0);
3201
3202 return time + len;
3203 }
3204 }
3205
3206 // try all time formats we may think about starting with the standard one
3207 const wxChar *result = ParseFormat(time, _T("%X"));
3208 if ( !result )
3209 {
3210 // normally, it's the same, but why not try it?
3211 result = ParseFormat(time, _T("%H:%M:%S"));
3212 }
3213
3214 if ( !result )
3215 {
3216 // 12hour with AM/PM?
3217 result = ParseFormat(time, _T("%I:%M:%S %p"));
3218 }
3219
3220 if ( !result )
3221 {
3222 // without seconds?
3223 result = ParseFormat(time, _T("%H:%M"));
3224 }
3225
3226 if ( !result )
3227 {
3228 // 12hour with AM/PM but without seconds?
3229 result = ParseFormat(time, _T("%I:%M %p"));
3230 }
3231
3232 if ( !result )
3233 {
3234 // just the hour?
3235 result = ParseFormat(time, _T("%H"));
3236 }
3237
3238 if ( !result )
3239 {
3240 // just the hour and AM/PM?
3241 result = ParseFormat(time, _T("%I %p"));
3242 }
3243
3244 // TODO: parse timezones
3245
3246 return result;
3247 }
3248
3249 // ============================================================================
3250 // wxTimeSpan
3251 // ============================================================================
3252
3253 // not all strftime(3) format specifiers make sense here because, for example,
3254 // a time span doesn't have a year nor a timezone
3255 //
3256 // Here are the ones which are supported (all of them are supported by strftime
3257 // as well):
3258 // %H hour in 24 hour format
3259 // %M minute (00 - 59)
3260 // %S second (00 - 59)
3261 // %% percent sign
3262 //
3263 // Also, for MFC CTimeSpan compatibility, we support
3264 // %D number of days
3265 //
3266 // And, to be better than MFC :-), we also have
3267 // %E number of wEeks
3268 // %l milliseconds (000 - 999)
3269 wxString wxTimeSpan::Format(const wxChar *format) const
3270 {
3271 wxCHECK_MSG( format, _T(""), _T("NULL format in wxTimeSpan::Format") );
3272
3273 wxString str;
3274 str.Alloc(strlen(format));
3275
3276 for ( const wxChar *pch = format; pch; pch++ )
3277 {
3278 wxChar ch = *pch;
3279
3280 if ( ch == '%' )
3281 {
3282 wxString tmp;
3283
3284 ch = *pch++;
3285 switch ( ch )
3286 {
3287 default:
3288 wxFAIL_MSG( _T("invalid format character") );
3289 // fall through
3290
3291 case '%':
3292 // will get to str << ch below
3293 break;
3294
3295 case 'D':
3296 tmp.Printf(_T("%d"), GetDays());
3297 break;
3298
3299 case 'E':
3300 tmp.Printf(_T("%d"), GetWeeks());
3301 break;
3302
3303 case 'H':
3304 tmp.Printf(_T("%02d"), GetHours());
3305 break;
3306
3307 case 'l':
3308 tmp.Printf(_T("%03ld"), GetMilliseconds().ToLong());
3309 break;
3310
3311 case 'M':
3312 tmp.Printf(_T("%02d"), GetMinutes());
3313 break;
3314
3315 case 'S':
3316 tmp.Printf(_T("%02ld"), GetSeconds().ToLong());
3317 break;
3318 }
3319
3320 if ( !!tmp )
3321 {
3322 str += tmp;
3323
3324 // skip str += ch below
3325 continue;
3326 }
3327 }
3328
3329 str += ch;
3330 }
3331
3332 return str;
3333 }