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