From 239446b4150e55c1dfed186340f144d78c2f1cc9 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 17 Dec 1999 19:56:32 +0000 Subject: [PATCH] wxDateTime progress: DST compuation, weekday computation, day-in-year and week number computations all implemented and seem to work git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@5026 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/datetime.h | 49 ++++-- include/wx/datetime.inl | 10 -- samples/console/console.cpp | 338 +++++++++++++++++++++++++++++++++-- src/common/datetime.cpp | 343 ++++++++++++++++++++++++++++++++++-- 4 files changed, 695 insertions(+), 45 deletions(-) diff --git a/include/wx/datetime.h b/include/wx/datetime.h index c0091ce142..da7f5ee231 100644 --- a/include/wx/datetime.h +++ b/include/wx/datetime.h @@ -295,10 +295,22 @@ public: { Country_Unknown, // no special information for this country Country_Default, // set the default country with SetCountry() method + // or use the default country with any other // TODO add more countries (for this we must know about DST and/or // holidays for this country) + + // Western European countries: we assume that they all follow the same + // DST rules (true or false?) + Country_WesternEurope_Start, + Country_EEC = Country_WesternEurope_Start, France, + Germany, + UK, + Country_WesternEurope_End = UK, + + Russia, + USA }; @@ -393,7 +405,11 @@ public: // set the current country static void SetCountry(Country country); // get the current country - static inline Country GetCountry(); + static Country GetCountry(); + + // return TRUE if the country is a West European one (in practice, + // this means that the same DST rules as for EEC apply) + static bool IsWestEuropeanCountry(Country country = Country_Default); // return the current year static int GetCurrentYear(Calendar cal = Gregorian); @@ -431,14 +447,20 @@ public: // locale, returns empty string on error static wxString GetWeekDayName(WeekDay weekday, bool abbr = FALSE); + // return TRUE if the given country uses DST for this year + static bool IsDSTApplicable(int year = Inv_Year, + Country country = Country_Default); + // get the beginning of DST for this year, will return invalid object // if no DST applicable in this year. The default value of the // parameter means to take the current year. - static wxDateTime GetBeginDST(int year = Inv_Year); + static wxDateTime GetBeginDST(int year = Inv_Year, + Country country = Country_Default); // get the end of DST for this year, will return invalid object // if no DST applicable in this year. The default value of the // parameter means to take the current year. - static wxDateTime GetEndDST(int year = Inv_Year); + static wxDateTime GetEndDST(int year = Inv_Year, + Country country = Country_Default); // return the wxDateTime object for the current time static inline wxDateTime Now(); @@ -600,6 +622,13 @@ public: // get the Julian Day number (the fractional part specifies the time of // the day, related to noon - beware of rounding errors!) double GetJulianDayNumber() const; + double GetJDN() const { return GetJulianDayNumber(); } + + // get the Modified Julian Day number: it is equal to JDN - 2400000.5 + // and so integral MJDs correspond to the midnights (and not noons). + // MJD 0 is Nov 17, 1858 + double GetModifiedJulianDayNumber() const { return GetJDN() - 2400000.5; } + double GetMJD() const { return GetModifiedJulianDayNumber(); } // get the Rata Die number double GetRataDie() const; @@ -624,6 +653,13 @@ public: wxDateTime ToGMT() const { return ToTimezone(GMT0); } wxDateTime& MakeGMT() { return MakeTimezone(GMT0); } + // is daylight savings time in effect at this moment according to the + // rules of the specified country? + // + // Return value is > 0 if DST is in effect, 0 if it is not and -1 if + // the information is not available (this is compatible with ANSI C) + int IsDST(Country country = Country_Default) const; + // accessors: many of them take the timezone parameter which indicates the // timezone for which to make the calculations and the default value means // to do it for the current timezone of this machine (even if the function @@ -690,13 +726,6 @@ public: // adoption of the Gregorian calendar is simply unknown. bool IsGregorianDate(GregorianAdoption country = Gr_Standard) const; - // is daylight savings time in effect at this moment according to the - // rules of the specified country? - // - // Return value is > 0 if DST is in effect, 0 if it is not and -1 if - // the information is not available (this is compatible with ANSI C) - int IsDST(Country country = Country_Default) const; - // comparison (see also functions below for operator versions) // ------------------------------------------------------------------------ diff --git a/include/wx/datetime.inl b/include/wx/datetime.inl index 5b1d4652e3..0bfdb87fea 100644 --- a/include/wx/datetime.inl +++ b/include/wx/datetime.inl @@ -16,16 +16,6 @@ #error "This file is only included by wx/datetime.h, don't include it manually!" #endif -// ---------------------------------------------------------------------------- -// wxDateTime statics -// ---------------------------------------------------------------------------- - -/* static */ -wxDateTime::Country wxDateTime::GetCountry() -{ - return ms_country; -} - // ---------------------------------------------------------------------------- // wxDateTime construction // ---------------------------------------------------------------------------- diff --git a/samples/console/console.cpp b/samples/console/console.cpp index 8bb9a2002a..ab1b4c9ffb 100644 --- a/samples/console/console.cpp +++ b/samples/console/console.cpp @@ -280,6 +280,11 @@ struct Date wxDateTime DT() const { return wxDateTime(day, month, year, hour, min, sec); } + bool SameDay(const wxDateTime::Tm& tm) const + { + return day == tm.mday && month == tm.mon && year == tm.year; + } + wxString Format() const { wxString s; @@ -291,6 +296,17 @@ struct Date year > 0 ? "AD" : "BC"); return s; } + + wxString FormatDate() const + { + wxString s; + s.Printf("%02d-%s-%4d%s", + day, + wxDateTime::GetMonthName(month, TRUE).c_str(), + abs(wxDateTime::ConvertYearToBC(year)), + year > 0 ? "AD" : "BC"); + return s; + } }; static const Date testDates[] = @@ -305,10 +321,10 @@ static const Date testDates[] = { 4, wxDateTime::Oct, 1582, 00, 00, 00, 2299149.5, wxDateTime::Mon, -1, -1 }, { 1, wxDateTime::Mar, 1, 00, 00, 00, 1721484.5, wxDateTime::Thu, -1, -1 }, { 1, wxDateTime::Jan, 1, 00, 00, 00, 1721425.5, wxDateTime::Mon, -1, -1 }, - { 31, wxDateTime::Dec, 0, 00, 00, 00, 1721424.5, wxDateTime::Tue, -1, -1 }, - { 1, wxDateTime::Jan, 0, 00, 00, 00, 1721059.5, wxDateTime::Wed, -1, -1 }, - { 12, wxDateTime::Aug, -1234, 00, 00, 00, 1270573.5, wxDateTime::Thu, -1, -1 }, - { 12, wxDateTime::Aug, -4000, 00, 00, 00, 260313.5, wxDateTime::Wed, -1, -1 }, + { 31, wxDateTime::Dec, 0, 00, 00, 00, 1721424.5, wxDateTime::Sun, -1, -1 }, + { 1, wxDateTime::Jan, 0, 00, 00, 00, 1721059.5, wxDateTime::Sat, -1, -1 }, + { 12, wxDateTime::Aug, -1234, 00, 00, 00, 1270573.5, wxDateTime::Fri, -1, -1 }, + { 12, wxDateTime::Aug, -4000, 00, 00, 00, 260313.5, wxDateTime::Sat, -1, -1 }, { 24, wxDateTime::Nov, -4713, 00, 00, 00, -0.5, wxDateTime::Mon, -1, -1 }, }; @@ -344,12 +360,13 @@ static void TestTimeStatic() for ( size_t n = 0; n < nYears; n++ ) { int year = years[0][n]; - bool should = years[1][n] != 0; + bool should = years[1][n] != 0, + is = wxDateTime::IsLeapYear(year); - printf("Year %d is %sa leap year (should be: %s)\n", + printf("Year %d is %sa leap year (%s)\n", year, - wxDateTime::IsLeapYear(year) ? "" : "not ", - should ? "yes" : "no"); + is ? "" : "not ", + should == is ? "ok" : "ERROR"); wxASSERT( should == wxDateTime::IsLeapYear(year) ); } @@ -480,7 +497,9 @@ static void TestTimeWDays() { puts("\n*** wxDateTime weekday test ***"); - for ( size_t n = 0; n < WXSIZEOF(testDates); n++ ) + // test GetWeekDay() + size_t n; + for ( n = 0; n < WXSIZEOF(testDates); n++ ) { const Date& d = testDates[n]; wxDateTime dt(d.day, d.month, d.year, d.hour, d.min, d.sec); @@ -488,7 +507,7 @@ static void TestTimeWDays() wxDateTime::WeekDay wday = dt.GetWeekDay(); printf("%s is: %s", d.Format().c_str(), - wxDateTime::GetWeekDayName(wday)); + wxDateTime::GetWeekDayName(wday).c_str()); if ( wday == d.wday ) { puts(" (ok)"); @@ -496,9 +515,302 @@ static void TestTimeWDays() else { printf(" (ERROR: should be %s)\n", - wxDateTime::GetWeekDayName(d.wday)); + wxDateTime::GetWeekDayName(d.wday).c_str()); } } + + puts(""); + + // test SetToWeekDay() + struct WeekDateTestData + { + Date date; // the real date (precomputed) + int nWeek; // its week index in the month + wxDateTime::WeekDay wday; // the weekday + wxDateTime::Month month; // the month + int year; // and the year + + wxString Format() const + { + wxString s, which; + switch ( nWeek < -1 ? -nWeek : nWeek ) + { + case 1: which = "first"; break; + case 2: which = "second"; break; + case 3: which = "third"; break; + case 4: which = "fourth"; break; + case 5: which = "fifth"; break; + + case -1: which = "last"; break; + } + + if ( nWeek < -1 ) + { + which += " from end"; + } + + s.Printf("The %s %s of %s in %d", + which.c_str(), + wxDateTime::GetWeekDayName(wday).c_str(), + wxDateTime::GetMonthName(month).c_str(), + year); + + return s; + } + }; + + // the array data was generated by the following python program + /* +from DateTime import * +from whrandom import * +from string import * + +monthNames = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] +wdayNames = [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ] + +week = DateTimeDelta(7) + +for n in range(20): + year = randint(1900, 2100) + month = randint(1, 12) + day = randint(1, 28) + dt = DateTime(year, month, day) + wday = dt.day_of_week + + countFromEnd = choice([-1, 1]) + weekNum = 0; + + while dt.month is month: + dt = dt - countFromEnd * week + weekNum = weekNum + countFromEnd + + data = { 'day': rjust(`day`, 2), 'month': monthNames[month - 1], 'year': year, 'weekNum': rjust(`weekNum`, 2), 'wday': wdayNames[wday] } + + print "{ { %(day)s, wxDateTime::%(month)s, %(year)d }, %(weekNum)d, "\ + "wxDateTime::%(wday)s, wxDateTime::%(month)s, %(year)d }," % data + */ + + static const WeekDateTestData weekDatesTestData[] = + { + { { 20, wxDateTime::Mar, 2045 }, 3, wxDateTime::Mon, wxDateTime::Mar, 2045 }, + { { 5, wxDateTime::Jun, 1985 }, -4, wxDateTime::Wed, wxDateTime::Jun, 1985 }, + { { 12, wxDateTime::Nov, 1961 }, -3, wxDateTime::Sun, wxDateTime::Nov, 1961 }, + { { 27, wxDateTime::Feb, 2093 }, -1, wxDateTime::Fri, wxDateTime::Feb, 2093 }, + { { 4, wxDateTime::Jul, 2070 }, -4, wxDateTime::Fri, wxDateTime::Jul, 2070 }, + { { 2, wxDateTime::Apr, 1906 }, -5, wxDateTime::Mon, wxDateTime::Apr, 1906 }, + { { 19, wxDateTime::Jul, 2023 }, -2, wxDateTime::Wed, wxDateTime::Jul, 2023 }, + { { 5, wxDateTime::May, 1958 }, -4, wxDateTime::Mon, wxDateTime::May, 1958 }, + { { 11, wxDateTime::Aug, 1900 }, 2, wxDateTime::Sat, wxDateTime::Aug, 1900 }, + { { 14, wxDateTime::Feb, 1945 }, 2, wxDateTime::Wed, wxDateTime::Feb, 1945 }, + { { 25, wxDateTime::Jul, 1967 }, -1, wxDateTime::Tue, wxDateTime::Jul, 1967 }, + { { 9, wxDateTime::May, 1916 }, -4, wxDateTime::Tue, wxDateTime::May, 1916 }, + { { 20, wxDateTime::Jun, 1927 }, 3, wxDateTime::Mon, wxDateTime::Jun, 1927 }, + { { 2, wxDateTime::Aug, 2000 }, 1, wxDateTime::Wed, wxDateTime::Aug, 2000 }, + { { 20, wxDateTime::Apr, 2044 }, 3, wxDateTime::Wed, wxDateTime::Apr, 2044 }, + { { 20, wxDateTime::Feb, 1932 }, -2, wxDateTime::Sat, wxDateTime::Feb, 1932 }, + { { 25, wxDateTime::Jul, 2069 }, 4, wxDateTime::Thu, wxDateTime::Jul, 2069 }, + { { 3, wxDateTime::Apr, 1925 }, 1, wxDateTime::Fri, wxDateTime::Apr, 1925 }, + { { 21, wxDateTime::Mar, 2093 }, 3, wxDateTime::Sat, wxDateTime::Mar, 2093 }, + { { 3, wxDateTime::Dec, 2074 }, -5, wxDateTime::Mon, wxDateTime::Dec, 2074 }, + }; + + static const char *fmt = "%d-%b-%Y"; + + wxDateTime dt; + for ( n = 0; n < WXSIZEOF(weekDatesTestData); n++ ) + { + const WeekDateTestData& wd = weekDatesTestData[n]; + + dt.SetToWeekDay(wd.wday, wd.nWeek, wd.month, wd.year); + + printf("%s is %s", wd.Format().c_str(), dt.Format(fmt).c_str()); + + const Date& d = wd.date; + if ( d.SameDay(dt.GetTm()) ) + { + puts(" (ok)"); + } + else + { + dt.Set(d.day, d.month, d.year); + + printf(" (ERROR: should be %s)\n", dt.Format(fmt).c_str()); + } + } +} + +// test the computation of (ISO) week numbers +static void TestTimeWNumber() +{ + puts("\n*** wxDateTime week number test ***"); + + struct WeekNumberTestData + { + Date date; // the date + wxDateTime::wxDateTime_t week; // the week number + wxDateTime::wxDateTime_t dnum; // day number in the year + }; + + // data generated with the following python script: + /* +from DateTime import * +from whrandom import * +from string import * + +monthNames = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] +wdayNames = [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ] + +for n in range(20): + year = randint(1900, 2100) + month = randint(1, 12) + day = randint(1, 28) + dt = DateTime(year, month, day) + dayNum = dt.day_of_year + weekNum = dt.iso_week[1] + + data = { 'day': rjust(`day`, 2), 'month': monthNames[month - 1], 'year': year, 'weekNum': rjust(`weekNum`, 2), 'dayNum': rjust(`dayNum`, 3) } + + print "{ { %(day)s, wxDateTime::%(month)s, %(year)d }, %(weekNum)s, "\ + "%(dayNum)s }," % data + */ + static const WeekNumberTestData weekNumberTestDates[] = + { + { { 2, wxDateTime::Jul, 2093 }, 27, 183 }, + { { 25, wxDateTime::Jun, 1986 }, 26, 176 }, + { { 15, wxDateTime::Jun, 2014 }, 24, 166 }, + { { 20, wxDateTime::Jul, 2018 }, 29, 201 }, + { { 3, wxDateTime::Aug, 2074 }, 31, 215 }, + { { 26, wxDateTime::Jul, 2012 }, 30, 208 }, + { { 4, wxDateTime::Nov, 1915 }, 44, 308 }, + { { 11, wxDateTime::Feb, 2035 }, 6, 42 }, + { { 15, wxDateTime::Feb, 1942 }, 7, 46 }, + { { 5, wxDateTime::Jan, 2087 }, 1, 5 }, + { { 6, wxDateTime::Nov, 2016 }, 44, 311 }, + { { 6, wxDateTime::Jun, 2057 }, 23, 157 }, + { { 25, wxDateTime::Feb, 1976 }, 9, 56 }, + { { 12, wxDateTime::Jan, 2073 }, 2, 12 }, + { { 12, wxDateTime::Sep, 2040 }, 37, 256 }, + { { 15, wxDateTime::Jul, 1931 }, 29, 196 }, + { { 23, wxDateTime::Mar, 2084 }, 12, 83 }, + { { 12, wxDateTime::Dec, 1970 }, 50, 346 }, + { { 6, wxDateTime::Sep, 1996 }, 36, 250 }, + { { 7, wxDateTime::Jan, 2076 }, 2, 7 }, + }; + + for ( size_t n = 0; n < WXSIZEOF(weekNumberTestDates); n++ ) + { + const WeekNumberTestData& wn = weekNumberTestDates[n]; + const Date& d = wn.date; + + wxDateTime dt = d.DT(); + + wxDateTime::wxDateTime_t week = dt.GetWeekOfYear(), + dnum = dt.GetDayOfYear(); + + printf("%s: the day number is %d", + d.FormatDate().c_str(), dnum); + if ( dnum == wn.dnum ) + { + printf(" (ok)"); + } + else + { + printf(" (ERROR: should be %d)", wn.dnum); + } + + printf(", week number is %d", week); + if ( week == wn.week ) + { + puts(" (ok)"); + } + else + { + printf(" (ERROR: should be %d)\n", wn.week); + } + } +} + +// test DST calculations +static void TestTimeDST() +{ + puts("\n*** wxDateTime DST test ***"); + + printf("DST is%s in effect now.\n\n", + wxDateTime::Now().IsDST() ? "" : " not"); + + // taken from http://www.energy.ca.gov/daylightsaving.html + static const Date datesDST[2][2004 - 1900 + 1] = + { + { + { 1, wxDateTime::Apr, 1990 }, + { 7, wxDateTime::Apr, 1991 }, + { 5, wxDateTime::Apr, 1992 }, + { 4, wxDateTime::Apr, 1993 }, + { 3, wxDateTime::Apr, 1994 }, + { 2, wxDateTime::Apr, 1995 }, + { 7, wxDateTime::Apr, 1996 }, + { 6, wxDateTime::Apr, 1997 }, + { 5, wxDateTime::Apr, 1998 }, + { 4, wxDateTime::Apr, 1999 }, + { 2, wxDateTime::Apr, 2000 }, + { 1, wxDateTime::Apr, 2001 }, + { 7, wxDateTime::Apr, 2002 }, + { 6, wxDateTime::Apr, 2003 }, + { 4, wxDateTime::Apr, 2004 }, + }, + { + { 28, wxDateTime::Oct, 1990 }, + { 27, wxDateTime::Oct, 1991 }, + { 25, wxDateTime::Oct, 1992 }, + { 31, wxDateTime::Oct, 1993 }, + { 30, wxDateTime::Oct, 1994 }, + { 29, wxDateTime::Oct, 1995 }, + { 27, wxDateTime::Oct, 1996 }, + { 26, wxDateTime::Oct, 1997 }, + { 25, wxDateTime::Oct, 1998 }, + { 31, wxDateTime::Oct, 1999 }, + { 29, wxDateTime::Oct, 2000 }, + { 28, wxDateTime::Oct, 2001 }, + { 27, wxDateTime::Oct, 2002 }, + { 26, wxDateTime::Oct, 2003 }, + { 31, wxDateTime::Oct, 2004 }, + } + }; + + int year; + for ( year = 1990; year < 2005; year++ ) + { + wxDateTime dtBegin = wxDateTime::GetBeginDST(year, wxDateTime::USA), + dtEnd = wxDateTime::GetEndDST(year, wxDateTime::USA); + + printf("DST period in the US for year %d: from %s to %s", + year, dtBegin.Format().c_str(), dtEnd.Format().c_str()); + + size_t n = year - 1990; + const Date& dBegin = datesDST[0][n]; + const Date& dEnd = datesDST[1][n]; + + if ( dBegin.SameDay(dtBegin.GetTm()) && dEnd.SameDay(dtEnd.GetTm()) ) + { + puts(" (ok)"); + } + else + { + printf(" (ERROR: should be %s %d to %s %d)\n", + wxDateTime::GetMonthName(dBegin.month).c_str(), dBegin.day, + wxDateTime::GetMonthName(dEnd.month).c_str(), dEnd.day); + } + } + + puts(""); + + for ( year = 1990; year < 2005; year++ ) + { + printf("DST period in Europe for year %d: from %s to %s\n", + year, + wxDateTime::GetBeginDST(year, wxDateTime::Country_EEC).Format().c_str(), + wxDateTime::GetEndDST(year, wxDateTime::Country_EEC).Format().c_str()); + } } #endif // TEST_TIME @@ -948,8 +1260,10 @@ int main(int argc, char **argv) TestTimeRange(); TestTimeTicks(); TestTimeJDN(); - } + TestTimeDST(); TestTimeWDays(); + } + TestTimeWNumber(); #endif // TEST_TIME wxUninitialize(); diff --git a/src/common/datetime.cpp b/src/common/datetime.cpp index 75ca1af34d..93c8dc6b84 100644 --- a/src/common/datetime.cpp +++ b/src/common/datetime.cpp @@ -300,6 +300,8 @@ void wxDateTime::Tm::AddMonths(int monDiff) while ( monDiff + mon > MONTHS_IN_YEAR ) { year++; + + monDiff -= MONTHS_IN_YEAR; } mon = (wxDateTime::Month)(mon + monDiff); @@ -421,12 +423,6 @@ int wxDateTime::GetCentury(int year) return year > 0 ? year / 100 : year / 100 - 1; } -/* static */ -void wxDateTime::SetCountry(wxDateTime::Country country) -{ - ms_country = country; -} - /* static */ int wxDateTime::ConvertYearToBC(int year) { @@ -552,6 +548,281 @@ wxString wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday, bool abbr) return CallStrftime(abbr ? _T("%a") : _T("%A"), &tm); } +// ---------------------------------------------------------------------------- +// Country stuff: date calculations depend on the country (DST, work days, +// ...), so we need to know which rules to follow. +// ---------------------------------------------------------------------------- + +/* static */ +wxDateTime::Country wxDateTime::GetCountry() +{ + if ( ms_country == Country_Unknown ) + { + // try to guess from the time zone name + time_t t = time(NULL); + struct tm *tm = localtime(&t); + + wxString tz = CallStrftime(_T("%Z"), tm); + if ( tz == _T("WET") || tz == _T("WEST") ) + { + ms_country = UK; + } + else if ( tz == _T("CET") || tz == _T("CEST") ) + { + ms_country = Country_EEC; + } + else if ( tz == _T("MSK") || tz == _T("MSD") ) + { + ms_country = Russia; + } + else if ( tz == _T("AST") || tz == _T("ADT") || + tz == _T("EST") || tz == _T("EDT") || + tz == _T("CST") || tz == _T("CDT") || + tz == _T("MST") || tz == _T("MDT") || + tz == _T("PST") || tz == _T("PDT") ) + { + ms_country = USA; + } + else + { + // well, choose a default one + ms_country = USA; + } + } + + return ms_country; +} + +/* static */ +void wxDateTime::SetCountry(wxDateTime::Country country) +{ + ms_country = country; +} + +/* static */ +bool wxDateTime::IsWestEuropeanCountry(Country country) +{ + if ( country == Country_Default ) + { + country = GetCountry(); + } + + 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 ms_InvDateTime; + } + + 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( _T("no last Sunday in March?") ); + } + + dt += wxTimeSpan::Hours(1); + + dt.MakeGMT(); + } + 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( _T("no first Sunday in April?") ); + } + } + else + { + if ( !dt.SetToWeekDay(Sun, 1, Apr, year) ) + { + // weird... + wxFAIL_MSG( _T("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 ms_InvDateTime; + } + + 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( _T("no last Sunday in October?") ); + } + + dt += wxTimeSpan::Hours(1); + + dt.MakeGMT(); + } + 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: + // DST ends at 2 a.m. on the last Sunday of October + if ( !dt.SetToLastWeekDay(Sun, Oct, year) ) + { + // weirder and weirder... + wxFAIL_MSG( _T("no last Sunday in October?") ); + } + + dt += wxTimeSpan::Hours(2); + + // TODO what about timezone?? + } + break; + + default: + // assume October 26th as the end of the DST - totally bogus too + dt.Set(26, Oct, year); + } + + return dt; +} + // ---------------------------------------------------------------------------- // constructors and assignment operators // ---------------------------------------------------------------------------- @@ -960,9 +1231,9 @@ bool wxDateTime::SetToWeekDay(WeekDay weekday, // add advance n-1 weeks more diff += 7*(n - 1); - dt -= wxDateSpan::Days(diff); + dt += wxDateSpan::Days(diff); } - else + else // count from the end of the month { // get the last day of the month dt.SetToLastMonthDay(month, year); @@ -976,7 +1247,7 @@ bool wxDateTime::SetToWeekDay(WeekDay weekday, diff += 7; // and rewind n-1 weeks from there - diff += 7*(n - 1); + diff += 7*(-n - 1); dt -= wxDateSpan::Days(diff); } @@ -995,6 +1266,46 @@ bool wxDateTime::SetToWeekDay(WeekDay weekday, } } +wxDateTime::wxDateTime_t wxDateTime::GetDayOfYear(const TimeZone& tz) const +{ + // this array contains the cumulated number of days in all previous months + // for normal and leap years + static const wxDateTime_t cumulatedDays[2][MONTHS_IN_YEAR] = + { + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }, + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 } + }; + + Tm tm(GetTm(tz)); + + return cumulatedDays[IsLeapYear(tm.year)][tm.mon] + tm.mday; +} + +wxDateTime::wxDateTime_t wxDateTime::GetWeekOfYear(const TimeZone& tz) const +{ + // the first week of the year is the one which contains Jan, 4 (according + // to ISO standard rule), so the year day N0 = 4 + 7*W always lies in the + // week W+1. As any day N = 7*W + 4 + (N - 4)%7, it lies in the same week + // as N0 or in the next one. + + // TODO this surely may be optimized - I got confused while writing it + + wxDateTime_t nDayInYear = GetDayOfYear(tz); + + // the week day of the day lying in the first week + WeekDay wdayStart = wxDateTime(4, Jan, GetYear()).GetWeekDay(); + + wxDateTime_t week = (nDayInYear - 4) / 7 + 1; + + // notice that Sunday shoould be counted as 7, not 0 here! + if ( ((nDayInYear - 4) % 7) + (!wdayStart ? 7 : wdayStart) > 7 ) + { + week++; + } + + return week; +} + // ---------------------------------------------------------------------------- // Julian day number conversion and related stuff // ---------------------------------------------------------------------------- @@ -1040,9 +1351,15 @@ int wxDateTime::IsDST(wxDateTime::Country country) const } else { - // wxFAIL_MSG( _T("TODO") ); + int year = GetYear(); - return -1; + if ( !IsDSTApplicable(year, country) ) + { + // no DST time in this year in this country + return -1; + } + + return IsBetween(GetBeginDST(year, country), GetEndDST(year, country)); } } @@ -1243,7 +1560,7 @@ wxString wxTimeSpan::Format(const wxChar *format) const break; case 'l': - tmp.Printf(_T("%03d"), GetMilliseconds()); + tmp.Printf(_T("%03d"), GetMilliseconds().GetValue()); break; case 'M': @@ -1251,7 +1568,7 @@ wxString wxTimeSpan::Format(const wxChar *format) const break; case 'S': - tmp.Printf(_T("%02d"), GetSeconds()); + tmp.Printf(_T("%02d"), GetSeconds().GetValue()); break; }