1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: implementation of time/date related classes
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
21 #pragma implementation "datetime.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
32 #include "wx/string.h"
37 #include "wx/thread.h"
39 #define wxDEFINE_TIME_CONSTANTS
41 #include "wx/datetime.h"
43 // ----------------------------------------------------------------------------
45 // ----------------------------------------------------------------------------
47 // note that all these constants should be signed or we'd get some big
48 // surprizes with C integer arithmetics
49 static const int MONTHS_IN_YEAR
= 12;
51 static const int SECONDS_IN_MINUTE
= 60;
53 // the number of days in month in Julian/Gregorian calendar: the first line is
54 // for normal years, the second one is for the leap ones
55 static wxDateTime::wxDateTime_t gs_daysInMonth
[2][MONTHS_IN_YEAR
] =
57 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
58 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
61 // ----------------------------------------------------------------------------
63 // ----------------------------------------------------------------------------
65 // a critical section is needed to protect GetTimeZone() static
66 // variable in MT case
68 wxCriticalSection gs_critsectTimezone
;
69 #endif // wxUSE_THREADS
71 // ----------------------------------------------------------------------------
73 // ----------------------------------------------------------------------------
75 // get the number of days in the given month of the given year
77 wxDateTime::wxDateTime_t
GetNumOfDaysInMonth(int year
, wxDateTime::Month month
)
79 return gs_daysInMonth
[wxDateTime::IsLeapYear(year
)][month
];
82 // ensure that the timezone variable is set by calling localtime
83 static int GetTimeZone()
85 // set to TRUE when the timezone is set
86 static bool s_timezoneSet
= FALSE
;
88 wxCRIT_SECT_LOCKER(lock
, gs_critsectTimezone
);
100 // this function is a wrapper around strftime(3)
101 static wxString
CallStrftime(const wxChar
*format
, const tm
* tm
)
104 if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) )
106 // is ti really possible that 1024 is too short?
107 wxFAIL_MSG(_T("strftime() failed"));
110 return wxString(buf
);
113 // if year and/or month have invalid values, replace them with the current ones
114 static void ReplaceDefaultYearMonthWithCurrent(int *year
,
115 wxDateTime::Month
*month
)
117 struct tm
*tmNow
= NULL
;
119 if ( *year
== wxDateTime::Inv_Year
)
121 tmNow
= wxDateTime::GetTmNow();
123 *year
= 1900 + tmNow
->tm_year
;
126 if ( *month
== wxDateTime::Inv_Month
)
129 tmNow
= wxDateTime::GetTmNow();
131 *month
= (wxDateTime::Month
)tmNow
->tm_mon
;
135 // ============================================================================
136 // implementation of wxDateTime
137 // ============================================================================
139 // ----------------------------------------------------------------------------
141 // ----------------------------------------------------------------------------
143 wxDateTime::Country
wxDateTime::ms_country
= wxDateTime::Country_Unknown
;
144 wxDateTime
wxDateTime::ms_InvDateTime
;
146 // ----------------------------------------------------------------------------
148 // ----------------------------------------------------------------------------
152 year
= (wxDateTime_t
)wxDateTime::Inv_Year
;
153 mon
= wxDateTime::Inv_Month
;
155 hour
= min
= sec
= 0;
156 wday
= wxDateTime::Inv_WeekDay
;
159 wxDateTime::Tm::Tm(const struct tm
& tm
)
165 mon
= (wxDateTime::Month
)tm
.tm_mon
;
166 year
= 1900 + tm
.tm_year
;
171 bool wxDateTime::Tm::IsValid() const
173 // we allow for the leap seconds, although we don't use them (yet)
174 return (year
!= wxDateTime::Inv_Year
) && (mon
!= wxDateTime::Inv_Month
) &&
175 (mday
< GetNumOfDaysInMonth(year
, mon
)) &&
176 (hour
< 24) && (min
< 60) && (sec
< 62);
179 void wxDateTime::Tm::ComputeWeekDay()
181 wxFAIL_MSG(_T("TODO"));
184 void wxDateTime::Tm::AddMonths(wxDateTime::wxDateTime_t monDiff
)
186 // normalize the months field
187 while ( monDiff
< -mon
)
191 monDiff
+= MONTHS_IN_YEAR
;
194 while ( monDiff
+ mon
> MONTHS_IN_YEAR
)
199 mon
= (wxDateTime::Month
)(mon
+ monDiff
);
201 wxASSERT_MSG( mon
>= 0 && mon
< 12, _T("logic error") );
204 void wxDateTime::Tm::AddDays(wxDateTime::wxDateTime_t dayDiff
)
206 // normalize the days field
212 mday
+= GetNumOfDaysInMonth(year
, mon
);
215 while ( mday
> GetNumOfDaysInMonth(year
, mon
) )
217 mday
-= GetNumOfDaysInMonth(year
, mon
);
222 wxASSERT_MSG( mday
> 0 && mday
<= GetNumOfDaysInMonth(year
, mon
),
226 // ----------------------------------------------------------------------------
228 // ----------------------------------------------------------------------------
230 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz
)
234 case wxDateTime::Local
:
235 // leave offset to be 0
238 case wxDateTime::GMT_12
:
239 case wxDateTime::GMT_11
:
240 case wxDateTime::GMT_10
:
241 case wxDateTime::GMT_9
:
242 case wxDateTime::GMT_8
:
243 case wxDateTime::GMT_7
:
244 case wxDateTime::GMT_6
:
245 case wxDateTime::GMT_5
:
246 case wxDateTime::GMT_4
:
247 case wxDateTime::GMT_3
:
248 case wxDateTime::GMT_2
:
249 case wxDateTime::GMT_1
:
250 m_offset
= -60*(wxDateTime::GMT0
- tz
);
253 case wxDateTime::GMT0
:
254 case wxDateTime::GMT1
:
255 case wxDateTime::GMT2
:
256 case wxDateTime::GMT3
:
257 case wxDateTime::GMT4
:
258 case wxDateTime::GMT5
:
259 case wxDateTime::GMT6
:
260 case wxDateTime::GMT7
:
261 case wxDateTime::GMT8
:
262 case wxDateTime::GMT9
:
263 case wxDateTime::GMT10
:
264 case wxDateTime::GMT11
:
265 case wxDateTime::GMT12
:
266 m_offset
= 60*(tz
- wxDateTime::GMT0
);
269 case wxDateTime::A_CST
:
270 // Central Standard Time in use in Australia = UTC + 9.5
271 m_offset
= 9*60 + 30;
275 wxFAIL_MSG( _T("unknown time zone") );
279 // ----------------------------------------------------------------------------
281 // ----------------------------------------------------------------------------
284 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
)
286 if ( year
== Inv_Year
)
287 year
= GetCurrentYear();
289 if ( cal
== Gregorian
)
291 // in Gregorian calendar leap years are those divisible by 4 except
292 // those divisible by 100 unless they're also divisible by 400
293 // (in some countries, like Russia and Greece, additional corrections
294 // exist, but they won't manifest themselves until 2700)
295 return (year
% 4 == 0) && ((year
% 100 != 0) || (year
% 400 == 0));
297 else if ( cal
== Julian
)
299 // in Julian calendar the rule is simpler
300 return year
% 4 == 0;
304 wxFAIL_MSG(_T("unknown calendar"));
311 int wxDateTime::GetCentury(int year
)
313 return year
> 0 ? year
/ 100 : year
/ 100 - 1;
317 void wxDateTime::SetCountry(wxDateTime::Country country
)
319 ms_country
= country
;
323 int wxDateTime::ConvertYearToBC(int year
)
326 return year
> 0 ? year
: year
- 1;
330 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
)
335 return Now().GetYear();
338 wxFAIL_MSG(_T("TODO"));
342 wxFAIL_MSG(_T("unsupported calendar"));
350 wxDateTime::Month
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
)
355 return Now().GetMonth();
359 wxFAIL_MSG(_T("TODO"));
363 wxFAIL_MSG(_T("unsupported calendar"));
371 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(int year
, Calendar cal
)
373 if ( year
== Inv_Year
)
375 // take the current year if none given
376 year
= GetCurrentYear();
383 return IsLeapYear(year
) ? 366 : 365;
387 wxFAIL_MSG(_T("unsupported calendar"));
395 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(wxDateTime::Month month
,
397 wxDateTime::Calendar cal
)
399 wxCHECK_MSG( month
< MONTHS_IN_YEAR
, 0, _T("invalid month") );
401 if ( cal
== Gregorian
|| cal
== Julian
)
403 if ( year
== Inv_Year
)
405 // take the current year if none given
406 year
= GetCurrentYear();
409 return GetNumOfDaysInMonth(year
, month
);
413 wxFAIL_MSG(_T("unsupported calendar"));
420 wxString
wxDateTime::GetMonthName(wxDateTime::Month month
, bool abbr
)
422 wxCHECK_MSG( month
!= Inv_Month
, _T(""), _T("invalid month") );
424 tm tm
= { 0, 0, 0, 1, month
, 76 }; // any year will do
426 return CallStrftime(abbr
? _T("%b") : _T("%B"), &tm
);
430 wxString
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
, bool abbr
)
432 wxCHECK_MSG( wday
!= Inv_WeekDay
, _T(""), _T("invalid weekday") );
434 // take some arbitrary Sunday
435 tm tm
= { 0, 0, 0, 28, Nov
, 99 };
437 // and offset it by the number of days needed to get
440 return CallStrftime(abbr
? _T("%a") : _T("%A"), &tm
);
443 // ----------------------------------------------------------------------------
444 // constructors and assignment operators
445 // ----------------------------------------------------------------------------
447 wxDateTime
& wxDateTime::Set(const struct tm
& tm1
)
449 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
452 time_t timet
= mktime(&tm2
);
453 if ( timet
== (time_t)(-1) )
455 wxFAIL_MSG(_T("Invalid time"));
457 return ms_InvDateTime
;
465 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
,
468 wxDateTime_t millisec
)
470 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
472 // we allow seconds to be 61 to account for the leap seconds, even if we
473 // don't use them really
474 wxCHECK_MSG( hour
< 24 && second
< 62 && minute
< 60 && millisec
< 1000,
476 _T("Invalid time in wxDateTime::Set()") );
478 // get the current date from system
479 time_t timet
= GetTimeNow();
480 struct tm
*tm
= localtime(&timet
);
489 // and finally adjust milliseconds
490 return SetMillisecond(millisec
);
493 wxDateTime
& wxDateTime::Set(wxDateTime_t day
,
499 wxDateTime_t millisec
)
501 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
503 wxCHECK_MSG( hour
< 24 && second
< 62 && minute
< 60 && millisec
< 1000,
505 _T("Invalid time in wxDateTime::Set()") );
507 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
509 wxCHECK_MSG( day
<= GetNumberOfDays(month
, year
), ms_InvDateTime
,
510 _T("Invalid date in wxDateTime::Set()") );
512 // the range of time_t type (inclusive)
513 static const int yearMinInRange
= 1970;
514 static const int yearMaxInRange
= 2037;
516 // test only the year instead of testing for the exact end of the Unix
517 // time_t range - it doesn't bring anything to do more precise checks
518 if ( year
>= yearMinInRange
&& year
<= yearMaxInRange
)
520 // use the standard library version if the date is in range - this is
521 // probably more efficient than our code
523 tm
.tm_year
= year
- 1900;
532 // and finally adjust milliseconds
533 return SetMillisecond(millisec
);
537 // do time calculations ourselves: we want to calculate the number of
538 // milliseconds between the given date and the epoch
539 wxFAIL_MSG(_T("TODO"));
545 // ----------------------------------------------------------------------------
546 // time_t <-> broken down time conversions
547 // ----------------------------------------------------------------------------
549 wxDateTime::Tm
wxDateTime::GetTm() const
551 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
553 time_t time
= GetTicks();
554 if ( time
!= (time_t)-1 )
556 // use C RTL functions
557 tm
*tm
= localtime(&time
);
559 // should never happen
560 wxCHECK_MSG( tm
, Tm(), _T("localtime() failed") );
566 wxFAIL_MSG(_T("TODO"));
572 wxDateTime
& wxDateTime::SetYear(int year
)
574 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
583 wxDateTime
& wxDateTime::SetMonth(Month month
)
585 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
594 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
)
596 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
605 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
)
607 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
616 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
)
618 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
627 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
)
629 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
638 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
)
640 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
642 // we don't need to use GetTm() for this one
643 m_time
-= m_time
% 1000l;
644 m_time
+= millisecond
;
649 // ----------------------------------------------------------------------------
650 // wxDateTime arithmetics
651 // ----------------------------------------------------------------------------
653 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
)
657 tm
.year
+= diff
.GetYears();
658 tm
.AddMonths(diff
.GetMonths());
659 tm
.AddDays(diff
.GetTotalDays());
666 // ----------------------------------------------------------------------------
667 // Weekday and monthday stuff
668 // ----------------------------------------------------------------------------
670 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
,
673 // take the current month/year if none specified
674 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
676 return Set(GetNumOfDaysInMonth(year
, month
), month
, year
);
679 bool wxDateTime::SetToWeekDay(WeekDay weekday
,
684 wxCHECK_MSG( weekday
!= Inv_WeekDay
, FALSE
, _T("invalid weekday") );
686 // we don't check explicitly that -5 <= n <= 5 because we will return FALSE
687 // anyhow in such case - but may be should still give an assert for it?
689 // take the current month/year if none specified
690 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
694 // TODO this probably could be optimised somehow...
698 // get the first day of the month
699 dt
.Set(1, month
, year
);
702 WeekDay wdayFirst
= dt
.GetWeekDay();
704 // go to the first weekday of the month
705 int diff
= weekday
- wdayFirst
;
709 // add advance n-1 weeks more
712 dt
-= wxDateSpan::Days(diff
);
716 // get the last day of the month
717 dt
.SetToLastMonthDay(month
, year
);
720 WeekDay wdayLast
= dt
.GetWeekDay();
722 // go to the last weekday of the month
723 int diff
= wdayLast
- weekday
;
727 // and rewind n-1 weeks from there
730 dt
-= wxDateSpan::Days(diff
);
733 // check that it is still in the same month
734 if ( dt
.GetMonth() == month
)
742 // no such day in this month
747 // ----------------------------------------------------------------------------
749 // ----------------------------------------------------------------------------
751 wxDateTime
& wxDateTime::MakeUTC()
753 return Add(wxTimeSpan::Seconds(GetTimeZone()));
756 wxDateTime
& wxDateTime::MakeTimezone(const TimeZone
& tz
)
758 int minDiff
= GetTimeZone() / SECONDS_IN_MINUTE
+ tz
.GetOffset();
759 return Add(wxTimeSpan::Minutes(minDiff
));
762 wxDateTime
& wxDateTime::MakeLocalTime(const TimeZone
& tz
)
764 int minDiff
= GetTimeZone() / SECONDS_IN_MINUTE
+ tz
.GetOffset();
765 return Substract(wxTimeSpan::Minutes(minDiff
));
768 // ----------------------------------------------------------------------------
769 // wxDateTime to/from text representations
770 // ----------------------------------------------------------------------------
772 wxString
wxDateTime::Format(const wxChar
*format
) const
774 time_t time
= GetTicks();
775 if ( time
!= (time_t)-1 )
778 tm
*tm
= localtime(&time
);
780 // should never happen
781 wxCHECK_MSG( tm
, _T(""), _T("localtime() failed") );
783 return CallStrftime(format
, tm
);
787 wxFAIL_MSG(_T("TODO"));
793 // ============================================================================
795 // ============================================================================
797 wxString
wxTimeSpan::Format(const wxChar
*format
) const
799 wxFAIL_MSG( _T("TODO") );