]> git.saurik.com Git - wxWidgets.git/blob - src/common/datetime.cpp
46f65d001fb8f8caacea6629fb8726e14b13ce6b
[wxWidgets.git] / src / common / datetime.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: wx/datetime.h
3 // Purpose: implementation of time/date related classes
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 11.05.99
7 // RCS-ID: $Id$
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "datetime.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef WX_PRECOMP
32 #include "wx/string.h"
33 #include "wx/intl.h"
34 #include "wx/log.h"
35 #endif // WX_PRECOMP
36
37 #define wxDEFINE_TIME_CONSTANTS
38
39 #include "wx/datetime.h"
40
41 // ----------------------------------------------------------------------------
42 // constants
43 // ----------------------------------------------------------------------------
44
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] =
48 {
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 }
51 };
52
53 // ----------------------------------------------------------------------------
54 // private functions
55 // ----------------------------------------------------------------------------
56
57 // this function is a wrapper around strftime(3)
58 static wxString CallStrftime(const wxChar *format, const tm* tm)
59 {
60 wxChar buf[1024];
61 if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) )
62 {
63 // is ti really possible that 1024 is too short?
64 wxFAIL_MSG(_T("strftime() failed"));
65 }
66
67 return wxString(buf);
68 }
69
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)
73 {
74 struct tm *tmNow = NULL;
75
76 if ( *year == wxDateTime::Inv_Year )
77 {
78 tmNow = wxDateTime::GetTmNow();
79
80 *year = 1900 + tmNow->tm_year;
81 }
82
83 if ( *month == wxDateTime::Inv_Month )
84 {
85 if ( !tmNow )
86 tmNow = wxDateTime::GetTmNow();
87
88 *month = (wxDateTime::Month)tmNow->tm_mon;
89 }
90 }
91
92 // ============================================================================
93 // implementation of wxDateTime
94 // ============================================================================
95
96 // ----------------------------------------------------------------------------
97 // static data
98 // ----------------------------------------------------------------------------
99
100 wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown;
101 wxDateTime wxDateTime::ms_InvDateTime;
102
103 // ----------------------------------------------------------------------------
104 // struct Tm
105 // ----------------------------------------------------------------------------
106
107 wxDateTime::Tm::Tm()
108 {
109 year = (wxDateTime_t)wxDateTime::Inv_Year;
110 mon = wxDateTime::Inv_Month;
111 mday = 0;
112 hour = min = sec = 0;
113 wday = wxDateTime::Inv_WeekDay;
114 }
115
116 wxDateTime::Tm::Tm(const struct tm& tm)
117 {
118 sec = tm.tm_sec;
119 min = tm.tm_min;
120 hour = tm.tm_hour;
121 mday = tm.tm_mday;
122 mon = tm.tm_mon;
123 year = 1900 + tm.tm_year;
124 wday = tm.tm_wday;
125 yday = tm.tm_yday;
126 }
127
128 bool wxDateTime::Tm::IsValid() const
129 {
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);
134 }
135
136 void wxDateTime::Tm::ComputeWeekDay()
137 {
138 wxFAIL_MSG(_T("TODO"));
139 }
140
141 // ----------------------------------------------------------------------------
142 // static functions
143 // ----------------------------------------------------------------------------
144
145 /* static */
146 bool wxDateTime::IsLeapYear(int year, wxDateTime::Calendar cal)
147 {
148 if ( year == Inv_Year )
149 year = GetCurrentYear();
150
151 if ( cal == Gregorian )
152 {
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));
158 }
159 else if ( cal == Julian )
160 {
161 // in Julian calendar the rule is simpler
162 return year % 4 == 0;
163 }
164 else
165 {
166 wxFAIL_MSG(_T("unknown calendar"));
167
168 return FALSE;
169 }
170 }
171
172 /* static */
173 void wxDateTime::SetCountry(wxDateTime::Country country)
174 {
175 ms_country = country;
176 }
177
178 /* static */
179 int wxDateTime::ConvertYearToBC(int year)
180 {
181 // year 0 is BC 1
182 return year > 0 ? year : year - 1;
183 }
184
185 /* static */
186 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal)
187 {
188 switch ( cal )
189 {
190 case Gregorian:
191 return Now().GetYear();
192
193 case Julian:
194 wxFAIL_MSG(_T("TODO"));
195 break;
196
197 default:
198 wxFAIL_MSG(_T("unsupported calendar"));
199 break;
200 }
201
202 return Inv_Year;
203 }
204
205 /* static */
206 wxDateTime::Month wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal)
207 {
208 switch ( cal )
209 {
210 case Gregorian:
211 return Now().GetMonth();
212 break;
213
214 case Julian:
215 wxFAIL_MSG(_T("TODO"));
216 break;
217
218 default:
219 wxFAIL_MSG(_T("unsupported calendar"));
220 break;
221 }
222
223 return Inv_Month;
224 }
225
226 /* static */
227 wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(int year, Calendar cal)
228 {
229 if ( year == Inv_Year )
230 {
231 // take the current year if none given
232 year = GetCurrentYear();
233 }
234
235 switch ( cal )
236 {
237 case Gregorian:
238 case Julian:
239 return IsLeapYear(year) ? 366 : 365;
240 break;
241
242 default:
243 wxFAIL_MSG(_T("unsupported calendar"));
244 break;
245 }
246
247 return 0;
248 }
249
250 /* static */
251 wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(wxDateTime::Month month,
252 int year,
253 wxDateTime::Calendar cal)
254 {
255 wxCHECK_MSG( month < 12, 0, _T("invalid month") );
256
257 if ( cal == Gregorian || cal == Julian )
258 {
259 if ( year == Inv_Year )
260 {
261 // take the current year if none given
262 year = GetCurrentYear();
263 }
264
265 return gs_daysInMonth[IsLeapYear(year)][month];
266 }
267 else
268 {
269 wxFAIL_MSG(_T("unsupported calendar"));
270
271 return 0;
272 }
273 }
274
275 /* static */
276 wxString wxDateTime::GetMonthName(wxDateTime::Month month, bool abbr)
277 {
278 wxCHECK_MSG( month != Inv_Month, _T(""), _T("invalid month") );
279
280 tm tm = { 0, 0, 0, 1, month, 76 }; // any year will do
281
282 return CallStrftime(abbr ? _T("%b") : _T("%B"), &tm);
283 }
284
285 /* static */
286 wxString wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday, bool abbr)
287 {
288 wxCHECK_MSG( wday != Inv_WeekDay, _T(""), _T("invalid weekday") );
289
290 // take some arbitrary Sunday
291 tm tm = { 0, 0, 0, 28, Nov, 99 };
292
293 // and offset it by the number of days needed to get
294 tm.tm_mday += wday;
295
296 return CallStrftime(abbr ? _T("%a") : _T("%A"), &tm);
297 }
298
299 // ----------------------------------------------------------------------------
300 // constructors and assignment operators
301 // ----------------------------------------------------------------------------
302
303 wxDateTime& wxDateTime::Set(const struct tm& tm1)
304 {
305 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
306
307 tm tm2(tm1);
308 time_t timet = mktime(&tm2);
309 if ( timet == (time_t)(-1) )
310 {
311 wxFAIL_MSG(_T("Invalid time"));
312
313 return ms_InvDateTime;
314 }
315 else
316 {
317 return Set(timet);
318 }
319 }
320
321 wxDateTime& wxDateTime::Set(wxDateTime_t hour,
322 wxDateTime_t minute,
323 wxDateTime_t second,
324 wxDateTime_t millisec)
325 {
326 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
327
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,
331 ms_InvDateTime,
332 _T("Invalid time in wxDateTime::Set()") );
333
334 // get the current date from system
335 time_t timet = GetTimeNow();
336 struct tm *tm = localtime(&timet);
337
338 // adjust the time
339 tm->tm_hour = hour;
340 tm->tm_min = minute;
341 tm->tm_sec = second;
342
343 (void)Set(*tm);
344
345 // and finally adjust milliseconds
346 return SetMillisecond(millisec);
347 }
348
349 wxDateTime& wxDateTime::Set(wxDateTime_t day,
350 Month month,
351 int year,
352 wxDateTime_t hour,
353 wxDateTime_t minute,
354 wxDateTime_t second,
355 wxDateTime_t millisec)
356 {
357 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
358
359 wxCHECK_MSG( hour < 24 && second < 62 && minute < 60 && millisec < 1000,
360 ms_InvDateTime,
361 _T("Invalid time in wxDateTime::Set()") );
362
363 ReplaceDefaultYearMonthWithCurrent(&year, &month);
364
365 wxCHECK_MSG( day <= GetNumberOfDays(month, year), ms_InvDateTime,
366 _T("Invalid date in wxDateTime::Set()") );
367
368 // the range of time_t type (inclusive)
369 static const int yearMinInRange = 1970;
370 static const int yearMaxInRange = 2037;
371
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 )
375 {
376 // use the standard library version if the date is in range - this is
377 // probably more efficient than our code
378 struct tm tm;
379 tm.tm_year = year - 1900;
380 tm.tm_mon = month;
381 tm.tm_mday = day;
382 tm.tm_hour = hour;
383 tm.tm_min = minute;
384 tm.tm_sec = second;
385
386 (void)Set(tm);
387
388 // and finally adjust milliseconds
389 return SetMillisecond(millisec);
390 }
391 else
392 {
393 // do time calculations ourselves: we want to calculate the number of
394 // milliseconds between the given date and the epoch (necessarily
395 // negative)
396 wxFAIL_MSG(_T("TODO"));
397 }
398
399 return *this;
400 }
401
402 // ----------------------------------------------------------------------------
403 // time_t <-> broken down time conversions
404 // ----------------------------------------------------------------------------
405
406 wxDateTime::Tm wxDateTime::GetTm() const
407 {
408 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
409
410 time_t time = GetTicks();
411 if ( time != (time_t)-1 )
412 {
413 // use C RTL functions
414 tm *tm = localtime(&time);
415
416 // should never happen
417 wxCHECK_MSG( tm, Tm(), _T("localtime() failed") );
418
419 return Tm(*tm);
420 }
421 else
422 {
423 wxFAIL_MSG(_T("TODO"));
424
425 return Tm();
426 }
427 }
428
429 wxDateTime& wxDateTime::SetYear(int year)
430 {
431 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
432
433 Tm tm(GetTm());
434 tm.year = year;
435 Set(tm);
436
437 return *this;
438 }
439
440 wxDateTime& wxDateTime::SetMonth(Month month)
441 {
442 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
443
444 Tm tm(GetTm());
445 tm.mon = month;
446 Set(tm);
447
448 return *this;
449 }
450
451 wxDateTime& wxDateTime::SetDay(wxDateTime_t mday)
452 {
453 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
454
455 Tm tm(GetTm());
456 tm.mday = mday;
457 Set(tm);
458
459 return *this;
460 }
461
462 wxDateTime& wxDateTime::SetHour(wxDateTime_t hour)
463 {
464 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
465
466 Tm tm(GetTm());
467 tm.hour = hour;
468 Set(tm);
469
470 return *this;
471 }
472
473 wxDateTime& wxDateTime::SetMinute(wxDateTime_t min)
474 {
475 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
476
477 Tm tm(GetTm());
478 tm.min = min;
479 Set(tm);
480
481 return *this;
482 }
483
484 wxDateTime& wxDateTime::SetSecond(wxDateTime_t sec)
485 {
486 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
487
488 Tm tm(GetTm());
489 tm.sec = sec;
490 Set(tm);
491
492 return *this;
493 }
494
495 wxDateTime& wxDateTime::SetMillisecond(wxDateTime_t millisecond)
496 {
497 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
498
499 // we don't need to use GetTm() for this one
500 m_time -= m_time % 1000l;
501 m_time += millisecond;
502
503 return *this;
504 }
505
506 // ----------------------------------------------------------------------------
507 // wxDateTime arithmetics
508 // ----------------------------------------------------------------------------
509
510 wxDateTime& wxDateTime::Add(const wxDateSpan& diff)
511 {
512 Tm tm(GetTm());
513
514 tm.year += diff.GetYears();
515 tm.mon += diff.GetMonths();
516 tm.mday += diff.GetTotalDays();
517
518 Set(tm);
519
520 return *this;
521 }
522
523 // ----------------------------------------------------------------------------
524 // Weekday and monthday stuff
525 // ----------------------------------------------------------------------------
526
527 wxDateTime& wxDateTime::SetToLastMonthDay(Month month,
528 int year)
529 {
530 // take the current month/year if none specified
531 ReplaceDefaultYearMonthWithCurrent(&year, &month);
532
533 return Set(gs_daysInMonth[IsLeapYear(year)][month], month, year);
534 }
535
536 bool wxDateTime::SetToWeekDay(WeekDay weekday,
537 int n,
538 Month month,
539 int year)
540 {
541 wxCHECK_MSG( weekday != Inv_WeekDay, FALSE, _T("invalid weekday") );
542
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?
545
546 // take the current month/year if none specified
547 ReplaceDefaultYearMonthWithCurrent(&year, &month);
548
549 wxDateTime dt;
550
551 // TODO this probably could be optimised somehow...
552
553 if ( n > 0 )
554 {
555 // get the first day of the month
556 dt.Set(1, month, year);
557
558 // get its wday
559 WeekDay wdayFirst = dt.GetWeekDay();
560
561 // go to the first weekday of the month
562 int diff = weekday - wdayFirst;
563 if ( diff < 0 )
564 diff += 7;
565
566 // add advance n-1 weeks more
567 diff += 7*(n - 1);
568
569 dt -= wxDateSpan::Days(diff);
570 }
571 else
572 {
573 // get the last day of the month
574 dt.SetToLastMonthDay(month, year);
575
576 // get its wday
577 WeekDay wdayLast = dt.GetWeekDay();
578
579 // go to the last weekday of the month
580 int diff = wdayLast - weekday;
581 if ( diff < 0 )
582 diff += 7;
583
584 // and rewind n-1 weeks from there
585 diff += 7*(n - 1);
586
587 dt -= wxDateSpan::Days(diff);
588 }
589
590 // check that it is still in the same month
591 if ( dt.GetMonth() == month )
592 {
593 *this = dt;
594
595 return TRUE;
596 }
597 else
598 {
599 // no such day in this month
600 return FALSE;
601 }
602 }
603
604 // ----------------------------------------------------------------------------
605 // wxDateTime to/from text representations
606 // ----------------------------------------------------------------------------
607
608 wxString wxDateTime::Format(const wxChar *format) const
609 {
610 time_t time = GetTicks();
611 if ( time != (time_t)-1 )
612 {
613 // use strftime()
614 tm *tm = localtime(&time);
615
616 // should never happen
617 wxCHECK_MSG( tm, _T(""), _T("localtime() failed") );
618
619 return CallStrftime(format, tm);
620 }
621 else
622 {
623 wxFAIL_MSG(_T("TODO"));
624
625 return _T("");
626 }
627 }