/[pcsx2_0.9.7]/trunk/3rdparty/wxWidgets/src/common/datetime.cpp
ViewVC logotype

Contents of /trunk/3rdparty/wxWidgets/src/common/datetime.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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