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 #define wxDEFINE_TIME_CONSTANTS
39 #include "wx/datetime.h"
41 // ----------------------------------------------------------------------------
43 // ----------------------------------------------------------------------------
45 // the number of days in month in Julian/Gregorian calendar: the first line is
46 // for normal years, the second one is for the leap ones
47 static wxDateTime::wxDateTime_t gs_daysInMonth
[2][12] =
49 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
50 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
53 // ----------------------------------------------------------------------------
55 // ----------------------------------------------------------------------------
57 // this function is a wrapper around strftime(3)
58 static wxString
CallStrftime(const wxChar
*format
, const tm
* tm
)
61 if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) )
63 // is ti really possible that 1024 is too short?
64 wxFAIL_MSG(_T("strftime() failed"));
70 // if year and/or month have invalid values, replace them with the current ones
71 static void ReplaceDefaultYearMonthWithCurrent(int *year
,
72 wxDateTime::Month
*month
)
74 struct tm
*tmNow
= NULL
;
76 if ( *year
== wxDateTime::Inv_Year
)
78 tmNow
= wxDateTime::GetTmNow();
80 *year
= 1900 + tmNow
->tm_year
;
83 if ( *month
== wxDateTime::Inv_Month
)
86 tmNow
= wxDateTime::GetTmNow();
88 *month
= (wxDateTime::Month
)tmNow
->tm_mon
;
92 // ============================================================================
93 // implementation of wxDateTime
94 // ============================================================================
96 // ----------------------------------------------------------------------------
98 // ----------------------------------------------------------------------------
100 wxDateTime::Country
wxDateTime::ms_country
= wxDateTime::Country_Unknown
;
101 wxDateTime
wxDateTime::ms_InvDateTime
;
103 // ----------------------------------------------------------------------------
105 // ----------------------------------------------------------------------------
109 year
= (wxDateTime_t
)wxDateTime::Inv_Year
;
110 mon
= wxDateTime::Inv_Month
;
112 hour
= min
= sec
= 0;
113 wday
= wxDateTime::Inv_WeekDay
;
116 wxDateTime::Tm::Tm(const struct tm
& tm
)
123 year
= 1900 + tm
.tm_year
;
128 bool wxDateTime::Tm::IsValid() const
130 // we allow for the leap seconds, although we don't use them (yet)
131 return (year
!= wxDateTime::Inv_Year
) && (mon
< 12) &&
132 (mday
< gs_daysInMonth
[IsLeapYear(year
)][mon
]) &&
133 (hour
< 24) && (min
< 60) && (sec
< 62);
136 void wxDateTime::Tm::ComputeWeekDay()
138 wxFAIL_MSG(_T("TODO"));
141 // ----------------------------------------------------------------------------
143 // ----------------------------------------------------------------------------
146 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
)
148 if ( year
== Inv_Year
)
149 year
= GetCurrentYear();
151 if ( cal
== Gregorian
)
153 // in Gregorian calendar leap years are those divisible by 4 except
154 // those divisible by 100 unless they're also divisible by 400
155 // (in some countries, like Russia and Greece, additional corrections
156 // exist, but they won't manifest themselves until 2700)
157 return (year
% 4 == 0) && ((year
% 100 != 0) || (year
% 400 == 0));
159 else if ( cal
== Julian
)
161 // in Julian calendar the rule is simpler
162 return year
% 4 == 0;
166 wxFAIL_MSG(_T("unknown calendar"));
173 void wxDateTime::SetCountry(wxDateTime::Country country
)
175 ms_country
= country
;
179 int wxDateTime::ConvertYearToBC(int year
)
182 return year
> 0 ? year
: year
- 1;
186 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
)
191 return Now().GetYear();
194 wxFAIL_MSG(_T("TODO"));
198 wxFAIL_MSG(_T("unsupported calendar"));
206 wxDateTime::Month
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
)
211 return Now().GetMonth();
215 wxFAIL_MSG(_T("TODO"));
219 wxFAIL_MSG(_T("unsupported calendar"));
227 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(int year
, Calendar cal
)
229 if ( year
== Inv_Year
)
231 // take the current year if none given
232 year
= GetCurrentYear();
239 return IsLeapYear(year
) ? 366 : 365;
243 wxFAIL_MSG(_T("unsupported calendar"));
251 wxDateTime::wxDateTime_t
wxDateTime::GetNumberOfDays(wxDateTime::Month month
,
253 wxDateTime::Calendar cal
)
255 wxCHECK_MSG( month
< 12, 0, _T("invalid month") );
257 if ( cal
== Gregorian
|| cal
== Julian
)
259 if ( year
== Inv_Year
)
261 // take the current year if none given
262 year
= GetCurrentYear();
265 return gs_daysInMonth
[IsLeapYear(year
)][month
];
269 wxFAIL_MSG(_T("unsupported calendar"));
276 wxString
wxDateTime::GetMonthName(wxDateTime::Month month
, bool abbr
)
278 wxCHECK_MSG( month
!= Inv_Month
, _T(""), _T("invalid month") );
280 tm tm
= { 0, 0, 0, 1, month
, 76 }; // any year will do
282 return CallStrftime(abbr
? _T("%b") : _T("%B"), &tm
);
286 wxString
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
, bool abbr
)
288 wxCHECK_MSG( wday
!= Inv_WeekDay
, _T(""), _T("invalid weekday") );
290 // take some arbitrary Sunday
291 tm tm
= { 0, 0, 0, 28, Nov
, 99 };
293 // and offset it by the number of days needed to get
296 return CallStrftime(abbr
? _T("%a") : _T("%A"), &tm
);
299 // ----------------------------------------------------------------------------
300 // constructors and assignment operators
301 // ----------------------------------------------------------------------------
303 wxDateTime
& wxDateTime::Set(const struct tm
& tm1
)
305 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
308 time_t timet
= mktime(&tm2
);
309 if ( timet
== (time_t)(-1) )
311 wxFAIL_MSG(_T("Invalid time"));
313 return ms_InvDateTime
;
321 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
,
324 wxDateTime_t millisec
)
326 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
328 // we allow seconds to be 61 to account for the leap seconds, even if we
329 // don't use them really
330 wxCHECK_MSG( hour
< 24 && second
< 62 && minute
< 60 && millisec
< 1000,
332 _T("Invalid time in wxDateTime::Set()") );
334 // get the current date from system
335 time_t timet
= GetTimeNow();
336 struct tm
*tm
= localtime(&timet
);
345 // and finally adjust milliseconds
346 return SetMillisecond(millisec
);
349 wxDateTime
& wxDateTime::Set(wxDateTime_t day
,
355 wxDateTime_t millisec
)
357 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
359 wxCHECK_MSG( hour
< 24 && second
< 62 && minute
< 60 && millisec
< 1000,
361 _T("Invalid time in wxDateTime::Set()") );
363 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
365 wxCHECK_MSG( day
<= GetNumberOfDays(month
, year
), ms_InvDateTime
,
366 _T("Invalid date in wxDateTime::Set()") );
368 // the range of time_t type (inclusive)
369 static const int yearMinInRange
= 1970;
370 static const int yearMaxInRange
= 2037;
372 // test only the year instead of testing for the exact end of the Unix
373 // time_t range - it doesn't bring anything to do more precise checks
374 if ( year
>= yearMinInRange
&& year
<= yearMaxInRange
)
376 // use the standard library version if the date is in range - this is
377 // probably more efficient than our code
379 tm
.tm_year
= year
- 1900;
388 // and finally adjust milliseconds
389 return SetMillisecond(millisec
);
393 // do time calculations ourselves: we want to calculate the number of
394 // milliseconds between the given date and the epoch (necessarily
396 wxFAIL_MSG(_T("TODO"));
402 // ----------------------------------------------------------------------------
403 // time_t <-> broken down time conversions
404 // ----------------------------------------------------------------------------
406 wxDateTime::Tm
wxDateTime::GetTm() const
408 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
410 time_t time
= GetTicks();
411 if ( time
!= (time_t)-1 )
413 // use C RTL functions
414 tm
*tm
= localtime(&time
);
416 // should never happen
417 wxCHECK_MSG( tm
, Tm(), _T("localtime() failed") );
423 wxFAIL_MSG(_T("TODO"));
429 wxDateTime
& wxDateTime::SetYear(int year
)
431 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
440 wxDateTime
& wxDateTime::SetMonth(Month month
)
442 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
451 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
)
453 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
462 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
)
464 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
473 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
)
475 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
484 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
)
486 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
495 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
)
497 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
499 // we don't need to use GetTm() for this one
500 m_time
-= m_time
% 1000l;
501 m_time
+= millisecond
;
506 // ----------------------------------------------------------------------------
507 // wxDateTime arithmetics
508 // ----------------------------------------------------------------------------
510 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
)
514 tm
.year
+= diff
.GetYears();
515 tm
.mon
+= diff
.GetMonths();
516 tm
.mday
+= diff
.GetTotalDays();
523 // ----------------------------------------------------------------------------
524 // Weekday and monthday stuff
525 // ----------------------------------------------------------------------------
527 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
,
530 // take the current month/year if none specified
531 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
533 return Set(gs_daysInMonth
[IsLeapYear(year
)][month
], month
, year
);
536 bool wxDateTime::SetToWeekDay(WeekDay weekday
,
541 wxCHECK_MSG( weekday
!= Inv_WeekDay
, FALSE
, _T("invalid weekday") );
543 // we don't check explicitly that -5 <= n <= 5 because we will return FALSE
544 // anyhow in such case - but may be should still give an assert for it?
546 // take the current month/year if none specified
547 ReplaceDefaultYearMonthWithCurrent(&year
, &month
);
551 // TODO this probably could be optimised somehow...
555 // get the first day of the month
556 dt
.Set(1, month
, year
);
559 WeekDay wdayFirst
= dt
.GetWeekDay();
561 // go to the first weekday of the month
562 int diff
= weekday
- wdayFirst
;
566 // add advance n-1 weeks more
569 dt
-= wxDateSpan::Days(diff
);
573 // get the last day of the month
574 dt
.SetToLastMonthDay(month
, year
);
577 WeekDay wdayLast
= dt
.GetWeekDay();
579 // go to the last weekday of the month
580 int diff
= wdayLast
- weekday
;
584 // and rewind n-1 weeks from there
587 dt
-= wxDateSpan::Days(diff
);
590 // check that it is still in the same month
591 if ( dt
.GetMonth() == month
)
599 // no such day in this month
604 // ----------------------------------------------------------------------------
605 // wxDateTime to/from text representations
606 // ----------------------------------------------------------------------------
608 wxString
wxDateTime::Format(const wxChar
*format
) const
610 time_t time
= GetTicks();
611 if ( time
!= (time_t)-1 )
614 tm
*tm
= localtime(&time
);
616 // should never happen
617 wxCHECK_MSG( tm
, _T(""), _T("localtime() failed") );
619 return CallStrftime(format
, tm
);
623 wxFAIL_MSG(_T("TODO"));