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