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