+ return (Country_WesternEurope_Start <= country) &&
+ (country <= Country_WesternEurope_End);
+}
+
+// ----------------------------------------------------------------------------
+// DST calculations: we use 3 different rules for the West European countries,
+// USA and for the rest of the world. This is undoubtedly false for many
+// countries, but I lack the necessary info (and the time to gather it),
+// please add the other rules here!
+// ----------------------------------------------------------------------------
+
+/* static */
+bool wxDateTime::IsDSTApplicable(int year, Country country)
+{
+ if ( year == Inv_Year )
+ {
+ // take the current year if none given
+ year = GetCurrentYear();
+ }
+
+ if ( country == Country_Default )
+ {
+ country = GetCountry();
+ }
+
+ switch ( country )
+ {
+ case USA:
+ case UK:
+ // DST was first observed in the US and UK during WWI, reused
+ // during WWII and used again since 1966
+ return year >= 1966 ||
+ (year >= 1942 && year <= 1945) ||
+ (year == 1918 || year == 1919);
+
+ default:
+ // assume that it started after WWII
+ return year > 1950;
+ }
+}
+
+/* static */
+wxDateTime wxDateTime::GetBeginDST(int year, Country country)
+{
+ if ( year == Inv_Year )
+ {
+ // take the current year if none given
+ year = GetCurrentYear();
+ }
+
+ if ( country == Country_Default )
+ {
+ country = GetCountry();
+ }
+
+ if ( !IsDSTApplicable(year, country) )
+ {
+ return wxInvalidDateTime;
+ }
+
+ wxDateTime dt;
+
+ if ( IsWestEuropeanCountry(country) || (country == Russia) )
+ {
+ // DST begins at 1 a.m. GMT on the last Sunday of March
+ if ( !dt.SetToLastWeekDay(Sun, Mar, year) )
+ {
+ // weird...
+ wxFAIL_MSG( wxT("no last Sunday in March?") );
+ }
+
+ dt += wxTimeSpan::Hours(1);
+ }
+ else switch ( country )
+ {
+ case USA:
+ switch ( year )
+ {
+ case 1918:
+ case 1919:
+ // don't know for sure - assume it was in effect all year
+
+ case 1943:
+ case 1944:
+ case 1945:
+ dt.Set(1, Jan, year);
+ break;
+
+ case 1942:
+ // DST was installed Feb 2, 1942 by the Congress
+ dt.Set(2, Feb, year);
+ break;
+
+ // Oil embargo changed the DST period in the US
+ case 1974:
+ dt.Set(6, Jan, 1974);
+ break;
+
+ case 1975:
+ dt.Set(23, Feb, 1975);
+ break;
+
+ default:
+ // before 1986, DST begun on the last Sunday of April, but
+ // in 1986 Reagan changed it to begin at 2 a.m. of the
+ // first Sunday in April
+ if ( year < 1986 )
+ {
+ if ( !dt.SetToLastWeekDay(Sun, Apr, year) )
+ {
+ // weird...
+ wxFAIL_MSG( wxT("no first Sunday in April?") );
+ }
+ }
+ else if ( year > 2006 )
+ // Energy Policy Act of 2005, Pub. L. no. 109-58, 119 Stat 594 (2005).
+ // Starting in 2007, daylight time begins in the United States on the
+ // second Sunday in March and ends on the first Sunday in November
+ {
+ if ( !dt.SetToWeekDay(Sun, 2, Mar, year) )
+ {
+ // weird...
+ wxFAIL_MSG( wxT("no second Sunday in March?") );
+ }
+ }
+ else
+ {
+ if ( !dt.SetToWeekDay(Sun, 1, Apr, year) )
+ {
+ // weird...
+ wxFAIL_MSG( wxT("no first Sunday in April?") );
+ }
+ }
+
+ dt += wxTimeSpan::Hours(2);
+
+ // TODO what about timezone??
+ }
+
+ break;
+
+ default:
+ // assume Mar 30 as the start of the DST for the rest of the world
+ // - totally bogus, of course
+ dt.Set(30, Mar, year);
+ }
+
+ return dt;
+}
+
+/* static */
+wxDateTime wxDateTime::GetEndDST(int year, Country country)
+{
+ if ( year == Inv_Year )
+ {
+ // take the current year if none given
+ year = GetCurrentYear();
+ }
+
+ if ( country == Country_Default )
+ {
+ country = GetCountry();
+ }
+
+ if ( !IsDSTApplicable(year, country) )
+ {
+ return wxInvalidDateTime;
+ }
+
+ wxDateTime dt;
+
+ if ( IsWestEuropeanCountry(country) || (country == Russia) )
+ {
+ // DST ends at 1 a.m. GMT on the last Sunday of October
+ if ( !dt.SetToLastWeekDay(Sun, Oct, year) )
+ {
+ // weirder and weirder...
+ wxFAIL_MSG( wxT("no last Sunday in October?") );
+ }
+
+ dt += wxTimeSpan::Hours(1);
+ }
+ else switch ( country )
+ {
+ case USA:
+ switch ( year )
+ {
+ case 1918:
+ case 1919:
+ // don't know for sure - assume it was in effect all year
+
+ case 1943:
+ case 1944:
+ dt.Set(31, Dec, year);
+ break;
+
+ case 1945:
+ // the time was reset after the end of the WWII
+ dt.Set(30, Sep, year);
+ break;
+
+ default: // default for switch (year)
+ if ( year > 2006 )
+ // Energy Policy Act of 2005, Pub. L. no. 109-58, 119 Stat 594 (2005).
+ // Starting in 2007, daylight time begins in the United States on the
+ // second Sunday in March and ends on the first Sunday in November
+ {
+ if ( !dt.SetToWeekDay(Sun, 1, Nov, year) )
+ {
+ // weird...
+ wxFAIL_MSG( wxT("no first Sunday in November?") );
+ }
+ }
+ else
+ // pre-2007
+ // DST ends at 2 a.m. on the last Sunday of October
+ {
+ if ( !dt.SetToLastWeekDay(Sun, Oct, year) )
+ {
+ // weirder and weirder...
+ wxFAIL_MSG( wxT("no last Sunday in October?") );
+ }
+ }
+
+ dt += wxTimeSpan::Hours(2);
+
+ // TODO: what about timezone??
+ }
+ break;
+
+ default: // default for switch (country)
+ // assume October 26th as the end of the DST - totally bogus too
+ dt.Set(26, Oct, year);
+ }
+
+ return dt;
+}
+
+// ----------------------------------------------------------------------------
+// constructors and assignment operators
+// ----------------------------------------------------------------------------
+
+// return the current time with ms precision
+/* static */ wxDateTime wxDateTime::UNow()
+{
+ return wxDateTime(wxGetUTCTimeMillis());
+}
+
+// the values in the tm structure contain the local time
+wxDateTime& wxDateTime::Set(const struct tm& tm)
+{
+ struct tm tm2(tm);
+ time_t timet = mktime(&tm2);
+
+ if ( timet == (time_t)-1 )
+ {
+ // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
+ // less than timezone - try to make it work for this case
+ if ( tm2.tm_year == 70 && tm2.tm_mon == 0 && tm2.tm_mday == 1 )
+ {
+ return Set((time_t)(
+ wxGetTimeZone() +
+ tm2.tm_hour * MIN_PER_HOUR * SEC_PER_MIN +
+ tm2.tm_min * SEC_PER_MIN +
+ tm2.tm_sec));
+ }
+
+ wxFAIL_MSG( wxT("mktime() failed") );
+
+ *this = wxInvalidDateTime;
+
+ return *this;
+ }
+ else
+ {
+ return Set(timet);
+ }
+}
+
+wxDateTime& wxDateTime::Set(wxDateTime_t hour,
+ wxDateTime_t minute,
+ wxDateTime_t second,
+ wxDateTime_t millisec)
+{
+ // we allow seconds to be 61 to account for the leap seconds, even if we
+ // don't use them really
+ wxDATETIME_CHECK( hour < 24 &&
+ second < 62 &&
+ minute < 60 &&
+ millisec < 1000,
+ wxT("Invalid time in wxDateTime::Set()") );
+
+ // get the current date from system
+ struct tm tmstruct;
+ struct tm *tm = GetTmNow(&tmstruct);
+
+ wxDATETIME_CHECK( tm, wxT("wxLocaltime_r() failed") );
+
+ // make a copy so it isn't clobbered by the call to mktime() below
+ struct tm tm1(*tm);
+
+ // adjust the time
+ tm1.tm_hour = hour;
+ tm1.tm_min = minute;
+ tm1.tm_sec = second;
+
+ // and the DST in case it changes on this date
+ struct tm tm2(tm1);
+ mktime(&tm2);
+ if ( tm2.tm_isdst != tm1.tm_isdst )
+ tm1.tm_isdst = tm2.tm_isdst;
+
+ (void)Set(tm1);
+
+ // and finally adjust milliseconds
+ return SetMillisecond(millisec);
+}
+
+wxDateTime& wxDateTime::Set(wxDateTime_t day,
+ Month month,
+ int year,
+ wxDateTime_t hour,
+ wxDateTime_t minute,
+ wxDateTime_t second,
+ wxDateTime_t millisec)
+{
+ wxDATETIME_CHECK( hour < 24 &&
+ second < 62 &&
+ minute < 60 &&
+ millisec < 1000,
+ wxT("Invalid time in wxDateTime::Set()") );
+
+ ReplaceDefaultYearMonthWithCurrent(&year, &month);
+
+ wxDATETIME_CHECK( (0 < day) && (day <= GetNumberOfDays(month, year)),
+ wxT("Invalid date in wxDateTime::Set()") );
+
+ // the range of time_t type (inclusive)
+ static const int yearMinInRange = 1970;
+ static const int yearMaxInRange = 2037;
+
+ // test only the year instead of testing for the exact end of the Unix
+ // time_t range - it doesn't bring anything to do more precise checks
+ if ( year >= yearMinInRange && year <= yearMaxInRange )
+ {
+ // use the standard library version if the date is in range - this is
+ // probably more efficient than our code
+ struct tm tm;
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month;
+ tm.tm_mday = day;
+ tm.tm_hour = hour;
+ tm.tm_min = minute;
+ tm.tm_sec = second;
+ tm.tm_isdst = -1; // mktime() will guess it
+
+ (void)Set(tm);
+
+ // and finally adjust milliseconds
+ if (IsValid())
+ SetMillisecond(millisec);
+
+ return *this;
+ }
+ else
+ {
+ // do time calculations ourselves: we want to calculate the number of
+ // milliseconds between the given date and the epoch
+
+ // get the JDN for the midnight of this day
+ m_time = GetTruncatedJDN(day, month, year);
+ m_time -= EPOCH_JDN;
+ m_time *= SECONDS_PER_DAY * TIME_T_FACTOR;
+
+ // JDN corresponds to GMT, we take localtime
+ Add(wxTimeSpan(hour, minute, second + wxGetTimeZone(), millisec));
+ }
+
+ return *this;
+}
+
+wxDateTime& wxDateTime::Set(double jdn)
+{
+ // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
+ // EPOCH_JDN + 0.5
+ jdn -= EPOCH_JDN + 0.5;
+
+ m_time.Assign(jdn*MILLISECONDS_PER_DAY);
+
+ // JDNs always are in UTC, so we don't need any adjustments for time zone
+
+ return *this;
+}
+
+wxDateTime& wxDateTime::ResetTime()
+{
+ Tm tm = GetTm();
+
+ if ( tm.hour || tm.min || tm.sec || tm.msec )
+ {
+ tm.msec =
+ tm.sec =
+ tm.min =
+ tm.hour = 0;
+
+ Set(tm);
+ }
+
+ return *this;
+}
+
+wxDateTime wxDateTime::GetDateOnly() const
+{
+ Tm tm = GetTm();
+ tm.msec =
+ tm.sec =
+ tm.min =
+ tm.hour = 0;
+ return wxDateTime(tm);
+}
+
+// ----------------------------------------------------------------------------
+// DOS Date and Time Format functions
+// ----------------------------------------------------------------------------
+// the dos date and time value is an unsigned 32 bit value in the format:
+// YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
+//
+// Y = year offset from 1980 (0-127)
+// M = month (1-12)
+// D = day of month (1-31)
+// h = hour (0-23)
+// m = minute (0-59)
+// s = bisecond (0-29) each bisecond indicates two seconds
+// ----------------------------------------------------------------------------
+
+wxDateTime& wxDateTime::SetFromDOS(unsigned long ddt)
+{
+ struct tm tm;
+ InitTm(tm);
+
+ long year = ddt & 0xFE000000;
+ year >>= 25;
+ year += 80;
+ tm.tm_year = year;
+
+ long month = ddt & 0x1E00000;
+ month >>= 21;
+ month -= 1;
+ tm.tm_mon = month;
+
+ long day = ddt & 0x1F0000;
+ day >>= 16;
+ tm.tm_mday = day;
+
+ long hour = ddt & 0xF800;
+ hour >>= 11;
+ tm.tm_hour = hour;
+
+ long minute = ddt & 0x7E0;
+ minute >>= 5;
+ tm.tm_min = minute;
+
+ long second = ddt & 0x1F;
+ tm.tm_sec = second * 2;
+
+ return Set(mktime(&tm));
+}
+
+unsigned long wxDateTime::GetAsDOS() const
+{
+ unsigned long ddt;
+ time_t ticks = GetTicks();
+ struct tm tmstruct;
+ struct tm *tm = wxLocaltime_r(&ticks, &tmstruct);
+ wxCHECK_MSG( tm, ULONG_MAX, wxT("time can't be represented in DOS format") );
+
+ long year = tm->tm_year;
+ year -= 80;
+ year <<= 25;
+
+ long month = tm->tm_mon;
+ month += 1;
+ month <<= 21;
+
+ long day = tm->tm_mday;
+ day <<= 16;
+
+ long hour = tm->tm_hour;
+ hour <<= 11;
+
+ long minute = tm->tm_min;
+ minute <<= 5;
+
+ long second = tm->tm_sec;
+ second /= 2;
+
+ ddt = year | month | day | hour | minute | second;
+ return ddt;
+}
+
+// ----------------------------------------------------------------------------
+// time_t <-> broken down time conversions
+// ----------------------------------------------------------------------------
+
+wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
+{
+ wxASSERT_MSG( IsValid(), wxT("invalid wxDateTime") );
+
+ time_t time = GetTicks();
+ if ( time != (time_t)-1 )
+ {
+ // use C RTL functions
+ struct tm tmstruct;
+ tm *tm;
+ if ( tz.GetOffset() == -wxGetTimeZone() )
+ {
+ // we are working with local time
+ tm = wxLocaltime_r(&time, &tmstruct);
+
+ // should never happen
+ wxCHECK_MSG( tm, Tm(), wxT("wxLocaltime_r() failed") );
+ }
+ else
+ {
+ time += (time_t)tz.GetOffset();
+#if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
+ int time2 = (int) time;
+ if ( time2 >= 0 )
+#else
+ if ( time >= 0 )
+#endif
+ {
+ tm = wxGmtime_r(&time, &tmstruct);
+
+ // should never happen
+ wxCHECK_MSG( tm, Tm(), wxT("wxGmtime_r() failed") );
+ }
+ else
+ {
+ tm = (struct tm *)NULL;
+ }
+ }
+
+ if ( tm )
+ {
+ // adjust the milliseconds
+ Tm tm2(*tm, tz);
+ long timeOnly = (m_time % MILLISECONDS_PER_DAY).ToLong();
+ tm2.msec = (wxDateTime_t)(timeOnly % 1000);
+ return tm2;
+ }
+ //else: use generic code below
+ }
+
+ // remember the time and do the calculations with the date only - this
+ // eliminates rounding errors of the floating point arithmetics
+
+ wxLongLong timeMidnight = m_time + tz.GetOffset() * 1000;
+
+ long timeOnly = (timeMidnight % MILLISECONDS_PER_DAY).ToLong();
+
+ // we want to always have positive time and timeMidnight to be really
+ // the midnight before it
+ if ( timeOnly < 0 )
+ {
+ timeOnly = MILLISECONDS_PER_DAY + timeOnly;
+ }
+
+ timeMidnight -= timeOnly;
+
+ // calculate the Gregorian date from JDN for the midnight of our date:
+ // this will yield day, month (in 1..12 range) and year
+
+ // actually, this is the JDN for the noon of the previous day
+ long jdn = (timeMidnight / MILLISECONDS_PER_DAY).ToLong() + EPOCH_JDN;
+
+ // CREDIT: code below is by Scott E. Lee (but bugs are mine)
+
+ wxASSERT_MSG( jdn > -2, wxT("JDN out of range") );
+
+ // calculate the century
+ long temp = (jdn + JDN_OFFSET) * 4 - 1;
+ long century = temp / DAYS_PER_400_YEARS;
+
+ // then the year and day of year (1 <= dayOfYear <= 366)
+ temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3;
+ long year = (century * 100) + (temp / DAYS_PER_4_YEARS);
+ long dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
+
+ // and finally the month and day of the month
+ temp = dayOfYear * 5 - 3;
+ long month = temp / DAYS_PER_5_MONTHS;
+ long day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
+
+ // month is counted from March - convert to normal
+ if ( month < 10 )
+ {
+ month += 3;
+ }
+ else
+ {
+ year += 1;
+ month -= 9;
+ }
+
+ // year is offset by 4800
+ year -= 4800;
+
+ // check that the algorithm gave us something reasonable
+ wxASSERT_MSG( (0 < month) && (month <= 12), wxT("invalid month") );
+ wxASSERT_MSG( (1 <= day) && (day < 32), wxT("invalid day") );
+
+ // construct Tm from these values
+ Tm tm;
+ tm.year = (int)year;
+ tm.yday = (wxDateTime_t)(dayOfYear - 1); // use C convention for day number
+ tm.mon = (Month)(month - 1); // algorithm yields 1 for January, not 0
+ tm.mday = (wxDateTime_t)day;
+ tm.msec = (wxDateTime_t)(timeOnly % 1000);
+ timeOnly -= tm.msec;
+ timeOnly /= 1000; // now we have time in seconds
+
+ tm.sec = (wxDateTime_t)(timeOnly % SEC_PER_MIN);
+ timeOnly -= tm.sec;
+ timeOnly /= SEC_PER_MIN; // now we have time in minutes
+
+ tm.min = (wxDateTime_t)(timeOnly % MIN_PER_HOUR);
+ timeOnly -= tm.min;
+
+ tm.hour = (wxDateTime_t)(timeOnly / MIN_PER_HOUR);
+
+ return tm;
+}
+
+wxDateTime& wxDateTime::SetYear(int year)
+{
+ wxASSERT_MSG( IsValid(), wxT("invalid wxDateTime") );
+
+ Tm tm(GetTm());
+ tm.year = year;
+ Set(tm);
+
+ return *this;
+}
+
+wxDateTime& wxDateTime::SetMonth(Month month)
+{
+ wxASSERT_MSG( IsValid(), wxT("invalid wxDateTime") );
+
+ Tm tm(GetTm());
+ tm.mon = month;
+ Set(tm);
+
+ return *this;