]> git.saurik.com Git - wxWidgets.git/blob - src/common/datetime.cpp
better compatibility with old wxList in wxUSE_STL==1 mode (patch 1075432)
[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 licence
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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
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 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
67
68 #ifndef WX_PRECOMP
69 #include "wx/string.h"
70 #include "wx/log.h"
71 #endif // WX_PRECOMP
72
73 #include "wx/intl.h"
74 #include "wx/thread.h"
75 #include "wx/tokenzr.h"
76 #include "wx/module.h"
77
78 #include <ctype.h>
79
80 #ifdef __WINDOWS__
81 #include "wx/msw/wrapwin.h"
82 #include <winnls.h>
83 #include <locale.h>
84 #endif
85
86 #include "wx/datetime.h"
87 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
88
89 const long wxDateTime::TIME_T_FACTOR = 1000l;
90
91 #if wxUSE_EXTENDED_RTTI
92
93 template<> void wxStringReadValue(const wxString &s , wxDateTime &data )
94 {
95 data.ParseFormat(s,wxT("%Y-%m-%d %H:%M:%S")) ;
96 }
97
98 template<> void wxStringWriteValue(wxString &s , const wxDateTime &data )
99 {
100 s = data.Format(wxT("%Y-%m-%d %H:%M:%S")) ;
101 }
102
103 wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter<wxDateTime> , wxFromStringConverter<wxDateTime>)
104
105 #endif
106
107 //
108 // ----------------------------------------------------------------------------
109 // conditional compilation
110 // ----------------------------------------------------------------------------
111
112 #if defined(HAVE_STRPTIME) && defined(__GLIBC__) && \
113 ((__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0))
114 // glibc 2.0.7 strptime() is broken - the following snippet causes it to
115 // crash (instead of just failing):
116 //
117 // strncpy(buf, "Tue Dec 21 20:25:40 1999", 128);
118 // strptime(buf, "%x", &tm);
119 //
120 // so don't use it
121 #undef HAVE_STRPTIME
122 #endif // broken strptime()
123
124 #if defined(__MWERKS__) && wxUSE_UNICODE
125 #include <wtime.h>
126 #endif
127
128 #if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM)
129 #if defined(__WXPALMOS__)
130 #define WX_GMTOFF_IN_TM
131 #elif defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
132 #define WX_TIMEZONE _timezone
133 #elif defined(__MWERKS__)
134 long wxmw_timezone = 28800;
135 #define WX_TIMEZONE wxmw_timezone
136 #elif defined(__DJGPP__) || defined(__WINE__)
137 #include <sys/timeb.h>
138 #include <values.h>
139 static long wxGetTimeZone()
140 {
141 static long timezone = MAXLONG; // invalid timezone
142 if (timezone == MAXLONG)
143 {
144 struct timeb tb;
145 ftime(&tb);
146 timezone = tb.timezone;
147 }
148 return timezone;
149 }
150 #define WX_TIMEZONE wxGetTimeZone()
151 #elif defined(__DARWIN__)
152 #define WX_GMTOFF_IN_TM
153 #else // unknown platform - try timezone
154 #define WX_TIMEZONE timezone
155 #endif
156 #endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
157
158 // ----------------------------------------------------------------------------
159 // macros
160 // ----------------------------------------------------------------------------
161
162 // debugging helper: just a convenient replacement of wxCHECK()
163 #define wxDATETIME_CHECK(expr, msg) \
164 if ( !(expr) ) \
165 { \
166 wxFAIL_MSG(msg); \
167 *this = wxInvalidDateTime; \
168 return *this; \
169 }
170
171 // ----------------------------------------------------------------------------
172 // private classes
173 // ----------------------------------------------------------------------------
174
175 class wxDateTimeHolidaysModule : public wxModule
176 {
177 public:
178 virtual bool OnInit()
179 {
180 wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays);
181
182 return true;
183 }
184
185 virtual void OnExit()
186 {
187 wxDateTimeHolidayAuthority::ClearAllAuthorities();
188 wxDateTimeHolidayAuthority::ms_authorities.clear();
189 }
190
191 private:
192 DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule)
193 };
194
195 IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule, wxModule)
196
197 // ----------------------------------------------------------------------------
198 // constants
199 // ----------------------------------------------------------------------------
200
201 // some trivial ones
202 static const int MONTHS_IN_YEAR = 12;
203
204 static const int SEC_PER_MIN = 60;
205
206 static const int MIN_PER_HOUR = 60;
207
208 static const int HOURS_PER_DAY = 24;
209
210 static const long SECONDS_PER_DAY = 86400l;
211
212 static const int DAYS_PER_WEEK = 7;
213
214 static const long MILLISECONDS_PER_DAY = 86400000l;
215
216 // this is the integral part of JDN of the midnight of Jan 1, 1970
217 // (i.e. JDN(Jan 1, 1970) = 2440587.5)
218 static const long EPOCH_JDN = 2440587l;
219
220 // the date of JDN -0.5 (as we don't work with fractional parts, this is the
221 // reference date for us) is Nov 24, 4714BC
222 static const int JDN_0_YEAR = -4713;
223 static const int JDN_0_MONTH = wxDateTime::Nov;
224 static const int JDN_0_DAY = 24;
225
226 // the constants used for JDN calculations
227 static const long JDN_OFFSET = 32046l;
228 static const long DAYS_PER_5_MONTHS = 153l;
229 static const long DAYS_PER_4_YEARS = 1461l;
230 static const long DAYS_PER_400_YEARS = 146097l;
231
232 // this array contains the cumulated number of days in all previous months for
233 // normal and leap years
234 static const wxDateTime::wxDateTime_t gs_cumulatedDays[2][MONTHS_IN_YEAR] =
235 {
236 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
237 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
238 };
239
240 // ----------------------------------------------------------------------------
241 // global data
242 // ----------------------------------------------------------------------------
243
244 const wxChar * wxDefaultDateTimeFormat = wxT("%c");
245 const wxChar * wxDefaultTimeSpanFormat = wxT("%H:%M:%S");
246
247 // in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
248 // indicate an invalid wxDateTime object
249 const wxDateTime wxDefaultDateTime;
250
251 wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown;
252
253 // ----------------------------------------------------------------------------
254 // private functions
255 // ----------------------------------------------------------------------------
256
257 // debugger helper: shows what the date really is
258 #ifdef __WXDEBUG__
259 extern const wxChar *wxDumpDate(const wxDateTime* dt)
260 {
261 static wxChar buf[128];
262
263 wxStrcpy(buf, dt->Format(_T("%Y-%m-%d (%a) %H:%M:%S")));
264
265 return buf;
266 }
267 #endif // Debug
268
269 // get the number of days in the given month of the given year
270 static inline
271 wxDateTime::wxDateTime_t GetNumOfDaysInMonth(int year, wxDateTime::Month month)
272 {
273 // the number of days in month in Julian/Gregorian calendar: the first line
274 // is for normal years, the second one is for the leap ones
275 static wxDateTime::wxDateTime_t daysInMonth[2][MONTHS_IN_YEAR] =
276 {
277 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
278 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
279 };
280
281 return daysInMonth[wxDateTime::IsLeapYear(year)][month];
282 }
283
284 // returns the time zone in the C sense, i.e. the difference UTC - local
285 // (in seconds)
286 static int GetTimeZone()
287 {
288 #ifdef WX_GMTOFF_IN_TM
289 // set to true when the timezone is set
290 static bool s_timezoneSet = false;
291 static long gmtoffset = LONG_MAX; // invalid timezone
292
293 // ensure that the timezone variable is set by calling localtime
294 if ( !s_timezoneSet )
295 {
296 // just call localtime() instead of figuring out whether this system
297 // supports tzset(), _tzset() or something else
298 time_t t = 0;
299 struct tm *tm;
300
301 tm = localtime(&t);
302 s_timezoneSet = true;
303
304 // note that GMT offset is the opposite of time zone and so to return
305 // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM
306 // cases we have to negate it
307 gmtoffset = -tm->tm_gmtoff;
308 }
309
310 return (int)gmtoffset;
311 #else // !WX_GMTOFF_IN_TM
312 return (int)WX_TIMEZONE;
313 #endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
314 }
315
316 // return the integral part of the JDN for the midnight of the given date (to
317 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
318 // noon of the previous day)
319 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day,
320 wxDateTime::Month mon,
321 int year)
322 {
323 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
324
325 // check the date validity
326 wxASSERT_MSG(
327 (year > JDN_0_YEAR) ||
328 ((year == JDN_0_YEAR) && (mon > JDN_0_MONTH)) ||
329 ((year == JDN_0_YEAR) && (mon == JDN_0_MONTH) && (day >= JDN_0_DAY)),
330 _T("date out of range - can't convert to JDN")
331 );
332
333 // make the year positive to avoid problems with negative numbers division
334 year += 4800;
335
336 // months are counted from March here
337 int month;
338 if ( mon >= wxDateTime::Mar )
339 {
340 month = mon - 2;
341 }
342 else
343 {
344 month = mon + 10;
345 year--;
346 }
347
348 // now we can simply add all the contributions together
349 return ((year / 100) * DAYS_PER_400_YEARS) / 4
350 + ((year % 100) * DAYS_PER_4_YEARS) / 4
351 + (month * DAYS_PER_5_MONTHS + 2) / 5
352 + day
353 - JDN_OFFSET;
354 }
355
356 // this function is a wrapper around strftime(3) adding error checking
357 static wxString CallStrftime(const wxChar *format, const tm* tm)
358 {
359 wxChar buf[4096];
360 if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) )
361 {
362 // buffer is too small?
363 wxFAIL_MSG(_T("strftime() failed"));
364 }
365
366 return wxString(buf);
367 }
368
369 #ifdef HAVE_STRPTIME
370
371 // glibc2 doesn't define this in the headers unless _XOPEN_SOURCE is defined
372 // which, unfortunately, wreaks havoc elsewhere
373 #if defined(__GLIBC__) && (__GLIBC__ == 2)
374 extern "C" char *strptime(const char *, const char *, struct tm *);
375 #endif
376
377 // Unicode-friendly strptime() wrapper
378 static const wxChar *
379 CallStrptime(const wxChar *input, const char *fmt, tm *tm)
380 {
381 // the problem here is that strptime() returns pointer into the string we
382 // passed to it while we're really interested in the pointer into the
383 // original, Unicode, string so we try to transform the pointer back
384 #if wxUSE_UNICODE
385 wxCharBuffer inputMB(wxConvertWX2MB(input));
386 #else // ASCII
387 const char * const inputMB = input;
388 #endif // Unicode/Ascii
389
390 const char *result = strptime(inputMB, fmt, tm);
391 if ( !result )
392 return NULL;
393
394 #if wxUSE_UNICODE
395 // FIXME: this is wrong in presence of surrogates &c
396 return input + (result - inputMB.data());
397 #else // ASCII
398 return result;
399 #endif // Unicode/Ascii
400 }
401
402 #endif // HAVE_STRPTIME
403
404 // if year and/or month have invalid values, replace them with the current ones
405 static void ReplaceDefaultYearMonthWithCurrent(int *year,
406 wxDateTime::Month *month)
407 {
408 struct tm *tmNow = NULL;
409
410 if ( *year == wxDateTime::Inv_Year )
411 {
412 tmNow = wxDateTime::GetTmNow();
413
414 *year = 1900 + tmNow->tm_year;
415 }
416
417 if ( *month == wxDateTime::Inv_Month )
418 {
419 if ( !tmNow )
420 tmNow = wxDateTime::GetTmNow();
421
422 *month = (wxDateTime::Month)tmNow->tm_mon;
423 }
424 }
425
426 // fll the struct tm with default values
427 static void InitTm(struct tm& tm)
428 {
429 // struct tm may have etxra fields (undocumented and with unportable
430 // names) which, nevertheless, must be set to 0
431 memset(&tm, 0, sizeof(struct tm));
432
433 tm.tm_mday = 1; // mday 0 is invalid
434 tm.tm_year = 76; // any valid year
435 tm.tm_isdst = -1; // auto determine
436 }
437
438 // parsing helpers
439 // ---------------
440
441 // return the month if the string is a month name or Inv_Month otherwise
442 static wxDateTime::Month GetMonthFromName(const wxString& name, int flags)
443 {
444 wxDateTime::Month mon;
445 for ( mon = wxDateTime::Jan; mon < wxDateTime::Inv_Month; wxNextMonth(mon) )
446 {
447 // case-insensitive comparison either one of or with both abbreviated
448 // and not versions
449 if ( flags & wxDateTime::Name_Full )
450 {
451 if ( name.CmpNoCase(wxDateTime::
452 GetMonthName(mon, wxDateTime::Name_Full)) == 0 )
453 {
454 break;
455 }
456 }
457
458 if ( flags & wxDateTime::Name_Abbr )
459 {
460 if ( name.CmpNoCase(wxDateTime::
461 GetMonthName(mon, wxDateTime::Name_Abbr)) == 0 )
462 {
463 break;
464 }
465 }
466 }
467
468 return mon;
469 }
470
471 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
472 static wxDateTime::WeekDay GetWeekDayFromName(const wxString& name, int flags)
473 {
474 wxDateTime::WeekDay wd;
475 for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) )
476 {
477 // case-insensitive comparison either one of or with both abbreviated
478 // and not versions
479 if ( flags & wxDateTime::Name_Full )
480 {
481 if ( name.CmpNoCase(wxDateTime::
482 GetWeekDayName(wd, wxDateTime::Name_Full)) == 0 )
483 {
484 break;
485 }
486 }
487
488 if ( flags & wxDateTime::Name_Abbr )
489 {
490 if ( name.CmpNoCase(wxDateTime::
491 GetWeekDayName(wd, wxDateTime::Name_Abbr)) == 0 )
492 {
493 break;
494 }
495 }
496 }
497
498 return wd;
499 }
500
501 // scans all digits (but no more than len) and returns the resulting number
502 static bool GetNumericToken(size_t len, const wxChar*& p, unsigned long *number)
503 {
504 size_t n = 1;
505 wxString s;
506 while ( wxIsdigit(*p) )
507 {
508 s += *p++;
509
510 if ( len && ++n > len )
511 break;
512 }
513
514 return !s.empty() && s.ToULong(number);
515 }
516
517 // scans all alphabetic characters and returns the resulting string
518 static wxString GetAlphaToken(const wxChar*& p)
519 {
520 wxString s;
521 while ( wxIsalpha(*p) )
522 {
523 s += *p++;
524 }
525
526 return s;
527 }
528
529 // ============================================================================
530 // implementation of wxDateTime
531 // ============================================================================
532
533 // ----------------------------------------------------------------------------
534 // struct Tm
535 // ----------------------------------------------------------------------------
536
537 wxDateTime::Tm::Tm()
538 {
539 year = (wxDateTime_t)wxDateTime::Inv_Year;
540 mon = wxDateTime::Inv_Month;
541 mday = 0;
542 hour = min = sec = msec = 0;
543 wday = wxDateTime::Inv_WeekDay;
544 }
545
546 wxDateTime::Tm::Tm(const struct tm& tm, const TimeZone& tz)
547 : m_tz(tz)
548 {
549 msec = 0;
550 sec = (wxDateTime::wxDateTime_t)tm.tm_sec;
551 min = (wxDateTime::wxDateTime_t)tm.tm_min;
552 hour = (wxDateTime::wxDateTime_t)tm.tm_hour;
553 mday = (wxDateTime::wxDateTime_t)tm.tm_mday;
554 mon = (wxDateTime::Month)tm.tm_mon;
555 year = 1900 + tm.tm_year;
556 wday = (wxDateTime::wxDateTime_t)tm.tm_wday;
557 yday = (wxDateTime::wxDateTime_t)tm.tm_yday;
558 }
559
560 bool wxDateTime::Tm::IsValid() const
561 {
562 // we allow for the leap seconds, although we don't use them (yet)
563 return (year != wxDateTime::Inv_Year) && (mon != wxDateTime::Inv_Month) &&
564 (mday <= GetNumOfDaysInMonth(year, mon)) &&
565 (hour < 24) && (min < 60) && (sec < 62) && (msec < 1000);
566 }
567
568 void wxDateTime::Tm::ComputeWeekDay()
569 {
570 // compute the week day from day/month/year: we use the dumbest algorithm
571 // possible: just compute our JDN and then use the (simple to derive)
572 // formula: weekday = (JDN + 1.5) % 7
573 wday = (wxDateTime::wxDateTime_t)((wxDateTime::WeekDay)(GetTruncatedJDN(mday, mon, year) + 2) % 7);
574 }
575
576 void wxDateTime::Tm::AddMonths(int monDiff)
577 {
578 // normalize the months field
579 while ( monDiff < -mon )
580 {
581 year--;
582
583 monDiff += MONTHS_IN_YEAR;
584 }
585
586 while ( monDiff + mon >= MONTHS_IN_YEAR )
587 {
588 year++;
589
590 monDiff -= MONTHS_IN_YEAR;
591 }
592
593 mon = (wxDateTime::Month)(mon + monDiff);
594
595 wxASSERT_MSG( mon >= 0 && mon < MONTHS_IN_YEAR, _T("logic error") );
596
597 // NB: we don't check here that the resulting date is valid, this function
598 // is private and the caller must check it if needed
599 }
600
601 void wxDateTime::Tm::AddDays(int dayDiff)
602 {
603 // normalize the days field
604 while ( dayDiff + mday < 1 )
605 {
606 AddMonths(-1);
607
608 dayDiff += GetNumOfDaysInMonth(year, mon);
609 }
610
611 mday = (wxDateTime::wxDateTime_t)( mday + dayDiff );
612 while ( mday > GetNumOfDaysInMonth(year, mon) )
613 {
614 mday -= GetNumOfDaysInMonth(year, mon);
615
616 AddMonths(1);
617 }
618
619 wxASSERT_MSG( mday > 0 && mday <= GetNumOfDaysInMonth(year, mon),
620 _T("logic error") );
621 }
622
623 // ----------------------------------------------------------------------------
624 // class TimeZone
625 // ----------------------------------------------------------------------------
626
627 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz)
628 {
629 switch ( tz )
630 {
631 case wxDateTime::Local:
632 // get the offset from C RTL: it returns the difference GMT-local
633 // while we want to have the offset _from_ GMT, hence the '-'
634 m_offset = -GetTimeZone();
635 break;
636
637 case wxDateTime::GMT_12:
638 case wxDateTime::GMT_11:
639 case wxDateTime::GMT_10:
640 case wxDateTime::GMT_9:
641 case wxDateTime::GMT_8:
642 case wxDateTime::GMT_7:
643 case wxDateTime::GMT_6:
644 case wxDateTime::GMT_5:
645 case wxDateTime::GMT_4:
646 case wxDateTime::GMT_3:
647 case wxDateTime::GMT_2:
648 case wxDateTime::GMT_1:
649 m_offset = -3600*(wxDateTime::GMT0 - tz);
650 break;
651
652 case wxDateTime::GMT0:
653 case wxDateTime::GMT1:
654 case wxDateTime::GMT2:
655 case wxDateTime::GMT3:
656 case wxDateTime::GMT4:
657 case wxDateTime::GMT5:
658 case wxDateTime::GMT6:
659 case wxDateTime::GMT7:
660 case wxDateTime::GMT8:
661 case wxDateTime::GMT9:
662 case wxDateTime::GMT10:
663 case wxDateTime::GMT11:
664 case wxDateTime::GMT12:
665 m_offset = 3600*(tz - wxDateTime::GMT0);
666 break;
667
668 case wxDateTime::A_CST:
669 // Central Standard Time in use in Australia = UTC + 9.5
670 m_offset = 60l*(9*60 + 30);
671 break;
672
673 default:
674 wxFAIL_MSG( _T("unknown time zone") );
675 }
676 }
677
678 // ----------------------------------------------------------------------------
679 // static functions
680 // ----------------------------------------------------------------------------
681
682 /* static */
683 bool wxDateTime::IsLeapYear(int year, wxDateTime::Calendar cal)
684 {
685 if ( year == Inv_Year )
686 year = GetCurrentYear();
687
688 if ( cal == Gregorian )
689 {
690 // in Gregorian calendar leap years are those divisible by 4 except
691 // those divisible by 100 unless they're also divisible by 400
692 // (in some countries, like Russia and Greece, additional corrections
693 // exist, but they won't manifest themselves until 2700)
694 return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
695 }
696 else if ( cal == Julian )
697 {
698 // in Julian calendar the rule is simpler
699 return year % 4 == 0;
700 }
701 else
702 {
703 wxFAIL_MSG(_T("unknown calendar"));
704
705 return false;
706 }
707 }
708
709 /* static */
710 int wxDateTime::GetCentury(int year)
711 {
712 return year > 0 ? year / 100 : year / 100 - 1;
713 }
714
715 /* static */
716 int wxDateTime::ConvertYearToBC(int year)
717 {
718 // year 0 is BC 1
719 return year > 0 ? year : year - 1;
720 }
721
722 /* static */
723 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal)
724 {
725 switch ( cal )
726 {
727 case Gregorian:
728 return Now().GetYear();
729
730 case Julian:
731 wxFAIL_MSG(_T("TODO"));
732 break;
733
734 default:
735 wxFAIL_MSG(_T("unsupported calendar"));
736 break;
737 }
738
739 return Inv_Year;
740 }
741
742 /* static */
743 wxDateTime::Month wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal)
744 {
745 switch ( cal )
746 {
747 case Gregorian:
748 return Now().GetMonth();
749
750 case Julian:
751 wxFAIL_MSG(_T("TODO"));
752 break;
753
754 default:
755 wxFAIL_MSG(_T("unsupported calendar"));
756 break;
757 }
758
759 return Inv_Month;
760 }
761
762 /* static */
763 wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(int year, Calendar cal)
764 {
765 if ( year == Inv_Year )
766 {
767 // take the current year if none given
768 year = GetCurrentYear();
769 }
770
771 switch ( cal )
772 {
773 case Gregorian:
774 case Julian:
775 return IsLeapYear(year) ? 366 : 365;
776
777 default:
778 wxFAIL_MSG(_T("unsupported calendar"));
779 break;
780 }
781
782 return 0;
783 }
784
785 /* static */
786 wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(wxDateTime::Month month,
787 int year,
788 wxDateTime::Calendar cal)
789 {
790 wxCHECK_MSG( month < MONTHS_IN_YEAR, 0, _T("invalid month") );
791
792 if ( cal == Gregorian || cal == Julian )
793 {
794 if ( year == Inv_Year )
795 {
796 // take the current year if none given
797 year = GetCurrentYear();
798 }
799
800 return GetNumOfDaysInMonth(year, month);
801 }
802 else
803 {
804 wxFAIL_MSG(_T("unsupported calendar"));
805
806 return 0;
807 }
808 }
809
810 /* static */
811 wxString wxDateTime::GetMonthName(wxDateTime::Month month,
812 wxDateTime::NameFlags flags)
813 {
814 wxCHECK_MSG( month != Inv_Month, wxEmptyString, _T("invalid month") );
815 #ifndef __WXWINCE__
816 // notice that we must set all the fields to avoid confusing libc (GNU one
817 // gets confused to a crash if we don't do this)
818 tm tm;
819 InitTm(tm);
820 tm.tm_mon = month;
821
822 return CallStrftime(flags == Name_Abbr ? _T("%b") : _T("%B"), &tm);
823 #else
824 wxString ret;
825 switch(month)
826 {
827 case Jan:
828 ret = (flags == Name_Abbr ? wxT("Jan"): wxT("January"));
829 break;
830 case Feb:
831 ret = (flags == Name_Abbr ? wxT("Feb"): wxT("Febuary"));
832 break;
833 case Mar:
834 ret = (flags == Name_Abbr ? wxT("Mar"): wxT("March"));
835 break;
836 case Apr:
837 ret = (flags == Name_Abbr ? wxT("Apr"): wxT("April"));
838 break;
839 case May:
840 ret = (flags == Name_Abbr ? wxT("May"): wxT("May"));
841 break;
842 case Jun:
843 ret = (flags == Name_Abbr ? wxT("Jun"): wxT("June"));
844 break;
845 case Jul:
846 ret = (flags == Name_Abbr ? wxT("Jul"): wxT("July"));
847 break;
848 case Aug:
849 ret = (flags == Name_Abbr ? wxT("Aug"): wxT("August"));
850 break;
851 case Sep:
852 ret = (flags == Name_Abbr ? wxT("Sep"): wxT("September"));
853 break;
854 case Oct:
855 ret = (flags == Name_Abbr ? wxT("Oct"): wxT("October"));
856 break;
857 case Nov:
858 ret = (flags == Name_Abbr ? wxT("Nov"): wxT("November"));
859 break;
860 case Dec:
861 ret = (flags == Name_Abbr ? wxT("Dec"): wxT("December"));
862 break;
863 }
864 return ret;
865 #endif
866 }
867
868 /* static */
869 wxString wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday,
870 wxDateTime::NameFlags flags)
871 {
872 wxCHECK_MSG( wday != Inv_WeekDay, wxEmptyString, _T("invalid weekday") );
873 #ifndef __WXWINCE__
874 // take some arbitrary Sunday (but notice that the day should be such that
875 // after adding wday to it below we still have a valid date, e.g. don't
876 // take 28 here!)
877 tm tm;
878 InitTm(tm);
879 tm.tm_mday = 21;
880 tm.tm_mon = Nov;
881 tm.tm_year = 99;
882
883 // and offset it by the number of days needed to get the correct wday
884 tm.tm_mday += wday;
885
886 // call mktime() to normalize it...
887 (void)mktime(&tm);
888
889 // ... and call strftime()
890 return CallStrftime(flags == Name_Abbr ? _T("%a") : _T("%A"), &tm);
891 #else
892 wxString ret;
893 switch(wday)
894 {
895 case Sun:
896 ret = (flags == Name_Abbr ? wxT("Sun") : wxT("Sunday"));
897 break;
898 case Mon:
899 ret = (flags == Name_Abbr ? wxT("Mon") : wxT("Monday"));
900 break;
901 case Tue:
902 ret = (flags == Name_Abbr ? wxT("Tue") : wxT("Tuesday"));
903 break;
904 case Wed:
905 ret = (flags == Name_Abbr ? wxT("Wed") : wxT("Wednesday"));
906 break;
907 case Thu:
908 ret = (flags == Name_Abbr ? wxT("Thu") : wxT("Thursday"));
909 break;
910 case Fri:
911 ret = (flags == Name_Abbr ? wxT("Fri") : wxT("Friday"));
912 break;
913 case Sat:
914 ret = (flags == Name_Abbr ? wxT("Sat") : wxT("Saturday"));
915 break;
916 }
917 return ret;
918
919 #endif
920 }
921
922 /* static */
923 void wxDateTime::GetAmPmStrings(wxString *am, wxString *pm)
924 {
925 tm tm;
926 InitTm(tm);
927 wxChar buffer[64];
928 // @Note: Do not call 'CallStrftime' here! CallStrftime checks the return code
929 // and causes an assertion failed if the buffer is to small (which is good) - OR -
930 // if strftime does not return anything because the format string is invalid - OR -
931 // if there are no 'am' / 'pm' tokens defined for the current locale (which is not good).
932 // wxDateTime::ParseTime will try several different formats to parse the time.
933 // As a result, GetAmPmStrings might get called, even if the current locale
934 // does not define any 'am' / 'pm' tokens. In this case, wxStrftime would
935 // assert, even though it is a perfectly legal use.
936 if ( am )
937 {
938 if (wxStrftime(buffer, sizeof buffer, _T("%p"), &tm) > 0)
939 *am = wxString(buffer);
940 else
941 *am = wxString();
942 }
943 if ( pm )
944 {
945 tm.tm_hour = 13;
946 if (wxStrftime(buffer, sizeof buffer, _T("%p"), &tm) > 0)
947 *pm = wxString(buffer);
948 else
949 *pm = wxString();
950 }
951 }
952
953 // ----------------------------------------------------------------------------
954 // Country stuff: date calculations depend on the country (DST, work days,
955 // ...), so we need to know which rules to follow.
956 // ----------------------------------------------------------------------------
957
958 /* static */
959 wxDateTime::Country wxDateTime::GetCountry()
960 {
961 // TODO use LOCALE_ICOUNTRY setting under Win32
962 #ifndef __WXWINCE__
963 if ( ms_country == Country_Unknown )
964 {
965 // try to guess from the time zone name
966 time_t t = time(NULL);
967 struct tm *tm = localtime(&t);
968
969 wxString tz = CallStrftime(_T("%Z"), tm);
970 if ( tz == _T("WET") || tz == _T("WEST") )
971 {
972 ms_country = UK;
973 }
974 else if ( tz == _T("CET") || tz == _T("CEST") )
975 {
976 ms_country = Country_EEC;
977 }
978 else if ( tz == _T("MSK") || tz == _T("MSD") )
979 {
980 ms_country = Russia;
981 }
982 else if ( tz == _T("AST") || tz == _T("ADT") ||
983 tz == _T("EST") || tz == _T("EDT") ||
984 tz == _T("CST") || tz == _T("CDT") ||
985 tz == _T("MST") || tz == _T("MDT") ||
986 tz == _T("PST") || tz == _T("PDT") )
987 {
988 ms_country = USA;
989 }
990 else
991 {
992 // well, choose a default one
993 ms_country = USA;
994 }
995 }
996 #else
997 ms_country = USA;
998 #endif
999
1000 return ms_country;
1001 }
1002
1003 /* static */
1004 void wxDateTime::SetCountry(wxDateTime::Country country)
1005 {
1006 ms_country = country;
1007 }
1008
1009 /* static */
1010 bool wxDateTime::IsWestEuropeanCountry(Country country)
1011 {
1012 if ( country == Country_Default )
1013 {
1014 country = GetCountry();
1015 }
1016
1017 return (Country_WesternEurope_Start <= country) &&
1018 (country <= Country_WesternEurope_End);
1019 }
1020
1021 // ----------------------------------------------------------------------------
1022 // DST calculations: we use 3 different rules for the West European countries,
1023 // USA and for the rest of the world. This is undoubtedly false for many
1024 // countries, but I lack the necessary info (and the time to gather it),
1025 // please add the other rules here!
1026 // ----------------------------------------------------------------------------
1027
1028 /* static */
1029 bool wxDateTime::IsDSTApplicable(int year, Country country)
1030 {
1031 if ( year == Inv_Year )
1032 {
1033 // take the current year if none given
1034 year = GetCurrentYear();
1035 }
1036
1037 if ( country == Country_Default )
1038 {
1039 country = GetCountry();
1040 }
1041
1042 switch ( country )
1043 {
1044 case USA:
1045 case UK:
1046 // DST was first observed in the US and UK during WWI, reused
1047 // during WWII and used again since 1966
1048 return year >= 1966 ||
1049 (year >= 1942 && year <= 1945) ||
1050 (year == 1918 || year == 1919);
1051
1052 default:
1053 // assume that it started after WWII
1054 return year > 1950;
1055 }
1056 }
1057
1058 /* static */
1059 wxDateTime wxDateTime::GetBeginDST(int year, Country country)
1060 {
1061 if ( year == Inv_Year )
1062 {
1063 // take the current year if none given
1064 year = GetCurrentYear();
1065 }
1066
1067 if ( country == Country_Default )
1068 {
1069 country = GetCountry();
1070 }
1071
1072 if ( !IsDSTApplicable(year, country) )
1073 {
1074 return wxInvalidDateTime;
1075 }
1076
1077 wxDateTime dt;
1078
1079 if ( IsWestEuropeanCountry(country) || (country == Russia) )
1080 {
1081 // DST begins at 1 a.m. GMT on the last Sunday of March
1082 if ( !dt.SetToLastWeekDay(Sun, Mar, year) )
1083 {
1084 // weird...
1085 wxFAIL_MSG( _T("no last Sunday in March?") );
1086 }
1087
1088 dt += wxTimeSpan::Hours(1);
1089
1090 // disable DST tests because it could result in an infinite recursion!
1091 dt.MakeGMT(true);
1092 }
1093 else switch ( country )
1094 {
1095 case USA:
1096 switch ( year )
1097 {
1098 case 1918:
1099 case 1919:
1100 // don't know for sure - assume it was in effect all year
1101
1102 case 1943:
1103 case 1944:
1104 case 1945:
1105 dt.Set(1, Jan, year);
1106 break;
1107
1108 case 1942:
1109 // DST was installed Feb 2, 1942 by the Congress
1110 dt.Set(2, Feb, year);
1111 break;
1112
1113 // Oil embargo changed the DST period in the US
1114 case 1974:
1115 dt.Set(6, Jan, 1974);
1116 break;
1117
1118 case 1975:
1119 dt.Set(23, Feb, 1975);
1120 break;
1121
1122 default:
1123 // before 1986, DST begun on the last Sunday of April, but
1124 // in 1986 Reagan changed it to begin at 2 a.m. of the
1125 // first Sunday in April
1126 if ( year < 1986 )
1127 {
1128 if ( !dt.SetToLastWeekDay(Sun, Apr, year) )
1129 {
1130 // weird...
1131 wxFAIL_MSG( _T("no first Sunday in April?") );
1132 }
1133 }
1134 else
1135 {
1136 if ( !dt.SetToWeekDay(Sun, 1, Apr, year) )
1137 {
1138 // weird...
1139 wxFAIL_MSG( _T("no first Sunday in April?") );
1140 }
1141 }
1142
1143 dt += wxTimeSpan::Hours(2);
1144
1145 // TODO what about timezone??
1146 }
1147
1148 break;
1149
1150 default:
1151 // assume Mar 30 as the start of the DST for the rest of the world
1152 // - totally bogus, of course
1153 dt.Set(30, Mar, year);
1154 }
1155
1156 return dt;
1157 }
1158
1159 /* static */
1160 wxDateTime wxDateTime::GetEndDST(int year, Country country)
1161 {
1162 if ( year == Inv_Year )
1163 {
1164 // take the current year if none given
1165 year = GetCurrentYear();
1166 }
1167
1168 if ( country == Country_Default )
1169 {
1170 country = GetCountry();
1171 }
1172
1173 if ( !IsDSTApplicable(year, country) )
1174 {
1175 return wxInvalidDateTime;
1176 }
1177
1178 wxDateTime dt;
1179
1180 if ( IsWestEuropeanCountry(country) || (country == Russia) )
1181 {
1182 // DST ends at 1 a.m. GMT on the last Sunday of October
1183 if ( !dt.SetToLastWeekDay(Sun, Oct, year) )
1184 {
1185 // weirder and weirder...
1186 wxFAIL_MSG( _T("no last Sunday in October?") );
1187 }
1188
1189 dt += wxTimeSpan::Hours(1);
1190
1191 // disable DST tests because it could result in an infinite recursion!
1192 dt.MakeGMT(true);
1193 }
1194 else switch ( country )
1195 {
1196 case USA:
1197 switch ( year )
1198 {
1199 case 1918:
1200 case 1919:
1201 // don't know for sure - assume it was in effect all year
1202
1203 case 1943:
1204 case 1944:
1205 dt.Set(31, Dec, year);
1206 break;
1207
1208 case 1945:
1209 // the time was reset after the end of the WWII
1210 dt.Set(30, Sep, year);
1211 break;
1212
1213 default:
1214 // DST ends at 2 a.m. on the last Sunday of October
1215 if ( !dt.SetToLastWeekDay(Sun, Oct, year) )
1216 {
1217 // weirder and weirder...
1218 wxFAIL_MSG( _T("no last Sunday in October?") );
1219 }
1220
1221 dt += wxTimeSpan::Hours(2);
1222
1223 // TODO what about timezone??
1224 }
1225 break;
1226
1227 default:
1228 // assume October 26th as the end of the DST - totally bogus too
1229 dt.Set(26, Oct, year);
1230 }
1231
1232 return dt;
1233 }
1234
1235 // ----------------------------------------------------------------------------
1236 // constructors and assignment operators
1237 // ----------------------------------------------------------------------------
1238
1239 // return the current time with ms precision
1240 /* static */ wxDateTime wxDateTime::UNow()
1241 {
1242 return wxDateTime(wxGetLocalTimeMillis());
1243 }
1244
1245 // the values in the tm structure contain the local time
1246 wxDateTime& wxDateTime::Set(const struct tm& tm)
1247 {
1248 struct tm tm2(tm);
1249 time_t timet = mktime(&tm2);
1250
1251 if ( timet == (time_t)-1 )
1252 {
1253 // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
1254 // less than timezone - try to make it work for this case
1255 if ( tm2.tm_year == 70 && tm2.tm_mon == 0 && tm2.tm_mday == 1 )
1256 {
1257 return Set((time_t)(
1258 GetTimeZone() +
1259 tm2.tm_hour * MIN_PER_HOUR * SEC_PER_MIN +
1260 tm2.tm_min * SEC_PER_MIN +
1261 tm2.tm_sec));
1262 }
1263
1264 wxFAIL_MSG( _T("mktime() failed") );
1265
1266 *this = wxInvalidDateTime;
1267
1268 return *this;
1269 }
1270 else
1271 {
1272 return Set(timet);
1273 }
1274 }
1275
1276 wxDateTime& wxDateTime::Set(wxDateTime_t hour,
1277 wxDateTime_t minute,
1278 wxDateTime_t second,
1279 wxDateTime_t millisec)
1280 {
1281 // we allow seconds to be 61 to account for the leap seconds, even if we
1282 // don't use them really
1283 wxDATETIME_CHECK( hour < 24 &&
1284 second < 62 &&
1285 minute < 60 &&
1286 millisec < 1000,
1287 _T("Invalid time in wxDateTime::Set()") );
1288
1289 // get the current date from system
1290 struct tm *tm = GetTmNow();
1291
1292 wxDATETIME_CHECK( tm, _T("localtime() failed") );
1293
1294 // make a copy so it isn't clobbered by the call to mktime() below
1295 struct tm tm1(*tm);
1296
1297 // adjust the time
1298 tm1.tm_hour = hour;
1299 tm1.tm_min = minute;
1300 tm1.tm_sec = second;
1301
1302 // and the DST in case it changes on this date
1303 struct tm tm2(tm1);
1304 mktime(&tm2);
1305 if ( tm2.tm_isdst != tm1.tm_isdst )
1306 tm1.tm_isdst = tm2.tm_isdst;
1307
1308 (void)Set(tm1);
1309
1310 // and finally adjust milliseconds
1311 return SetMillisecond(millisec);
1312 }
1313
1314 wxDateTime& wxDateTime::Set(wxDateTime_t day,
1315 Month month,
1316 int year,
1317 wxDateTime_t hour,
1318 wxDateTime_t minute,
1319 wxDateTime_t second,
1320 wxDateTime_t millisec)
1321 {
1322 wxDATETIME_CHECK( hour < 24 &&
1323 second < 62 &&
1324 minute < 60 &&
1325 millisec < 1000,
1326 _T("Invalid time in wxDateTime::Set()") );
1327
1328 ReplaceDefaultYearMonthWithCurrent(&year, &month);
1329
1330 wxDATETIME_CHECK( (0 < day) && (day <= GetNumberOfDays(month, year)),
1331 _T("Invalid date in wxDateTime::Set()") );
1332
1333 // the range of time_t type (inclusive)
1334 static const int yearMinInRange = 1970;
1335 static const int yearMaxInRange = 2037;
1336
1337 // test only the year instead of testing for the exact end of the Unix
1338 // time_t range - it doesn't bring anything to do more precise checks
1339 if ( year >= yearMinInRange && year <= yearMaxInRange )
1340 {
1341 // use the standard library version if the date is in range - this is
1342 // probably more efficient than our code
1343 struct tm tm;
1344 tm.tm_year = year - 1900;
1345 tm.tm_mon = month;
1346 tm.tm_mday = day;
1347 tm.tm_hour = hour;
1348 tm.tm_min = minute;
1349 tm.tm_sec = second;
1350 tm.tm_isdst = -1; // mktime() will guess it
1351
1352 (void)Set(tm);
1353
1354 // and finally adjust milliseconds
1355 if (IsValid())
1356 SetMillisecond(millisec);
1357
1358 return *this;
1359 }
1360 else
1361 {
1362 // do time calculations ourselves: we want to calculate the number of
1363 // milliseconds between the given date and the epoch
1364
1365 // get the JDN for the midnight of this day
1366 m_time = GetTruncatedJDN(day, month, year);
1367 m_time -= EPOCH_JDN;
1368 m_time *= SECONDS_PER_DAY * TIME_T_FACTOR;
1369
1370 // JDN corresponds to GMT, we take localtime
1371 Add(wxTimeSpan(hour, minute, second + GetTimeZone(), millisec));
1372 }
1373
1374 return *this;
1375 }
1376
1377 wxDateTime& wxDateTime::Set(double jdn)
1378 {
1379 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1380 // EPOCH_JDN + 0.5
1381 jdn -= EPOCH_JDN + 0.5;
1382
1383 jdn *= MILLISECONDS_PER_DAY;
1384
1385 // JDNs always suppose an UTC date, so bring it back to local time zone
1386 // (also see GetJulianDayNumber() implementation)
1387 long tzDiff = GetTimeZone();
1388 if ( IsDST() == 1 )
1389 {
1390 // FIXME: again, we suppose that DST is always one hour
1391 tzDiff -= 3600;
1392 }
1393
1394 jdn += tzDiff*1000; // tzDiff is in seconds
1395
1396 m_time.Assign(jdn);
1397
1398 return *this;
1399 }
1400
1401 wxDateTime& wxDateTime::ResetTime()
1402 {
1403 Tm tm = GetTm();
1404
1405 if ( tm.hour || tm.min || tm.sec || tm.msec )
1406 {
1407 tm.msec =
1408 tm.sec =
1409 tm.min =
1410 tm.hour = 0;
1411
1412 Set(tm);
1413 }
1414
1415 return *this;
1416 }
1417
1418 // ----------------------------------------------------------------------------
1419 // DOS Date and Time Format functions
1420 // ----------------------------------------------------------------------------
1421 // the dos date and time value is an unsigned 32 bit value in the format:
1422 // YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
1423 //
1424 // Y = year offset from 1980 (0-127)
1425 // M = month (1-12)
1426 // D = day of month (1-31)
1427 // h = hour (0-23)
1428 // m = minute (0-59)
1429 // s = bisecond (0-29) each bisecond indicates two seconds
1430 // ----------------------------------------------------------------------------
1431
1432 wxDateTime& wxDateTime::SetFromDOS(unsigned long ddt)
1433 {
1434 struct tm tm;
1435 InitTm(tm);
1436
1437 long year = ddt & 0xFE000000;
1438 year >>= 25;
1439 year += 80;
1440 tm.tm_year = year;
1441
1442 long month = ddt & 0x1E00000;
1443 month >>= 21;
1444 month -= 1;
1445 tm.tm_mon = month;
1446
1447 long day = ddt & 0x1F0000;
1448 day >>= 16;
1449 tm.tm_mday = day;
1450
1451 long hour = ddt & 0xF800;
1452 hour >>= 11;
1453 tm.tm_hour = hour;
1454
1455 long minute = ddt & 0x7E0;
1456 minute >>= 5;
1457 tm.tm_min = minute;
1458
1459 long second = ddt & 0x1F;
1460 tm.tm_sec = second * 2;
1461
1462 return Set(mktime(&tm));
1463 }
1464
1465 unsigned long wxDateTime::GetAsDOS() const
1466 {
1467 unsigned long ddt;
1468 time_t ticks = GetTicks();
1469 struct tm *tm = localtime(&ticks);
1470
1471 long year = tm->tm_year;
1472 year -= 80;
1473 year <<= 25;
1474
1475 long month = tm->tm_mon;
1476 month += 1;
1477 month <<= 21;
1478
1479 long day = tm->tm_mday;
1480 day <<= 16;
1481
1482 long hour = tm->tm_hour;
1483 hour <<= 11;
1484
1485 long minute = tm->tm_min;
1486 minute <<= 5;
1487
1488 long second = tm->tm_sec;
1489 second /= 2;
1490
1491 ddt = year | month | day | hour | minute | second;
1492 return ddt;
1493 }
1494
1495 // ----------------------------------------------------------------------------
1496 // time_t <-> broken down time conversions
1497 // ----------------------------------------------------------------------------
1498
1499 wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
1500 {
1501 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1502
1503 time_t time = GetTicks();
1504 if ( time != (time_t)-1 )
1505 {
1506 // use C RTL functions
1507 tm *tm;
1508 if ( tz.GetOffset() == -GetTimeZone() )
1509 {
1510 // we are working with local time
1511 tm = localtime(&time);
1512
1513 // should never happen
1514 wxCHECK_MSG( tm, Tm(), _T("localtime() failed") );
1515 }
1516 else
1517 {
1518 time += (time_t)tz.GetOffset();
1519 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
1520 int time2 = (int) time;
1521 if ( time2 >= 0 )
1522 #else
1523 if ( time >= 0 )
1524 #endif
1525 {
1526 tm = gmtime(&time);
1527
1528 // should never happen
1529 wxCHECK_MSG( tm, Tm(), _T("gmtime() failed") );
1530 }
1531 else
1532 {
1533 tm = (struct tm *)NULL;
1534 }
1535 }
1536
1537 if ( tm )
1538 {
1539 // adjust the milliseconds
1540 Tm tm2(*tm, tz);
1541 long timeOnly = (m_time % MILLISECONDS_PER_DAY).ToLong();
1542 tm2.msec = (wxDateTime_t)(timeOnly % 1000);
1543 return tm2;
1544 }
1545 //else: use generic code below
1546 }
1547
1548 // remember the time and do the calculations with the date only - this
1549 // eliminates rounding errors of the floating point arithmetics
1550
1551 wxLongLong timeMidnight = m_time + tz.GetOffset() * 1000;
1552
1553 long timeOnly = (timeMidnight % MILLISECONDS_PER_DAY).ToLong();
1554
1555 // we want to always have positive time and timeMidnight to be really
1556 // the midnight before it
1557 if ( timeOnly < 0 )
1558 {
1559 timeOnly = MILLISECONDS_PER_DAY + timeOnly;
1560 }
1561
1562 timeMidnight -= timeOnly;
1563
1564 // calculate the Gregorian date from JDN for the midnight of our date:
1565 // this will yield day, month (in 1..12 range) and year
1566
1567 // actually, this is the JDN for the noon of the previous day
1568 long jdn = (timeMidnight / MILLISECONDS_PER_DAY).ToLong() + EPOCH_JDN;
1569
1570 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1571
1572 wxASSERT_MSG( jdn > -2, _T("JDN out of range") );
1573
1574 // calculate the century
1575 long temp = (jdn + JDN_OFFSET) * 4 - 1;
1576 long century = temp / DAYS_PER_400_YEARS;
1577
1578 // then the year and day of year (1 <= dayOfYear <= 366)
1579 temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3;
1580 long year = (century * 100) + (temp / DAYS_PER_4_YEARS);
1581 long dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
1582
1583 // and finally the month and day of the month
1584 temp = dayOfYear * 5 - 3;
1585 long month = temp / DAYS_PER_5_MONTHS;
1586 long day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
1587
1588 // month is counted from March - convert to normal
1589 if ( month < 10 )
1590 {
1591 month += 3;
1592 }
1593 else
1594 {
1595 year += 1;
1596 month -= 9;
1597 }
1598
1599 // year is offset by 4800
1600 year -= 4800;
1601
1602 // check that the algorithm gave us something reasonable
1603 wxASSERT_MSG( (0 < month) && (month <= 12), _T("invalid month") );
1604 wxASSERT_MSG( (1 <= day) && (day < 32), _T("invalid day") );
1605
1606 // construct Tm from these values
1607 Tm tm;
1608 tm.year = (int)year;
1609 tm.mon = (Month)(month - 1); // algorithm yields 1 for January, not 0
1610 tm.mday = (wxDateTime_t)day;
1611 tm.msec = (wxDateTime_t)(timeOnly % 1000);
1612 timeOnly -= tm.msec;
1613 timeOnly /= 1000; // now we have time in seconds
1614
1615 tm.sec = (wxDateTime_t)(timeOnly % 60);
1616 timeOnly -= tm.sec;
1617 timeOnly /= 60; // now we have time in minutes
1618
1619 tm.min = (wxDateTime_t)(timeOnly % 60);
1620 timeOnly -= tm.min;
1621
1622 tm.hour = (wxDateTime_t)(timeOnly / 60);
1623
1624 return tm;
1625 }
1626
1627 wxDateTime& wxDateTime::SetYear(int year)
1628 {
1629 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1630
1631 Tm tm(GetTm());
1632 tm.year = year;
1633 Set(tm);
1634
1635 return *this;
1636 }
1637
1638 wxDateTime& wxDateTime::SetMonth(Month month)
1639 {
1640 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1641
1642 Tm tm(GetTm());
1643 tm.mon = month;
1644 Set(tm);
1645
1646 return *this;
1647 }
1648
1649 wxDateTime& wxDateTime::SetDay(wxDateTime_t mday)
1650 {
1651 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1652
1653 Tm tm(GetTm());
1654 tm.mday = mday;
1655 Set(tm);
1656
1657 return *this;
1658 }
1659
1660 wxDateTime& wxDateTime::SetHour(wxDateTime_t hour)
1661 {
1662 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1663
1664 Tm tm(GetTm());
1665 tm.hour = hour;
1666 Set(tm);
1667
1668 return *this;
1669 }
1670
1671 wxDateTime& wxDateTime::SetMinute(wxDateTime_t min)
1672 {
1673 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1674
1675 Tm tm(GetTm());
1676 tm.min = min;
1677 Set(tm);
1678
1679 return *this;
1680 }
1681
1682 wxDateTime& wxDateTime::SetSecond(wxDateTime_t sec)
1683 {
1684 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1685
1686 Tm tm(GetTm());
1687 tm.sec = sec;
1688 Set(tm);
1689
1690 return *this;
1691 }
1692
1693 wxDateTime& wxDateTime::SetMillisecond(wxDateTime_t millisecond)
1694 {
1695 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1696
1697 // we don't need to use GetTm() for this one
1698 m_time -= m_time % 1000l;
1699 m_time += millisecond;
1700
1701 return *this;
1702 }
1703
1704 // ----------------------------------------------------------------------------
1705 // wxDateTime arithmetics
1706 // ----------------------------------------------------------------------------
1707
1708 wxDateTime& wxDateTime::Add(const wxDateSpan& diff)
1709 {
1710 Tm tm(GetTm());
1711
1712 tm.year += diff.GetYears();
1713 tm.AddMonths(diff.GetMonths());
1714
1715 // check that the resulting date is valid
1716 if ( tm.mday > GetNumOfDaysInMonth(tm.year, tm.mon) )
1717 {
1718 // We suppose that when adding one month to Jan 31 we want to get Feb
1719 // 28 (or 29), i.e. adding a month to the last day of the month should
1720 // give the last day of the next month which is quite logical.
1721 //
1722 // Unfortunately, there is no logic way to understand what should
1723 // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1724 // We make it Feb 28 (last day too), but it is highly questionable.
1725 tm.mday = GetNumOfDaysInMonth(tm.year, tm.mon);
1726 }
1727
1728 tm.AddDays(diff.GetTotalDays());
1729
1730 Set(tm);
1731
1732 wxASSERT_MSG( IsSameTime(tm),
1733 _T("Add(wxDateSpan) shouldn't modify time") );
1734
1735 return *this;
1736 }
1737
1738 // ----------------------------------------------------------------------------
1739 // Weekday and monthday stuff
1740 // ----------------------------------------------------------------------------
1741
1742 // convert Sun, Mon, ..., Sat into 6, 0, ..., 5
1743 static inline int ConvertWeekDayToMondayBase(int wd)
1744 {
1745 return wd == wxDateTime::Sun ? 6 : wd - 1;
1746 }
1747
1748 /* static */
1749 wxDateTime
1750 wxDateTime::SetToWeekOfYear(int year, wxDateTime_t numWeek, WeekDay wd)
1751 {
1752 wxASSERT_MSG( numWeek > 0,
1753 _T("invalid week number: weeks are counted from 1") );
1754
1755 // Jan 4 always lies in the 1st week of the year
1756 wxDateTime dt(4, Jan, year);
1757 dt.SetToWeekDayInSameWeek(wd);
1758 dt += wxDateSpan::Weeks(numWeek - 1);
1759
1760 return dt;
1761 }
1762
1763 // use a separate function to avoid warnings about using deprecated
1764 // SetToTheWeek in GetWeek below
1765 static wxDateTime
1766 SetToTheWeek(int year,
1767 wxDateTime::wxDateTime_t numWeek,
1768 wxDateTime::WeekDay weekday,
1769 wxDateTime::WeekFlags flags)
1770 {
1771 // Jan 4 always lies in the 1st week of the year
1772 wxDateTime dt(4, wxDateTime::Jan, year);
1773 dt.SetToWeekDayInSameWeek(weekday, flags);
1774 dt += wxDateSpan::Weeks(numWeek - 1);
1775
1776 return dt;
1777 }
1778
1779 bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek,
1780 WeekDay weekday,
1781 WeekFlags flags)
1782 {
1783 int year = GetYear();
1784 *this = ::SetToTheWeek(year, numWeek, weekday, flags);
1785 if ( GetYear() != year )
1786 {
1787 // oops... numWeek was too big
1788 return false;
1789 }
1790
1791 return true;
1792 }
1793
1794 wxDateTime wxDateTime::GetWeek(wxDateTime_t numWeek,
1795 WeekDay weekday,
1796 WeekFlags flags) const
1797 {
1798 return ::SetToTheWeek(GetYear(), numWeek, weekday, flags);
1799 }
1800
1801 wxDateTime& wxDateTime::SetToLastMonthDay(Month month,
1802 int year)
1803 {
1804 // take the current month/year if none specified
1805 if ( year == Inv_Year )
1806 year = GetYear();
1807 if ( month == Inv_Month )
1808 month = GetMonth();
1809
1810 return Set(GetNumOfDaysInMonth(year, month), month, year);
1811 }
1812
1813 wxDateTime& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday, WeekFlags flags)
1814 {
1815 wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
1816
1817 int wdayThis = GetWeekDay();
1818 if ( weekday == wdayThis )
1819 {
1820 // nothing to do
1821 return *this;
1822 }
1823
1824 if ( flags == Default_First )
1825 {
1826 flags = GetCountry() == USA ? Sunday_First : Monday_First;
1827 }
1828
1829 // the logic below based on comparing weekday and wdayThis works if Sun (0)
1830 // is the first day in the week, but breaks down for Monday_First case so
1831 // we adjust the week days in this case
1832 if( flags == Monday_First )
1833 {
1834 if ( wdayThis == Sun )
1835 wdayThis += 7;
1836 }
1837 //else: Sunday_First, nothing to do
1838
1839 // go forward or back in time to the day we want
1840 if ( weekday < wdayThis )
1841 {
1842 return Subtract(wxDateSpan::Days(wdayThis - weekday));
1843 }
1844 else // weekday > wdayThis
1845 {
1846 return Add(wxDateSpan::Days(weekday - wdayThis));
1847 }
1848 }
1849
1850 wxDateTime& wxDateTime::SetToNextWeekDay(WeekDay weekday)
1851 {
1852 wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
1853
1854 int diff;
1855 WeekDay wdayThis = GetWeekDay();
1856 if ( weekday == wdayThis )
1857 {
1858 // nothing to do
1859 return *this;
1860 }
1861 else if ( weekday < wdayThis )
1862 {
1863 // need to advance a week
1864 diff = 7 - (wdayThis - weekday);
1865 }
1866 else // weekday > wdayThis
1867 {
1868 diff = weekday - wdayThis;
1869 }
1870
1871 return Add(wxDateSpan::Days(diff));
1872 }
1873
1874 wxDateTime& wxDateTime::SetToPrevWeekDay(WeekDay weekday)
1875 {
1876 wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
1877
1878 int diff;
1879 WeekDay wdayThis = GetWeekDay();
1880 if ( weekday == wdayThis )
1881 {
1882 // nothing to do
1883 return *this;
1884 }
1885 else if ( weekday > wdayThis )
1886 {
1887 // need to go to previous week
1888 diff = 7 - (weekday - wdayThis);
1889 }
1890 else // weekday < wdayThis
1891 {
1892 diff = wdayThis - weekday;
1893 }
1894
1895 return Subtract(wxDateSpan::Days(diff));
1896 }
1897
1898 bool wxDateTime::SetToWeekDay(WeekDay weekday,
1899 int n,
1900 Month month,
1901 int year)
1902 {
1903 wxCHECK_MSG( weekday != Inv_WeekDay, false, _T("invalid weekday") );
1904
1905 // we don't check explicitly that -5 <= n <= 5 because we will return false
1906 // anyhow in such case - but may be should still give an assert for it?
1907
1908 // take the current month/year if none specified
1909 ReplaceDefaultYearMonthWithCurrent(&year, &month);
1910
1911 wxDateTime dt;
1912
1913 // TODO this probably could be optimised somehow...
1914
1915 if ( n > 0 )
1916 {
1917 // get the first day of the month
1918 dt.Set(1, month, year);
1919
1920 // get its wday
1921 WeekDay wdayFirst = dt.GetWeekDay();
1922
1923 // go to the first weekday of the month
1924 int diff = weekday - wdayFirst;
1925 if ( diff < 0 )
1926 diff += 7;
1927
1928 // add advance n-1 weeks more
1929 diff += 7*(n - 1);
1930
1931 dt += wxDateSpan::Days(diff);
1932 }
1933 else // count from the end of the month
1934 {
1935 // get the last day of the month
1936 dt.SetToLastMonthDay(month, year);
1937
1938 // get its wday
1939 WeekDay wdayLast = dt.GetWeekDay();
1940
1941 // go to the last weekday of the month
1942 int diff = wdayLast - weekday;
1943 if ( diff < 0 )
1944 diff += 7;
1945
1946 // and rewind n-1 weeks from there
1947 diff += 7*(-n - 1);
1948
1949 dt -= wxDateSpan::Days(diff);
1950 }
1951
1952 // check that it is still in the same month
1953 if ( dt.GetMonth() == month )
1954 {
1955 *this = dt;
1956
1957 return true;
1958 }
1959 else
1960 {
1961 // no such day in this month
1962 return false;
1963 }
1964 }
1965
1966 static inline
1967 wxDateTime::wxDateTime_t GetDayOfYearFromTm(const wxDateTime::Tm& tm)
1968 {
1969 return (wxDateTime::wxDateTime_t)(gs_cumulatedDays[wxDateTime::IsLeapYear(tm.year)][tm.mon] + tm.mday);
1970 }
1971
1972 wxDateTime::wxDateTime_t wxDateTime::GetDayOfYear(const TimeZone& tz) const
1973 {
1974 return GetDayOfYearFromTm(GetTm(tz));
1975 }
1976
1977 wxDateTime::wxDateTime_t
1978 wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags, const TimeZone& tz) const
1979 {
1980 if ( flags == Default_First )
1981 {
1982 flags = GetCountry() == USA ? Sunday_First : Monday_First;
1983 }
1984
1985 Tm tm(GetTm(tz));
1986 wxDateTime_t nDayInYear = GetDayOfYearFromTm(tm);
1987
1988 int wdTarget = GetWeekDay(tz);
1989 int wdYearStart = wxDateTime(1, Jan, GetYear()).GetWeekDay();
1990 int week;
1991 if ( flags == Sunday_First )
1992 {
1993 // FIXME: First week is not calculated correctly.
1994 week = (nDayInYear - wdTarget + 7) / 7;
1995 if ( wdYearStart == Wed || wdYearStart == Thu )
1996 week++;
1997 }
1998 else // week starts with monday
1999 {
2000 // adjust the weekdays to non-US style.
2001 wdYearStart = ConvertWeekDayToMondayBase(wdYearStart);
2002 wdTarget = ConvertWeekDayToMondayBase(wdTarget);
2003
2004 // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html:
2005 //
2006 // Week 01 of a year is per definition the first week that has the
2007 // Thursday in this year, which is equivalent to the week that
2008 // contains the fourth day of January. In other words, the first
2009 // week of a new year is the week that has the majority of its
2010 // days in the new year. Week 01 might also contain days from the
2011 // previous year and the week before week 01 of a year is the last
2012 // week (52 or 53) of the previous year even if it contains days
2013 // from the new year. A week starts with Monday (day 1) and ends
2014 // with Sunday (day 7).
2015 //
2016
2017 // if Jan 1 is Thursday or less, it is in the first week of this year
2018 if ( wdYearStart < 4 )
2019 {
2020 // count the number of entire weeks between Jan 1 and this date
2021 week = (nDayInYear + wdYearStart + 6 - wdTarget)/7;
2022
2023 // be careful to check for overflow in the next year
2024 if ( week == 53 && tm.mday - wdTarget > 28 )
2025 week = 1;
2026 }
2027 else // Jan 1 is in the last week of the previous year
2028 {
2029 // check if we happen to be at the last week of previous year:
2030 if ( tm.mon == Jan && tm.mday < 8 - wdYearStart )
2031 week = wxDateTime(31, Dec, GetYear()-1).GetWeekOfYear();
2032 else
2033 week = (nDayInYear + wdYearStart - 1 - wdTarget)/7;
2034 }
2035 }
2036
2037 return (wxDateTime::wxDateTime_t)week;
2038 }
2039
2040 wxDateTime::wxDateTime_t wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags,
2041 const TimeZone& tz) const
2042 {
2043 Tm tm = GetTm(tz);
2044 wxDateTime dtMonthStart = wxDateTime(1, tm.mon, tm.year);
2045 int nWeek = GetWeekOfYear(flags) - dtMonthStart.GetWeekOfYear(flags) + 1;
2046 if ( nWeek < 0 )
2047 {
2048 // this may happen for January when Jan, 1 is the last week of the
2049 // previous year
2050 nWeek += IsLeapYear(tm.year - 1) ? 53 : 52;
2051 }
2052
2053 return (wxDateTime::wxDateTime_t)nWeek;
2054 }
2055
2056 wxDateTime& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday)
2057 {
2058 int year = GetYear();
2059 wxDATETIME_CHECK( (0 < yday) && (yday <= GetNumberOfDays(year)),
2060 _T("invalid year day") );
2061
2062 bool isLeap = IsLeapYear(year);
2063 for ( Month mon = Jan; mon < Inv_Month; wxNextMonth(mon) )
2064 {
2065 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
2066 // don't need it neither - because of the CHECK above we know that
2067 // yday lies in December then
2068 if ( (mon == Dec) || (yday <= gs_cumulatedDays[isLeap][mon + 1]) )
2069 {
2070 Set((wxDateTime::wxDateTime_t)(yday - gs_cumulatedDays[isLeap][mon]), mon, year);
2071
2072 break;
2073 }
2074 }
2075
2076 return *this;
2077 }
2078
2079 // ----------------------------------------------------------------------------
2080 // Julian day number conversion and related stuff
2081 // ----------------------------------------------------------------------------
2082
2083 double wxDateTime::GetJulianDayNumber() const
2084 {
2085 // JDN are always expressed for the UTC dates
2086 Tm tm(ToTimezone(UTC).GetTm(UTC));
2087
2088 double result = GetTruncatedJDN(tm.mday, tm.mon, tm.year);
2089
2090 // add the part GetTruncatedJDN() neglected
2091 result += 0.5;
2092
2093 // and now add the time: 86400 sec = 1 JDN
2094 return result + ((double)(60*(60*tm.hour + tm.min) + tm.sec)) / 86400;
2095 }
2096
2097 double wxDateTime::GetRataDie() const
2098 {
2099 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
2100 return GetJulianDayNumber() - 1721119.5 - 306;
2101 }
2102
2103 // ----------------------------------------------------------------------------
2104 // timezone and DST stuff
2105 // ----------------------------------------------------------------------------
2106
2107 int wxDateTime::IsDST(wxDateTime::Country country) const
2108 {
2109 wxCHECK_MSG( country == Country_Default, -1,
2110 _T("country support not implemented") );
2111
2112 // use the C RTL for the dates in the standard range
2113 time_t timet = GetTicks();
2114 if ( timet != (time_t)-1 )
2115 {
2116 tm *tm = localtime(&timet);
2117
2118 wxCHECK_MSG( tm, -1, _T("localtime() failed") );
2119
2120 return tm->tm_isdst;
2121 }
2122 else
2123 {
2124 int year = GetYear();
2125
2126 if ( !IsDSTApplicable(year, country) )
2127 {
2128 // no DST time in this year in this country
2129 return -1;
2130 }
2131
2132 return IsBetween(GetBeginDST(year, country), GetEndDST(year, country));
2133 }
2134 }
2135
2136 wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST)
2137 {
2138 long secDiff = GetTimeZone() + tz.GetOffset();
2139
2140 // we need to know whether DST is or not in effect for this date unless
2141 // the test disabled by the caller
2142 if ( !noDST && (IsDST() == 1) )
2143 {
2144 // FIXME we assume that the DST is always shifted by 1 hour
2145 secDiff -= 3600;
2146 }
2147
2148 return Subtract(wxTimeSpan::Seconds(secDiff));
2149 }
2150
2151 // ----------------------------------------------------------------------------
2152 // wxDateTime to/from text representations
2153 // ----------------------------------------------------------------------------
2154
2155 wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
2156 {
2157 wxCHECK_MSG( format, wxEmptyString, _T("NULL format in wxDateTime::Format") );
2158
2159 // we have to use our own implementation if the date is out of range of
2160 // strftime() or if we use non standard specificators
2161 time_t time = GetTicks();
2162 if ( (time != (time_t)-1) && !wxStrstr(format, _T("%l")) )
2163 {
2164 // use strftime()
2165 tm *tm;
2166 if ( tz.GetOffset() == -GetTimeZone() )
2167 {
2168 // we are working with local time
2169 tm = localtime(&time);
2170
2171 // should never happen
2172 wxCHECK_MSG( tm, wxEmptyString, _T("localtime() failed") );
2173 }
2174 else
2175 {
2176 time += (int)tz.GetOffset();
2177
2178 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
2179 int time2 = (int) time;
2180 if ( time2 >= 0 )
2181 #else
2182 if ( time >= 0 )
2183 #endif
2184 {
2185 tm = gmtime(&time);
2186
2187 // should never happen
2188 wxCHECK_MSG( tm, wxEmptyString, _T("gmtime() failed") );
2189 }
2190 else
2191 {
2192 tm = (struct tm *)NULL;
2193 }
2194 }
2195 #ifndef __WXWINCE__
2196 //Windows CE doesn't support strftime or wcsftime, so we use the generic implementation
2197 if ( tm )
2198 {
2199 return CallStrftime(format, tm);
2200 }
2201 #endif
2202 //else: use generic code below
2203 }
2204
2205 // we only parse ANSI C format specifications here, no POSIX 2
2206 // complications, no GNU extensions but we do add support for a "%l" format
2207 // specifier allowing to get the number of milliseconds
2208 Tm tm = GetTm(tz);
2209
2210 // used for calls to strftime() when we only deal with time
2211 struct tm tmTimeOnly;
2212 tmTimeOnly.tm_hour = tm.hour;
2213 tmTimeOnly.tm_min = tm.min;
2214 tmTimeOnly.tm_sec = tm.sec;
2215 tmTimeOnly.tm_wday = 0;
2216 tmTimeOnly.tm_yday = 0;
2217 tmTimeOnly.tm_mday = 1; // any date will do
2218 tmTimeOnly.tm_mon = 0;
2219 tmTimeOnly.tm_year = 76;
2220 tmTimeOnly.tm_isdst = 0; // no DST, we adjust for tz ourselves
2221
2222 wxString tmp, res, fmt;
2223 for ( const wxChar *p = format; *p; p++ )
2224 {
2225 if ( *p != _T('%') )
2226 {
2227 // copy as is
2228 res += *p;
2229
2230 continue;
2231 }
2232
2233 // set the default format
2234 switch ( *++p )
2235 {
2236 case _T('Y'): // year has 4 digits
2237 fmt = _T("%04d");
2238 break;
2239
2240 case _T('j'): // day of year has 3 digits
2241 case _T('l'): // milliseconds have 3 digits
2242 fmt = _T("%03d");
2243 break;
2244
2245 case _T('w'): // week day as number has only one
2246 fmt = _T("%d");
2247 break;
2248
2249 default:
2250 // it's either another valid format specifier in which case
2251 // the format is "%02d" (for all the rest) or we have the
2252 // field width preceding the format in which case it will
2253 // override the default format anyhow
2254 fmt = _T("%02d");
2255 }
2256
2257 bool restart = true;
2258 while ( restart )
2259 {
2260 restart = false;
2261
2262 // start of the format specification
2263 switch ( *p )
2264 {
2265 case _T('a'): // a weekday name
2266 case _T('A'):
2267 // second parameter should be true for abbreviated names
2268 res += GetWeekDayName(tm.GetWeekDay(),
2269 *p == _T('a') ? Name_Abbr : Name_Full);
2270 break;
2271
2272 case _T('b'): // a month name
2273 case _T('B'):
2274 res += GetMonthName(tm.mon,
2275 *p == _T('b') ? Name_Abbr : Name_Full);
2276 break;
2277
2278 case _T('c'): // locale default date and time representation
2279 case _T('x'): // locale default date representation
2280 #ifndef __WXWINCE__
2281 //
2282 // the problem: there is no way to know what do these format
2283 // specifications correspond to for the current locale.
2284 //
2285 // the solution: use a hack and still use strftime(): first
2286 // find the YEAR which is a year in the strftime() range (1970
2287 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
2288 // of the real year. Then make a copy of the format and
2289 // replace all occurences of YEAR in it with some unique
2290 // string not appearing anywhere else in it, then use
2291 // strftime() to format the date in year YEAR and then replace
2292 // YEAR back by the real year and the unique replacement
2293 // string back with YEAR. Notice that "all occurences of YEAR"
2294 // means all occurences of 4 digit as well as 2 digit form!
2295 //
2296 // the bugs: we assume that neither of %c nor %x contains any
2297 // fields which may change between the YEAR and real year. For
2298 // example, the week number (%U, %W) and the day number (%j)
2299 // will change if one of these years is leap and the other one
2300 // is not!
2301 {
2302 // find the YEAR: normally, for any year X, Jan 1 or the
2303 // year X + 28 is the same weekday as Jan 1 of X (because
2304 // the weekday advances by 1 for each normal X and by 2
2305 // for each leap X, hence by 5 every 4 years or by 35
2306 // which is 0 mod 7 every 28 years) but this rule breaks
2307 // down if there are years between X and Y which are
2308 // divisible by 4 but not leap (i.e. divisible by 100 but
2309 // not 400), hence the correction.
2310
2311 int yearReal = GetYear(tz);
2312 int mod28 = yearReal % 28;
2313
2314 // be careful to not go too far - we risk to leave the
2315 // supported range
2316 int year;
2317 if ( mod28 < 10 )
2318 {
2319 year = 1988 + mod28; // 1988 == 0 (mod 28)
2320 }
2321 else
2322 {
2323 year = 1970 + mod28 - 10; // 1970 == 10 (mod 28)
2324 }
2325
2326 int nCentury = year / 100,
2327 nCenturyReal = yearReal / 100;
2328
2329 // need to adjust for the years divisble by 400 which are
2330 // not leap but are counted like leap ones if we just take
2331 // the number of centuries in between for nLostWeekDays
2332 int nLostWeekDays = (nCentury - nCenturyReal) -
2333 (nCentury / 4 - nCenturyReal / 4);
2334
2335 // we have to gain back the "lost" weekdays: note that the
2336 // effect of this loop is to not do anything to
2337 // nLostWeekDays (which we won't use any more), but to
2338 // (indirectly) set the year correctly
2339 while ( (nLostWeekDays % 7) != 0 )
2340 {
2341 nLostWeekDays += year++ % 4 ? 1 : 2;
2342 }
2343
2344 // at any rate, we couldn't go further than 1988 + 9 + 28!
2345 wxASSERT_MSG( year < 2030,
2346 _T("logic error in wxDateTime::Format") );
2347
2348 wxString strYear, strYear2;
2349 strYear.Printf(_T("%d"), year);
2350 strYear2.Printf(_T("%d"), year % 100);
2351
2352 // find two strings not occuring in format (this is surely
2353 // not optimal way of doing it... improvements welcome!)
2354 wxString fmt = format;
2355 wxString replacement = (wxChar)-1;
2356 while ( fmt.Find(replacement) != wxNOT_FOUND )
2357 {
2358 replacement << (wxChar)-1;
2359 }
2360
2361 wxString replacement2 = (wxChar)-2;
2362 while ( fmt.Find(replacement) != wxNOT_FOUND )
2363 {
2364 replacement << (wxChar)-2;
2365 }
2366
2367 // replace all occurences of year with it
2368 bool wasReplaced = fmt.Replace(strYear, replacement) > 0;
2369 if ( !wasReplaced )
2370 wasReplaced = fmt.Replace(strYear2, replacement2) > 0;
2371
2372 // use strftime() to format the same date but in supported
2373 // year
2374 //
2375 // NB: we assume that strftime() doesn't check for the
2376 // date validity and will happily format the date
2377 // corresponding to Feb 29 of a non leap year (which
2378 // may happen if yearReal was leap and year is not)
2379 struct tm tmAdjusted;
2380 InitTm(tmAdjusted);
2381 tmAdjusted.tm_hour = tm.hour;
2382 tmAdjusted.tm_min = tm.min;
2383 tmAdjusted.tm_sec = tm.sec;
2384 tmAdjusted.tm_wday = tm.GetWeekDay();
2385 tmAdjusted.tm_yday = GetDayOfYear();
2386 tmAdjusted.tm_mday = tm.mday;
2387 tmAdjusted.tm_mon = tm.mon;
2388 tmAdjusted.tm_year = year - 1900;
2389 tmAdjusted.tm_isdst = 0; // no DST, already adjusted
2390 wxString str = CallStrftime(*p == _T('c') ? _T("%c")
2391 : _T("%x"),
2392 &tmAdjusted);
2393
2394 // now replace the occurence of 1999 with the real year
2395 wxString strYearReal, strYearReal2;
2396 strYearReal.Printf(_T("%04d"), yearReal);
2397 strYearReal2.Printf(_T("%02d"), yearReal % 100);
2398 str.Replace(strYear, strYearReal);
2399 str.Replace(strYear2, strYearReal2);
2400
2401 // and replace back all occurences of replacement string
2402 if ( wasReplaced )
2403 {
2404 str.Replace(replacement2, strYear2);
2405 str.Replace(replacement, strYear);
2406 }
2407
2408 res += str;
2409 }
2410 #else
2411 //Use "%m/%d/%y %H:%M:%S" format instead
2412 res += wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
2413 tm.mon+1,tm.mday, tm.year, tm.hour, tm.min, tm.sec);
2414 #endif
2415 break;
2416
2417 case _T('d'): // day of a month (01-31)
2418 res += wxString::Format(fmt, tm.mday);
2419 break;
2420
2421 case _T('H'): // hour in 24h format (00-23)
2422 res += wxString::Format(fmt, tm.hour);
2423 break;
2424
2425 case _T('I'): // hour in 12h format (01-12)
2426 {
2427 // 24h -> 12h, 0h -> 12h too
2428 int hour12 = tm.hour > 12 ? tm.hour - 12
2429 : tm.hour ? tm.hour : 12;
2430 res += wxString::Format(fmt, hour12);
2431 }
2432 break;
2433
2434 case _T('j'): // day of the year
2435 res += wxString::Format(fmt, GetDayOfYear(tz));
2436 break;
2437
2438 case _T('l'): // milliseconds (NOT STANDARD)
2439 res += wxString::Format(fmt, GetMillisecond(tz));
2440 break;
2441
2442 case _T('m'): // month as a number (01-12)
2443 res += wxString::Format(fmt, tm.mon + 1);
2444 break;
2445
2446 case _T('M'): // minute as a decimal number (00-59)
2447 res += wxString::Format(fmt, tm.min);
2448 break;
2449
2450 case _T('p'): // AM or PM string
2451 #ifndef __WXWINCE__
2452 res += CallStrftime(_T("%p"), &tmTimeOnly);
2453 #else
2454 res += (tmTimeOnly.tm_hour > 12) ? wxT("pm") : wxT("am");
2455 #endif
2456 break;
2457
2458 case _T('S'): // second as a decimal number (00-61)
2459 res += wxString::Format(fmt, tm.sec);
2460 break;
2461
2462 case _T('U'): // week number in the year (Sunday 1st week day)
2463 res += wxString::Format(fmt, GetWeekOfYear(Sunday_First, tz));
2464 break;
2465
2466 case _T('W'): // week number in the year (Monday 1st week day)
2467 res += wxString::Format(fmt, GetWeekOfYear(Monday_First, tz));
2468 break;
2469
2470 case _T('w'): // weekday as a number (0-6), Sunday = 0
2471 res += wxString::Format(fmt, tm.GetWeekDay());
2472 break;
2473
2474 // case _T('x'): -- handled with "%c"
2475
2476 case _T('X'): // locale default time representation
2477 // just use strftime() to format the time for us
2478 #ifndef __WXWINCE__
2479 res += CallStrftime(_T("%X"), &tmTimeOnly);
2480 #else
2481 res += wxString::Format(wxT("%02d:%02d:%02d"),tm.hour, tm.min, tm.sec);
2482 #endif
2483 break;
2484
2485 case _T('y'): // year without century (00-99)
2486 res += wxString::Format(fmt, tm.year % 100);
2487 break;
2488
2489 case _T('Y'): // year with century
2490 res += wxString::Format(fmt, tm.year);
2491 break;
2492
2493 case _T('Z'): // timezone name
2494 #ifndef __WXWINCE__
2495 res += CallStrftime(_T("%Z"), &tmTimeOnly);
2496 #endif
2497 break;
2498
2499 default:
2500 // is it the format width?
2501 fmt.Empty();
2502 while ( *p == _T('-') || *p == _T('+') ||
2503 *p == _T(' ') || wxIsdigit(*p) )
2504 {
2505 fmt += *p;
2506 }
2507
2508 if ( !fmt.empty() )
2509 {
2510 // we've only got the flags and width so far in fmt
2511 fmt.Prepend(_T('%'));
2512 fmt.Append(_T('d'));
2513
2514 restart = true;
2515
2516 break;
2517 }
2518
2519 // no, it wasn't the width
2520 wxFAIL_MSG(_T("unknown format specificator"));
2521
2522 // fall through and just copy it nevertheless
2523
2524 case _T('%'): // a percent sign
2525 res += *p;
2526 break;
2527
2528 case 0: // the end of string
2529 wxFAIL_MSG(_T("missing format at the end of string"));
2530
2531 // just put the '%' which was the last char in format
2532 res += _T('%');
2533 break;
2534 }
2535 }
2536 }
2537
2538 return res;
2539 }
2540
2541 // this function parses a string in (strict) RFC 822 format: see the section 5
2542 // of the RFC for the detailed description, but briefly it's something of the
2543 // form "Sat, 18 Dec 1999 00:48:30 +0100"
2544 //
2545 // this function is "strict" by design - it must reject anything except true
2546 // RFC822 time specs.
2547 //
2548 // TODO a great candidate for using reg exps
2549 const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date)
2550 {
2551 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
2552
2553 const wxChar *p = date;
2554 const wxChar *comma = wxStrchr(p, _T(','));
2555 if ( comma )
2556 {
2557 // the part before comma is the weekday
2558
2559 // skip it for now - we don't use but might check that it really
2560 // corresponds to the specfied date
2561 p = comma + 1;
2562
2563 if ( *p != _T(' ') )
2564 {
2565 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
2566
2567 return (wxChar *)NULL;
2568 }
2569
2570 p++; // skip space
2571 }
2572
2573 // the following 1 or 2 digits are the day number
2574 if ( !wxIsdigit(*p) )
2575 {
2576 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
2577
2578 return (wxChar *)NULL;
2579 }
2580
2581 wxDateTime_t day = (wxDateTime_t)(*p++ - _T('0'));
2582 if ( wxIsdigit(*p) )
2583 {
2584 day *= 10;
2585 day = (wxDateTime_t)(day + (*p++ - _T('0')));
2586 }
2587
2588 if ( *p++ != _T(' ') )
2589 {
2590 return (wxChar *)NULL;
2591 }
2592
2593 // the following 3 letters specify the month
2594 wxString monName(p, 3);
2595 Month mon;
2596 if ( monName == _T("Jan") )
2597 mon = Jan;
2598 else if ( monName == _T("Feb") )
2599 mon = Feb;
2600 else if ( monName == _T("Mar") )
2601 mon = Mar;
2602 else if ( monName == _T("Apr") )
2603 mon = Apr;
2604 else if ( monName == _T("May") )
2605 mon = May;
2606 else if ( monName == _T("Jun") )
2607 mon = Jun;
2608 else if ( monName == _T("Jul") )
2609 mon = Jul;
2610 else if ( monName == _T("Aug") )
2611 mon = Aug;
2612 else if ( monName == _T("Sep") )
2613 mon = Sep;
2614 else if ( monName == _T("Oct") )
2615 mon = Oct;
2616 else if ( monName == _T("Nov") )
2617 mon = Nov;
2618 else if ( monName == _T("Dec") )
2619 mon = Dec;
2620 else
2621 {
2622 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName.c_str());
2623
2624 return (wxChar *)NULL;
2625 }
2626
2627 p += 3;
2628
2629 if ( *p++ != _T(' ') )
2630 {
2631 return (wxChar *)NULL;
2632 }
2633
2634 // next is the year
2635 if ( !wxIsdigit(*p) )
2636 {
2637 // no year?
2638 return (wxChar *)NULL;
2639 }
2640
2641 int year = *p++ - _T('0');
2642
2643 if ( !wxIsdigit(*p) )
2644 {
2645 // should have at least 2 digits in the year
2646 return (wxChar *)NULL;
2647 }
2648
2649 year *= 10;
2650 year += *p++ - _T('0');
2651
2652 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
2653 if ( wxIsdigit(*p) )
2654 {
2655 year *= 10;
2656 year += *p++ - _T('0');
2657
2658 if ( !wxIsdigit(*p) )
2659 {
2660 // no 3 digit years please
2661 return (wxChar *)NULL;
2662 }
2663
2664 year *= 10;
2665 year += *p++ - _T('0');
2666 }
2667
2668 if ( *p++ != _T(' ') )
2669 {
2670 return (wxChar *)NULL;
2671 }
2672
2673 // time is in the format hh:mm:ss and seconds are optional
2674 if ( !wxIsdigit(*p) )
2675 {
2676 return (wxChar *)NULL;
2677 }
2678
2679 wxDateTime_t hour = (wxDateTime_t)(*p++ - _T('0'));
2680
2681 if ( !wxIsdigit(*p) )
2682 {
2683 return (wxChar *)NULL;
2684 }
2685
2686 hour *= 10;
2687 hour = (wxDateTime_t)(hour + (*p++ - _T('0')));
2688
2689 if ( *p++ != _T(':') )
2690 {
2691 return (wxChar *)NULL;
2692 }
2693
2694 if ( !wxIsdigit(*p) )
2695 {
2696 return (wxChar *)NULL;
2697 }
2698
2699 wxDateTime_t min = (wxDateTime_t)(*p++ - _T('0'));
2700
2701 if ( !wxIsdigit(*p) )
2702 {
2703 return (wxChar *)NULL;
2704 }
2705
2706 min *= 10;
2707 min = (wxDateTime_t)(min + *p++ - _T('0'));
2708
2709 wxDateTime_t sec = 0;
2710 if ( *p++ == _T(':') )
2711 {
2712 if ( !wxIsdigit(*p) )
2713 {
2714 return (wxChar *)NULL;
2715 }
2716
2717 sec = (wxDateTime_t)(*p++ - _T('0'));
2718
2719 if ( !wxIsdigit(*p) )
2720 {
2721 return (wxChar *)NULL;
2722 }
2723
2724 sec *= 10;
2725 sec = (wxDateTime_t)(sec + *p++ - _T('0'));
2726 }
2727
2728 if ( *p++ != _T(' ') )
2729 {
2730 return (wxChar *)NULL;
2731 }
2732
2733 // and now the interesting part: the timezone
2734 int offset;
2735 if ( *p == _T('-') || *p == _T('+') )
2736 {
2737 // the explicit offset given: it has the form of hhmm
2738 bool plus = *p++ == _T('+');
2739
2740 if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) )
2741 {
2742 return (wxChar *)NULL;
2743 }
2744
2745 // hours
2746 offset = 60*(10*(*p - _T('0')) + (*(p + 1) - _T('0')));
2747
2748 p += 2;
2749
2750 if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) )
2751 {
2752 return (wxChar *)NULL;
2753 }
2754
2755 // minutes
2756 offset += 10*(*p - _T('0')) + (*(p + 1) - _T('0'));
2757
2758 if ( !plus )
2759 {
2760 offset = -offset;
2761 }
2762
2763 p += 2;
2764 }
2765 else
2766 {
2767 // the symbolic timezone given: may be either military timezone or one
2768 // of standard abbreviations
2769 if ( !*(p + 1) )
2770 {
2771 // military: Z = UTC, J unused, A = -1, ..., Y = +12
2772 static const int offsets[26] =
2773 {
2774 //A B C D E F G H I J K L M
2775 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
2776 //N O P R Q S T U V W Z Y Z
2777 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
2778 };
2779
2780 if ( *p < _T('A') || *p > _T('Z') || *p == _T('J') )
2781 {
2782 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p);
2783
2784 return (wxChar *)NULL;
2785 }
2786
2787 offset = offsets[*p++ - _T('A')];
2788 }
2789 else
2790 {
2791 // abbreviation
2792 wxString tz = p;
2793 if ( tz == _T("UT") || tz == _T("UTC") || tz == _T("GMT") )
2794 offset = 0;
2795 else if ( tz == _T("AST") )
2796 offset = AST - GMT0;
2797 else if ( tz == _T("ADT") )
2798 offset = ADT - GMT0;
2799 else if ( tz == _T("EST") )
2800 offset = EST - GMT0;
2801 else if ( tz == _T("EDT") )
2802 offset = EDT - GMT0;
2803 else if ( tz == _T("CST") )
2804 offset = CST - GMT0;
2805 else if ( tz == _T("CDT") )
2806 offset = CDT - GMT0;
2807 else if ( tz == _T("MST") )
2808 offset = MST - GMT0;
2809 else if ( tz == _T("MDT") )
2810 offset = MDT - GMT0;
2811 else if ( tz == _T("PST") )
2812 offset = PST - GMT0;
2813 else if ( tz == _T("PDT") )
2814 offset = PDT - GMT0;
2815 else
2816 {
2817 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p);
2818
2819 return (wxChar *)NULL;
2820 }
2821
2822 p += tz.length();
2823 }
2824
2825 // make it minutes
2826 offset *= 60;
2827 }
2828
2829 // the spec was correct
2830 Set(day, mon, year, hour, min, sec);
2831 MakeTimezone((wxDateTime_t)(60*offset));
2832
2833 return p;
2834 }
2835
2836 #ifdef __WINDOWS__
2837 // Get's current locale's date formatting string and stores it in fmt if
2838 // the locale is set; otherwise or in case of failure, leaves fmt unchanged
2839 void GetLocaleDateFormat(wxString *fmt)
2840 {
2841 if ( strcmp(setlocale(LC_ALL, NULL), "C") != 0 )
2842 {
2843 // The locale was programatically set to non-C. We assume that this was
2844 // done using wxLocale, in which case thread's current locale is also
2845 // set to correct LCID value and we can use GetLocaleInfo to determine
2846 // the correct formatting string:
2847 LCID lcid = GetThreadLocale();
2848 wxChar delim[5]; // fields deliminer, 4 chars max
2849 if ( GetLocaleInfo(lcid, LOCALE_SDATE, delim, 5) )
2850 {
2851 wxChar centurybuf[2]; // use %y or %Y, 1 char max
2852 wxChar century = 'y';
2853 if ( GetLocaleInfo(lcid, LOCALE_ICENTURY, centurybuf, 2) )
2854 {
2855 if ( centurybuf[0] == _T('1') )
2856 century = 'Y';
2857 // else 'y' as above
2858 }
2859
2860 wxChar order[2]; // order code, 1 char max
2861 if ( GetLocaleInfo(lcid, LOCALE_IDATE, order, 2) )
2862 {
2863 if ( order[0] == _T('0') ) // M-D-Y
2864 {
2865 *fmt = wxString::Format(_T("%%m%s%%d%s%%%c"),
2866 delim, delim, century);
2867 }
2868 else if ( order[0] == _T('1') ) // D-M-Y
2869 {
2870 *fmt = wxString::Format(_T("%%d%s%%m%s%%%c"),
2871 delim, delim, century);
2872 }
2873 else if ( order[0] == _T('2') ) // Y-M-D
2874 {
2875 *fmt = wxString::Format(_T("%%%c%s%%m%s%%d"),
2876 century, delim, delim);
2877 }
2878 else
2879 {
2880 wxFAIL_MSG(_T("unexpected GetLocaleInfo return value"));
2881 }
2882 }
2883 }
2884 // if we failed, leave fmtDate value unchanged and
2885 // try our luck with the default set above
2886 }
2887 }
2888 #endif // __WINDOWS__
2889
2890 const wxChar *wxDateTime::ParseFormat(const wxChar *date,
2891 const wxChar *format,
2892 const wxDateTime& dateDef)
2893 {
2894 wxCHECK_MSG( date && format, (wxChar *)NULL,
2895 _T("NULL pointer in wxDateTime::ParseFormat()") );
2896
2897 wxString str;
2898 unsigned long num;
2899
2900 // what fields have we found?
2901 bool haveWDay = false,
2902 haveYDay = false,
2903 haveDay = false,
2904 haveMon = false,
2905 haveYear = false,
2906 haveHour = false,
2907 haveMin = false,
2908 haveSec = false;
2909
2910 bool hourIsIn12hFormat = false, // or in 24h one?
2911 isPM = false; // AM by default
2912
2913 // and the value of the items we have (init them to get rid of warnings)
2914 wxDateTime_t sec = 0,
2915 min = 0,
2916 hour = 0;
2917 WeekDay wday = Inv_WeekDay;
2918 wxDateTime_t yday = 0,
2919 mday = 0;
2920 wxDateTime::Month mon = Inv_Month;
2921 int year = 0;
2922
2923 const wxChar *input = date;
2924 for ( const wxChar *fmt = format; *fmt; fmt++ )
2925 {
2926 if ( *fmt != _T('%') )
2927 {
2928 if ( wxIsspace(*fmt) )
2929 {
2930 // a white space in the format string matches 0 or more white
2931 // spaces in the input
2932 while ( wxIsspace(*input) )
2933 {
2934 input++;
2935 }
2936 }
2937 else // !space
2938 {
2939 // any other character (not whitespace, not '%') must be
2940 // matched by itself in the input
2941 if ( *input++ != *fmt )
2942 {
2943 // no match
2944 return (wxChar *)NULL;
2945 }
2946 }
2947
2948 // done with this format char
2949 continue;
2950 }
2951
2952 // start of a format specification
2953
2954 // parse the optional width
2955 size_t width = 0;
2956 while ( wxIsdigit(*++fmt) )
2957 {
2958 width *= 10;
2959 width += *fmt - _T('0');
2960 }
2961
2962 // the default widths for the various fields
2963 if ( !width )
2964 {
2965 switch ( *fmt )
2966 {
2967 case _T('Y'): // year has 4 digits
2968 width = 4;
2969 break;
2970
2971 case _T('j'): // day of year has 3 digits
2972 case _T('l'): // milliseconds have 3 digits
2973 width = 3;
2974 break;
2975
2976 case _T('w'): // week day as number has only one
2977 width = 1;
2978 break;
2979
2980 default:
2981 // default for all other fields
2982 width = 2;
2983 }
2984 }
2985
2986 // then the format itself
2987 switch ( *fmt )
2988 {
2989 case _T('a'): // a weekday name
2990 case _T('A'):
2991 {
2992 int flag = *fmt == _T('a') ? Name_Abbr : Name_Full;
2993 wday = GetWeekDayFromName(GetAlphaToken(input), flag);
2994 if ( wday == Inv_WeekDay )
2995 {
2996 // no match
2997 return (wxChar *)NULL;
2998 }
2999 }
3000 haveWDay = true;
3001 break;
3002
3003 case _T('b'): // a month name
3004 case _T('B'):
3005 {
3006 int flag = *fmt == _T('b') ? Name_Abbr : Name_Full;
3007 mon = GetMonthFromName(GetAlphaToken(input), flag);
3008 if ( mon == Inv_Month )
3009 {
3010 // no match
3011 return (wxChar *)NULL;
3012 }
3013 }
3014 haveMon = true;
3015 break;
3016
3017 case _T('c'): // locale default date and time representation
3018 {
3019 wxDateTime dt;
3020
3021 // this is the format which corresponds to ctime() output
3022 // and strptime("%c") should parse it, so try it first
3023 static const wxChar *fmtCtime = _T("%a %b %d %H:%M:%S %Y");
3024
3025 const wxChar *result = dt.ParseFormat(input, fmtCtime);
3026 if ( !result )
3027 {
3028 result = dt.ParseFormat(input, _T("%x %X"));
3029 }
3030
3031 if ( !result )
3032 {
3033 result = dt.ParseFormat(input, _T("%X %x"));
3034 }
3035
3036 if ( !result )
3037 {
3038 // we've tried everything and still no match
3039 return (wxChar *)NULL;
3040 }
3041
3042 Tm tm = dt.GetTm();
3043
3044 haveDay = haveMon = haveYear =
3045 haveHour = haveMin = haveSec = true;
3046
3047 hour = tm.hour;
3048 min = tm.min;
3049 sec = tm.sec;
3050
3051 year = tm.year;
3052 mon = tm.mon;
3053 mday = tm.mday;
3054
3055 input = result;
3056 }
3057 break;
3058
3059 case _T('d'): // day of a month (01-31)
3060 if ( !GetNumericToken(width, input, &num) ||
3061 (num > 31) || (num < 1) )
3062 {
3063 // no match
3064 return (wxChar *)NULL;
3065 }
3066
3067 // we can't check whether the day range is correct yet, will
3068 // do it later - assume ok for now
3069 haveDay = true;
3070 mday = (wxDateTime_t)num;
3071 break;
3072
3073 case _T('H'): // hour in 24h format (00-23)
3074 if ( !GetNumericToken(width, input, &num) || (num > 23) )
3075 {
3076 // no match
3077 return (wxChar *)NULL;
3078 }
3079
3080 haveHour = true;
3081 hour = (wxDateTime_t)num;
3082 break;
3083
3084 case _T('I'): // hour in 12h format (01-12)
3085 if ( !GetNumericToken(width, input, &num) || !num || (num > 12) )
3086 {
3087 // no match
3088 return (wxChar *)NULL;
3089 }
3090
3091 haveHour = true;
3092 hourIsIn12hFormat = true;
3093 hour = (wxDateTime_t)(num % 12); // 12 should be 0
3094 break;
3095
3096 case _T('j'): // day of the year
3097 if ( !GetNumericToken(width, input, &num) || !num || (num > 366) )
3098 {
3099 // no match
3100 return (wxChar *)NULL;
3101 }
3102
3103 haveYDay = true;
3104 yday = (wxDateTime_t)num;
3105 break;
3106
3107 case _T('m'): // month as a number (01-12)
3108 if ( !GetNumericToken(width, input, &num) || !num || (num > 12) )
3109 {
3110 // no match
3111 return (wxChar *)NULL;
3112 }
3113
3114 haveMon = true;
3115 mon = (Month)(num - 1);
3116 break;
3117
3118 case _T('M'): // minute as a decimal number (00-59)
3119 if ( !GetNumericToken(width, input, &num) || (num > 59) )
3120 {
3121 // no match
3122 return (wxChar *)NULL;
3123 }
3124
3125 haveMin = true;
3126 min = (wxDateTime_t)num;
3127 break;
3128
3129 case _T('p'): // AM or PM string
3130 {
3131 wxString am, pm, token = GetAlphaToken(input);
3132
3133 GetAmPmStrings(&am, &pm);
3134 if (am.empty() && pm.empty())
3135 return (wxChar *)NULL; // no am/pm strings defined
3136 if ( token.CmpNoCase(pm) == 0 )
3137 {
3138 isPM = true;
3139 }
3140 else if ( token.CmpNoCase(am) != 0 )
3141 {
3142 // no match
3143 return (wxChar *)NULL;
3144 }
3145 }
3146 break;
3147
3148 case _T('r'): // time as %I:%M:%S %p
3149 {
3150 wxDateTime dt;
3151 input = dt.ParseFormat(input, _T("%I:%M:%S %p"));
3152 if ( !input )
3153 {
3154 // no match
3155 return (wxChar *)NULL;
3156 }
3157
3158 haveHour = haveMin = haveSec = true;
3159
3160 Tm tm = dt.GetTm();
3161 hour = tm.hour;
3162 min = tm.min;
3163 sec = tm.sec;
3164 }
3165 break;
3166
3167 case _T('R'): // time as %H:%M
3168 {
3169 wxDateTime dt;
3170 input = dt.ParseFormat(input, _T("%H:%M"));
3171 if ( !input )
3172 {
3173 // no match
3174 return (wxChar *)NULL;
3175 }
3176
3177 haveHour = haveMin = true;
3178
3179 Tm tm = dt.GetTm();
3180 hour = tm.hour;
3181 min = tm.min;
3182 }
3183
3184 case _T('S'): // second as a decimal number (00-61)
3185 if ( !GetNumericToken(width, input, &num) || (num > 61) )
3186 {
3187 // no match
3188 return (wxChar *)NULL;
3189 }
3190
3191 haveSec = true;
3192 sec = (wxDateTime_t)num;
3193 break;
3194
3195 case _T('T'): // time as %H:%M:%S
3196 {
3197 wxDateTime dt;
3198 input = dt.ParseFormat(input, _T("%H:%M:%S"));
3199 if ( !input )
3200 {
3201 // no match
3202 return (wxChar *)NULL;
3203 }
3204
3205 haveHour = haveMin = haveSec = true;
3206
3207 Tm tm = dt.GetTm();
3208 hour = tm.hour;
3209 min = tm.min;
3210 sec = tm.sec;
3211 }
3212 break;
3213
3214 case _T('w'): // weekday as a number (0-6), Sunday = 0
3215 if ( !GetNumericToken(width, input, &num) || (wday > 6) )
3216 {
3217 // no match
3218 return (wxChar *)NULL;
3219 }
3220
3221 haveWDay = true;
3222 wday = (WeekDay)num;
3223 break;
3224
3225 case _T('x'): // locale default date representation
3226 #ifdef HAVE_STRPTIME
3227 // try using strptime() -- it may fail even if the input is
3228 // correct but the date is out of range, so we will fall back
3229 // to our generic code anyhow
3230 {
3231 struct tm tm;
3232
3233 const wxChar *result = CallStrptime(input, "%x", &tm);
3234 if ( result )
3235 {
3236 input = result;
3237
3238 haveDay = haveMon = haveYear = true;
3239
3240 year = 1900 + tm.tm_year;
3241 mon = (Month)tm.tm_mon;
3242 mday = tm.tm_mday;
3243
3244 break;
3245 }
3246 }
3247 #endif // HAVE_STRPTIME
3248
3249 {
3250 wxDateTime dt;
3251
3252 wxString fmtDate, fmtDateAlt;
3253
3254 if ( IsWestEuropeanCountry(GetCountry()) ||
3255 GetCountry() == Russia )
3256 {
3257 fmtDate = _T("%d/%m/%y");
3258 fmtDateAlt = _T("%m/%d/%y");
3259 }
3260 else // assume USA
3261 {
3262 fmtDate = _T("%m/%d/%y");
3263 fmtDateAlt = _T("%d/%m/%y");
3264 }
3265
3266 #ifdef __WINDOWS__
3267 // The above doesn't work for all locales, try to query
3268 // Windows for the right way of formatting the date:
3269 GetLocaleDateFormat(&fmtDate);
3270 #endif
3271
3272 const wxChar *result = dt.ParseFormat(input, fmtDate);
3273
3274 if ( !result )
3275 {
3276 // ok, be nice and try another one
3277 result = dt.ParseFormat(input, fmtDateAlt);
3278 }
3279
3280 if ( !result )
3281 {
3282 // bad luck
3283 return (wxChar *)NULL;
3284 }
3285
3286 Tm tm = dt.GetTm();
3287
3288 haveDay = haveMon = haveYear = true;
3289
3290 year = tm.year;
3291 mon = tm.mon;
3292 mday = tm.mday;
3293
3294 input = result;
3295 }
3296
3297 break;
3298
3299 case _T('X'): // locale default time representation
3300 #ifdef HAVE_STRPTIME
3301 {
3302 // use strptime() to do it for us (FIXME !Unicode friendly)
3303 struct tm tm;
3304 input = CallStrptime(input, "%X", &tm);
3305 if ( !input )
3306 {
3307 return (wxChar *)NULL;
3308 }
3309
3310 haveHour = haveMin = haveSec = true;
3311
3312 hour = tm.tm_hour;
3313 min = tm.tm_min;
3314 sec = tm.tm_sec;
3315 }
3316 #else // !HAVE_STRPTIME
3317 // TODO under Win32 we can query the LOCALE_ITIME system
3318 // setting which says whether the default time format is
3319 // 24 or 12 hour
3320 {
3321 // try to parse what follows as "%H:%M:%S" and, if this
3322 // fails, as "%I:%M:%S %p" - this should catch the most
3323 // common cases
3324 wxDateTime dt;
3325
3326 const wxChar *result = dt.ParseFormat(input, _T("%T"));
3327 if ( !result )
3328 {
3329 result = dt.ParseFormat(input, _T("%r"));
3330 }
3331
3332 if ( !result )
3333 {
3334 // no match
3335 return (wxChar *)NULL;
3336 }
3337
3338 haveHour = haveMin = haveSec = true;
3339
3340 Tm tm = dt.GetTm();
3341 hour = tm.hour;
3342 min = tm.min;
3343 sec = tm.sec;
3344
3345 input = result;
3346 }
3347 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
3348 break;
3349
3350 case _T('y'): // year without century (00-99)
3351 if ( !GetNumericToken(width, input, &num) || (num > 99) )
3352 {
3353 // no match
3354 return (wxChar *)NULL;
3355 }
3356
3357 haveYear = true;
3358
3359 // TODO should have an option for roll over date instead of
3360 // hard coding it here
3361 year = (num > 30 ? 1900 : 2000) + (wxDateTime_t)num;
3362 break;
3363
3364 case _T('Y'): // year with century
3365 if ( !GetNumericToken(width, input, &num) )
3366 {
3367 // no match
3368 return (wxChar *)NULL;
3369 }
3370
3371 haveYear = true;
3372 year = (wxDateTime_t)num;
3373 break;
3374
3375 case _T('Z'): // timezone name
3376 wxFAIL_MSG(_T("TODO"));
3377 break;
3378
3379 case _T('%'): // a percent sign
3380 if ( *input++ != _T('%') )
3381 {
3382 // no match
3383 return (wxChar *)NULL;
3384 }
3385 break;
3386
3387 case 0: // the end of string
3388 wxFAIL_MSG(_T("unexpected format end"));
3389
3390 // fall through
3391
3392 default: // not a known format spec
3393 return (wxChar *)NULL;
3394 }
3395 }
3396
3397 // format matched, try to construct a date from what we have now
3398 Tm tmDef;
3399 if ( dateDef.IsValid() )
3400 {
3401 // take this date as default
3402 tmDef = dateDef.GetTm();
3403 }
3404 else if ( IsValid() )
3405 {
3406 // if this date is valid, don't change it
3407 tmDef = GetTm();
3408 }
3409 else
3410 {
3411 // no default and this date is invalid - fall back to Today()
3412 tmDef = Today().GetTm();
3413 }
3414
3415 Tm tm = tmDef;
3416
3417 // set the date
3418 if ( haveYear )
3419 {
3420 tm.year = year;
3421 }
3422
3423 // TODO we don't check here that the values are consistent, if both year
3424 // day and month/day were found, we just ignore the year day and we
3425 // also always ignore the week day
3426 if ( haveMon && haveDay )
3427 {
3428 if ( mday > GetNumOfDaysInMonth(tm.year, mon) )
3429 {
3430 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
3431
3432 return (wxChar *)NULL;
3433 }
3434
3435 tm.mon = mon;
3436 tm.mday = mday;
3437 }
3438 else if ( haveYDay )
3439 {
3440 if ( yday > GetNumberOfDays(tm.year) )
3441 {
3442 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
3443
3444 return (wxChar *)NULL;
3445 }
3446
3447 Tm tm2 = wxDateTime(1, Jan, tm.year).SetToYearDay(yday).GetTm();
3448
3449 tm.mon = tm2.mon;
3450 tm.mday = tm2.mday;
3451 }
3452
3453 // deal with AM/PM
3454 if ( haveHour && hourIsIn12hFormat && isPM )
3455 {
3456 // translate to 24hour format
3457 hour += 12;
3458 }
3459 //else: either already in 24h format or no translation needed
3460
3461 // set the time
3462 if ( haveHour )
3463 {
3464 tm.hour = hour;
3465 }
3466
3467 if ( haveMin )
3468 {
3469 tm.min = min;
3470 }
3471
3472 if ( haveSec )
3473 {
3474 tm.sec = sec;
3475 }
3476
3477 Set(tm);
3478
3479 // finally check that the week day is consistent -- if we had it
3480 if ( haveWDay && GetWeekDay() != wday )
3481 {
3482 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
3483
3484 return NULL;
3485 }
3486
3487 return input;
3488 }
3489
3490 const wxChar *wxDateTime::ParseDateTime(const wxChar *date)
3491 {
3492 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
3493
3494 // Set to current day and hour, so strings like '14:00' becomes today at
3495 // 14, not some other random date
3496 wxDateTime dtDate = wxDateTime::Today();
3497 wxDateTime dtTime = wxDateTime::Today();
3498
3499 const wxChar* pchTime;
3500
3501 // Try to parse the beginning of the string as a date
3502 const wxChar* pchDate = dtDate.ParseDate(date);
3503
3504 // We got a date in the beginning, see if there is a time specified after the date
3505 if ( pchDate )
3506 {
3507 // Skip spaces, as the ParseTime() function fails on spaces
3508 while ( wxIsspace(*pchDate) )
3509 pchDate++;
3510
3511 pchTime = dtTime.ParseTime(pchDate);
3512 }
3513 else // no date in the beginning
3514 {
3515 // check and see if we have a time followed by a date
3516 pchTime = dtTime.ParseTime(date);
3517 if ( pchTime )
3518 {
3519 while ( wxIsspace(*pchTime) )
3520 pchTime++;
3521
3522 pchDate = dtDate.ParseDate(pchTime);
3523 }
3524 }
3525
3526 // If we have a date specified, set our own data to the same date
3527 if ( !pchDate || !pchTime )
3528 return NULL;
3529
3530 Set(dtDate.GetDay(), dtDate.GetMonth(), dtDate.GetYear(),
3531 dtTime.GetHour(), dtTime.GetMinute(), dtTime.GetSecond(),
3532 dtTime.GetMillisecond());
3533
3534 // Return endpoint of scan
3535 return pchDate > pchTime ? pchDate : pchTime;
3536 }
3537
3538 const wxChar *wxDateTime::ParseDate(const wxChar *date)
3539 {
3540 // this is a simplified version of ParseDateTime() which understands only
3541 // "today" (for wxDate compatibility) and digits only otherwise (and not
3542 // all esoteric constructions ParseDateTime() knows about)
3543
3544 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
3545
3546 const wxChar *p = date;
3547 while ( wxIsspace(*p) )
3548 p++;
3549
3550 // some special cases
3551 static struct
3552 {
3553 const wxChar *str;
3554 int dayDiffFromToday;
3555 } literalDates[] =
3556 {
3557 { wxTRANSLATE("today"), 0 },
3558 { wxTRANSLATE("yesterday"), -1 },
3559 { wxTRANSLATE("tomorrow"), 1 },
3560 };
3561
3562 for ( size_t n = 0; n < WXSIZEOF(literalDates); n++ )
3563 {
3564 wxString date = wxGetTranslation(literalDates[n].str);
3565 size_t len = date.length();
3566 if ( wxStrlen(p) >= len )
3567 {
3568 wxString str(p, len);
3569 if ( str.CmpNoCase(date) == 0 )
3570 {
3571 // nothing can follow this, so stop here
3572 p += len;
3573
3574 int dayDiffFromToday = literalDates[n].dayDiffFromToday;
3575 *this = Today();
3576 if ( dayDiffFromToday )
3577 {
3578 *this += wxDateSpan::Days(dayDiffFromToday);
3579 }
3580
3581 return p;
3582 }
3583 }
3584 }
3585
3586 // We try to guess what we have here: for each new (numeric) token, we
3587 // determine if it can be a month, day or a year. Of course, there is an
3588 // ambiguity as some numbers may be days as well as months, so we also
3589 // have the ability to back track.
3590
3591 // what do we have?
3592 bool haveDay = false, // the months day?
3593 haveWDay = false, // the day of week?
3594 haveMon = false, // the month?
3595 haveYear = false; // the year?
3596
3597 // and the value of the items we have (init them to get rid of warnings)
3598 WeekDay wday = Inv_WeekDay;
3599 wxDateTime_t day = 0;
3600 wxDateTime::Month mon = Inv_Month;
3601 int year = 0;
3602
3603 // tokenize the string
3604 size_t nPosCur = 0;
3605 static const wxChar *dateDelimiters = _T(".,/-\t\r\n ");
3606 wxStringTokenizer tok(p, dateDelimiters);
3607 while ( tok.HasMoreTokens() )
3608 {
3609 wxString token = tok.GetNextToken();
3610 if ( !token )
3611 continue;
3612
3613 // is it a number?
3614 unsigned long val;
3615 if ( token.ToULong(&val) )
3616 {
3617 // guess what this number is
3618
3619 bool isDay = false,
3620 isMonth = false,
3621 isYear = false;
3622
3623 if ( !haveMon && val > 0 && val <= 12 )
3624 {
3625 // assume it is month
3626 isMonth = true;
3627 }
3628 else // not the month
3629 {
3630 if ( haveDay )
3631 {
3632 // this can only be the year
3633 isYear = true;
3634 }
3635 else // may be either day or year
3636 {
3637 wxDateTime_t max_days = (wxDateTime_t)(
3638 haveMon
3639 ? GetNumOfDaysInMonth(haveYear ? year : Inv_Year, mon)
3640 : 31
3641 );
3642
3643 // can it be day?
3644 if ( (val == 0) || (val > (unsigned long)max_days) )
3645 {
3646 // no
3647 isYear = true;
3648 }
3649 else // yes, suppose it's the day
3650 {
3651 isDay = true;
3652 }
3653 }
3654 }
3655
3656 if ( isYear )
3657 {
3658 if ( haveYear )
3659 break;
3660
3661 haveYear = true;
3662
3663 year = (wxDateTime_t)val;
3664 }
3665 else if ( isDay )
3666 {
3667 if ( haveDay )
3668 break;
3669
3670 haveDay = true;
3671
3672 day = (wxDateTime_t)val;
3673 }
3674 else if ( isMonth )
3675 {
3676 haveMon = true;
3677
3678 mon = (Month)(val - 1);
3679 }
3680 }
3681 else // not a number
3682 {
3683 // be careful not to overwrite the current mon value
3684 Month mon2 = GetMonthFromName(token, Name_Full | Name_Abbr);
3685 if ( mon2 != Inv_Month )
3686 {
3687 // it's a month
3688 if ( haveMon )
3689 {
3690 // but we already have a month - maybe we guessed wrong?
3691 if ( !haveDay )
3692 {
3693 // no need to check in month range as always < 12, but
3694 // the days are counted from 1 unlike the months
3695 day = (wxDateTime_t)(mon + 1);
3696 haveDay = true;
3697 }
3698 else
3699 {
3700 // could possible be the year (doesn't the year come
3701 // before the month in the japanese format?) (FIXME)
3702 break;
3703 }
3704 }
3705
3706 mon = mon2;
3707
3708 haveMon = true;
3709 }
3710 else // not a valid month name
3711 {
3712 wday = GetWeekDayFromName(token, Name_Full | Name_Abbr);
3713 if ( wday != Inv_WeekDay )
3714 {
3715 // a week day
3716 if ( haveWDay )
3717 {
3718 break;
3719 }
3720
3721 haveWDay = true;
3722 }
3723 else // not a valid weekday name
3724 {
3725 // try the ordinals
3726 static const wxChar *ordinals[] =
3727 {
3728 wxTRANSLATE("first"),
3729 wxTRANSLATE("second"),
3730 wxTRANSLATE("third"),
3731 wxTRANSLATE("fourth"),
3732 wxTRANSLATE("fifth"),
3733 wxTRANSLATE("sixth"),
3734 wxTRANSLATE("seventh"),
3735 wxTRANSLATE("eighth"),
3736 wxTRANSLATE("ninth"),
3737 wxTRANSLATE("tenth"),
3738 wxTRANSLATE("eleventh"),
3739 wxTRANSLATE("twelfth"),
3740 wxTRANSLATE("thirteenth"),
3741 wxTRANSLATE("fourteenth"),
3742 wxTRANSLATE("fifteenth"),
3743 wxTRANSLATE("sixteenth"),
3744 wxTRANSLATE("seventeenth"),
3745 wxTRANSLATE("eighteenth"),
3746 wxTRANSLATE("nineteenth"),
3747 wxTRANSLATE("twentieth"),
3748 // that's enough - otherwise we'd have problems with
3749 // composite (or not) ordinals
3750 };
3751
3752 size_t n;
3753 for ( n = 0; n < WXSIZEOF(ordinals); n++ )
3754 {
3755 if ( token.CmpNoCase(ordinals[n]) == 0 )
3756 {
3757 break;
3758 }
3759 }
3760
3761 if ( n == WXSIZEOF(ordinals) )
3762 {
3763 // stop here - something unknown
3764 break;
3765 }
3766
3767 // it's a day
3768 if ( haveDay )
3769 {
3770 // don't try anything here (as in case of numeric day
3771 // above) - the symbolic day spec should always
3772 // precede the month/year
3773 break;
3774 }
3775
3776 haveDay = true;
3777
3778 day = (wxDateTime_t)(n + 1);
3779 }
3780 }
3781 }
3782
3783 nPosCur = tok.GetPosition();
3784 }
3785
3786 // either no more tokens or the scan was stopped by something we couldn't
3787 // parse - in any case, see if we can construct a date from what we have
3788 if ( !haveDay && !haveWDay )
3789 {
3790 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
3791
3792 return (wxChar *)NULL;
3793 }
3794
3795 if ( haveWDay && (haveMon || haveYear || haveDay) &&
3796 !(haveDay && haveMon && haveYear) )
3797 {
3798 // without adjectives (which we don't support here) the week day only
3799 // makes sense completely separately or with the full date
3800 // specification (what would "Wed 1999" mean?)
3801 return (wxChar *)NULL;
3802 }
3803
3804 if ( !haveWDay && haveYear && !(haveDay && haveMon) )
3805 {
3806 // may be we have month and day instead of day and year?
3807 if ( haveDay && !haveMon )
3808 {
3809 if ( day <= 12 )
3810 {
3811 // exchange day and month
3812 mon = (wxDateTime::Month)(day - 1);
3813
3814 // we're in the current year then
3815 if ( (year > 0) && (year <= (int)GetNumOfDaysInMonth(Inv_Year, mon)) )
3816 {
3817 day = (wxDateTime_t)year;
3818
3819 haveMon = true;
3820 haveYear = false;
3821 }
3822 //else: no, can't exchange, leave haveMon == false
3823 }
3824 }
3825
3826 if ( !haveMon )
3827 {
3828 // if we give the year, month and day must be given too
3829 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
3830
3831 return (wxChar *)NULL;
3832 }
3833 }
3834
3835 if ( !haveMon )
3836 {
3837 mon = GetCurrentMonth();
3838 }
3839
3840 if ( !haveYear )
3841 {
3842 year = GetCurrentYear();
3843 }
3844
3845 if ( haveDay )
3846 {
3847 Set(day, mon, year);
3848
3849 if ( haveWDay )
3850 {
3851 // check that it is really the same
3852 if ( GetWeekDay() != wday )
3853 {
3854 // inconsistency detected
3855 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
3856
3857 return (wxChar *)NULL;
3858 }
3859 }
3860 }
3861 else // haveWDay
3862 {
3863 *this = Today();
3864
3865 SetToWeekDayInSameWeek(wday);
3866 }
3867
3868 // return the pointer to the first unparsed char
3869 p += nPosCur;
3870 if ( nPosCur && wxStrchr(dateDelimiters, *(p - 1)) )
3871 {
3872 // if we couldn't parse the token after the delimiter, put back the
3873 // delimiter as well
3874 p--;
3875 }
3876
3877 return p;
3878 }
3879
3880 const wxChar *wxDateTime::ParseTime(const wxChar *time)
3881 {
3882 wxCHECK_MSG( time, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
3883
3884 // first try some extra things
3885 static const struct
3886 {
3887 const wxChar *name;
3888 wxDateTime_t hour;
3889 } stdTimes[] =
3890 {
3891 { wxTRANSLATE("noon"), 12 },
3892 { wxTRANSLATE("midnight"), 00 },
3893 // anything else?
3894 };
3895
3896 for ( size_t n = 0; n < WXSIZEOF(stdTimes); n++ )
3897 {
3898 wxString timeString = wxGetTranslation(stdTimes[n].name);
3899 size_t len = timeString.length();
3900 if ( timeString.CmpNoCase(wxString(time, len)) == 0 )
3901 {
3902 // casts required by DigitalMars
3903 Set(stdTimes[n].hour, wxDateTime_t(0), wxDateTime_t(0));
3904
3905 return time + len;
3906 }
3907 }
3908
3909 // try all time formats we may think about in the order from longest to
3910 // shortest
3911
3912 // 12hour with AM/PM?
3913 const wxChar *result = ParseFormat(time, _T("%I:%M:%S %p"));
3914
3915 if ( !result )
3916 {
3917 // normally, it's the same, but why not try it?
3918 result = ParseFormat(time, _T("%H:%M:%S"));
3919 }
3920
3921 if ( !result )
3922 {
3923 // 12hour with AM/PM but without seconds?
3924 result = ParseFormat(time, _T("%I:%M %p"));
3925 }
3926
3927 if ( !result )
3928 {
3929 // without seconds?
3930 result = ParseFormat(time, _T("%H:%M"));
3931 }
3932
3933 if ( !result )
3934 {
3935 // just the hour and AM/PM?
3936 result = ParseFormat(time, _T("%I %p"));
3937 }
3938
3939 if ( !result )
3940 {
3941 // just the hour?
3942 result = ParseFormat(time, _T("%H"));
3943 }
3944
3945 if ( !result )
3946 {
3947 // parse the standard format: normally it is one of the formats above
3948 // but it may be set to something completely different by the user
3949 result = ParseFormat(time, _T("%X"));
3950 }
3951
3952 // TODO: parse timezones
3953
3954 return result;
3955 }
3956
3957 // ----------------------------------------------------------------------------
3958 // Workdays and holidays support
3959 // ----------------------------------------------------------------------------
3960
3961 bool wxDateTime::IsWorkDay(Country WXUNUSED(country)) const
3962 {
3963 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
3964 }
3965
3966 // ============================================================================
3967 // wxDateSpan
3968 // ============================================================================
3969
3970 wxDateSpan WXDLLIMPEXP_BASE operator*(int n, const wxDateSpan& ds)
3971 {
3972 wxDateSpan ds1(ds);
3973 return ds1.Multiply(n);
3974 }
3975
3976 // ============================================================================
3977 // wxTimeSpan
3978 // ============================================================================
3979
3980 wxTimeSpan WXDLLIMPEXP_BASE operator*(int n, const wxTimeSpan& ts)
3981 {
3982 return wxTimeSpan(ts).Multiply(n);
3983 }
3984
3985 // this enum is only used in wxTimeSpan::Format() below but we can't declare
3986 // it locally to the method as it provokes an internal compiler error in egcs
3987 // 2.91.60 when building with -O2
3988 enum TimeSpanPart
3989 {
3990 Part_Week,
3991 Part_Day,
3992 Part_Hour,
3993 Part_Min,
3994 Part_Sec,
3995 Part_MSec
3996 };
3997
3998 // not all strftime(3) format specifiers make sense here because, for example,
3999 // a time span doesn't have a year nor a timezone
4000 //
4001 // Here are the ones which are supported (all of them are supported by strftime
4002 // as well):
4003 // %H hour in 24 hour format
4004 // %M minute (00 - 59)
4005 // %S second (00 - 59)
4006 // %% percent sign
4007 //
4008 // Also, for MFC CTimeSpan compatibility, we support
4009 // %D number of days
4010 //
4011 // And, to be better than MFC :-), we also have
4012 // %E number of wEeks
4013 // %l milliseconds (000 - 999)
4014 wxString wxTimeSpan::Format(const wxChar *format) const
4015 {
4016 wxCHECK_MSG( format, wxEmptyString, _T("NULL format in wxTimeSpan::Format") );
4017
4018 wxString str;
4019 str.Alloc(wxStrlen(format));
4020
4021 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
4022 //
4023 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
4024 // question is what should ts.Format("%S") do? The code here returns "3273"
4025 // in this case (i.e. the total number of seconds, not just seconds % 60)
4026 // because, for me, this call means "give me entire time interval in
4027 // seconds" and not "give me the seconds part of the time interval"
4028 //
4029 // If we agree that it should behave like this, it is clear that the
4030 // interpretation of each format specifier depends on the presence of the
4031 // other format specs in the string: if there was "%H" before "%M", we
4032 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
4033
4034 // we remember the most important unit found so far
4035 TimeSpanPart partBiggest = Part_MSec;
4036
4037 for ( const wxChar *pch = format; *pch; pch++ )
4038 {
4039 wxChar ch = *pch;
4040
4041 if ( ch == _T('%') )
4042 {
4043 // the start of the format specification of the printf() below
4044 wxString fmtPrefix = _T('%');
4045
4046 // the number
4047 long n;
4048
4049 ch = *++pch; // get the format spec char
4050 switch ( ch )
4051 {
4052 default:
4053 wxFAIL_MSG( _T("invalid format character") );
4054 // fall through
4055
4056 case _T('%'):
4057 str += ch;
4058
4059 // skip the part below switch
4060 continue;
4061
4062 case _T('D'):
4063 n = GetDays();
4064 if ( partBiggest < Part_Day )
4065 {
4066 n %= DAYS_PER_WEEK;
4067 }
4068 else
4069 {
4070 partBiggest = Part_Day;
4071 }
4072 break;
4073
4074 case _T('E'):
4075 partBiggest = Part_Week;
4076 n = GetWeeks();
4077 break;
4078
4079 case _T('H'):
4080 n = GetHours();
4081 if ( partBiggest < Part_Hour )
4082 {
4083 n %= HOURS_PER_DAY;
4084 }
4085 else
4086 {
4087 partBiggest = Part_Hour;
4088 }
4089
4090 fmtPrefix += _T("02");
4091 break;
4092
4093 case _T('l'):
4094 n = GetMilliseconds().ToLong();
4095 if ( partBiggest < Part_MSec )
4096 {
4097 n %= 1000;
4098 }
4099 //else: no need to reset partBiggest to Part_MSec, it is
4100 // the least significant one anyhow
4101
4102 fmtPrefix += _T("03");
4103 break;
4104
4105 case _T('M'):
4106 n = GetMinutes();
4107 if ( partBiggest < Part_Min )
4108 {
4109 n %= MIN_PER_HOUR;
4110 }
4111 else
4112 {
4113 partBiggest = Part_Min;
4114 }
4115
4116 fmtPrefix += _T("02");
4117 break;
4118
4119 case _T('S'):
4120 n = GetSeconds().ToLong();
4121 if ( partBiggest < Part_Sec )
4122 {
4123 n %= SEC_PER_MIN;
4124 }
4125 else
4126 {
4127 partBiggest = Part_Sec;
4128 }
4129
4130 fmtPrefix += _T("02");
4131 break;
4132 }
4133
4134 str += wxString::Format(fmtPrefix + _T("ld"), n);
4135 }
4136 else
4137 {
4138 // normal character, just copy
4139 str += ch;
4140 }
4141 }
4142
4143 return str;
4144 }
4145
4146 // ============================================================================
4147 // wxDateTimeHolidayAuthority and related classes
4148 // ============================================================================
4149
4150 #include "wx/arrimpl.cpp"
4151
4152 WX_DEFINE_OBJARRAY(wxDateTimeArray);
4153
4154 static int wxCMPFUNC_CONV
4155 wxDateTimeCompareFunc(wxDateTime **first, wxDateTime **second)
4156 {
4157 wxDateTime dt1 = **first,
4158 dt2 = **second;
4159
4160 return dt1 == dt2 ? 0 : dt1 < dt2 ? -1 : +1;
4161 }
4162
4163 // ----------------------------------------------------------------------------
4164 // wxDateTimeHolidayAuthority
4165 // ----------------------------------------------------------------------------
4166
4167 wxHolidayAuthoritiesArray wxDateTimeHolidayAuthority::ms_authorities;
4168
4169 /* static */
4170 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime& dt)
4171 {
4172 size_t count = ms_authorities.size();
4173 for ( size_t n = 0; n < count; n++ )
4174 {
4175 if ( ms_authorities[n]->DoIsHoliday(dt) )
4176 {
4177 return true;
4178 }
4179 }
4180
4181 return false;
4182 }
4183
4184 /* static */
4185 size_t
4186 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime& dtStart,
4187 const wxDateTime& dtEnd,
4188 wxDateTimeArray& holidays)
4189 {
4190 wxDateTimeArray hol;
4191
4192 holidays.Clear();
4193
4194 size_t count = ms_authorities.size();
4195 for ( size_t nAuth = 0; nAuth < count; nAuth++ )
4196 {
4197 ms_authorities[nAuth]->DoGetHolidaysInRange(dtStart, dtEnd, hol);
4198
4199 WX_APPEND_ARRAY(holidays, hol);
4200 }
4201
4202 holidays.Sort(wxDateTimeCompareFunc);
4203
4204 return holidays.size();
4205 }
4206
4207 /* static */
4208 void wxDateTimeHolidayAuthority::ClearAllAuthorities()
4209 {
4210 WX_CLEAR_ARRAY(ms_authorities);
4211 }
4212
4213 /* static */
4214 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority *auth)
4215 {
4216 ms_authorities.push_back(auth);
4217 }
4218
4219 wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
4220 {
4221 // required here for Darwin
4222 }
4223
4224 // ----------------------------------------------------------------------------
4225 // wxDateTimeWorkDays
4226 // ----------------------------------------------------------------------------
4227
4228 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime& dt) const
4229 {
4230 wxDateTime::WeekDay wd = dt.GetWeekDay();
4231
4232 return (wd == wxDateTime::Sun) || (wd == wxDateTime::Sat);
4233 }
4234
4235 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime& dtStart,
4236 const wxDateTime& dtEnd,
4237 wxDateTimeArray& holidays) const
4238 {
4239 if ( dtStart > dtEnd )
4240 {
4241 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
4242
4243 return 0u;
4244 }
4245
4246 holidays.Empty();
4247
4248 // instead of checking all days, start with the first Sat after dtStart and
4249 // end with the last Sun before dtEnd
4250 wxDateTime dtSatFirst = dtStart.GetNextWeekDay(wxDateTime::Sat),
4251 dtSatLast = dtEnd.GetPrevWeekDay(wxDateTime::Sat),
4252 dtSunFirst = dtStart.GetNextWeekDay(wxDateTime::Sun),
4253 dtSunLast = dtEnd.GetPrevWeekDay(wxDateTime::Sun),
4254 dt;
4255
4256 for ( dt = dtSatFirst; dt <= dtSatLast; dt += wxDateSpan::Week() )
4257 {
4258 holidays.Add(dt);
4259 }
4260
4261 for ( dt = dtSunFirst; dt <= dtSunLast; dt += wxDateSpan::Week() )
4262 {
4263 holidays.Add(dt);
4264 }
4265
4266 return holidays.GetCount();
4267 }
4268
4269 // ============================================================================
4270 // other helper functions
4271 // ============================================================================
4272
4273 // ----------------------------------------------------------------------------
4274 // iteration helpers: can be used to write a for loop over enum variable like
4275 // this:
4276 // for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
4277 // ----------------------------------------------------------------------------
4278
4279 WXDLLIMPEXP_BASE void wxNextMonth(wxDateTime::Month& m)
4280 {
4281 wxASSERT_MSG( m < wxDateTime::Inv_Month, _T("invalid month") );
4282
4283 // no wrapping or the for loop above would never end!
4284 m = (wxDateTime::Month)(m + 1);
4285 }
4286
4287 WXDLLIMPEXP_BASE void wxPrevMonth(wxDateTime::Month& m)
4288 {
4289 wxASSERT_MSG( m < wxDateTime::Inv_Month, _T("invalid month") );
4290
4291 m = m == wxDateTime::Jan ? wxDateTime::Inv_Month
4292 : (wxDateTime::Month)(m - 1);
4293 }
4294
4295 WXDLLIMPEXP_BASE void wxNextWDay(wxDateTime::WeekDay& wd)
4296 {
4297 wxASSERT_MSG( wd < wxDateTime::Inv_WeekDay, _T("invalid week day") );
4298
4299 // no wrapping or the for loop above would never end!
4300 wd = (wxDateTime::WeekDay)(wd + 1);
4301 }
4302
4303 WXDLLIMPEXP_BASE void wxPrevWDay(wxDateTime::WeekDay& wd)
4304 {
4305 wxASSERT_MSG( wd < wxDateTime::Inv_WeekDay, _T("invalid week day") );
4306
4307 wd = wd == wxDateTime::Sun ? wxDateTime::Inv_WeekDay
4308 : (wxDateTime::WeekDay)(wd - 1);
4309 }
4310
4311 #endif // wxUSE_DATETIME