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