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