]> git.saurik.com Git - wxWidgets.git/blob - src/common/datetime.cpp
No res-include for rpmspec wx24dsp
[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)/sizeof(wxChar), _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)/sizeof(wxChar), _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 #ifdef __WXWINCE__
2848 LCID lcid = LOCALE_USER_DEFAULT;
2849 #else
2850 LCID lcid = GetThreadLocale();
2851 #endif
2852 wxChar delim[5]; // fields deliminer, 4 chars max
2853 if ( GetLocaleInfo(lcid, LOCALE_SDATE, delim, 5) )
2854 {
2855 wxChar centurybuf[2]; // use %y or %Y, 1 char max
2856 wxChar century = 'y';
2857 if ( GetLocaleInfo(lcid, LOCALE_ICENTURY, centurybuf, 2) )
2858 {
2859 if ( centurybuf[0] == _T('1') )
2860 century = 'Y';
2861 // else 'y' as above
2862 }
2863
2864 wxChar order[2]; // order code, 1 char max
2865 if ( GetLocaleInfo(lcid, LOCALE_IDATE, order, 2) )
2866 {
2867 if ( order[0] == _T('0') ) // M-D-Y
2868 {
2869 *fmt = wxString::Format(_T("%%m%s%%d%s%%%c"),
2870 delim, delim, century);
2871 }
2872 else if ( order[0] == _T('1') ) // D-M-Y
2873 {
2874 *fmt = wxString::Format(_T("%%d%s%%m%s%%%c"),
2875 delim, delim, century);
2876 }
2877 else if ( order[0] == _T('2') ) // Y-M-D
2878 {
2879 *fmt = wxString::Format(_T("%%%c%s%%m%s%%d"),
2880 century, delim, delim);
2881 }
2882 else
2883 {
2884 wxFAIL_MSG(_T("unexpected GetLocaleInfo return value"));
2885 }
2886 }
2887 }
2888 // if we failed, leave fmtDate value unchanged and
2889 // try our luck with the default set above
2890 }
2891 }
2892 #endif // __WINDOWS__
2893
2894 const wxChar *wxDateTime::ParseFormat(const wxChar *date,
2895 const wxChar *format,
2896 const wxDateTime& dateDef)
2897 {
2898 wxCHECK_MSG( date && format, (wxChar *)NULL,
2899 _T("NULL pointer in wxDateTime::ParseFormat()") );
2900
2901 wxString str;
2902 unsigned long num;
2903
2904 // what fields have we found?
2905 bool haveWDay = false,
2906 haveYDay = false,
2907 haveDay = false,
2908 haveMon = false,
2909 haveYear = false,
2910 haveHour = false,
2911 haveMin = false,
2912 haveSec = false;
2913
2914 bool hourIsIn12hFormat = false, // or in 24h one?
2915 isPM = false; // AM by default
2916
2917 // and the value of the items we have (init them to get rid of warnings)
2918 wxDateTime_t sec = 0,
2919 min = 0,
2920 hour = 0;
2921 WeekDay wday = Inv_WeekDay;
2922 wxDateTime_t yday = 0,
2923 mday = 0;
2924 wxDateTime::Month mon = Inv_Month;
2925 int year = 0;
2926
2927 const wxChar *input = date;
2928 for ( const wxChar *fmt = format; *fmt; fmt++ )
2929 {
2930 if ( *fmt != _T('%') )
2931 {
2932 if ( wxIsspace(*fmt) )
2933 {
2934 // a white space in the format string matches 0 or more white
2935 // spaces in the input
2936 while ( wxIsspace(*input) )
2937 {
2938 input++;
2939 }
2940 }
2941 else // !space
2942 {
2943 // any other character (not whitespace, not '%') must be
2944 // matched by itself in the input
2945 if ( *input++ != *fmt )
2946 {
2947 // no match
2948 return (wxChar *)NULL;
2949 }
2950 }
2951
2952 // done with this format char
2953 continue;
2954 }
2955
2956 // start of a format specification
2957
2958 // parse the optional width
2959 size_t width = 0;
2960 while ( wxIsdigit(*++fmt) )
2961 {
2962 width *= 10;
2963 width += *fmt - _T('0');
2964 }
2965
2966 // the default widths for the various fields
2967 if ( !width )
2968 {
2969 switch ( *fmt )
2970 {
2971 case _T('Y'): // year has 4 digits
2972 width = 4;
2973 break;
2974
2975 case _T('j'): // day of year has 3 digits
2976 case _T('l'): // milliseconds have 3 digits
2977 width = 3;
2978 break;
2979
2980 case _T('w'): // week day as number has only one
2981 width = 1;
2982 break;
2983
2984 default:
2985 // default for all other fields
2986 width = 2;
2987 }
2988 }
2989
2990 // then the format itself
2991 switch ( *fmt )
2992 {
2993 case _T('a'): // a weekday name
2994 case _T('A'):
2995 {
2996 int flag = *fmt == _T('a') ? Name_Abbr : Name_Full;
2997 wday = GetWeekDayFromName(GetAlphaToken(input), flag);
2998 if ( wday == Inv_WeekDay )
2999 {
3000 // no match
3001 return (wxChar *)NULL;
3002 }
3003 }
3004 haveWDay = true;
3005 break;
3006
3007 case _T('b'): // a month name
3008 case _T('B'):
3009 {
3010 int flag = *fmt == _T('b') ? Name_Abbr : Name_Full;
3011 mon = GetMonthFromName(GetAlphaToken(input), flag);
3012 if ( mon == Inv_Month )
3013 {
3014 // no match
3015 return (wxChar *)NULL;
3016 }
3017 }
3018 haveMon = true;
3019 break;
3020
3021 case _T('c'): // locale default date and time representation
3022 {
3023 wxDateTime dt;
3024
3025 // this is the format which corresponds to ctime() output
3026 // and strptime("%c") should parse it, so try it first
3027 static const wxChar *fmtCtime = _T("%a %b %d %H:%M:%S %Y");
3028
3029 const wxChar *result = dt.ParseFormat(input, fmtCtime);
3030 if ( !result )
3031 {
3032 result = dt.ParseFormat(input, _T("%x %X"));
3033 }
3034
3035 if ( !result )
3036 {
3037 result = dt.ParseFormat(input, _T("%X %x"));
3038 }
3039
3040 if ( !result )
3041 {
3042 // we've tried everything and still no match
3043 return (wxChar *)NULL;
3044 }
3045
3046 Tm tm = dt.GetTm();
3047
3048 haveDay = haveMon = haveYear =
3049 haveHour = haveMin = haveSec = true;
3050
3051 hour = tm.hour;
3052 min = tm.min;
3053 sec = tm.sec;
3054
3055 year = tm.year;
3056 mon = tm.mon;
3057 mday = tm.mday;
3058
3059 input = result;
3060 }
3061 break;
3062
3063 case _T('d'): // day of a month (01-31)
3064 if ( !GetNumericToken(width, input, &num) ||
3065 (num > 31) || (num < 1) )
3066 {
3067 // no match
3068 return (wxChar *)NULL;
3069 }
3070
3071 // we can't check whether the day range is correct yet, will
3072 // do it later - assume ok for now
3073 haveDay = true;
3074 mday = (wxDateTime_t)num;
3075 break;
3076
3077 case _T('H'): // hour in 24h format (00-23)
3078 if ( !GetNumericToken(width, input, &num) || (num > 23) )
3079 {
3080 // no match
3081 return (wxChar *)NULL;
3082 }
3083
3084 haveHour = true;
3085 hour = (wxDateTime_t)num;
3086 break;
3087
3088 case _T('I'): // hour in 12h format (01-12)
3089 if ( !GetNumericToken(width, input, &num) || !num || (num > 12) )
3090 {
3091 // no match
3092 return (wxChar *)NULL;
3093 }
3094
3095 haveHour = true;
3096 hourIsIn12hFormat = true;
3097 hour = (wxDateTime_t)(num % 12); // 12 should be 0
3098 break;
3099
3100 case _T('j'): // day of the year
3101 if ( !GetNumericToken(width, input, &num) || !num || (num > 366) )
3102 {
3103 // no match
3104 return (wxChar *)NULL;
3105 }
3106
3107 haveYDay = true;
3108 yday = (wxDateTime_t)num;
3109 break;
3110
3111 case _T('m'): // month as a number (01-12)
3112 if ( !GetNumericToken(width, input, &num) || !num || (num > 12) )
3113 {
3114 // no match
3115 return (wxChar *)NULL;
3116 }
3117
3118 haveMon = true;
3119 mon = (Month)(num - 1);
3120 break;
3121
3122 case _T('M'): // minute as a decimal number (00-59)
3123 if ( !GetNumericToken(width, input, &num) || (num > 59) )
3124 {
3125 // no match
3126 return (wxChar *)NULL;
3127 }
3128
3129 haveMin = true;
3130 min = (wxDateTime_t)num;
3131 break;
3132
3133 case _T('p'): // AM or PM string
3134 {
3135 wxString am, pm, token = GetAlphaToken(input);
3136
3137 GetAmPmStrings(&am, &pm);
3138 if (am.empty() && pm.empty())
3139 return (wxChar *)NULL; // no am/pm strings defined
3140 if ( token.CmpNoCase(pm) == 0 )
3141 {
3142 isPM = true;
3143 }
3144 else if ( token.CmpNoCase(am) != 0 )
3145 {
3146 // no match
3147 return (wxChar *)NULL;
3148 }
3149 }
3150 break;
3151
3152 case _T('r'): // time as %I:%M:%S %p
3153 {
3154 wxDateTime dt;
3155 input = dt.ParseFormat(input, _T("%I:%M:%S %p"));
3156 if ( !input )
3157 {
3158 // no match
3159 return (wxChar *)NULL;
3160 }
3161
3162 haveHour = haveMin = haveSec = true;
3163
3164 Tm tm = dt.GetTm();
3165 hour = tm.hour;
3166 min = tm.min;
3167 sec = tm.sec;
3168 }
3169 break;
3170
3171 case _T('R'): // time as %H:%M
3172 {
3173 wxDateTime dt;
3174 input = dt.ParseFormat(input, _T("%H:%M"));
3175 if ( !input )
3176 {
3177 // no match
3178 return (wxChar *)NULL;
3179 }
3180
3181 haveHour = haveMin = true;
3182
3183 Tm tm = dt.GetTm();
3184 hour = tm.hour;
3185 min = tm.min;
3186 }
3187
3188 case _T('S'): // second as a decimal number (00-61)
3189 if ( !GetNumericToken(width, input, &num) || (num > 61) )
3190 {
3191 // no match
3192 return (wxChar *)NULL;
3193 }
3194
3195 haveSec = true;
3196 sec = (wxDateTime_t)num;
3197 break;
3198
3199 case _T('T'): // time as %H:%M:%S
3200 {
3201 wxDateTime dt;
3202 input = dt.ParseFormat(input, _T("%H:%M:%S"));
3203 if ( !input )
3204 {
3205 // no match
3206 return (wxChar *)NULL;
3207 }
3208
3209 haveHour = haveMin = haveSec = true;
3210
3211 Tm tm = dt.GetTm();
3212 hour = tm.hour;
3213 min = tm.min;
3214 sec = tm.sec;
3215 }
3216 break;
3217
3218 case _T('w'): // weekday as a number (0-6), Sunday = 0
3219 if ( !GetNumericToken(width, input, &num) || (wday > 6) )
3220 {
3221 // no match
3222 return (wxChar *)NULL;
3223 }
3224
3225 haveWDay = true;
3226 wday = (WeekDay)num;
3227 break;
3228
3229 case _T('x'): // locale default date representation
3230 #ifdef HAVE_STRPTIME
3231 // try using strptime() -- it may fail even if the input is
3232 // correct but the date is out of range, so we will fall back
3233 // to our generic code anyhow
3234 {
3235 struct tm tm;
3236
3237 const wxChar *result = CallStrptime(input, "%x", &tm);
3238 if ( result )
3239 {
3240 input = result;
3241
3242 haveDay = haveMon = haveYear = true;
3243
3244 year = 1900 + tm.tm_year;
3245 mon = (Month)tm.tm_mon;
3246 mday = tm.tm_mday;
3247
3248 break;
3249 }
3250 }
3251 #endif // HAVE_STRPTIME
3252
3253 {
3254 wxDateTime dt;
3255
3256 wxString fmtDate, fmtDateAlt;
3257
3258 if ( IsWestEuropeanCountry(GetCountry()) ||
3259 GetCountry() == Russia )
3260 {
3261 fmtDate = _T("%d/%m/%y");
3262 fmtDateAlt = _T("%m/%d/%y");
3263 }
3264 else // assume USA
3265 {
3266 fmtDate = _T("%m/%d/%y");
3267 fmtDateAlt = _T("%d/%m/%y");
3268 }
3269
3270 #ifdef __WINDOWS__
3271 // The above doesn't work for all locales, try to query
3272 // Windows for the right way of formatting the date:
3273 GetLocaleDateFormat(&fmtDate);
3274 #endif
3275
3276 const wxChar *result = dt.ParseFormat(input, fmtDate);
3277
3278 if ( !result )
3279 {
3280 // ok, be nice and try another one
3281 result = dt.ParseFormat(input, fmtDateAlt);
3282 }
3283
3284 if ( !result )
3285 {
3286 // bad luck
3287 return (wxChar *)NULL;
3288 }
3289
3290 Tm tm = dt.GetTm();
3291
3292 haveDay = haveMon = haveYear = true;
3293
3294 year = tm.year;
3295 mon = tm.mon;
3296 mday = tm.mday;
3297
3298 input = result;
3299 }
3300
3301 break;
3302
3303 case _T('X'): // locale default time representation
3304 #ifdef HAVE_STRPTIME
3305 {
3306 // use strptime() to do it for us (FIXME !Unicode friendly)
3307 struct tm tm;
3308 input = CallStrptime(input, "%X", &tm);
3309 if ( !input )
3310 {
3311 return (wxChar *)NULL;
3312 }
3313
3314 haveHour = haveMin = haveSec = true;
3315
3316 hour = tm.tm_hour;
3317 min = tm.tm_min;
3318 sec = tm.tm_sec;
3319 }
3320 #else // !HAVE_STRPTIME
3321 // TODO under Win32 we can query the LOCALE_ITIME system
3322 // setting which says whether the default time format is
3323 // 24 or 12 hour
3324 {
3325 // try to parse what follows as "%H:%M:%S" and, if this
3326 // fails, as "%I:%M:%S %p" - this should catch the most
3327 // common cases
3328 wxDateTime dt;
3329
3330 const wxChar *result = dt.ParseFormat(input, _T("%T"));
3331 if ( !result )
3332 {
3333 result = dt.ParseFormat(input, _T("%r"));
3334 }
3335
3336 if ( !result )
3337 {
3338 // no match
3339 return (wxChar *)NULL;
3340 }
3341
3342 haveHour = haveMin = haveSec = true;
3343
3344 Tm tm = dt.GetTm();
3345 hour = tm.hour;
3346 min = tm.min;
3347 sec = tm.sec;
3348
3349 input = result;
3350 }
3351 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
3352 break;
3353
3354 case _T('y'): // year without century (00-99)
3355 if ( !GetNumericToken(width, input, &num) || (num > 99) )
3356 {
3357 // no match
3358 return (wxChar *)NULL;
3359 }
3360
3361 haveYear = true;
3362
3363 // TODO should have an option for roll over date instead of
3364 // hard coding it here
3365 year = (num > 30 ? 1900 : 2000) + (wxDateTime_t)num;
3366 break;
3367
3368 case _T('Y'): // year with century
3369 if ( !GetNumericToken(width, input, &num) )
3370 {
3371 // no match
3372 return (wxChar *)NULL;
3373 }
3374
3375 haveYear = true;
3376 year = (wxDateTime_t)num;
3377 break;
3378
3379 case _T('Z'): // timezone name
3380 wxFAIL_MSG(_T("TODO"));
3381 break;
3382
3383 case _T('%'): // a percent sign
3384 if ( *input++ != _T('%') )
3385 {
3386 // no match
3387 return (wxChar *)NULL;
3388 }
3389 break;
3390
3391 case 0: // the end of string
3392 wxFAIL_MSG(_T("unexpected format end"));
3393
3394 // fall through
3395
3396 default: // not a known format spec
3397 return (wxChar *)NULL;
3398 }
3399 }
3400
3401 // format matched, try to construct a date from what we have now
3402 Tm tmDef;
3403 if ( dateDef.IsValid() )
3404 {
3405 // take this date as default
3406 tmDef = dateDef.GetTm();
3407 }
3408 else if ( IsValid() )
3409 {
3410 // if this date is valid, don't change it
3411 tmDef = GetTm();
3412 }
3413 else
3414 {
3415 // no default and this date is invalid - fall back to Today()
3416 tmDef = Today().GetTm();
3417 }
3418
3419 Tm tm = tmDef;
3420
3421 // set the date
3422 if ( haveYear )
3423 {
3424 tm.year = year;
3425 }
3426
3427 // TODO we don't check here that the values are consistent, if both year
3428 // day and month/day were found, we just ignore the year day and we
3429 // also always ignore the week day
3430 if ( haveMon && haveDay )
3431 {
3432 if ( mday > GetNumOfDaysInMonth(tm.year, mon) )
3433 {
3434 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
3435
3436 return (wxChar *)NULL;
3437 }
3438
3439 tm.mon = mon;
3440 tm.mday = mday;
3441 }
3442 else if ( haveYDay )
3443 {
3444 if ( yday > GetNumberOfDays(tm.year) )
3445 {
3446 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
3447
3448 return (wxChar *)NULL;
3449 }
3450
3451 Tm tm2 = wxDateTime(1, Jan, tm.year).SetToYearDay(yday).GetTm();
3452
3453 tm.mon = tm2.mon;
3454 tm.mday = tm2.mday;
3455 }
3456
3457 // deal with AM/PM
3458 if ( haveHour && hourIsIn12hFormat && isPM )
3459 {
3460 // translate to 24hour format
3461 hour += 12;
3462 }
3463 //else: either already in 24h format or no translation needed
3464
3465 // set the time
3466 if ( haveHour )
3467 {
3468 tm.hour = hour;
3469 }
3470
3471 if ( haveMin )
3472 {
3473 tm.min = min;
3474 }
3475
3476 if ( haveSec )
3477 {
3478 tm.sec = sec;
3479 }
3480
3481 Set(tm);
3482
3483 // finally check that the week day is consistent -- if we had it
3484 if ( haveWDay && GetWeekDay() != wday )
3485 {
3486 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
3487
3488 return NULL;
3489 }
3490
3491 return input;
3492 }
3493
3494 const wxChar *wxDateTime::ParseDateTime(const wxChar *date)
3495 {
3496 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
3497
3498 // Set to current day and hour, so strings like '14:00' becomes today at
3499 // 14, not some other random date
3500 wxDateTime dtDate = wxDateTime::Today();
3501 wxDateTime dtTime = wxDateTime::Today();
3502
3503 const wxChar* pchTime;
3504
3505 // Try to parse the beginning of the string as a date
3506 const wxChar* pchDate = dtDate.ParseDate(date);
3507
3508 // We got a date in the beginning, see if there is a time specified after the date
3509 if ( pchDate )
3510 {
3511 // Skip spaces, as the ParseTime() function fails on spaces
3512 while ( wxIsspace(*pchDate) )
3513 pchDate++;
3514
3515 pchTime = dtTime.ParseTime(pchDate);
3516 }
3517 else // no date in the beginning
3518 {
3519 // check and see if we have a time followed by a date
3520 pchTime = dtTime.ParseTime(date);
3521 if ( pchTime )
3522 {
3523 while ( wxIsspace(*pchTime) )
3524 pchTime++;
3525
3526 pchDate = dtDate.ParseDate(pchTime);
3527 }
3528 }
3529
3530 // If we have a date specified, set our own data to the same date
3531 if ( !pchDate || !pchTime )
3532 return NULL;
3533
3534 Set(dtDate.GetDay(), dtDate.GetMonth(), dtDate.GetYear(),
3535 dtTime.GetHour(), dtTime.GetMinute(), dtTime.GetSecond(),
3536 dtTime.GetMillisecond());
3537
3538 // Return endpoint of scan
3539 return pchDate > pchTime ? pchDate : pchTime;
3540 }
3541
3542 const wxChar *wxDateTime::ParseDate(const wxChar *date)
3543 {
3544 // this is a simplified version of ParseDateTime() which understands only
3545 // "today" (for wxDate compatibility) and digits only otherwise (and not
3546 // all esoteric constructions ParseDateTime() knows about)
3547
3548 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
3549
3550 const wxChar *p = date;
3551 while ( wxIsspace(*p) )
3552 p++;
3553
3554 // some special cases
3555 static struct
3556 {
3557 const wxChar *str;
3558 int dayDiffFromToday;
3559 } literalDates[] =
3560 {
3561 { wxTRANSLATE("today"), 0 },
3562 { wxTRANSLATE("yesterday"), -1 },
3563 { wxTRANSLATE("tomorrow"), 1 },
3564 };
3565
3566 for ( size_t n = 0; n < WXSIZEOF(literalDates); n++ )
3567 {
3568 wxString date = wxGetTranslation(literalDates[n].str);
3569 size_t len = date.length();
3570 if ( wxStrlen(p) >= len )
3571 {
3572 wxString str(p, len);
3573 if ( str.CmpNoCase(date) == 0 )
3574 {
3575 // nothing can follow this, so stop here
3576 p += len;
3577
3578 int dayDiffFromToday = literalDates[n].dayDiffFromToday;
3579 *this = Today();
3580 if ( dayDiffFromToday )
3581 {
3582 *this += wxDateSpan::Days(dayDiffFromToday);
3583 }
3584
3585 return p;
3586 }
3587 }
3588 }
3589
3590 // We try to guess what we have here: for each new (numeric) token, we
3591 // determine if it can be a month, day or a year. Of course, there is an
3592 // ambiguity as some numbers may be days as well as months, so we also
3593 // have the ability to back track.
3594
3595 // what do we have?
3596 bool haveDay = false, // the months day?
3597 haveWDay = false, // the day of week?
3598 haveMon = false, // the month?
3599 haveYear = false; // the year?
3600
3601 // and the value of the items we have (init them to get rid of warnings)
3602 WeekDay wday = Inv_WeekDay;
3603 wxDateTime_t day = 0;
3604 wxDateTime::Month mon = Inv_Month;
3605 int year = 0;
3606
3607 // tokenize the string
3608 size_t nPosCur = 0;
3609 static const wxChar *dateDelimiters = _T(".,/-\t\r\n ");
3610 wxStringTokenizer tok(p, dateDelimiters);
3611 while ( tok.HasMoreTokens() )
3612 {
3613 wxString token = tok.GetNextToken();
3614 if ( !token )
3615 continue;
3616
3617 // is it a number?
3618 unsigned long val;
3619 if ( token.ToULong(&val) )
3620 {
3621 // guess what this number is
3622
3623 bool isDay = false,
3624 isMonth = false,
3625 isYear = false;
3626
3627 if ( !haveMon && val > 0 && val <= 12 )
3628 {
3629 // assume it is month
3630 isMonth = true;
3631 }
3632 else // not the month
3633 {
3634 if ( haveDay )
3635 {
3636 // this can only be the year
3637 isYear = true;
3638 }
3639 else // may be either day or year
3640 {
3641 wxDateTime_t max_days = (wxDateTime_t)(
3642 haveMon
3643 ? GetNumOfDaysInMonth(haveYear ? year : Inv_Year, mon)
3644 : 31
3645 );
3646
3647 // can it be day?
3648 if ( (val == 0) || (val > (unsigned long)max_days) )
3649 {
3650 // no
3651 isYear = true;
3652 }
3653 else // yes, suppose it's the day
3654 {
3655 isDay = true;
3656 }
3657 }
3658 }
3659
3660 if ( isYear )
3661 {
3662 if ( haveYear )
3663 break;
3664
3665 haveYear = true;
3666
3667 year = (wxDateTime_t)val;
3668 }
3669 else if ( isDay )
3670 {
3671 if ( haveDay )
3672 break;
3673
3674 haveDay = true;
3675
3676 day = (wxDateTime_t)val;
3677 }
3678 else if ( isMonth )
3679 {
3680 haveMon = true;
3681
3682 mon = (Month)(val - 1);
3683 }
3684 }
3685 else // not a number
3686 {
3687 // be careful not to overwrite the current mon value
3688 Month mon2 = GetMonthFromName(token, Name_Full | Name_Abbr);
3689 if ( mon2 != Inv_Month )
3690 {
3691 // it's a month
3692 if ( haveMon )
3693 {
3694 // but we already have a month - maybe we guessed wrong?
3695 if ( !haveDay )
3696 {
3697 // no need to check in month range as always < 12, but
3698 // the days are counted from 1 unlike the months
3699 day = (wxDateTime_t)(mon + 1);
3700 haveDay = true;
3701 }
3702 else
3703 {
3704 // could possible be the year (doesn't the year come
3705 // before the month in the japanese format?) (FIXME)
3706 break;
3707 }
3708 }
3709
3710 mon = mon2;
3711
3712 haveMon = true;
3713 }
3714 else // not a valid month name
3715 {
3716 wday = GetWeekDayFromName(token, Name_Full | Name_Abbr);
3717 if ( wday != Inv_WeekDay )
3718 {
3719 // a week day
3720 if ( haveWDay )
3721 {
3722 break;
3723 }
3724
3725 haveWDay = true;
3726 }
3727 else // not a valid weekday name
3728 {
3729 // try the ordinals
3730 static const wxChar *ordinals[] =
3731 {
3732 wxTRANSLATE("first"),
3733 wxTRANSLATE("second"),
3734 wxTRANSLATE("third"),
3735 wxTRANSLATE("fourth"),
3736 wxTRANSLATE("fifth"),
3737 wxTRANSLATE("sixth"),
3738 wxTRANSLATE("seventh"),
3739 wxTRANSLATE("eighth"),
3740 wxTRANSLATE("ninth"),
3741 wxTRANSLATE("tenth"),
3742 wxTRANSLATE("eleventh"),
3743 wxTRANSLATE("twelfth"),
3744 wxTRANSLATE("thirteenth"),
3745 wxTRANSLATE("fourteenth"),
3746 wxTRANSLATE("fifteenth"),
3747 wxTRANSLATE("sixteenth"),
3748 wxTRANSLATE("seventeenth"),
3749 wxTRANSLATE("eighteenth"),
3750 wxTRANSLATE("nineteenth"),
3751 wxTRANSLATE("twentieth"),
3752 // that's enough - otherwise we'd have problems with
3753 // composite (or not) ordinals
3754 };
3755
3756 size_t n;
3757 for ( n = 0; n < WXSIZEOF(ordinals); n++ )
3758 {
3759 if ( token.CmpNoCase(ordinals[n]) == 0 )
3760 {
3761 break;
3762 }
3763 }
3764
3765 if ( n == WXSIZEOF(ordinals) )
3766 {
3767 // stop here - something unknown
3768 break;
3769 }
3770
3771 // it's a day
3772 if ( haveDay )
3773 {
3774 // don't try anything here (as in case of numeric day
3775 // above) - the symbolic day spec should always
3776 // precede the month/year
3777 break;
3778 }
3779
3780 haveDay = true;
3781
3782 day = (wxDateTime_t)(n + 1);
3783 }
3784 }
3785 }
3786
3787 nPosCur = tok.GetPosition();
3788 }
3789
3790 // either no more tokens or the scan was stopped by something we couldn't
3791 // parse - in any case, see if we can construct a date from what we have
3792 if ( !haveDay && !haveWDay )
3793 {
3794 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
3795
3796 return (wxChar *)NULL;
3797 }
3798
3799 if ( haveWDay && (haveMon || haveYear || haveDay) &&
3800 !(haveDay && haveMon && haveYear) )
3801 {
3802 // without adjectives (which we don't support here) the week day only
3803 // makes sense completely separately or with the full date
3804 // specification (what would "Wed 1999" mean?)
3805 return (wxChar *)NULL;
3806 }
3807
3808 if ( !haveWDay && haveYear && !(haveDay && haveMon) )
3809 {
3810 // may be we have month and day instead of day and year?
3811 if ( haveDay && !haveMon )
3812 {
3813 if ( day <= 12 )
3814 {
3815 // exchange day and month
3816 mon = (wxDateTime::Month)(day - 1);
3817
3818 // we're in the current year then
3819 if ( (year > 0) && (year <= (int)GetNumOfDaysInMonth(Inv_Year, mon)) )
3820 {
3821 day = (wxDateTime_t)year;
3822
3823 haveMon = true;
3824 haveYear = false;
3825 }
3826 //else: no, can't exchange, leave haveMon == false
3827 }
3828 }
3829
3830 if ( !haveMon )
3831 {
3832 // if we give the year, month and day must be given too
3833 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
3834
3835 return (wxChar *)NULL;
3836 }
3837 }
3838
3839 if ( !haveMon )
3840 {
3841 mon = GetCurrentMonth();
3842 }
3843
3844 if ( !haveYear )
3845 {
3846 year = GetCurrentYear();
3847 }
3848
3849 if ( haveDay )
3850 {
3851 Set(day, mon, year);
3852
3853 if ( haveWDay )
3854 {
3855 // check that it is really the same
3856 if ( GetWeekDay() != wday )
3857 {
3858 // inconsistency detected
3859 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
3860
3861 return (wxChar *)NULL;
3862 }
3863 }
3864 }
3865 else // haveWDay
3866 {
3867 *this = Today();
3868
3869 SetToWeekDayInSameWeek(wday);
3870 }
3871
3872 // return the pointer to the first unparsed char
3873 p += nPosCur;
3874 if ( nPosCur && wxStrchr(dateDelimiters, *(p - 1)) )
3875 {
3876 // if we couldn't parse the token after the delimiter, put back the
3877 // delimiter as well
3878 p--;
3879 }
3880
3881 return p;
3882 }
3883
3884 const wxChar *wxDateTime::ParseTime(const wxChar *time)
3885 {
3886 wxCHECK_MSG( time, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
3887
3888 // first try some extra things
3889 static const struct
3890 {
3891 const wxChar *name;
3892 wxDateTime_t hour;
3893 } stdTimes[] =
3894 {
3895 { wxTRANSLATE("noon"), 12 },
3896 { wxTRANSLATE("midnight"), 00 },
3897 // anything else?
3898 };
3899
3900 for ( size_t n = 0; n < WXSIZEOF(stdTimes); n++ )
3901 {
3902 wxString timeString = wxGetTranslation(stdTimes[n].name);
3903 size_t len = timeString.length();
3904 if ( timeString.CmpNoCase(wxString(time, len)) == 0 )
3905 {
3906 // casts required by DigitalMars
3907 Set(stdTimes[n].hour, wxDateTime_t(0), wxDateTime_t(0));
3908
3909 return time + len;
3910 }
3911 }
3912
3913 // try all time formats we may think about in the order from longest to
3914 // shortest
3915
3916 // 12hour with AM/PM?
3917 const wxChar *result = ParseFormat(time, _T("%I:%M:%S %p"));
3918
3919 if ( !result )
3920 {
3921 // normally, it's the same, but why not try it?
3922 result = ParseFormat(time, _T("%H:%M:%S"));
3923 }
3924
3925 if ( !result )
3926 {
3927 // 12hour with AM/PM but without seconds?
3928 result = ParseFormat(time, _T("%I:%M %p"));
3929 }
3930
3931 if ( !result )
3932 {
3933 // without seconds?
3934 result = ParseFormat(time, _T("%H:%M"));
3935 }
3936
3937 if ( !result )
3938 {
3939 // just the hour and AM/PM?
3940 result = ParseFormat(time, _T("%I %p"));
3941 }
3942
3943 if ( !result )
3944 {
3945 // just the hour?
3946 result = ParseFormat(time, _T("%H"));
3947 }
3948
3949 if ( !result )
3950 {
3951 // parse the standard format: normally it is one of the formats above
3952 // but it may be set to something completely different by the user
3953 result = ParseFormat(time, _T("%X"));
3954 }
3955
3956 // TODO: parse timezones
3957
3958 return result;
3959 }
3960
3961 // ----------------------------------------------------------------------------
3962 // Workdays and holidays support
3963 // ----------------------------------------------------------------------------
3964
3965 bool wxDateTime::IsWorkDay(Country WXUNUSED(country)) const
3966 {
3967 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
3968 }
3969
3970 // ============================================================================
3971 // wxDateSpan
3972 // ============================================================================
3973
3974 wxDateSpan WXDLLIMPEXP_BASE operator*(int n, const wxDateSpan& ds)
3975 {
3976 wxDateSpan ds1(ds);
3977 return ds1.Multiply(n);
3978 }
3979
3980 // ============================================================================
3981 // wxTimeSpan
3982 // ============================================================================
3983
3984 wxTimeSpan WXDLLIMPEXP_BASE operator*(int n, const wxTimeSpan& ts)
3985 {
3986 return wxTimeSpan(ts).Multiply(n);
3987 }
3988
3989 // this enum is only used in wxTimeSpan::Format() below but we can't declare
3990 // it locally to the method as it provokes an internal compiler error in egcs
3991 // 2.91.60 when building with -O2
3992 enum TimeSpanPart
3993 {
3994 Part_Week,
3995 Part_Day,
3996 Part_Hour,
3997 Part_Min,
3998 Part_Sec,
3999 Part_MSec
4000 };
4001
4002 // not all strftime(3) format specifiers make sense here because, for example,
4003 // a time span doesn't have a year nor a timezone
4004 //
4005 // Here are the ones which are supported (all of them are supported by strftime
4006 // as well):
4007 // %H hour in 24 hour format
4008 // %M minute (00 - 59)
4009 // %S second (00 - 59)
4010 // %% percent sign
4011 //
4012 // Also, for MFC CTimeSpan compatibility, we support
4013 // %D number of days
4014 //
4015 // And, to be better than MFC :-), we also have
4016 // %E number of wEeks
4017 // %l milliseconds (000 - 999)
4018 wxString wxTimeSpan::Format(const wxChar *format) const
4019 {
4020 wxCHECK_MSG( format, wxEmptyString, _T("NULL format in wxTimeSpan::Format") );
4021
4022 wxString str;
4023 str.Alloc(wxStrlen(format));
4024
4025 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
4026 //
4027 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
4028 // question is what should ts.Format("%S") do? The code here returns "3273"
4029 // in this case (i.e. the total number of seconds, not just seconds % 60)
4030 // because, for me, this call means "give me entire time interval in
4031 // seconds" and not "give me the seconds part of the time interval"
4032 //
4033 // If we agree that it should behave like this, it is clear that the
4034 // interpretation of each format specifier depends on the presence of the
4035 // other format specs in the string: if there was "%H" before "%M", we
4036 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
4037
4038 // we remember the most important unit found so far
4039 TimeSpanPart partBiggest = Part_MSec;
4040
4041 for ( const wxChar *pch = format; *pch; pch++ )
4042 {
4043 wxChar ch = *pch;
4044
4045 if ( ch == _T('%') )
4046 {
4047 // the start of the format specification of the printf() below
4048 wxString fmtPrefix = _T('%');
4049
4050 // the number
4051 long n;
4052
4053 ch = *++pch; // get the format spec char
4054 switch ( ch )
4055 {
4056 default:
4057 wxFAIL_MSG( _T("invalid format character") );
4058 // fall through
4059
4060 case _T('%'):
4061 str += ch;
4062
4063 // skip the part below switch
4064 continue;
4065
4066 case _T('D'):
4067 n = GetDays();
4068 if ( partBiggest < Part_Day )
4069 {
4070 n %= DAYS_PER_WEEK;
4071 }
4072 else
4073 {
4074 partBiggest = Part_Day;
4075 }
4076 break;
4077
4078 case _T('E'):
4079 partBiggest = Part_Week;
4080 n = GetWeeks();
4081 break;
4082
4083 case _T('H'):
4084 n = GetHours();
4085 if ( partBiggest < Part_Hour )
4086 {
4087 n %= HOURS_PER_DAY;
4088 }
4089 else
4090 {
4091 partBiggest = Part_Hour;
4092 }
4093
4094 fmtPrefix += _T("02");
4095 break;
4096
4097 case _T('l'):
4098 n = GetMilliseconds().ToLong();
4099 if ( partBiggest < Part_MSec )
4100 {
4101 n %= 1000;
4102 }
4103 //else: no need to reset partBiggest to Part_MSec, it is
4104 // the least significant one anyhow
4105
4106 fmtPrefix += _T("03");
4107 break;
4108
4109 case _T('M'):
4110 n = GetMinutes();
4111 if ( partBiggest < Part_Min )
4112 {
4113 n %= MIN_PER_HOUR;
4114 }
4115 else
4116 {
4117 partBiggest = Part_Min;
4118 }
4119
4120 fmtPrefix += _T("02");
4121 break;
4122
4123 case _T('S'):
4124 n = GetSeconds().ToLong();
4125 if ( partBiggest < Part_Sec )
4126 {
4127 n %= SEC_PER_MIN;
4128 }
4129 else
4130 {
4131 partBiggest = Part_Sec;
4132 }
4133
4134 fmtPrefix += _T("02");
4135 break;
4136 }
4137
4138 str += wxString::Format(fmtPrefix + _T("ld"), n);
4139 }
4140 else
4141 {
4142 // normal character, just copy
4143 str += ch;
4144 }
4145 }
4146
4147 return str;
4148 }
4149
4150 // ============================================================================
4151 // wxDateTimeHolidayAuthority and related classes
4152 // ============================================================================
4153
4154 #include "wx/arrimpl.cpp"
4155
4156 WX_DEFINE_OBJARRAY(wxDateTimeArray);
4157
4158 static int wxCMPFUNC_CONV
4159 wxDateTimeCompareFunc(wxDateTime **first, wxDateTime **second)
4160 {
4161 wxDateTime dt1 = **first,
4162 dt2 = **second;
4163
4164 return dt1 == dt2 ? 0 : dt1 < dt2 ? -1 : +1;
4165 }
4166
4167 // ----------------------------------------------------------------------------
4168 // wxDateTimeHolidayAuthority
4169 // ----------------------------------------------------------------------------
4170
4171 wxHolidayAuthoritiesArray wxDateTimeHolidayAuthority::ms_authorities;
4172
4173 /* static */
4174 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime& dt)
4175 {
4176 size_t count = ms_authorities.size();
4177 for ( size_t n = 0; n < count; n++ )
4178 {
4179 if ( ms_authorities[n]->DoIsHoliday(dt) )
4180 {
4181 return true;
4182 }
4183 }
4184
4185 return false;
4186 }
4187
4188 /* static */
4189 size_t
4190 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime& dtStart,
4191 const wxDateTime& dtEnd,
4192 wxDateTimeArray& holidays)
4193 {
4194 wxDateTimeArray hol;
4195
4196 holidays.Clear();
4197
4198 size_t count = ms_authorities.size();
4199 for ( size_t nAuth = 0; nAuth < count; nAuth++ )
4200 {
4201 ms_authorities[nAuth]->DoGetHolidaysInRange(dtStart, dtEnd, hol);
4202
4203 WX_APPEND_ARRAY(holidays, hol);
4204 }
4205
4206 holidays.Sort(wxDateTimeCompareFunc);
4207
4208 return holidays.size();
4209 }
4210
4211 /* static */
4212 void wxDateTimeHolidayAuthority::ClearAllAuthorities()
4213 {
4214 WX_CLEAR_ARRAY(ms_authorities);
4215 }
4216
4217 /* static */
4218 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority *auth)
4219 {
4220 ms_authorities.push_back(auth);
4221 }
4222
4223 wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
4224 {
4225 // required here for Darwin
4226 }
4227
4228 // ----------------------------------------------------------------------------
4229 // wxDateTimeWorkDays
4230 // ----------------------------------------------------------------------------
4231
4232 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime& dt) const
4233 {
4234 wxDateTime::WeekDay wd = dt.GetWeekDay();
4235
4236 return (wd == wxDateTime::Sun) || (wd == wxDateTime::Sat);
4237 }
4238
4239 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime& dtStart,
4240 const wxDateTime& dtEnd,
4241 wxDateTimeArray& holidays) const
4242 {
4243 if ( dtStart > dtEnd )
4244 {
4245 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
4246
4247 return 0u;
4248 }
4249
4250 holidays.Empty();
4251
4252 // instead of checking all days, start with the first Sat after dtStart and
4253 // end with the last Sun before dtEnd
4254 wxDateTime dtSatFirst = dtStart.GetNextWeekDay(wxDateTime::Sat),
4255 dtSatLast = dtEnd.GetPrevWeekDay(wxDateTime::Sat),
4256 dtSunFirst = dtStart.GetNextWeekDay(wxDateTime::Sun),
4257 dtSunLast = dtEnd.GetPrevWeekDay(wxDateTime::Sun),
4258 dt;
4259
4260 for ( dt = dtSatFirst; dt <= dtSatLast; dt += wxDateSpan::Week() )
4261 {
4262 holidays.Add(dt);
4263 }
4264
4265 for ( dt = dtSunFirst; dt <= dtSunLast; dt += wxDateSpan::Week() )
4266 {
4267 holidays.Add(dt);
4268 }
4269
4270 return holidays.GetCount();
4271 }
4272
4273 // ============================================================================
4274 // other helper functions
4275 // ============================================================================
4276
4277 // ----------------------------------------------------------------------------
4278 // iteration helpers: can be used to write a for loop over enum variable like
4279 // this:
4280 // for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
4281 // ----------------------------------------------------------------------------
4282
4283 WXDLLIMPEXP_BASE void wxNextMonth(wxDateTime::Month& m)
4284 {
4285 wxASSERT_MSG( m < wxDateTime::Inv_Month, _T("invalid month") );
4286
4287 // no wrapping or the for loop above would never end!
4288 m = (wxDateTime::Month)(m + 1);
4289 }
4290
4291 WXDLLIMPEXP_BASE void wxPrevMonth(wxDateTime::Month& m)
4292 {
4293 wxASSERT_MSG( m < wxDateTime::Inv_Month, _T("invalid month") );
4294
4295 m = m == wxDateTime::Jan ? wxDateTime::Inv_Month
4296 : (wxDateTime::Month)(m - 1);
4297 }
4298
4299 WXDLLIMPEXP_BASE void wxNextWDay(wxDateTime::WeekDay& wd)
4300 {
4301 wxASSERT_MSG( wd < wxDateTime::Inv_WeekDay, _T("invalid week day") );
4302
4303 // no wrapping or the for loop above would never end!
4304 wd = (wxDateTime::WeekDay)(wd + 1);
4305 }
4306
4307 WXDLLIMPEXP_BASE void wxPrevWDay(wxDateTime::WeekDay& wd)
4308 {
4309 wxASSERT_MSG( wd < wxDateTime::Inv_WeekDay, _T("invalid week day") );
4310
4311 wd = wd == wxDateTime::Sun ? wxDateTime::Inv_WeekDay
4312 : (wxDateTime::WeekDay)(wd - 1);
4313 }
4314
4315 #endif // wxUSE_DATETIME