move parsing helpers, too (part of r59656)
[wxWidgets.git] / src / common / datetime.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/datetime.cpp
3 // Purpose: implementation of time/date related classes
4 // (for formatting&parsing see datetimefmt.cpp)
5 // Author: Vadim Zeitlin
6 // Modified by:
7 // Created: 11.05.99
8 // RCS-ID: $Id$
9 // Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
10 // parts of code taken from sndcal library by Scott E. Lee:
11 //
12 // Copyright 1993-1995, Scott E. Lee, all rights reserved.
13 // Permission granted to use, copy, modify, distribute and sell
14 // so long as the above copyright and this permission statement
15 // are retained in all copies.
16 //
17 // Licence: wxWindows licence
18 ///////////////////////////////////////////////////////////////////////////////
19
20 /*
21 * Implementation notes:
22 *
23 * 1. the time is stored as a 64bit integer containing the signed number of
24 * milliseconds since Jan 1. 1970 (the Unix Epoch) - so it is always
25 * expressed in GMT.
26 *
27 * 2. the range is thus something about 580 million years, but due to current
28 * algorithms limitations, only dates from Nov 24, 4714BC are handled
29 *
30 * 3. standard ANSI C functions are used to do time calculations whenever
31 * possible, i.e. when the date is in the range Jan 1, 1970 to 2038
32 *
33 * 4. otherwise, the calculations are done by converting the date to/from JDN
34 * first (the range limitation mentioned above comes from here: the
35 * algorithm used by Scott E. Lee's code only works for positive JDNs, more
36 * or less)
37 *
38 * 5. the object constructed for the given DD-MM-YYYY HH:MM:SS corresponds to
39 * this moment in local time and may be converted to the object
40 * corresponding to the same date/time in another time zone by using
41 * ToTimezone()
42 *
43 * 6. the conversions to the current (or any other) timezone are done when the
44 * internal time representation is converted to the broken-down one in
45 * wxDateTime::Tm.
46 */
47
48 // ============================================================================
49 // declarations
50 // ============================================================================
51
52 // ----------------------------------------------------------------------------
53 // headers
54 // ----------------------------------------------------------------------------
55
56 // For compilers that support precompilation, includes "wx.h".
57 #include "wx/wxprec.h"
58
59 #ifdef __BORLANDC__
60 #pragma hdrstop
61 #endif
62
63 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
64
65 #ifndef WX_PRECOMP
66 #ifdef __WXMSW__
67 #include "wx/msw/wrapwin.h"
68 #endif
69 #include "wx/string.h"
70 #include "wx/log.h"
71 #include "wx/intl.h"
72 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
73 #include "wx/module.h"
74 #include "wx/crt.h"
75 #endif // WX_PRECOMP
76
77 #include "wx/thread.h"
78 #include "wx/tokenzr.h"
79
80 #include <ctype.h>
81
82 #ifdef __WINDOWS__
83 #include <winnls.h>
84 #ifndef __WXWINCE__
85 #include <locale.h>
86 #endif
87 #endif
88
89 #include "wx/datetime.h"
90
91 const long wxDateTime::TIME_T_FACTOR = 1000l;
92
93 #if wxUSE_EXTENDED_RTTI
94
95 template<> void wxStringReadValue(const wxString &s , wxDateTime &data )
96 {
97 data.ParseFormat(s,"%Y-%m-%d %H:%M:%S", NULL);
98 }
99
100 template<> void wxStringWriteValue(wxString &s , const wxDateTime &data )
101 {
102 s = data.Format("%Y-%m-%d %H:%M:%S");
103 }
104
105 wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter<wxDateTime> , wxFromStringConverter<wxDateTime>)
106
107 #endif // wxUSE_EXTENDED_RTTI
108
109 //
110 // ----------------------------------------------------------------------------
111 // conditional compilation
112 // ----------------------------------------------------------------------------
113
114 #if defined(HAVE_STRPTIME) && defined(__GLIBC__) && \
115 ((__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0))
116 // glibc 2.0.7 strptime() is broken - the following snippet causes it to
117 // crash (instead of just failing):
118 //
119 // strncpy(buf, "Tue Dec 21 20:25:40 1999", 128);
120 // strptime(buf, "%x", &tm);
121 //
122 // so don't use it
123 #undef HAVE_STRPTIME
124 #endif // broken strptime()
125
126 #if defined(HAVE_STRPTIME) && defined(__DARWIN__) && defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
127 // configure detects strptime as linkable because it's in the OS X
128 // System library but MSL headers don't declare it.
129
130 // char *strptime(const char *, const char *, struct tm *);
131 // However, we DON'T want to just provide it here because we would
132 // crash and/or overwrite data when strptime from OS X tries
133 // to fill in MW's struct tm which is two fields shorter (no TZ stuff)
134 // So for now let's just say we don't have strptime
135 #undef HAVE_STRPTIME
136 #endif
137
138 #if defined(__MWERKS__) && wxUSE_UNICODE
139 #include <wtime.h>
140 #endif
141
142 #if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM)
143 #if defined(__WXPALMOS__)
144 #define WX_GMTOFF_IN_TM
145 #elif defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
146 #define WX_TIMEZONE _timezone
147 #elif defined(__MWERKS__)
148 long wxmw_timezone = 28800;
149 #define WX_TIMEZONE wxmw_timezone
150 #elif defined(__DJGPP__) || defined(__WINE__)
151 #include <sys/timeb.h>
152 #include <values.h>
153 static long wxGetTimeZone()
154 {
155 static long timezone = MAXLONG; // invalid timezone
156 if (timezone == MAXLONG)
157 {
158 struct timeb tb;
159 ftime(&tb);
160 timezone = tb.timezone;
161 }
162 return timezone;
163 }
164 #define WX_TIMEZONE wxGetTimeZone()
165 #elif defined(__DARWIN__)
166 #define WX_GMTOFF_IN_TM
167 #elif defined(__WXWINCE__) && defined(__VISUALC8__)
168 // _timezone is not present in dynamic run-time library
169 #if 0
170 // Solution (1): use the function equivalent of _timezone
171 static long wxGetTimeZone()
172 {
173 static long s_Timezone = MAXLONG; // invalid timezone
174 if (s_Timezone == MAXLONG)
175 {
176 int t;
177 _get_timezone(& t);
178 s_Timezone = (long) t;
179 }
180 return s_Timezone;
181 }
182 #define WX_TIMEZONE wxGetTimeZone()
183 #elif 1
184 // Solution (2): using GetTimeZoneInformation
185 static long wxGetTimeZone()
186 {
187 static long timezone = MAXLONG; // invalid timezone
188 if (timezone == MAXLONG)
189 {
190 TIME_ZONE_INFORMATION tzi;
191 ::GetTimeZoneInformation(&tzi);
192 timezone = tzi.Bias;
193 }
194 return timezone;
195 }
196 #define WX_TIMEZONE wxGetTimeZone()
197 #else
198 // Old method using _timezone: this symbol doesn't exist in the dynamic run-time library (i.e. using /MD)
199 #define WX_TIMEZONE _timezone
200 #endif
201 #else // unknown platform - try timezone
202 #define WX_TIMEZONE timezone
203 #endif
204 #endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
205
206 // everyone has strftime except Win CE unless VC8 is used
207 #if !defined(__WXWINCE__) || defined(__VISUALC8__)
208 #define HAVE_STRFTIME
209 #endif
210
211 // NB: VC8 safe time functions could/should be used for wxMSW as well probably
212 #if defined(__WXWINCE__) && defined(__VISUALC8__)
213
214 struct tm *wxLocaltime_r(const time_t *t, struct tm* tm)
215 {
216 __time64_t t64 = *t;
217 return _localtime64_s(tm, &t64) == 0 ? tm : NULL;
218 }
219
220 struct tm *wxGmtime_r(const time_t* t, struct tm* tm)
221 {
222 __time64_t t64 = *t;
223 return _gmtime64_s(tm, &t64) == 0 ? tm : NULL;
224 }
225
226 #else // !wxWinCE with VC8
227
228 #if (!defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)) && wxUSE_THREADS && !defined(__WINDOWS__)
229 static wxMutex timeLock;
230 #endif
231
232 #ifndef HAVE_LOCALTIME_R
233 struct tm *wxLocaltime_r(const time_t* ticks, struct tm* temp)
234 {
235 #if wxUSE_THREADS && !defined(__WINDOWS__)
236 // No need to waste time with a mutex on windows since it's using
237 // thread local storage for localtime anyway.
238 wxMutexLocker locker(timeLock);
239 #endif
240
241 // Borland CRT crashes when passed 0 ticks for some reason, see SF bug 1704438
242 #ifdef __BORLANDC__
243 if ( !*ticks )
244 return NULL;
245 #endif
246
247 const tm * const t = localtime(ticks);
248 if ( !t )
249 return NULL;
250
251 memcpy(temp, t, sizeof(struct tm));
252 return temp;
253 }
254 #endif // !HAVE_LOCALTIME_R
255
256 #ifndef HAVE_GMTIME_R
257 struct tm *wxGmtime_r(const time_t* ticks, struct tm* temp)
258 {
259 #if wxUSE_THREADS && !defined(__WINDOWS__)
260 // No need to waste time with a mutex on windows since it's
261 // using thread local storage for gmtime anyway.
262 wxMutexLocker locker(timeLock);
263 #endif
264
265 #ifdef __BORLANDC__
266 if ( !*ticks )
267 return NULL;
268 #endif
269
270 const tm * const t = gmtime(ticks);
271 if ( !t )
272 return NULL;
273
274 memcpy(temp, gmtime(ticks), sizeof(struct tm));
275 return temp;
276 }
277 #endif // !HAVE_GMTIME_R
278
279 #endif // wxWinCE with VC8/other platforms
280
281 // ----------------------------------------------------------------------------
282 // macros
283 // ----------------------------------------------------------------------------
284
285 // debugging helper: just a convenient replacement of wxCHECK()
286 #define wxDATETIME_CHECK(expr, msg) \
287 wxCHECK2_MSG(expr, *this = wxInvalidDateTime; return *this, msg)
288
289 // ----------------------------------------------------------------------------
290 // private classes
291 // ----------------------------------------------------------------------------
292
293 class wxDateTimeHolidaysModule : public wxModule
294 {
295 public:
296 virtual bool OnInit()
297 {
298 wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays);
299
300 return true;
301 }
302
303 virtual void OnExit()
304 {
305 wxDateTimeHolidayAuthority::ClearAllAuthorities();
306 wxDateTimeHolidayAuthority::ms_authorities.clear();
307 }
308
309 private:
310 DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule)
311 };
312
313 IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule, wxModule)
314
315 // ----------------------------------------------------------------------------
316 // constants
317 // ----------------------------------------------------------------------------
318
319 // some trivial ones
320 static const int MONTHS_IN_YEAR = 12;
321
322 static const int SEC_PER_MIN = 60;
323
324 static const int MIN_PER_HOUR = 60;
325
326 static const int HOURS_PER_DAY = 24;
327
328 static const long SECONDS_PER_DAY = 86400l;
329
330 static const int DAYS_PER_WEEK = 7;
331
332 static const long MILLISECONDS_PER_DAY = 86400000l;
333
334 // this is the integral part of JDN of the midnight of Jan 1, 1970
335 // (i.e. JDN(Jan 1, 1970) = 2440587.5)
336 static const long EPOCH_JDN = 2440587l;
337
338 // used only in asserts
339 #ifdef __WXDEBUG__
340 // the date of JDN -0.5 (as we don't work with fractional parts, this is the
341 // reference date for us) is Nov 24, 4714BC
342 static const int JDN_0_YEAR = -4713;
343 static const int JDN_0_MONTH = wxDateTime::Nov;
344 static const int JDN_0_DAY = 24;
345 #endif // __WXDEBUG__
346
347 // the constants used for JDN calculations
348 static const long JDN_OFFSET = 32046l;
349 static const long DAYS_PER_5_MONTHS = 153l;
350 static const long DAYS_PER_4_YEARS = 1461l;
351 static const long DAYS_PER_400_YEARS = 146097l;
352
353 // this array contains the cumulated number of days in all previous months for
354 // normal and leap years
355 static const wxDateTime::wxDateTime_t gs_cumulatedDays[2][MONTHS_IN_YEAR] =
356 {
357 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
358 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
359 };
360
361 // ----------------------------------------------------------------------------
362 // global data
363 // ----------------------------------------------------------------------------
364
365 const char *wxDefaultDateTimeFormat = "%c";
366 const char *wxDefaultTimeSpanFormat = "%H:%M:%S";
367
368 // in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
369 // indicate an invalid wxDateTime object
370 const wxDateTime wxDefaultDateTime;
371
372 wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown;
373
374 // ----------------------------------------------------------------------------
375 // private functions
376 // ----------------------------------------------------------------------------
377
378 // debugger helper: shows what the date really is
379 #ifdef __WXDEBUG__
380 extern const char *wxDumpDate(const wxDateTime* dt)
381 {
382 static char buf[128];
383
384 wxString fmt(dt->Format("%Y-%m-%d (%a) %H:%M:%S"));
385 wxStrlcpy(buf,
386 (fmt + " (" + dt->GetValue().ToString() + " ticks)").ToAscii(),
387 WXSIZEOF(buf));
388
389 return buf;
390 }
391 #endif // Debug
392
393 // get the number of days in the given month of the given year
394 // NOTE: not static because required by datetimefmt.cpp, too
395 inline
396 wxDateTime::wxDateTime_t GetNumOfDaysInMonth(int year, wxDateTime::Month month)
397 {
398 // the number of days in month in Julian/Gregorian calendar: the first line
399 // is for normal years, the second one is for the leap ones
400 static wxDateTime::wxDateTime_t daysInMonth[2][MONTHS_IN_YEAR] =
401 {
402 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
403 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
404 };
405
406 return daysInMonth[wxDateTime::IsLeapYear(year)][month];
407 }
408
409 // returns the time zone in the C sense, i.e. the difference UTC - local
410 // (in seconds)
411 static int GetTimeZone()
412 {
413 // set to true when the timezone is set
414 static bool s_timezoneSet = false;
415 static long gmtoffset = LONG_MAX; // invalid timezone
416
417 // ensure that the timezone variable is set by calling wxLocaltime_r
418 if ( !s_timezoneSet )
419 {
420 // just call wxLocaltime_r() instead of figuring out whether this
421 // system supports tzset(), _tzset() or something else
422 time_t t = 0;
423 struct tm tm;
424
425 wxLocaltime_r(&t, &tm);
426 s_timezoneSet = true;
427
428 #ifdef WX_GMTOFF_IN_TM
429 // note that GMT offset is the opposite of time zone and so to return
430 // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM
431 // cases we have to negate it
432 gmtoffset = -tm.tm_gmtoff;
433 #else // !WX_GMTOFF_IN_TM
434 gmtoffset = WX_TIMEZONE;
435 #endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
436 }
437
438 return (int)gmtoffset;
439 }
440
441 // return the integral part of the JDN for the midnight of the given date (to
442 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
443 // noon of the previous day)
444 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day,
445 wxDateTime::Month mon,
446 int year)
447 {
448 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
449
450 // check the date validity
451 wxASSERT_MSG(
452 (year > JDN_0_YEAR) ||
453 ((year == JDN_0_YEAR) && (mon > JDN_0_MONTH)) ||
454 ((year == JDN_0_YEAR) && (mon == JDN_0_MONTH) && (day >= JDN_0_DAY)),
455 _T("date out of range - can't convert to JDN")
456 );
457
458 // make the year positive to avoid problems with negative numbers division
459 year += 4800;
460
461 // months are counted from March here
462 int month;
463 if ( mon >= wxDateTime::Mar )
464 {
465 month = mon - 2;
466 }
467 else
468 {
469 month = mon + 10;
470 year--;
471 }
472
473 // now we can simply add all the contributions together
474 return ((year / 100) * DAYS_PER_400_YEARS) / 4
475 + ((year % 100) * DAYS_PER_4_YEARS) / 4
476 + (month * DAYS_PER_5_MONTHS + 2) / 5
477 + day
478 - JDN_OFFSET;
479 }
480
481 #ifdef HAVE_STRFTIME
482
483 // this function is a wrapper around strftime(3) adding error checking
484 static wxString CallStrftime(const wxString& format, const tm* tm)
485 {
486 wxChar buf[4096];
487 // Create temp wxString here to work around mingw/cygwin bug 1046059
488 // http://sourceforge.net/tracker/?func=detail&atid=102435&aid=1046059&group_id=2435
489 wxString s;
490
491 if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) )
492 {
493 // if the format is valid, buffer must be too small?
494 wxFAIL_MSG(_T("strftime() failed"));
495
496 buf[0] = '\0';
497 }
498
499 s = buf;
500 return s;
501 }
502
503 #endif // HAVE_STRFTIME
504
505 #ifdef HAVE_STRPTIME
506
507 #if wxUSE_UNIX && !defined(HAVE_STRPTIME_DECL)
508 // configure detected that we had strptime() but not its declaration,
509 // provide it ourselves
510 extern "C" char *strptime(const char *, const char *, struct tm *);
511 #endif
512
513 // Unicode-friendly strptime() wrapper
514 static const wxStringCharType *
515 CallStrptime(const wxStringCharType *input, const char *fmt, tm *tm)
516 {
517 // the problem here is that strptime() returns pointer into the string we
518 // passed to it while we're really interested in the pointer into the
519 // original, Unicode, string so we try to transform the pointer back
520 #if wxUSE_UNICODE_WCHAR
521 wxCharBuffer inputMB(wxConvertWX2MB(input));
522 #else // ASCII
523 const char * const inputMB = input;
524 #endif // Unicode/Ascii
525
526 const char *result = strptime(inputMB, fmt, tm);
527 if ( !result )
528 return NULL;
529
530 #if wxUSE_UNICODE_WCHAR
531 // FIXME: this is wrong in presence of surrogates &c
532 return input + (result - inputMB.data());
533 #else // ASCII
534 return result;
535 #endif // Unicode/Ascii
536 }
537
538 #endif // HAVE_STRPTIME
539
540 // if year and/or month have invalid values, replace them with the current ones
541 static void ReplaceDefaultYearMonthWithCurrent(int *year,
542 wxDateTime::Month *month)
543 {
544 struct tm *tmNow = NULL;
545 struct tm tmstruct;
546
547 if ( *year == wxDateTime::Inv_Year )
548 {
549 tmNow = wxDateTime::GetTmNow(&tmstruct);
550
551 *year = 1900 + tmNow->tm_year;
552 }
553
554 if ( *month == wxDateTime::Inv_Month )
555 {
556 if ( !tmNow )
557 tmNow = wxDateTime::GetTmNow(&tmstruct);
558
559 *month = (wxDateTime::Month)tmNow->tm_mon;
560 }
561 }
562
563 // fll the struct tm with default values
564 static void InitTm(struct tm& tm)
565 {
566 // struct tm may have etxra fields (undocumented and with unportable
567 // names) which, nevertheless, must be set to 0
568 memset(&tm, 0, sizeof(struct tm));
569
570 tm.tm_mday = 1; // mday 0 is invalid
571 tm.tm_year = 76; // any valid year
572 tm.tm_isdst = -1; // auto determine
573 }
574
575 // ============================================================================
576 // implementation of wxDateTime
577 // ============================================================================
578
579 // ----------------------------------------------------------------------------
580 // struct Tm
581 // ----------------------------------------------------------------------------
582
583 wxDateTime::Tm::Tm()
584 {
585 year = (wxDateTime_t)wxDateTime::Inv_Year;
586 mon = wxDateTime::Inv_Month;
587 mday = 0;
588 hour = min = sec = msec = 0;
589 wday = wxDateTime::Inv_WeekDay;
590 }
591
592 wxDateTime::Tm::Tm(const struct tm& tm, const TimeZone& tz)
593 : m_tz(tz)
594 {
595 msec = 0;
596 sec = (wxDateTime::wxDateTime_t)tm.tm_sec;
597 min = (wxDateTime::wxDateTime_t)tm.tm_min;
598 hour = (wxDateTime::wxDateTime_t)tm.tm_hour;
599 mday = (wxDateTime::wxDateTime_t)tm.tm_mday;
600 mon = (wxDateTime::Month)tm.tm_mon;
601 year = 1900 + tm.tm_year;
602 wday = (wxDateTime::wxDateTime_t)tm.tm_wday;
603 yday = (wxDateTime::wxDateTime_t)tm.tm_yday;
604 }
605
606 bool wxDateTime::Tm::IsValid() const
607 {
608 // we allow for the leap seconds, although we don't use them (yet)
609 return (year != wxDateTime::Inv_Year) && (mon != wxDateTime::Inv_Month) &&
610 (mday <= GetNumOfDaysInMonth(year, mon)) &&
611 (hour < 24) && (min < 60) && (sec < 62) && (msec < 1000);
612 }
613
614 void wxDateTime::Tm::ComputeWeekDay()
615 {
616 // compute the week day from day/month/year: we use the dumbest algorithm
617 // possible: just compute our JDN and then use the (simple to derive)
618 // formula: weekday = (JDN + 1.5) % 7
619 wday = (wxDateTime::wxDateTime_t)((GetTruncatedJDN(mday, mon, year) + 2) % 7);
620 }
621
622 void wxDateTime::Tm::AddMonths(int monDiff)
623 {
624 // normalize the months field
625 while ( monDiff < -mon )
626 {
627 year--;
628
629 monDiff += MONTHS_IN_YEAR;
630 }
631
632 while ( monDiff + mon >= MONTHS_IN_YEAR )
633 {
634 year++;
635
636 monDiff -= MONTHS_IN_YEAR;
637 }
638
639 mon = (wxDateTime::Month)(mon + monDiff);
640
641 wxASSERT_MSG( mon >= 0 && mon < MONTHS_IN_YEAR, _T("logic error") );
642
643 // NB: we don't check here that the resulting date is valid, this function
644 // is private and the caller must check it if needed
645 }
646
647 void wxDateTime::Tm::AddDays(int dayDiff)
648 {
649 // normalize the days field
650 while ( dayDiff + mday < 1 )
651 {
652 AddMonths(-1);
653
654 dayDiff += GetNumOfDaysInMonth(year, mon);
655 }
656
657 mday = (wxDateTime::wxDateTime_t)( mday + dayDiff );
658 while ( mday > GetNumOfDaysInMonth(year, mon) )
659 {
660 mday -= GetNumOfDaysInMonth(year, mon);
661
662 AddMonths(1);
663 }
664
665 wxASSERT_MSG( mday > 0 && mday <= GetNumOfDaysInMonth(year, mon),
666 _T("logic error") );
667 }
668
669 // ----------------------------------------------------------------------------
670 // class TimeZone
671 // ----------------------------------------------------------------------------
672
673 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz)
674 {
675 switch ( tz )
676 {
677 case wxDateTime::Local:
678 // get the offset from C RTL: it returns the difference GMT-local
679 // while we want to have the offset _from_ GMT, hence the '-'
680 m_offset = -GetTimeZone();
681 break;
682
683 case wxDateTime::GMT_12:
684 case wxDateTime::GMT_11:
685 case wxDateTime::GMT_10:
686 case wxDateTime::GMT_9:
687 case wxDateTime::GMT_8:
688 case wxDateTime::GMT_7:
689 case wxDateTime::GMT_6:
690 case wxDateTime::GMT_5:
691 case wxDateTime::GMT_4:
692 case wxDateTime::GMT_3:
693 case wxDateTime::GMT_2:
694 case wxDateTime::GMT_1:
695 m_offset = -3600*(wxDateTime::GMT0 - tz);
696 break;
697
698 case wxDateTime::GMT0:
699 case wxDateTime::GMT1:
700 case wxDateTime::GMT2:
701 case wxDateTime::GMT3:
702 case wxDateTime::GMT4:
703 case wxDateTime::GMT5:
704 case wxDateTime::GMT6:
705 case wxDateTime::GMT7:
706 case wxDateTime::GMT8:
707 case wxDateTime::GMT9:
708 case wxDateTime::GMT10:
709 case wxDateTime::GMT11:
710 case wxDateTime::GMT12:
711 case wxDateTime::GMT13:
712 m_offset = 3600*(tz - wxDateTime::GMT0);
713 break;
714
715 case wxDateTime::A_CST:
716 // Central Standard Time in use in Australia = UTC + 9.5
717 m_offset = 60l*(9*MIN_PER_HOUR + MIN_PER_HOUR/2);
718 break;
719
720 default:
721 wxFAIL_MSG( _T("unknown time zone") );
722 }
723 }
724
725 // ----------------------------------------------------------------------------
726 // static functions
727 // ----------------------------------------------------------------------------
728
729 /* static */
730 bool wxDateTime::IsLeapYear(int year, wxDateTime::Calendar cal)
731 {
732 if ( year == Inv_Year )
733 year = GetCurrentYear();
734
735 if ( cal == Gregorian )
736 {
737 // in Gregorian calendar leap years are those divisible by 4 except
738 // those divisible by 100 unless they're also divisible by 400
739 // (in some countries, like Russia and Greece, additional corrections
740 // exist, but they won't manifest themselves until 2700)
741 return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
742 }
743 else if ( cal == Julian )
744 {
745 // in Julian calendar the rule is simpler
746 return year % 4 == 0;
747 }
748 else
749 {
750 wxFAIL_MSG(_T("unknown calendar"));
751
752 return false;
753 }
754 }
755
756 /* static */
757 int wxDateTime::GetCentury(int year)
758 {
759 return year > 0 ? year / 100 : year / 100 - 1;
760 }
761
762 /* static */
763 int wxDateTime::ConvertYearToBC(int year)
764 {
765 // year 0 is BC 1
766 return year > 0 ? year : year - 1;
767 }
768
769 /* static */
770 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal)
771 {
772 switch ( cal )
773 {
774 case Gregorian:
775 return Now().GetYear();
776
777 case Julian:
778 wxFAIL_MSG(_T("TODO"));
779 break;
780
781 default:
782 wxFAIL_MSG(_T("unsupported calendar"));
783 break;
784 }
785
786 return Inv_Year;
787 }
788
789 /* static */
790 wxDateTime::Month wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal)
791 {
792 switch ( cal )
793 {
794 case Gregorian:
795 return Now().GetMonth();
796
797 case Julian:
798 wxFAIL_MSG(_T("TODO"));
799 break;
800
801 default:
802 wxFAIL_MSG(_T("unsupported calendar"));
803 break;
804 }
805
806 return Inv_Month;
807 }
808
809 /* static */
810 wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(int year, Calendar cal)
811 {
812 if ( year == Inv_Year )
813 {
814 // take the current year if none given
815 year = GetCurrentYear();
816 }
817
818 switch ( cal )
819 {
820 case Gregorian:
821 case Julian:
822 return IsLeapYear(year) ? 366 : 365;
823
824 default:
825 wxFAIL_MSG(_T("unsupported calendar"));
826 break;
827 }
828
829 return 0;
830 }
831
832 /* static */
833 wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(wxDateTime::Month month,
834 int year,
835 wxDateTime::Calendar cal)
836 {
837 wxCHECK_MSG( month < MONTHS_IN_YEAR, 0, _T("invalid month") );
838
839 if ( cal == Gregorian || cal == Julian )
840 {
841 if ( year == Inv_Year )
842 {
843 // take the current year if none given
844 year = GetCurrentYear();
845 }
846
847 return GetNumOfDaysInMonth(year, month);
848 }
849 else
850 {
851 wxFAIL_MSG(_T("unsupported calendar"));
852
853 return 0;
854 }
855 }
856
857 /* static */
858 wxString wxDateTime::GetMonthName(wxDateTime::Month month,
859 wxDateTime::NameFlags flags)
860 {
861 wxCHECK_MSG( month != Inv_Month, wxEmptyString, _T("invalid month") );
862 #ifdef HAVE_STRFTIME
863 // notice that we must set all the fields to avoid confusing libc (GNU one
864 // gets confused to a crash if we don't do this)
865 tm tm;
866 InitTm(tm);
867 tm.tm_mon = month;
868
869 return CallStrftime(flags == Name_Abbr ? _T("%b") : _T("%B"), &tm);
870 #else // !HAVE_STRFTIME
871 wxString ret;
872 switch(month)
873 {
874 case Jan:
875 ret = (flags == Name_Abbr ? wxT("Jan"): wxT("January"));
876 break;
877 case Feb:
878 ret = (flags == Name_Abbr ? wxT("Feb"): wxT("Febuary"));
879 break;
880 case Mar:
881 ret = (flags == Name_Abbr ? wxT("Mar"): wxT("March"));
882 break;
883 case Apr:
884 ret = (flags == Name_Abbr ? wxT("Apr"): wxT("April"));
885 break;
886 case May:
887 ret = (flags == Name_Abbr ? wxT("May"): wxT("May"));
888 break;
889 case Jun:
890 ret = (flags == Name_Abbr ? wxT("Jun"): wxT("June"));
891 break;
892 case Jul:
893 ret = (flags == Name_Abbr ? wxT("Jul"): wxT("July"));
894 break;
895 case Aug:
896 ret = (flags == Name_Abbr ? wxT("Aug"): wxT("August"));
897 break;
898 case Sep:
899 ret = (flags == Name_Abbr ? wxT("Sep"): wxT("September"));
900 break;
901 case Oct:
902 ret = (flags == Name_Abbr ? wxT("Oct"): wxT("October"));
903 break;
904 case Nov:
905 ret = (flags == Name_Abbr ? wxT("Nov"): wxT("November"));
906 break;
907 case Dec:
908 ret = (flags == Name_Abbr ? wxT("Dec"): wxT("December"));
909 break;
910 }
911 return ret;
912 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
913 }
914
915 /* static */
916 wxString wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday,
917 wxDateTime::NameFlags flags)
918 {
919 wxCHECK_MSG( wday != Inv_WeekDay, wxEmptyString, _T("invalid weekday") );
920 #ifdef HAVE_STRFTIME
921 // take some arbitrary Sunday (but notice that the day should be such that
922 // after adding wday to it below we still have a valid date, e.g. don't
923 // take 28 here!)
924 tm tm;
925 InitTm(tm);
926 tm.tm_mday = 21;
927 tm.tm_mon = Nov;
928 tm.tm_year = 99;
929
930 // and offset it by the number of days needed to get the correct wday
931 tm.tm_mday += wday;
932
933 // call mktime() to normalize it...
934 (void)mktime(&tm);
935
936 // ... and call strftime()
937 return CallStrftime(flags == Name_Abbr ? _T("%a") : _T("%A"), &tm);
938 #else // !HAVE_STRFTIME
939 wxString ret;
940 switch(wday)
941 {
942 case Sun:
943 ret = (flags == Name_Abbr ? wxT("Sun") : wxT("Sunday"));
944 break;
945 case Mon:
946 ret = (flags == Name_Abbr ? wxT("Mon") : wxT("Monday"));
947 break;
948 case Tue:
949 ret = (flags == Name_Abbr ? wxT("Tue") : wxT("Tuesday"));
950 break;
951 case Wed:
952 ret = (flags == Name_Abbr ? wxT("Wed") : wxT("Wednesday"));
953 break;
954 case Thu:
955 ret = (flags == Name_Abbr ? wxT("Thu") : wxT("Thursday"));
956 break;
957 case Fri:
958 ret = (flags == Name_Abbr ? wxT("Fri") : wxT("Friday"));
959 break;
960 case Sat:
961 ret = (flags == Name_Abbr ? wxT("Sat") : wxT("Saturday"));
962 break;
963 }
964 return ret;
965 #endif // HAVE_STRFTIME/!HAVE_STRFTIME
966 }
967
968 /* static */
969 void wxDateTime::GetAmPmStrings(wxString *am, wxString *pm)
970 {
971 tm tm;
972 InitTm(tm);
973 wxChar buffer[64];
974 // @Note: Do not call 'CallStrftime' here! CallStrftime checks the return code
975 // and causes an assertion failed if the buffer is to small (which is good) - OR -
976 // if strftime does not return anything because the format string is invalid - OR -
977 // if there are no 'am' / 'pm' tokens defined for the current locale (which is not good).
978 // wxDateTime::ParseTime will try several different formats to parse the time.
979 // As a result, GetAmPmStrings might get called, even if the current locale
980 // does not define any 'am' / 'pm' tokens. In this case, wxStrftime would
981 // assert, even though it is a perfectly legal use.
982 if ( am )
983 {
984 if (wxStrftime(buffer, sizeof(buffer)/sizeof(wxChar), _T("%p"), &tm) > 0)
985 *am = wxString(buffer);
986 else
987 *am = wxString();
988 }
989 if ( pm )
990 {
991 tm.tm_hour = 13;
992 if (wxStrftime(buffer, sizeof(buffer)/sizeof(wxChar), _T("%p"), &tm) > 0)
993 *pm = wxString(buffer);
994 else
995 *pm = wxString();
996 }
997 }
998
999
1000 // ----------------------------------------------------------------------------
1001 // Country stuff: date calculations depend on the country (DST, work days,
1002 // ...), so we need to know which rules to follow.
1003 // ----------------------------------------------------------------------------
1004
1005 /* static */
1006 wxDateTime::Country wxDateTime::GetCountry()
1007 {
1008 // TODO use LOCALE_ICOUNTRY setting under Win32
1009 #ifndef __WXWINCE__
1010 if ( ms_country == Country_Unknown )
1011 {
1012 // try to guess from the time zone name
1013 time_t t = time(NULL);
1014 struct tm tmstruct;
1015 struct tm *tm = wxLocaltime_r(&t, &tmstruct);
1016
1017 wxString tz = CallStrftime(_T("%Z"), tm);
1018 if ( tz == _T("WET") || tz == _T("WEST") )
1019 {
1020 ms_country = UK;
1021 }
1022 else if ( tz == _T("CET") || tz == _T("CEST") )
1023 {
1024 ms_country = Country_EEC;
1025 }
1026 else if ( tz == _T("MSK") || tz == _T("MSD") )
1027 {
1028 ms_country = Russia;
1029 }
1030 else if ( tz == _T("AST") || tz == _T("ADT") ||
1031 tz == _T("EST") || tz == _T("EDT") ||
1032 tz == _T("CST") || tz == _T("CDT") ||
1033 tz == _T("MST") || tz == _T("MDT") ||
1034 tz == _T("PST") || tz == _T("PDT") )
1035 {
1036 ms_country = USA;
1037 }
1038 else
1039 {
1040 // well, choose a default one
1041 ms_country = USA;
1042 }
1043 }
1044 #else // __WXWINCE__
1045 ms_country = USA;
1046 #endif // !__WXWINCE__/__WXWINCE__
1047
1048 return ms_country;
1049 }
1050
1051 /* static */
1052 void wxDateTime::SetCountry(wxDateTime::Country country)
1053 {
1054 ms_country = country;
1055 }
1056
1057 /* static */
1058 bool wxDateTime::IsWestEuropeanCountry(Country country)
1059 {
1060 if ( country == Country_Default )
1061 {
1062 country = GetCountry();
1063 }
1064
1065 return (Country_WesternEurope_Start <= country) &&
1066 (country <= Country_WesternEurope_End);
1067 }
1068
1069 // ----------------------------------------------------------------------------
1070 // DST calculations: we use 3 different rules for the West European countries,
1071 // USA and for the rest of the world. This is undoubtedly false for many
1072 // countries, but I lack the necessary info (and the time to gather it),
1073 // please add the other rules here!
1074 // ----------------------------------------------------------------------------
1075
1076 /* static */
1077 bool wxDateTime::IsDSTApplicable(int year, Country country)
1078 {
1079 if ( year == Inv_Year )
1080 {
1081 // take the current year if none given
1082 year = GetCurrentYear();
1083 }
1084
1085 if ( country == Country_Default )
1086 {
1087 country = GetCountry();
1088 }
1089
1090 switch ( country )
1091 {
1092 case USA:
1093 case UK:
1094 // DST was first observed in the US and UK during WWI, reused
1095 // during WWII and used again since 1966
1096 return year >= 1966 ||
1097 (year >= 1942 && year <= 1945) ||
1098 (year == 1918 || year == 1919);
1099
1100 default:
1101 // assume that it started after WWII
1102 return year > 1950;
1103 }
1104 }
1105
1106 /* static */
1107 wxDateTime wxDateTime::GetBeginDST(int year, Country country)
1108 {
1109 if ( year == Inv_Year )
1110 {
1111 // take the current year if none given
1112 year = GetCurrentYear();
1113 }
1114
1115 if ( country == Country_Default )
1116 {
1117 country = GetCountry();
1118 }
1119
1120 if ( !IsDSTApplicable(year, country) )
1121 {
1122 return wxInvalidDateTime;
1123 }
1124
1125 wxDateTime dt;
1126
1127 if ( IsWestEuropeanCountry(country) || (country == Russia) )
1128 {
1129 // DST begins at 1 a.m. GMT on the last Sunday of March
1130 if ( !dt.SetToLastWeekDay(Sun, Mar, year) )
1131 {
1132 // weird...
1133 wxFAIL_MSG( _T("no last Sunday in March?") );
1134 }
1135
1136 dt += wxTimeSpan::Hours(1);
1137 }
1138 else switch ( country )
1139 {
1140 case USA:
1141 switch ( year )
1142 {
1143 case 1918:
1144 case 1919:
1145 // don't know for sure - assume it was in effect all year
1146
1147 case 1943:
1148 case 1944:
1149 case 1945:
1150 dt.Set(1, Jan, year);
1151 break;
1152
1153 case 1942:
1154 // DST was installed Feb 2, 1942 by the Congress
1155 dt.Set(2, Feb, year);
1156 break;
1157
1158 // Oil embargo changed the DST period in the US
1159 case 1974:
1160 dt.Set(6, Jan, 1974);
1161 break;
1162
1163 case 1975:
1164 dt.Set(23, Feb, 1975);
1165 break;
1166
1167 default:
1168 // before 1986, DST begun on the last Sunday of April, but
1169 // in 1986 Reagan changed it to begin at 2 a.m. of the
1170 // first Sunday in April
1171 if ( year < 1986 )
1172 {
1173 if ( !dt.SetToLastWeekDay(Sun, Apr, year) )
1174 {
1175 // weird...
1176 wxFAIL_MSG( _T("no first Sunday in April?") );
1177 }
1178 }
1179 else if ( year > 2006 )
1180 // Energy Policy Act of 2005, Pub. L. no. 109-58, 119 Stat 594 (2005).
1181 // Starting in 2007, daylight time begins in the United States on the
1182 // second Sunday in March and ends on the first Sunday in November
1183 {
1184 if ( !dt.SetToWeekDay(Sun, 2, Mar, year) )
1185 {
1186 // weird...
1187 wxFAIL_MSG( _T("no second Sunday in March?") );
1188 }
1189 }
1190 else
1191 {
1192 if ( !dt.SetToWeekDay(Sun, 1, Apr, year) )
1193 {
1194 // weird...
1195 wxFAIL_MSG( _T("no first Sunday in April?") );
1196 }
1197 }
1198
1199 dt += wxTimeSpan::Hours(2);
1200
1201 // TODO what about timezone??
1202 }
1203
1204 break;
1205
1206 default:
1207 // assume Mar 30 as the start of the DST for the rest of the world
1208 // - totally bogus, of course
1209 dt.Set(30, Mar, year);
1210 }
1211
1212 return dt;
1213 }
1214
1215 /* static */
1216 wxDateTime wxDateTime::GetEndDST(int year, Country country)
1217 {
1218 if ( year == Inv_Year )
1219 {
1220 // take the current year if none given
1221 year = GetCurrentYear();
1222 }
1223
1224 if ( country == Country_Default )
1225 {
1226 country = GetCountry();
1227 }
1228
1229 if ( !IsDSTApplicable(year, country) )
1230 {
1231 return wxInvalidDateTime;
1232 }
1233
1234 wxDateTime dt;
1235
1236 if ( IsWestEuropeanCountry(country) || (country == Russia) )
1237 {
1238 // DST ends at 1 a.m. GMT on the last Sunday of October
1239 if ( !dt.SetToLastWeekDay(Sun, Oct, year) )
1240 {
1241 // weirder and weirder...
1242 wxFAIL_MSG( _T("no last Sunday in October?") );
1243 }
1244
1245 dt += wxTimeSpan::Hours(1);
1246 }
1247 else switch ( country )
1248 {
1249 case USA:
1250 switch ( year )
1251 {
1252 case 1918:
1253 case 1919:
1254 // don't know for sure - assume it was in effect all year
1255
1256 case 1943:
1257 case 1944:
1258 dt.Set(31, Dec, year);
1259 break;
1260
1261 case 1945:
1262 // the time was reset after the end of the WWII
1263 dt.Set(30, Sep, year);
1264 break;
1265
1266 default: // default for switch (year)
1267 if ( year > 2006 )
1268 // Energy Policy Act of 2005, Pub. L. no. 109-58, 119 Stat 594 (2005).
1269 // Starting in 2007, daylight time begins in the United States on the
1270 // second Sunday in March and ends on the first Sunday in November
1271 {
1272 if ( !dt.SetToWeekDay(Sun, 1, Nov, year) )
1273 {
1274 // weird...
1275 wxFAIL_MSG( _T("no first Sunday in November?") );
1276 }
1277 }
1278 else
1279 // pre-2007
1280 // DST ends at 2 a.m. on the last Sunday of October
1281 {
1282 if ( !dt.SetToLastWeekDay(Sun, Oct, year) )
1283 {
1284 // weirder and weirder...
1285 wxFAIL_MSG( _T("no last Sunday in October?") );
1286 }
1287 }
1288
1289 dt += wxTimeSpan::Hours(2);
1290
1291 // TODO: what about timezone??
1292 }
1293 break;
1294
1295 default: // default for switch (country)
1296 // assume October 26th as the end of the DST - totally bogus too
1297 dt.Set(26, Oct, year);
1298 }
1299
1300 return dt;
1301 }
1302
1303 // ----------------------------------------------------------------------------
1304 // constructors and assignment operators
1305 // ----------------------------------------------------------------------------
1306
1307 // return the current time with ms precision
1308 /* static */ wxDateTime wxDateTime::UNow()
1309 {
1310 return wxDateTime(wxGetLocalTimeMillis());
1311 }
1312
1313 // the values in the tm structure contain the local time
1314 wxDateTime& wxDateTime::Set(const struct tm& tm)
1315 {
1316 struct tm tm2(tm);
1317 time_t timet = mktime(&tm2);
1318
1319 if ( timet == (time_t)-1 )
1320 {
1321 // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
1322 // less than timezone - try to make it work for this case
1323 if ( tm2.tm_year == 70 && tm2.tm_mon == 0 && tm2.tm_mday == 1 )
1324 {
1325 return Set((time_t)(
1326 GetTimeZone() +
1327 tm2.tm_hour * MIN_PER_HOUR * SEC_PER_MIN +
1328 tm2.tm_min * SEC_PER_MIN +
1329 tm2.tm_sec));
1330 }
1331
1332 wxFAIL_MSG( _T("mktime() failed") );
1333
1334 *this = wxInvalidDateTime;
1335
1336 return *this;
1337 }
1338 else
1339 {
1340 return Set(timet);
1341 }
1342 }
1343
1344 wxDateTime& wxDateTime::Set(wxDateTime_t hour,
1345 wxDateTime_t minute,
1346 wxDateTime_t second,
1347 wxDateTime_t millisec)
1348 {
1349 // we allow seconds to be 61 to account for the leap seconds, even if we
1350 // don't use them really
1351 wxDATETIME_CHECK( hour < 24 &&
1352 second < 62 &&
1353 minute < 60 &&
1354 millisec < 1000,
1355 _T("Invalid time in wxDateTime::Set()") );
1356
1357 // get the current date from system
1358 struct tm tmstruct;
1359 struct tm *tm = GetTmNow(&tmstruct);
1360
1361 wxDATETIME_CHECK( tm, _T("wxLocaltime_r() failed") );
1362
1363 // make a copy so it isn't clobbered by the call to mktime() below
1364 struct tm tm1(*tm);
1365
1366 // adjust the time
1367 tm1.tm_hour = hour;
1368 tm1.tm_min = minute;
1369 tm1.tm_sec = second;
1370
1371 // and the DST in case it changes on this date
1372 struct tm tm2(tm1);
1373 mktime(&tm2);
1374 if ( tm2.tm_isdst != tm1.tm_isdst )
1375 tm1.tm_isdst = tm2.tm_isdst;
1376
1377 (void)Set(tm1);
1378
1379 // and finally adjust milliseconds
1380 return SetMillisecond(millisec);
1381 }
1382
1383 wxDateTime& wxDateTime::Set(wxDateTime_t day,
1384 Month month,
1385 int year,
1386 wxDateTime_t hour,
1387 wxDateTime_t minute,
1388 wxDateTime_t second,
1389 wxDateTime_t millisec)
1390 {
1391 wxDATETIME_CHECK( hour < 24 &&
1392 second < 62 &&
1393 minute < 60 &&
1394 millisec < 1000,
1395 _T("Invalid time in wxDateTime::Set()") );
1396
1397 ReplaceDefaultYearMonthWithCurrent(&year, &month);
1398
1399 wxDATETIME_CHECK( (0 < day) && (day <= GetNumberOfDays(month, year)),
1400 _T("Invalid date in wxDateTime::Set()") );
1401
1402 // the range of time_t type (inclusive)
1403 static const int yearMinInRange = 1970;
1404 static const int yearMaxInRange = 2037;
1405
1406 // test only the year instead of testing for the exact end of the Unix
1407 // time_t range - it doesn't bring anything to do more precise checks
1408 if ( year >= yearMinInRange && year <= yearMaxInRange )
1409 {
1410 // use the standard library version if the date is in range - this is
1411 // probably more efficient than our code
1412 struct tm tm;
1413 tm.tm_year = year - 1900;
1414 tm.tm_mon = month;
1415 tm.tm_mday = day;
1416 tm.tm_hour = hour;
1417 tm.tm_min = minute;
1418 tm.tm_sec = second;
1419 tm.tm_isdst = -1; // mktime() will guess it
1420
1421 (void)Set(tm);
1422
1423 // and finally adjust milliseconds
1424 if (IsValid())
1425 SetMillisecond(millisec);
1426
1427 return *this;
1428 }
1429 else
1430 {
1431 // do time calculations ourselves: we want to calculate the number of
1432 // milliseconds between the given date and the epoch
1433
1434 // get the JDN for the midnight of this day
1435 m_time = GetTruncatedJDN(day, month, year);
1436 m_time -= EPOCH_JDN;
1437 m_time *= SECONDS_PER_DAY * TIME_T_FACTOR;
1438
1439 // JDN corresponds to GMT, we take localtime
1440 Add(wxTimeSpan(hour, minute, second + GetTimeZone(), millisec));
1441 }
1442
1443 return *this;
1444 }
1445
1446 wxDateTime& wxDateTime::Set(double jdn)
1447 {
1448 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1449 // EPOCH_JDN + 0.5
1450 jdn -= EPOCH_JDN + 0.5;
1451
1452 m_time.Assign(jdn*MILLISECONDS_PER_DAY);
1453
1454 // JDNs always are in UTC, so we don't need any adjustments for time zone
1455
1456 return *this;
1457 }
1458
1459 wxDateTime& wxDateTime::ResetTime()
1460 {
1461 Tm tm = GetTm();
1462
1463 if ( tm.hour || tm.min || tm.sec || tm.msec )
1464 {
1465 tm.msec =
1466 tm.sec =
1467 tm.min =
1468 tm.hour = 0;
1469
1470 Set(tm);
1471 }
1472
1473 return *this;
1474 }
1475
1476 wxDateTime wxDateTime::GetDateOnly() const
1477 {
1478 Tm tm = GetTm();
1479 tm.msec =
1480 tm.sec =
1481 tm.min =
1482 tm.hour = 0;
1483 return wxDateTime(tm);
1484 }
1485
1486 // ----------------------------------------------------------------------------
1487 // DOS Date and Time Format functions
1488 // ----------------------------------------------------------------------------
1489 // the dos date and time value is an unsigned 32 bit value in the format:
1490 // YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
1491 //
1492 // Y = year offset from 1980 (0-127)
1493 // M = month (1-12)
1494 // D = day of month (1-31)
1495 // h = hour (0-23)
1496 // m = minute (0-59)
1497 // s = bisecond (0-29) each bisecond indicates two seconds
1498 // ----------------------------------------------------------------------------
1499
1500 wxDateTime& wxDateTime::SetFromDOS(unsigned long ddt)
1501 {
1502 struct tm tm;
1503 InitTm(tm);
1504
1505 long year = ddt & 0xFE000000;
1506 year >>= 25;
1507 year += 80;
1508 tm.tm_year = year;
1509
1510 long month = ddt & 0x1E00000;
1511 month >>= 21;
1512 month -= 1;
1513 tm.tm_mon = month;
1514
1515 long day = ddt & 0x1F0000;
1516 day >>= 16;
1517 tm.tm_mday = day;
1518
1519 long hour = ddt & 0xF800;
1520 hour >>= 11;
1521 tm.tm_hour = hour;
1522
1523 long minute = ddt & 0x7E0;
1524 minute >>= 5;
1525 tm.tm_min = minute;
1526
1527 long second = ddt & 0x1F;
1528 tm.tm_sec = second * 2;
1529
1530 return Set(mktime(&tm));
1531 }
1532
1533 unsigned long wxDateTime::GetAsDOS() const
1534 {
1535 unsigned long ddt;
1536 time_t ticks = GetTicks();
1537 struct tm tmstruct;
1538 struct tm *tm = wxLocaltime_r(&ticks, &tmstruct);
1539 wxCHECK_MSG( tm, ULONG_MAX, _T("time can't be represented in DOS format") );
1540
1541 long year = tm->tm_year;
1542 year -= 80;
1543 year <<= 25;
1544
1545 long month = tm->tm_mon;
1546 month += 1;
1547 month <<= 21;
1548
1549 long day = tm->tm_mday;
1550 day <<= 16;
1551
1552 long hour = tm->tm_hour;
1553 hour <<= 11;
1554
1555 long minute = tm->tm_min;
1556 minute <<= 5;
1557
1558 long second = tm->tm_sec;
1559 second /= 2;
1560
1561 ddt = year | month | day | hour | minute | second;
1562 return ddt;
1563 }
1564
1565 // ----------------------------------------------------------------------------
1566 // time_t <-> broken down time conversions
1567 // ----------------------------------------------------------------------------
1568
1569 wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
1570 {
1571 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1572
1573 time_t time = GetTicks();
1574 if ( time != (time_t)-1 )
1575 {
1576 // use C RTL functions
1577 struct tm tmstruct;
1578 tm *tm;
1579 if ( tz.GetOffset() == -GetTimeZone() )
1580 {
1581 // we are working with local time
1582 tm = wxLocaltime_r(&time, &tmstruct);
1583
1584 // should never happen
1585 wxCHECK_MSG( tm, Tm(), _T("wxLocaltime_r() failed") );
1586 }
1587 else
1588 {
1589 time += (time_t)tz.GetOffset();
1590 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
1591 int time2 = (int) time;
1592 if ( time2 >= 0 )
1593 #else
1594 if ( time >= 0 )
1595 #endif
1596 {
1597 tm = wxGmtime_r(&time, &tmstruct);
1598
1599 // should never happen
1600 wxCHECK_MSG( tm, Tm(), _T("wxGmtime_r() failed") );
1601 }
1602 else
1603 {
1604 tm = (struct tm *)NULL;
1605 }
1606 }
1607
1608 if ( tm )
1609 {
1610 // adjust the milliseconds
1611 Tm tm2(*tm, tz);
1612 long timeOnly = (m_time % MILLISECONDS_PER_DAY).ToLong();
1613 tm2.msec = (wxDateTime_t)(timeOnly % 1000);
1614 return tm2;
1615 }
1616 //else: use generic code below
1617 }
1618
1619 // remember the time and do the calculations with the date only - this
1620 // eliminates rounding errors of the floating point arithmetics
1621
1622 wxLongLong timeMidnight = m_time + tz.GetOffset() * 1000;
1623
1624 long timeOnly = (timeMidnight % MILLISECONDS_PER_DAY).ToLong();
1625
1626 // we want to always have positive time and timeMidnight to be really
1627 // the midnight before it
1628 if ( timeOnly < 0 )
1629 {
1630 timeOnly = MILLISECONDS_PER_DAY + timeOnly;
1631 }
1632
1633 timeMidnight -= timeOnly;
1634
1635 // calculate the Gregorian date from JDN for the midnight of our date:
1636 // this will yield day, month (in 1..12 range) and year
1637
1638 // actually, this is the JDN for the noon of the previous day
1639 long jdn = (timeMidnight / MILLISECONDS_PER_DAY).ToLong() + EPOCH_JDN;
1640
1641 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1642
1643 wxASSERT_MSG( jdn > -2, _T("JDN out of range") );
1644
1645 // calculate the century
1646 long temp = (jdn + JDN_OFFSET) * 4 - 1;
1647 long century = temp / DAYS_PER_400_YEARS;
1648
1649 // then the year and day of year (1 <= dayOfYear <= 366)
1650 temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3;
1651 long year = (century * 100) + (temp / DAYS_PER_4_YEARS);
1652 long dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
1653
1654 // and finally the month and day of the month
1655 temp = dayOfYear * 5 - 3;
1656 long month = temp / DAYS_PER_5_MONTHS;
1657 long day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
1658
1659 // month is counted from March - convert to normal
1660 if ( month < 10 )
1661 {
1662 month += 3;
1663 }
1664 else
1665 {
1666 year += 1;
1667 month -= 9;
1668 }
1669
1670 // year is offset by 4800
1671 year -= 4800;
1672
1673 // check that the algorithm gave us something reasonable
1674 wxASSERT_MSG( (0 < month) && (month <= 12), _T("invalid month") );
1675 wxASSERT_MSG( (1 <= day) && (day < 32), _T("invalid day") );
1676
1677 // construct Tm from these values
1678 Tm tm;
1679 tm.year = (int)year;
1680 tm.mon = (Month)(month - 1); // algorithm yields 1 for January, not 0
1681 tm.mday = (wxDateTime_t)day;
1682 tm.msec = (wxDateTime_t)(timeOnly % 1000);
1683 timeOnly -= tm.msec;
1684 timeOnly /= 1000; // now we have time in seconds
1685
1686 tm.sec = (wxDateTime_t)(timeOnly % SEC_PER_MIN);
1687 timeOnly -= tm.sec;
1688 timeOnly /= SEC_PER_MIN; // now we have time in minutes
1689
1690 tm.min = (wxDateTime_t)(timeOnly % MIN_PER_HOUR);
1691 timeOnly -= tm.min;
1692
1693 tm.hour = (wxDateTime_t)(timeOnly / MIN_PER_HOUR);
1694
1695 return tm;
1696 }
1697
1698 wxDateTime& wxDateTime::SetYear(int year)
1699 {
1700 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1701
1702 Tm tm(GetTm());
1703 tm.year = year;
1704 Set(tm);
1705
1706 return *this;
1707 }
1708
1709 wxDateTime& wxDateTime::SetMonth(Month month)
1710 {
1711 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1712
1713 Tm tm(GetTm());
1714 tm.mon = month;
1715 Set(tm);
1716
1717 return *this;
1718 }
1719
1720 wxDateTime& wxDateTime::SetDay(wxDateTime_t mday)
1721 {
1722 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1723
1724 Tm tm(GetTm());
1725 tm.mday = mday;
1726 Set(tm);
1727
1728 return *this;
1729 }
1730
1731 wxDateTime& wxDateTime::SetHour(wxDateTime_t hour)
1732 {
1733 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1734
1735 Tm tm(GetTm());
1736 tm.hour = hour;
1737 Set(tm);
1738
1739 return *this;
1740 }
1741
1742 wxDateTime& wxDateTime::SetMinute(wxDateTime_t min)
1743 {
1744 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1745
1746 Tm tm(GetTm());
1747 tm.min = min;
1748 Set(tm);
1749
1750 return *this;
1751 }
1752
1753 wxDateTime& wxDateTime::SetSecond(wxDateTime_t sec)
1754 {
1755 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1756
1757 Tm tm(GetTm());
1758 tm.sec = sec;
1759 Set(tm);
1760
1761 return *this;
1762 }
1763
1764 wxDateTime& wxDateTime::SetMillisecond(wxDateTime_t millisecond)
1765 {
1766 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1767
1768 // we don't need to use GetTm() for this one
1769 m_time -= m_time % 1000l;
1770 m_time += millisecond;
1771
1772 return *this;
1773 }
1774
1775 // ----------------------------------------------------------------------------
1776 // wxDateTime arithmetics
1777 // ----------------------------------------------------------------------------
1778
1779 wxDateTime& wxDateTime::Add(const wxDateSpan& diff)
1780 {
1781 Tm tm(GetTm());
1782
1783 tm.year += diff.GetYears();
1784 tm.AddMonths(diff.GetMonths());
1785
1786 // check that the resulting date is valid
1787 if ( tm.mday > GetNumOfDaysInMonth(tm.year, tm.mon) )
1788 {
1789 // We suppose that when adding one month to Jan 31 we want to get Feb
1790 // 28 (or 29), i.e. adding a month to the last day of the month should
1791 // give the last day of the next month which is quite logical.
1792 //
1793 // Unfortunately, there is no logic way to understand what should
1794 // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1795 // We make it Feb 28 (last day too), but it is highly questionable.
1796 tm.mday = GetNumOfDaysInMonth(tm.year, tm.mon);
1797 }
1798
1799 tm.AddDays(diff.GetTotalDays());
1800
1801 Set(tm);
1802
1803 wxASSERT_MSG( IsSameTime(tm),
1804 _T("Add(wxDateSpan) shouldn't modify time") );
1805
1806 return *this;
1807 }
1808
1809 // ----------------------------------------------------------------------------
1810 // Weekday and monthday stuff
1811 // ----------------------------------------------------------------------------
1812
1813 // convert Sun, Mon, ..., Sat into 6, 0, ..., 5
1814 static inline int ConvertWeekDayToMondayBase(int wd)
1815 {
1816 return wd == wxDateTime::Sun ? 6 : wd - 1;
1817 }
1818
1819 /* static */
1820 wxDateTime
1821 wxDateTime::SetToWeekOfYear(int year, wxDateTime_t numWeek, WeekDay wd)
1822 {
1823 wxASSERT_MSG( numWeek > 0,
1824 _T("invalid week number: weeks are counted from 1") );
1825
1826 // Jan 4 always lies in the 1st week of the year
1827 wxDateTime dt(4, Jan, year);
1828 dt.SetToWeekDayInSameWeek(wd);
1829 dt += wxDateSpan::Weeks(numWeek - 1);
1830
1831 return dt;
1832 }
1833
1834 #if WXWIN_COMPATIBILITY_2_6
1835 // use a separate function to avoid warnings about using deprecated
1836 // SetToTheWeek in GetWeek below
1837 static wxDateTime
1838 SetToTheWeek(int year,
1839 wxDateTime::wxDateTime_t numWeek,
1840 wxDateTime::WeekDay weekday,
1841 wxDateTime::WeekFlags flags)
1842 {
1843 // Jan 4 always lies in the 1st week of the year
1844 wxDateTime dt(4, wxDateTime::Jan, year);
1845 dt.SetToWeekDayInSameWeek(weekday, flags);
1846 dt += wxDateSpan::Weeks(numWeek - 1);
1847
1848 return dt;
1849 }
1850
1851 bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek,
1852 WeekDay weekday,
1853 WeekFlags flags)
1854 {
1855 int year = GetYear();
1856 *this = ::SetToTheWeek(year, numWeek, weekday, flags);
1857 if ( GetYear() != year )
1858 {
1859 // oops... numWeek was too big
1860 return false;
1861 }
1862
1863 return true;
1864 }
1865
1866 wxDateTime wxDateTime::GetWeek(wxDateTime_t numWeek,
1867 WeekDay weekday,
1868 WeekFlags flags) const
1869 {
1870 return ::SetToTheWeek(GetYear(), numWeek, weekday, flags);
1871 }
1872 #endif // WXWIN_COMPATIBILITY_2_6
1873
1874 wxDateTime& wxDateTime::SetToLastMonthDay(Month month,
1875 int year)
1876 {
1877 // take the current month/year if none specified
1878 if ( year == Inv_Year )
1879 year = GetYear();
1880 if ( month == Inv_Month )
1881 month = GetMonth();
1882
1883 return Set(GetNumOfDaysInMonth(year, month), month, year);
1884 }
1885
1886 wxDateTime& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday, WeekFlags flags)
1887 {
1888 wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
1889
1890 int wdayDst = weekday,
1891 wdayThis = GetWeekDay();
1892 if ( wdayDst == wdayThis )
1893 {
1894 // nothing to do
1895 return *this;
1896 }
1897
1898 if ( flags == Default_First )
1899 {
1900 flags = GetCountry() == USA ? Sunday_First : Monday_First;
1901 }
1902
1903 // the logic below based on comparing weekday and wdayThis works if Sun (0)
1904 // is the first day in the week, but breaks down for Monday_First case so
1905 // we adjust the week days in this case
1906 if ( flags == Monday_First )
1907 {
1908 if ( wdayThis == Sun )
1909 wdayThis += 7;
1910 if ( wdayDst == Sun )
1911 wdayDst += 7;
1912 }
1913 //else: Sunday_First, nothing to do
1914
1915 // go forward or back in time to the day we want
1916 if ( wdayDst < wdayThis )
1917 {
1918 return Subtract(wxDateSpan::Days(wdayThis - wdayDst));
1919 }
1920 else // weekday > wdayThis
1921 {
1922 return Add(wxDateSpan::Days(wdayDst - wdayThis));
1923 }
1924 }
1925
1926 wxDateTime& wxDateTime::SetToNextWeekDay(WeekDay weekday)
1927 {
1928 wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
1929
1930 int diff;
1931 WeekDay wdayThis = GetWeekDay();
1932 if ( weekday == wdayThis )
1933 {
1934 // nothing to do
1935 return *this;
1936 }
1937 else if ( weekday < wdayThis )
1938 {
1939 // need to advance a week
1940 diff = 7 - (wdayThis - weekday);
1941 }
1942 else // weekday > wdayThis
1943 {
1944 diff = weekday - wdayThis;
1945 }
1946
1947 return Add(wxDateSpan::Days(diff));
1948 }
1949
1950 wxDateTime& wxDateTime::SetToPrevWeekDay(WeekDay weekday)
1951 {
1952 wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
1953
1954 int diff;
1955 WeekDay wdayThis = GetWeekDay();
1956 if ( weekday == wdayThis )
1957 {
1958 // nothing to do
1959 return *this;
1960 }
1961 else if ( weekday > wdayThis )
1962 {
1963 // need to go to previous week
1964 diff = 7 - (weekday - wdayThis);
1965 }
1966 else // weekday < wdayThis
1967 {
1968 diff = wdayThis - weekday;
1969 }
1970
1971 return Subtract(wxDateSpan::Days(diff));
1972 }
1973
1974 bool wxDateTime::SetToWeekDay(WeekDay weekday,
1975 int n,
1976 Month month,
1977 int year)
1978 {
1979 wxCHECK_MSG( weekday != Inv_WeekDay, false, _T("invalid weekday") );
1980
1981 // we don't check explicitly that -5 <= n <= 5 because we will return false
1982 // anyhow in such case - but may be should still give an assert for it?
1983
1984 // take the current month/year if none specified
1985 ReplaceDefaultYearMonthWithCurrent(&year, &month);
1986
1987 wxDateTime dt;
1988
1989 // TODO this probably could be optimised somehow...
1990
1991 if ( n > 0 )
1992 {
1993 // get the first day of the month
1994 dt.Set(1, month, year);
1995
1996 // get its wday
1997 WeekDay wdayFirst = dt.GetWeekDay();
1998
1999 // go to the first weekday of the month
2000 int diff = weekday - wdayFirst;
2001 if ( diff < 0 )
2002 diff += 7;
2003
2004 // add advance n-1 weeks more
2005 diff += 7*(n - 1);
2006
2007 dt += wxDateSpan::Days(diff);
2008 }
2009 else // count from the end of the month
2010 {
2011 // get the last day of the month
2012 dt.SetToLastMonthDay(month, year);
2013
2014 // get its wday
2015 WeekDay wdayLast = dt.GetWeekDay();
2016
2017 // go to the last weekday of the month
2018 int diff = wdayLast - weekday;
2019 if ( diff < 0 )
2020 diff += 7;
2021
2022 // and rewind n-1 weeks from there
2023 diff += 7*(-n - 1);
2024
2025 dt -= wxDateSpan::Days(diff);
2026 }
2027
2028 // check that it is still in the same month
2029 if ( dt.GetMonth() == month )
2030 {
2031 *this = dt;
2032
2033 return true;
2034 }
2035 else
2036 {
2037 // no such day in this month
2038 return false;
2039 }
2040 }
2041
2042 static inline
2043 wxDateTime::wxDateTime_t GetDayOfYearFromTm(const wxDateTime::Tm& tm)
2044 {
2045 return (wxDateTime::wxDateTime_t)(gs_cumulatedDays[wxDateTime::IsLeapYear(tm.year)][tm.mon] + tm.mday);
2046 }
2047
2048 wxDateTime::wxDateTime_t wxDateTime::GetDayOfYear(const TimeZone& tz) const
2049 {
2050 return GetDayOfYearFromTm(GetTm(tz));
2051 }
2052
2053 wxDateTime::wxDateTime_t
2054 wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags, const TimeZone& tz) const
2055 {
2056 if ( flags == Default_First )
2057 {
2058 flags = GetCountry() == USA ? Sunday_First : Monday_First;
2059 }
2060
2061 Tm tm(GetTm(tz));
2062 wxDateTime_t nDayInYear = GetDayOfYearFromTm(tm);
2063
2064 int wdTarget = GetWeekDay(tz);
2065 int wdYearStart = wxDateTime(1, Jan, GetYear()).GetWeekDay();
2066 int week;
2067 if ( flags == Sunday_First )
2068 {
2069 // FIXME: First week is not calculated correctly.
2070 week = (nDayInYear - wdTarget + 7) / 7;
2071 if ( wdYearStart == Wed || wdYearStart == Thu )
2072 week++;
2073 }
2074 else // week starts with monday
2075 {
2076 // adjust the weekdays to non-US style.
2077 wdYearStart = ConvertWeekDayToMondayBase(wdYearStart);
2078 wdTarget = ConvertWeekDayToMondayBase(wdTarget);
2079
2080 // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html:
2081 //
2082 // Week 01 of a year is per definition the first week that has the
2083 // Thursday in this year, which is equivalent to the week that
2084 // contains the fourth day of January. In other words, the first
2085 // week of a new year is the week that has the majority of its
2086 // days in the new year. Week 01 might also contain days from the
2087 // previous year and the week before week 01 of a year is the last
2088 // week (52 or 53) of the previous year even if it contains days
2089 // from the new year. A week starts with Monday (day 1) and ends
2090 // with Sunday (day 7).
2091 //
2092
2093 // if Jan 1 is Thursday or less, it is in the first week of this year
2094 if ( wdYearStart < 4 )
2095 {
2096 // count the number of entire weeks between Jan 1 and this date
2097 week = (nDayInYear + wdYearStart + 6 - wdTarget)/7;
2098
2099 // be careful to check for overflow in the next year
2100 if ( week == 53 && tm.mday - wdTarget > 28 )
2101 week = 1;
2102 }
2103 else // Jan 1 is in the last week of the previous year
2104 {
2105 // check if we happen to be at the last week of previous year:
2106 if ( tm.mon == Jan && tm.mday < 8 - wdYearStart )
2107 week = wxDateTime(31, Dec, GetYear()-1).GetWeekOfYear();
2108 else
2109 week = (nDayInYear + wdYearStart - 1 - wdTarget)/7;
2110 }
2111 }
2112
2113 return (wxDateTime::wxDateTime_t)week;
2114 }
2115
2116 wxDateTime::wxDateTime_t wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags,
2117 const TimeZone& tz) const
2118 {
2119 Tm tm = GetTm(tz);
2120 wxDateTime dtMonthStart = wxDateTime(1, tm.mon, tm.year);
2121 int nWeek = GetWeekOfYear(flags) - dtMonthStart.GetWeekOfYear(flags) + 1;
2122 if ( nWeek < 0 )
2123 {
2124 // this may happen for January when Jan, 1 is the last week of the
2125 // previous year
2126 nWeek += IsLeapYear(tm.year - 1) ? 53 : 52;
2127 }
2128
2129 return (wxDateTime::wxDateTime_t)nWeek;
2130 }
2131
2132 wxDateTime& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday)
2133 {
2134 int year = GetYear();
2135 wxDATETIME_CHECK( (0 < yday) && (yday <= GetNumberOfDays(year)),
2136 _T("invalid year day") );
2137
2138 bool isLeap = IsLeapYear(year);
2139 for ( Month mon = Jan; mon < Inv_Month; wxNextMonth(mon) )
2140 {
2141 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
2142 // don't need it neither - because of the CHECK above we know that
2143 // yday lies in December then
2144 if ( (mon == Dec) || (yday <= gs_cumulatedDays[isLeap][mon + 1]) )
2145 {
2146 Set((wxDateTime::wxDateTime_t)(yday - gs_cumulatedDays[isLeap][mon]), mon, year);
2147
2148 break;
2149 }
2150 }
2151
2152 return *this;
2153 }
2154
2155 // ----------------------------------------------------------------------------
2156 // Julian day number conversion and related stuff
2157 // ----------------------------------------------------------------------------
2158
2159 double wxDateTime::GetJulianDayNumber() const
2160 {
2161 return m_time.ToDouble() / MILLISECONDS_PER_DAY + EPOCH_JDN + 0.5;
2162 }
2163
2164 double wxDateTime::GetRataDie() const
2165 {
2166 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
2167 return GetJulianDayNumber() - 1721119.5 - 306;
2168 }
2169
2170 // ----------------------------------------------------------------------------
2171 // timezone and DST stuff
2172 // ----------------------------------------------------------------------------
2173
2174 int wxDateTime::IsDST(wxDateTime::Country country) const
2175 {
2176 wxCHECK_MSG( country == Country_Default, -1,
2177 _T("country support not implemented") );
2178
2179 // use the C RTL for the dates in the standard range
2180 time_t timet = GetTicks();
2181 if ( timet != (time_t)-1 )
2182 {
2183 struct tm tmstruct;
2184 tm *tm = wxLocaltime_r(&timet, &tmstruct);
2185
2186 wxCHECK_MSG( tm, -1, _T("wxLocaltime_r() failed") );
2187
2188 return tm->tm_isdst;
2189 }
2190 else
2191 {
2192 int year = GetYear();
2193
2194 if ( !IsDSTApplicable(year, country) )
2195 {
2196 // no DST time in this year in this country
2197 return -1;
2198 }
2199
2200 return IsBetween(GetBeginDST(year, country), GetEndDST(year, country));
2201 }
2202 }
2203
2204 wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST)
2205 {
2206 long secDiff = GetTimeZone() + tz.GetOffset();
2207
2208 // we need to know whether DST is or not in effect for this date unless
2209 // the test disabled by the caller
2210 if ( !noDST && (IsDST() == 1) )
2211 {
2212 // FIXME we assume that the DST is always shifted by 1 hour
2213 secDiff -= 3600;
2214 }
2215
2216 return Add(wxTimeSpan::Seconds(secDiff));
2217 }
2218
2219 wxDateTime& wxDateTime::MakeFromTimezone(const TimeZone& tz, bool noDST)
2220 {
2221 long secDiff = GetTimeZone() + tz.GetOffset();
2222
2223 // we need to know whether DST is or not in effect for this date unless
2224 // the test disabled by the caller
2225 if ( !noDST && (IsDST() == 1) )
2226 {
2227 // FIXME we assume that the DST is always shifted by 1 hour
2228 secDiff -= 3600;
2229 }
2230
2231 return Subtract(wxTimeSpan::Seconds(secDiff));
2232 }
2233
2234 // ============================================================================
2235 // wxDateTimeHolidayAuthority and related classes
2236 // ============================================================================
2237
2238 #include "wx/arrimpl.cpp"
2239
2240 WX_DEFINE_OBJARRAY(wxDateTimeArray)
2241
2242 static int wxCMPFUNC_CONV
2243 wxDateTimeCompareFunc(wxDateTime **first, wxDateTime **second)
2244 {
2245 wxDateTime dt1 = **first,
2246 dt2 = **second;
2247
2248 return dt1 == dt2 ? 0 : dt1 < dt2 ? -1 : +1;
2249 }
2250
2251 // ----------------------------------------------------------------------------
2252 // wxDateTimeHolidayAuthority
2253 // ----------------------------------------------------------------------------
2254
2255 wxHolidayAuthoritiesArray wxDateTimeHolidayAuthority::ms_authorities;
2256
2257 /* static */
2258 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime& dt)
2259 {
2260 size_t count = ms_authorities.size();
2261 for ( size_t n = 0; n < count; n++ )
2262 {
2263 if ( ms_authorities[n]->DoIsHoliday(dt) )
2264 {
2265 return true;
2266 }
2267 }
2268
2269 return false;
2270 }
2271
2272 /* static */
2273 size_t
2274 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime& dtStart,
2275 const wxDateTime& dtEnd,
2276 wxDateTimeArray& holidays)
2277 {
2278 wxDateTimeArray hol;
2279
2280 holidays.Clear();
2281
2282 const size_t countAuth = ms_authorities.size();
2283 for ( size_t nAuth = 0; nAuth < countAuth; nAuth++ )
2284 {
2285 ms_authorities[nAuth]->DoGetHolidaysInRange(dtStart, dtEnd, hol);
2286
2287 WX_APPEND_ARRAY(holidays, hol);
2288 }
2289
2290 holidays.Sort(wxDateTimeCompareFunc);
2291
2292 return holidays.size();
2293 }
2294
2295 /* static */
2296 void wxDateTimeHolidayAuthority::ClearAllAuthorities()
2297 {
2298 WX_CLEAR_ARRAY(ms_authorities);
2299 }
2300
2301 /* static */
2302 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority *auth)
2303 {
2304 ms_authorities.push_back(auth);
2305 }
2306
2307 wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
2308 {
2309 // required here for Darwin
2310 }
2311
2312 // ----------------------------------------------------------------------------
2313 // wxDateTimeWorkDays
2314 // ----------------------------------------------------------------------------
2315
2316 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime& dt) const
2317 {
2318 wxDateTime::WeekDay wd = dt.GetWeekDay();
2319
2320 return (wd == wxDateTime::Sun) || (wd == wxDateTime::Sat);
2321 }
2322
2323 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime& dtStart,
2324 const wxDateTime& dtEnd,
2325 wxDateTimeArray& holidays) const
2326 {
2327 if ( dtStart > dtEnd )
2328 {
2329 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
2330
2331 return 0u;
2332 }
2333
2334 holidays.Empty();
2335
2336 // instead of checking all days, start with the first Sat after dtStart and
2337 // end with the last Sun before dtEnd
2338 wxDateTime dtSatFirst = dtStart.GetNextWeekDay(wxDateTime::Sat),
2339 dtSatLast = dtEnd.GetPrevWeekDay(wxDateTime::Sat),
2340 dtSunFirst = dtStart.GetNextWeekDay(wxDateTime::Sun),
2341 dtSunLast = dtEnd.GetPrevWeekDay(wxDateTime::Sun),
2342 dt;
2343
2344 for ( dt = dtSatFirst; dt <= dtSatLast; dt += wxDateSpan::Week() )
2345 {
2346 holidays.Add(dt);
2347 }
2348
2349 for ( dt = dtSunFirst; dt <= dtSunLast; dt += wxDateSpan::Week() )
2350 {
2351 holidays.Add(dt);
2352 }
2353
2354 return holidays.GetCount();
2355 }
2356
2357 // ============================================================================
2358 // other helper functions
2359 // ============================================================================
2360
2361 // ----------------------------------------------------------------------------
2362 // iteration helpers: can be used to write a for loop over enum variable like
2363 // this:
2364 // for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
2365 // ----------------------------------------------------------------------------
2366
2367 WXDLLIMPEXP_BASE void wxNextMonth(wxDateTime::Month& m)
2368 {
2369 wxASSERT_MSG( m < wxDateTime::Inv_Month, _T("invalid month") );
2370
2371 // no wrapping or the for loop above would never end!
2372 m = (wxDateTime::Month)(m + 1);
2373 }
2374
2375 WXDLLIMPEXP_BASE void wxPrevMonth(wxDateTime::Month& m)
2376 {
2377 wxASSERT_MSG( m < wxDateTime::Inv_Month, _T("invalid month") );
2378
2379 m = m == wxDateTime::Jan ? wxDateTime::Inv_Month
2380 : (wxDateTime::Month)(m - 1);
2381 }
2382
2383 WXDLLIMPEXP_BASE void wxNextWDay(wxDateTime::WeekDay& wd)
2384 {
2385 wxASSERT_MSG( wd < wxDateTime::Inv_WeekDay, _T("invalid week day") );
2386
2387 // no wrapping or the for loop above would never end!
2388 wd = (wxDateTime::WeekDay)(wd + 1);
2389 }
2390
2391 WXDLLIMPEXP_BASE void wxPrevWDay(wxDateTime::WeekDay& wd)
2392 {
2393 wxASSERT_MSG( wd < wxDateTime::Inv_WeekDay, _T("invalid week day") );
2394
2395 wd = wd == wxDateTime::Sun ? wxDateTime::Inv_WeekDay
2396 : (wxDateTime::WeekDay)(wd - 1);
2397 }
2398
2399 #ifdef __WXMSW__
2400
2401 wxDateTime& wxDateTime::SetFromMSWSysTime(const SYSTEMTIME& st)
2402 {
2403 return Set(st.wDay,
2404 static_cast<wxDateTime::Month>(wxDateTime::Jan + st.wMonth - 1),
2405 st.wYear,
2406 0, 0, 0);
2407 }
2408
2409 void wxDateTime::GetAsMSWSysTime(SYSTEMTIME* st) const
2410 {
2411 const wxDateTime::Tm tm(GetTm());
2412
2413 st->wYear = (WXWORD)tm.year;
2414 st->wMonth = (WXWORD)(tm.mon - wxDateTime::Jan + 1);
2415 st->wDay = tm.mday;
2416
2417 st->wDayOfWeek =
2418 st->wHour =
2419 st->wMinute =
2420 st->wSecond =
2421 st->wMilliseconds = 0;
2422 }
2423 #endif // __WXMSW__
2424
2425 #endif // wxUSE_DATETIME