]> git.saurik.com Git - wxWidgets.git/blame - src/common/datetime.cpp
mingw32 compilation fixes
[wxWidgets.git] / src / common / datetime.cpp
CommitLineData
0979c962
VZ
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
fcc3d7cb
VZ
37#include "wx/thread.h"
38
b76b015e
VZ
39#define wxDEFINE_TIME_CONSTANTS
40
0979c962
VZ
41#include "wx/datetime.h"
42
b76b015e
VZ
43// ----------------------------------------------------------------------------
44// constants
45// ----------------------------------------------------------------------------
46
e6ec579c 47// some trivial ones
fcc3d7cb
VZ
48static const int MONTHS_IN_YEAR = 12;
49
50static const int SECONDS_IN_MINUTE = 60;
51
e6ec579c
VZ
52static const long SECONDS_PER_DAY = 86400l;
53
54static const long MILLISECONDS_PER_DAY = 86400000l;
55
56// this is the integral part of JDN of the midnight of Jan 1, 1970
57// (i.e. JDN(Jan 1, 1970) = 2440587.5)
58static const int EPOCH_JDN = 2440587;
b76b015e 59
fcc3d7cb
VZ
60// ----------------------------------------------------------------------------
61// globals
62// ----------------------------------------------------------------------------
63
64// a critical section is needed to protect GetTimeZone() static
65// variable in MT case
66#ifdef wxUSE_THREADS
67 wxCriticalSection gs_critsectTimezone;
68#endif // wxUSE_THREADS
69
b76b015e
VZ
70// ----------------------------------------------------------------------------
71// private functions
72// ----------------------------------------------------------------------------
73
fcc3d7cb
VZ
74// get the number of days in the given month of the given year
75static inline
76wxDateTime::wxDateTime_t GetNumOfDaysInMonth(int year, wxDateTime::Month month)
77{
e6ec579c
VZ
78 // the number of days in month in Julian/Gregorian calendar: the first line
79 // is for normal years, the second one is for the leap ones
80 static wxDateTime::wxDateTime_t daysInMonth[2][MONTHS_IN_YEAR] =
81 {
82 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
83 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
84 };
85
86 return daysInMonth[wxDateTime::IsLeapYear(year)][month];
fcc3d7cb
VZ
87}
88
89// ensure that the timezone variable is set by calling localtime
90static int GetTimeZone()
91{
92 // set to TRUE when the timezone is set
93 static bool s_timezoneSet = FALSE;
94
95 wxCRIT_SECT_LOCKER(lock, gs_critsectTimezone);
96
97 if ( !s_timezoneSet )
98 {
e6ec579c
VZ
99 // just call localtime() instead of figurin out whether this system
100 // supports tzset(), _tzset() or something else
101 time_t t;
102 (void)localtime(&t);
fcc3d7cb
VZ
103
104 s_timezoneSet = TRUE;
105 }
106
107 return (int)timezone;
108}
109
e6ec579c
VZ
110// return the integral part of the JDN for the midnight of the given date (to
111// get the real JDN you need to add 0.5, this is, in fact, JDN of the noon of
112// the previous day)
113static long GetTruncatedJDN(wxDateTime::wxDateTime_t day,
114 wxDateTime::Month mon,
115 int year)
116{
117 // CREDIT: the algorithm was taken from Peter Baum's home page
118
119 // the algorithm assumes Jan == 1
120 int month = mon + 1;
121
122 // we want the leap day (Feb 29) be at the end of the year, so we count
123 // March as the first month
124 if ( month < wxDateTime::Mar + 1 )
125 {
126 month += MONTHS_IN_YEAR;
127 year--;
128 }
129
130 // this table contains the number of the days before the 1st of the each
131 // month (in a non leap year) with the third value corresponding to March
132 // (and the last one to February)
133 static const int monthOffsets[14] =
134 {
135 0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306
136 };
137
138 // and now add contributions of all terms together to get the result (you'd
139 // better see the Web page for the description if you want to understand
140 // why it works (if it does :-))
141 return day +
142 // linear approximation for months
143 monthOffsets[month - (wxDateTime::Mar + 1)] +
144 // the year contribution
145 365*year + year/4 - year/100 + year/400 +
146 // 1721119.5 is the JDN of the midnight of Mar 1, year 0
147 1721118;
148}
149
2f02cb89 150// this function is a wrapper around strftime(3)
b76b015e
VZ
151static wxString CallStrftime(const wxChar *format, const tm* tm)
152{
153 wxChar buf[1024];
154 if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) )
155 {
156 // is ti really possible that 1024 is too short?
157 wxFAIL_MSG(_T("strftime() failed"));
158 }
159
160 return wxString(buf);
161}
162
2f02cb89
VZ
163// if year and/or month have invalid values, replace them with the current ones
164static void ReplaceDefaultYearMonthWithCurrent(int *year,
165 wxDateTime::Month *month)
166{
167 struct tm *tmNow = NULL;
168
169 if ( *year == wxDateTime::Inv_Year )
170 {
171 tmNow = wxDateTime::GetTmNow();
172
173 *year = 1900 + tmNow->tm_year;
174 }
175
176 if ( *month == wxDateTime::Inv_Month )
177 {
178 if ( !tmNow )
179 tmNow = wxDateTime::GetTmNow();
180
181 *month = (wxDateTime::Month)tmNow->tm_mon;
182 }
183}
184
0979c962
VZ
185// ============================================================================
186// implementation of wxDateTime
187// ============================================================================
188
189// ----------------------------------------------------------------------------
190// static data
191// ----------------------------------------------------------------------------
192
b76b015e 193wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown;
0979c962
VZ
194wxDateTime wxDateTime::ms_InvDateTime;
195
b76b015e
VZ
196// ----------------------------------------------------------------------------
197// struct Tm
198// ----------------------------------------------------------------------------
199
200wxDateTime::Tm::Tm()
201{
202 year = (wxDateTime_t)wxDateTime::Inv_Year;
203 mon = wxDateTime::Inv_Month;
204 mday = 0;
e6ec579c 205 hour = min = sec = msec = 0;
b76b015e
VZ
206 wday = wxDateTime::Inv_WeekDay;
207}
208
209wxDateTime::Tm::Tm(const struct tm& tm)
210{
e6ec579c 211 msec = 0;
b76b015e
VZ
212 sec = tm.tm_sec;
213 min = tm.tm_min;
214 hour = tm.tm_hour;
215 mday = tm.tm_mday;
fcc3d7cb 216 mon = (wxDateTime::Month)tm.tm_mon;
b76b015e
VZ
217 year = 1900 + tm.tm_year;
218 wday = tm.tm_wday;
219 yday = tm.tm_yday;
220}
221
222bool wxDateTime::Tm::IsValid() const
223{
224 // we allow for the leap seconds, although we don't use them (yet)
fcc3d7cb
VZ
225 return (year != wxDateTime::Inv_Year) && (mon != wxDateTime::Inv_Month) &&
226 (mday < GetNumOfDaysInMonth(year, mon)) &&
e6ec579c 227 (hour < 24) && (min < 60) && (sec < 62) && (msec < 1000);
b76b015e
VZ
228}
229
230void wxDateTime::Tm::ComputeWeekDay()
231{
232 wxFAIL_MSG(_T("TODO"));
233}
234
e6ec579c 235void wxDateTime::Tm::AddMonths(int monDiff)
fcc3d7cb
VZ
236{
237 // normalize the months field
238 while ( monDiff < -mon )
239 {
240 year--;
241
242 monDiff += MONTHS_IN_YEAR;
243 }
244
245 while ( monDiff + mon > MONTHS_IN_YEAR )
246 {
247 year++;
248 }
249
250 mon = (wxDateTime::Month)(mon + monDiff);
251
e6ec579c 252 wxASSERT_MSG( mon >= 0 && mon < MONTHS_IN_YEAR, _T("logic error") );
fcc3d7cb
VZ
253}
254
e6ec579c 255void wxDateTime::Tm::AddDays(int dayDiff)
fcc3d7cb
VZ
256{
257 // normalize the days field
258 mday += dayDiff;
259 while ( mday < 1 )
260 {
261 AddMonths(-1);
262
263 mday += GetNumOfDaysInMonth(year, mon);
264 }
265
266 while ( mday > GetNumOfDaysInMonth(year, mon) )
267 {
268 mday -= GetNumOfDaysInMonth(year, mon);
269
270 AddMonths(1);
271 }
272
273 wxASSERT_MSG( mday > 0 && mday <= GetNumOfDaysInMonth(year, mon),
274 _T("logic error") );
275}
276
277// ----------------------------------------------------------------------------
278// class TimeZone
279// ----------------------------------------------------------------------------
280
281wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz)
282{
283 switch ( tz )
284 {
285 case wxDateTime::Local:
286 // leave offset to be 0
287 break;
288
289 case wxDateTime::GMT_12:
290 case wxDateTime::GMT_11:
291 case wxDateTime::GMT_10:
292 case wxDateTime::GMT_9:
293 case wxDateTime::GMT_8:
294 case wxDateTime::GMT_7:
295 case wxDateTime::GMT_6:
296 case wxDateTime::GMT_5:
297 case wxDateTime::GMT_4:
298 case wxDateTime::GMT_3:
299 case wxDateTime::GMT_2:
300 case wxDateTime::GMT_1:
301 m_offset = -60*(wxDateTime::GMT0 - tz);
302 break;
303
304 case wxDateTime::GMT0:
305 case wxDateTime::GMT1:
306 case wxDateTime::GMT2:
307 case wxDateTime::GMT3:
308 case wxDateTime::GMT4:
309 case wxDateTime::GMT5:
310 case wxDateTime::GMT6:
311 case wxDateTime::GMT7:
312 case wxDateTime::GMT8:
313 case wxDateTime::GMT9:
314 case wxDateTime::GMT10:
315 case wxDateTime::GMT11:
316 case wxDateTime::GMT12:
317 m_offset = 60*(tz - wxDateTime::GMT0);
318 break;
319
320 case wxDateTime::A_CST:
321 // Central Standard Time in use in Australia = UTC + 9.5
322 m_offset = 9*60 + 30;
323 break;
324
325 default:
326 wxFAIL_MSG( _T("unknown time zone") );
327 }
328}
329
b76b015e
VZ
330// ----------------------------------------------------------------------------
331// static functions
332// ----------------------------------------------------------------------------
333
334/* static */
335bool wxDateTime::IsLeapYear(int year, wxDateTime::Calendar cal)
336{
2f02cb89
VZ
337 if ( year == Inv_Year )
338 year = GetCurrentYear();
339
b76b015e
VZ
340 if ( cal == Gregorian )
341 {
342 // in Gregorian calendar leap years are those divisible by 4 except
343 // those divisible by 100 unless they're also divisible by 400
344 // (in some countries, like Russia and Greece, additional corrections
345 // exist, but they won't manifest themselves until 2700)
346 return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
347 }
348 else if ( cal == Julian )
349 {
350 // in Julian calendar the rule is simpler
351 return year % 4 == 0;
352 }
353 else
354 {
355 wxFAIL_MSG(_T("unknown calendar"));
356
357 return FALSE;
358 }
359}
360
fcc3d7cb
VZ
361/* static */
362int wxDateTime::GetCentury(int year)
363{
364 return year > 0 ? year / 100 : year / 100 - 1;
365}
366
b76b015e
VZ
367/* static */
368void wxDateTime::SetCountry(wxDateTime::Country country)
369{
370 ms_country = country;
371}
372
373/* static */
374int wxDateTime::ConvertYearToBC(int year)
375{
376 // year 0 is BC 1
377 return year > 0 ? year : year - 1;
378}
379
380/* static */
381int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal)
382{
383 switch ( cal )
384 {
385 case Gregorian:
386 return Now().GetYear();
387
388 case Julian:
389 wxFAIL_MSG(_T("TODO"));
390 break;
391
392 default:
393 wxFAIL_MSG(_T("unsupported calendar"));
394 break;
395 }
396
397 return Inv_Year;
398}
399
400/* static */
401wxDateTime::Month wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal)
402{
403 switch ( cal )
404 {
405 case Gregorian:
406 return Now().GetMonth();
407 break;
408
409 case Julian:
410 wxFAIL_MSG(_T("TODO"));
411 break;
412
413 default:
414 wxFAIL_MSG(_T("unsupported calendar"));
415 break;
416 }
417
418 return Inv_Month;
419}
420
2f02cb89
VZ
421/* static */
422wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(int year, Calendar cal)
423{
424 if ( year == Inv_Year )
425 {
426 // take the current year if none given
427 year = GetCurrentYear();
428 }
429
430 switch ( cal )
431 {
432 case Gregorian:
433 case Julian:
434 return IsLeapYear(year) ? 366 : 365;
435 break;
436
437 default:
438 wxFAIL_MSG(_T("unsupported calendar"));
439 break;
440 }
441
442 return 0;
443}
444
b76b015e
VZ
445/* static */
446wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(wxDateTime::Month month,
447 int year,
448 wxDateTime::Calendar cal)
449{
fcc3d7cb 450 wxCHECK_MSG( month < MONTHS_IN_YEAR, 0, _T("invalid month") );
b76b015e
VZ
451
452 if ( cal == Gregorian || cal == Julian )
453 {
454 if ( year == Inv_Year )
455 {
456 // take the current year if none given
457 year = GetCurrentYear();
458 }
459
fcc3d7cb 460 return GetNumOfDaysInMonth(year, month);
b76b015e
VZ
461 }
462 else
463 {
464 wxFAIL_MSG(_T("unsupported calendar"));
465
466 return 0;
467 }
468}
469
470/* static */
471wxString wxDateTime::GetMonthName(wxDateTime::Month month, bool abbr)
472{
473 wxCHECK_MSG( month != Inv_Month, _T(""), _T("invalid month") );
474
475 tm tm = { 0, 0, 0, 1, month, 76 }; // any year will do
476
477 return CallStrftime(abbr ? _T("%b") : _T("%B"), &tm);
478}
479
480/* static */
481wxString wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday, bool abbr)
482{
483 wxCHECK_MSG( wday != Inv_WeekDay, _T(""), _T("invalid weekday") );
484
485 // take some arbitrary Sunday
486 tm tm = { 0, 0, 0, 28, Nov, 99 };
487
488 // and offset it by the number of days needed to get
489 tm.tm_mday += wday;
490
491 return CallStrftime(abbr ? _T("%a") : _T("%A"), &tm);
492}
493
0979c962
VZ
494// ----------------------------------------------------------------------------
495// constructors and assignment operators
496// ----------------------------------------------------------------------------
497
b76b015e 498wxDateTime& wxDateTime::Set(const struct tm& tm1)
0979c962 499{
b76b015e
VZ
500 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
501
502 tm tm2(tm1);
503 time_t timet = mktime(&tm2);
0979c962
VZ
504 if ( timet == (time_t)(-1) )
505 {
506 wxFAIL_MSG(_T("Invalid time"));
507
508 return ms_InvDateTime;
509 }
510 else
511 {
512 return Set(timet);
513 }
514}
515
516wxDateTime& wxDateTime::Set(wxDateTime_t hour,
517 wxDateTime_t minute,
518 wxDateTime_t second,
519 wxDateTime_t millisec)
520{
b76b015e
VZ
521 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
522
0979c962
VZ
523 // we allow seconds to be 61 to account for the leap seconds, even if we
524 // don't use them really
525 wxCHECK_MSG( hour < 24 && second < 62 && minute < 60 && millisec < 1000,
526 ms_InvDateTime,
527 _T("Invalid time in wxDateTime::Set()") );
528
529 // get the current date from system
530 time_t timet = GetTimeNow();
531 struct tm *tm = localtime(&timet);
532
533 // adjust the time
534 tm->tm_hour = hour;
535 tm->tm_min = minute;
536 tm->tm_sec = second;
537
b76b015e 538 (void)Set(*tm);
0979c962
VZ
539
540 // and finally adjust milliseconds
541 return SetMillisecond(millisec);
542}
543
544wxDateTime& wxDateTime::Set(wxDateTime_t day,
545 Month month,
546 int year,
547 wxDateTime_t hour,
548 wxDateTime_t minute,
549 wxDateTime_t second,
550 wxDateTime_t millisec)
551{
b76b015e
VZ
552 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
553
0979c962
VZ
554 wxCHECK_MSG( hour < 24 && second < 62 && minute < 60 && millisec < 1000,
555 ms_InvDateTime,
556 _T("Invalid time in wxDateTime::Set()") );
557
2f02cb89 558 ReplaceDefaultYearMonthWithCurrent(&year, &month);
0979c962 559
2f02cb89 560 wxCHECK_MSG( day <= GetNumberOfDays(month, year), ms_InvDateTime,
0979c962
VZ
561 _T("Invalid date in wxDateTime::Set()") );
562
563 // the range of time_t type (inclusive)
564 static const int yearMinInRange = 1970;
565 static const int yearMaxInRange = 2037;
566
567 // test only the year instead of testing for the exact end of the Unix
568 // time_t range - it doesn't bring anything to do more precise checks
2f02cb89 569 if ( year >= yearMinInRange && year <= yearMaxInRange )
0979c962
VZ
570 {
571 // use the standard library version if the date is in range - this is
b76b015e 572 // probably more efficient than our code
0979c962 573 struct tm tm;
b76b015e 574 tm.tm_year = year - 1900;
0979c962
VZ
575 tm.tm_mon = month;
576 tm.tm_mday = day;
577 tm.tm_hour = hour;
578 tm.tm_min = minute;
579 tm.tm_sec = second;
580
581 (void)Set(tm);
582
583 // and finally adjust milliseconds
584 return SetMillisecond(millisec);
585 }
586 else
587 {
588 // do time calculations ourselves: we want to calculate the number of
fcc3d7cb 589 // milliseconds between the given date and the epoch
e6ec579c
VZ
590
591 // get the JDN for the midnight of this day
592 m_time = GetTruncatedJDN(day, month, year);
593 m_time -= EPOCH_JDN;
594 m_time *= SECONDS_PER_DAY * TIME_T_FACTOR;
595
596 Add(wxTimeSpan(hour, minute, second, millisec));
b76b015e
VZ
597 }
598
599 return *this;
600}
601
e6ec579c
VZ
602wxDateTime& wxDateTime::Set(double jdn)
603{
604 m_time = (jdn - 0.5 - EPOCH_JDN) * TIME_T_FACTOR;
605
606 return *this;
607}
608
b76b015e
VZ
609// ----------------------------------------------------------------------------
610// time_t <-> broken down time conversions
611// ----------------------------------------------------------------------------
612
613wxDateTime::Tm wxDateTime::GetTm() const
614{
615 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
616
617 time_t time = GetTicks();
618 if ( time != (time_t)-1 )
619 {
620 // use C RTL functions
621 tm *tm = localtime(&time);
622
623 // should never happen
624 wxCHECK_MSG( tm, Tm(), _T("localtime() failed") );
625
626 return Tm(*tm);
627 }
628 else
629 {
e6ec579c
VZ
630 // CREDIT: the algorithm was taken from Peter Baum's home page
631
632 // calculate the Gregorian date from JDN for the midnight of our date
633 wxLongLong timeMidnight = m_time;
634 long timeOnly = (m_time % MILLISECONDS_PER_DAY).GetLo();
635 timeMidnight -= timeOnly;
636
637 // TODO this probably could be optimised somehow...
638
639 double jdn = (timeMidnight / MILLISECONDS_PER_DAY).GetLo();
640 jdn += EPOCH_JDN + 0.5;
641 long z = jdn - 1721118.5;
642 double r = jdn - 1721118.5 - z;
643 double g = z - 0.25;
644 long a = g/36524.25; // number of days per year
645 long b = a - a / 4;
646 int year = (b + g) / 365.25;
647 long c = b + z - 365.25*year;
648 int month = (5*c + 456)/153;
649 int day = c - (153*month - 457)/5 + (r < 0.5 ? 0 : 1);
650 if ( month > 12 )
651 {
652 year++;
653 month -= 12;
654 }
655
656 Tm tm;
657 tm.year = year;
658 tm.mon = (Month)(month - 1); // algorithm yields 1 for January, not 0
659 tm.mday = day;
660 tm.msec = timeOnly % 1000;
661 timeOnly -= tm.msec;
662 timeOnly /= 1000; // now we have time in seconds
663
664 tm.sec = timeOnly % 60;
665 timeOnly -= tm.sec;
666 timeOnly /= 60; // now we have time in minutes
667
668 tm.min = timeOnly % 60;
669 timeOnly -= tm.min;
670
671 tm.hour = timeOnly / 60;
b76b015e 672
e6ec579c 673 return tm;
0979c962 674 }
b76b015e
VZ
675}
676
677wxDateTime& wxDateTime::SetYear(int year)
678{
679 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
680
681 Tm tm(GetTm());
682 tm.year = year;
683 Set(tm);
684
685 return *this;
686}
687
688wxDateTime& wxDateTime::SetMonth(Month month)
689{
690 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
691
692 Tm tm(GetTm());
693 tm.mon = month;
694 Set(tm);
695
696 return *this;
697}
698
699wxDateTime& wxDateTime::SetDay(wxDateTime_t mday)
700{
701 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
702
703 Tm tm(GetTm());
704 tm.mday = mday;
705 Set(tm);
706
707 return *this;
708}
709
710wxDateTime& wxDateTime::SetHour(wxDateTime_t hour)
711{
712 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
713
714 Tm tm(GetTm());
715 tm.hour = hour;
716 Set(tm);
717
718 return *this;
719}
720
721wxDateTime& wxDateTime::SetMinute(wxDateTime_t min)
722{
723 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
724
725 Tm tm(GetTm());
726 tm.min = min;
727 Set(tm);
728
729 return *this;
730}
731
732wxDateTime& wxDateTime::SetSecond(wxDateTime_t sec)
733{
734 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
735
736 Tm tm(GetTm());
737 tm.sec = sec;
738 Set(tm);
0979c962
VZ
739
740 return *this;
741}
b76b015e
VZ
742
743wxDateTime& wxDateTime::SetMillisecond(wxDateTime_t millisecond)
744{
745 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
746
747 // we don't need to use GetTm() for this one
748 m_time -= m_time % 1000l;
749 m_time += millisecond;
750
751 return *this;
752}
753
754// ----------------------------------------------------------------------------
755// wxDateTime arithmetics
756// ----------------------------------------------------------------------------
757
758wxDateTime& wxDateTime::Add(const wxDateSpan& diff)
759{
760 Tm tm(GetTm());
761
762 tm.year += diff.GetYears();
fcc3d7cb
VZ
763 tm.AddMonths(diff.GetMonths());
764 tm.AddDays(diff.GetTotalDays());
b76b015e
VZ
765
766 Set(tm);
767
768 return *this;
769}
770
2f02cb89
VZ
771// ----------------------------------------------------------------------------
772// Weekday and monthday stuff
773// ----------------------------------------------------------------------------
774
775wxDateTime& wxDateTime::SetToLastMonthDay(Month month,
776 int year)
777{
778 // take the current month/year if none specified
779 ReplaceDefaultYearMonthWithCurrent(&year, &month);
780
fcc3d7cb 781 return Set(GetNumOfDaysInMonth(year, month), month, year);
2f02cb89
VZ
782}
783
784bool wxDateTime::SetToWeekDay(WeekDay weekday,
785 int n,
786 Month month,
787 int year)
788{
789 wxCHECK_MSG( weekday != Inv_WeekDay, FALSE, _T("invalid weekday") );
790
791 // we don't check explicitly that -5 <= n <= 5 because we will return FALSE
792 // anyhow in such case - but may be should still give an assert for it?
793
794 // take the current month/year if none specified
795 ReplaceDefaultYearMonthWithCurrent(&year, &month);
796
797 wxDateTime dt;
798
799 // TODO this probably could be optimised somehow...
800
801 if ( n > 0 )
802 {
803 // get the first day of the month
804 dt.Set(1, month, year);
805
806 // get its wday
807 WeekDay wdayFirst = dt.GetWeekDay();
808
809 // go to the first weekday of the month
810 int diff = weekday - wdayFirst;
811 if ( diff < 0 )
812 diff += 7;
813
814 // add advance n-1 weeks more
815 diff += 7*(n - 1);
816
817 dt -= wxDateSpan::Days(diff);
818 }
819 else
820 {
821 // get the last day of the month
822 dt.SetToLastMonthDay(month, year);
823
824 // get its wday
825 WeekDay wdayLast = dt.GetWeekDay();
826
827 // go to the last weekday of the month
828 int diff = wdayLast - weekday;
829 if ( diff < 0 )
830 diff += 7;
831
832 // and rewind n-1 weeks from there
833 diff += 7*(n - 1);
834
835 dt -= wxDateSpan::Days(diff);
836 }
837
838 // check that it is still in the same month
839 if ( dt.GetMonth() == month )
840 {
841 *this = dt;
842
843 return TRUE;
844 }
845 else
846 {
847 // no such day in this month
848 return FALSE;
849 }
850}
851
e6ec579c
VZ
852// ----------------------------------------------------------------------------
853// Julian day number conversion and related stuff
854// ----------------------------------------------------------------------------
855
856double wxDateTime::GetJulianDayNumber() const
857{
858 Tm tm(GetTm());
859
860 double result = GetTruncatedJDN(tm.mday, tm.mon, tm.year);
861
862 // add the part GetTruncatedJDN() neglected
863 result += 0.5;
864
865 // and now add the time: 86400 sec = 1 JDN
866 return result + ((double)(60*(60*tm.hour + tm.min) + tm.sec)) / 86400;
867}
868
869double wxDateTime::GetRataDie() const
870{
871 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
872 return GetJulianDayNumber() - 1721119.5 - 306;
873}
874
fcc3d7cb
VZ
875// ----------------------------------------------------------------------------
876// timezone stuff
877// ----------------------------------------------------------------------------
878
879wxDateTime& wxDateTime::MakeUTC()
880{
881 return Add(wxTimeSpan::Seconds(GetTimeZone()));
882}
883
884wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz)
885{
886 int minDiff = GetTimeZone() / SECONDS_IN_MINUTE + tz.GetOffset();
887 return Add(wxTimeSpan::Minutes(minDiff));
888}
889
890wxDateTime& wxDateTime::MakeLocalTime(const TimeZone& tz)
891{
892 int minDiff = GetTimeZone() / SECONDS_IN_MINUTE + tz.GetOffset();
893 return Substract(wxTimeSpan::Minutes(minDiff));
894}
895
b76b015e
VZ
896// ----------------------------------------------------------------------------
897// wxDateTime to/from text representations
898// ----------------------------------------------------------------------------
899
900wxString wxDateTime::Format(const wxChar *format) const
901{
e6ec579c
VZ
902 wxCHECK_MSG( format, _T(""), _T("NULL format in wxDateTime::Format") );
903
b76b015e
VZ
904 time_t time = GetTicks();
905 if ( time != (time_t)-1 )
906 {
907 // use strftime()
908 tm *tm = localtime(&time);
909
910 // should never happen
911 wxCHECK_MSG( tm, _T(""), _T("localtime() failed") );
912
913 return CallStrftime(format, tm);
914 }
915 else
916 {
e6ec579c
VZ
917 // use a hack and still use strftime(): make a copy of the format and
918 // replace all occurences of YEAR in it with some unique string not
919 // appearing anywhere else in it, then use strftime() to format the
920 // date in year YEAR and then replace YEAR back by the real year and
921 // the unique replacement string back with YEAR where YEAR is any year
922 // in the range supported by strftime() (1970 - 2037) which is equal to
923 // the real year modulo 28 (so the week days coincide for them)
924
925 // find the YEAR
926 int yearReal = GetYear();
927 int year = 1970 + yearReal % 28;
928
929 wxString strYear;
930 strYear.Printf(_T("%d"), year);
931
932 // find a string not occuring in format (this is surely not optimal way
933 // of doing it... improvements welcome!)
934 wxString fmt = format;
935 wxString replacement = (wxChar)-1;
936 while ( fmt.Find(replacement) != wxNOT_FOUND )
937 {
938 replacement << (wxChar)-1;
939 }
940
941 // replace all occurences of year with it
942 bool wasReplaced = fmt.Replace(strYear, replacement) > 0;
943
944 // use strftime() to format the same date but in supported year
945 wxDateTime dt(*this);
946 dt.SetYear(year);
947 wxString str = dt.Format(format);
b76b015e 948
e6ec579c
VZ
949 // now replace the occurence of 1999 with the real year
950 wxString strYearReal;
951 strYearReal.Printf(_T("%d"), yearReal);
952 str.Replace(strYear, strYearReal);
953
954 // and replace back all occurences of replacement string
955 if ( wasReplaced )
956 str.Replace(replacement, strYear);
957
958 return str;
b76b015e
VZ
959 }
960}
fcc3d7cb
VZ
961
962// ============================================================================
963// wxTimeSpan
964// ============================================================================
965
e6ec579c
VZ
966// not all strftime(3) format specifiers make sense here because, for example,
967// a time span doesn't have a year nor a timezone
968//
969// Here are the ones which are supported (all of them are supported by strftime
970// as well):
971// %H hour in 24 hour format
972// %M minute (00 - 59)
973// %S second (00 - 59)
974// %% percent sign
975//
976// Also, for MFC CTimeSpan compatibility, we support
977// %D number of days
978//
979// And, to be better than MFC :-), we also have
980// %E number of wEeks
981// %l milliseconds (000 - 999)
fcc3d7cb
VZ
982wxString wxTimeSpan::Format(const wxChar *format) const
983{
e6ec579c 984 wxCHECK_MSG( format, _T(""), _T("NULL format in wxTimeSpan::Format") );
fcc3d7cb
VZ
985
986 wxString str;
e6ec579c
VZ
987 str.Alloc(strlen(format));
988
989 for ( const wxChar *pch = format; pch; pch++ )
990 {
991 wxChar ch = *pch;
992
993 if ( ch == '%' )
994 {
995 wxString tmp;
996
997 ch = *pch++;
998 switch ( ch )
999 {
1000 default:
1001 wxFAIL_MSG( _T("invalid format character") );
1002 // fall through
1003
1004 case '%':
1005 // will get to str << ch below
1006 break;
1007
1008 case 'D':
1009 tmp.Printf(_T("%d"), GetDays());
1010 break;
1011
1012 case 'E':
1013 tmp.Printf(_T("%d"), GetWeeks());
1014 break;
1015
1016 case 'H':
1017 tmp.Printf(_T("%02d"), GetHours());
1018 break;
1019
1020 case 'l':
1021 tmp.Printf(_T("%03d"), GetMilliseconds());
1022 break;
1023
1024 case 'M':
1025 tmp.Printf(_T("%02d"), GetMinutes());
1026 break;
1027
1028 case 'S':
1029 tmp.Printf(_T("%02d"), GetSeconds());
1030 break;
1031 }
1032
1033 if ( !!tmp )
1034 {
1035 str += tmp;
1036
1037 // skip str += ch below
1038 continue;
1039 }
1040 }
1041
1042 str += ch;
1043 }
fcc3d7cb
VZ
1044
1045 return str;
1046}