1/////////////////////////////////////////////////////////////////////////////// 2// Name: tests/datetime/datetime.cpp 3// Purpose: wxDateTime unit test 4// Author: Vadim Zeitlin 5// Created: 2004-06-23 (extracted from samples/console/console.cpp) 6// RCS-ID: $Id: datetimetest.cpp 57474 2008-12-21 12:16:06Z VZ $ 7// Copyright: (c) 2004 Vadim Zeitlin <vadim@wxwindows.org> 8/////////////////////////////////////////////////////////////////////////////// 9 10// ---------------------------------------------------------------------------- 11// headers 12// ---------------------------------------------------------------------------- 13 14#include "testprec.h" 15 16#ifdef __BORLANDC__ 17 #pragma hdrstop 18#endif 19 20#ifndef WX_PRECOMP 21#endif // WX_PRECOMP 22 23#if wxUSE_DATETIME 24 25#include "wx/datetime.h" 26#include "wx/ioswrap.h" 27 28// need this to be able to use CPPUNIT_ASSERT_EQUAL with wxDateTime objects 29static std::ostream& operator<<(std::ostream& ostr, const wxDateTime& dt) 30{ 31 ostr << dt.FormatISODate() << " " << dt.FormatISOTime(); 32 33 return ostr; 34} 35 36// to test Today() meaningfully we must be able to change the system date which 37// is not usually the case, but if we're under Win32 we can try it -- define 38// the macro below to do it 39//#define CHANGE_SYSTEM_DATE 40 41#ifndef __WINDOWS__ 42 #undef CHANGE_SYSTEM_DATE 43#endif 44 45#ifdef CHANGE_SYSTEM_DATE 46 47class DateChanger 48{ 49public: 50 DateChanger(int year, int month, int day, int hour, int min, int sec) 51 { 52 SYSTEMTIME st; 53 st.wDay = day; 54 st.wMonth = month; 55 st.wYear = year; 56 st.wHour = hour; 57 st.wMinute = min; 58 st.wSecond = sec; 59 st.wMilliseconds = 0; 60 61 ::GetSystemTime(&m_savedTime); 62 ::GetTimeZoneInformation(&m_tzi); 63 64 m_changed = ::SetSystemTime(&st) != 0; 65 } 66 67 ~DateChanger() 68 { 69 if ( m_changed ) 70 { 71 ::SetSystemTime(&m_savedTime); 72 ::SetTimeZoneInformation(&m_tzi); 73 } 74 } 75 76private: 77 SYSTEMTIME m_savedTime; 78 TIME_ZONE_INFORMATION m_tzi; 79 bool m_changed; 80}; 81 82#endif // CHANGE_SYSTEM_DATE 83 84// ---------------------------------------------------------------------------- 85// broken down date representation used for testing 86// ---------------------------------------------------------------------------- 87 88struct Date 89{ 90 wxDateTime::wxDateTime_t day; 91 wxDateTime::Month month; 92 int year; 93 wxDateTime::wxDateTime_t hour, min, sec; 94 double jdn; 95 wxDateTime::WeekDay wday; 96 time_t gmticks, ticks; 97 98 void Init(const wxDateTime::Tm& tm) 99 { 100 day = tm.mday; 101 month = tm.mon; 102 year = tm.year; 103 hour = tm.hour; 104 min = tm.min; 105 sec = tm.sec; 106 jdn = 0.0; 107 gmticks = ticks = -1; 108 } 109 110 wxDateTime DT() const 111 { return wxDateTime(day, month, year, hour, min, sec); } 112 113 bool SameDay(const wxDateTime::Tm& tm) const 114 { 115 return day == tm.mday && month == tm.mon && year == tm.year; 116 } 117 118 wxString Format() const 119 { 120 wxString s; 121 s.Printf(_T("%02d:%02d:%02d %10s %02d, %4d%s"), 122 hour, min, sec, 123 wxDateTime::GetMonthName(month).c_str(), 124 day, 125 abs(wxDateTime::ConvertYearToBC(year)), 126 year > 0 ? _T("AD") : _T("BC")); 127 return s; 128 } 129 130 wxString FormatDate() const 131 { 132 wxString s; 133 s.Printf(_T("%02d-%s-%4d%s"), 134 day, 135 wxDateTime::GetMonthName(month, wxDateTime::Name_Abbr).c_str(), 136 abs(wxDateTime::ConvertYearToBC(year)), 137 year > 0 ? _T("AD") : _T("BC")); 138 return s; 139 } 140}; 141 142// ---------------------------------------------------------------------------- 143// test data 144// ---------------------------------------------------------------------------- 145 146static const Date testDates[] = 147{ 148 { 1, wxDateTime::Jan, 1970, 00, 00, 00, 2440587.5, wxDateTime::Thu, 0, -3600 }, 149 { 7, wxDateTime::Feb, 2036, 00, 00, 00, 2464730.5, wxDateTime::Thu, -1, -1 }, 150 { 8, wxDateTime::Feb, 2036, 00, 00, 00, 2464731.5, wxDateTime::Fri, -1, -1 }, 151 { 1, wxDateTime::Jan, 2037, 00, 00, 00, 2465059.5, wxDateTime::Thu, -1, -1 }, 152 { 1, wxDateTime::Jan, 2038, 00, 00, 00, 2465424.5, wxDateTime::Fri, -1, -1 }, 153 { 21, wxDateTime::Jan, 2222, 00, 00, 00, 2532648.5, wxDateTime::Mon, -1, -1 }, 154 { 29, wxDateTime::May, 1976, 12, 00, 00, 2442928.0, wxDateTime::Sat, 202219200, 202212000 }, 155 { 29, wxDateTime::Feb, 1976, 00, 00, 00, 2442837.5, wxDateTime::Sun, 194400000, 194396400 }, 156 { 1, wxDateTime::Jan, 1900, 12, 00, 00, 2415021.0, wxDateTime::Mon, -1, -1 }, 157 { 1, wxDateTime::Jan, 1900, 00, 00, 00, 2415020.5, wxDateTime::Mon, -1, -1 }, 158 { 15, wxDateTime::Oct, 1582, 00, 00, 00, 2299160.5, wxDateTime::Fri, -1, -1 }, 159 { 4, wxDateTime::Oct, 1582, 00, 00, 00, 2299149.5, wxDateTime::Mon, -1, -1 }, 160 { 1, wxDateTime::Mar, 1, 00, 00, 00, 1721484.5, wxDateTime::Thu, -1, -1 }, 161 { 1, wxDateTime::Jan, 1, 00, 00, 00, 1721425.5, wxDateTime::Mon, -1, -1 }, 162 { 31, wxDateTime::Dec, 0, 00, 00, 00, 1721424.5, wxDateTime::Sun, -1, -1 }, 163 { 1, wxDateTime::Jan, 0, 00, 00, 00, 1721059.5, wxDateTime::Sat, -1, -1 }, 164 { 12, wxDateTime::Aug, -1234, 00, 00, 00, 1270573.5, wxDateTime::Fri, -1, -1 }, 165 { 12, wxDateTime::Aug, -4000, 00, 00, 00, 260313.5, wxDateTime::Sat, -1, -1 }, 166 { 24, wxDateTime::Nov, -4713, 00, 00, 00, -0.5, wxDateTime::Mon, -1, -1 }, 167}; 168 169 170// ---------------------------------------------------------------------------- 171// test class 172// ---------------------------------------------------------------------------- 173 174class DateTimeTestCase : public CppUnit::TestCase 175{ 176public: 177 DateTimeTestCase() { } 178 179private: 180 CPPUNIT_TEST_SUITE( DateTimeTestCase ); 181 CPPUNIT_TEST( TestLeapYears ); 182 CPPUNIT_TEST( TestTimeSet ); 183 CPPUNIT_TEST( TestTimeJDN ); 184 CPPUNIT_TEST( TestTimeWNumber ); 185 CPPUNIT_TEST( TestTimeWDays ); 186 CPPUNIT_TEST( TestTimeDST ); 187 CPPUNIT_TEST( TestTimeFormat ); 188 CPPUNIT_TEST( TestTimeSpanFormat ); 189 CPPUNIT_TEST( TestTimeTicks ); 190 CPPUNIT_TEST( TestParceRFC822 ); 191 CPPUNIT_TEST( TestDateParse ); 192 CPPUNIT_TEST( TestTimeArithmetics ); 193 // FIXME: this test fails on test drive machine, disabling it until 194 // someone has time to look at it 195 //CPPUNIT_TEST( TestDSTBug ); 196 CPPUNIT_TEST( TestDateOnly ); 197 CPPUNIT_TEST_SUITE_END(); 198 199 void TestLeapYears(); 200 void TestTimeSet(); 201 void TestTimeJDN(); 202 void TestTimeWNumber(); 203 void TestTimeWDays(); 204 void TestTimeDST(); 205 void TestTimeFormat(); 206 void TestTimeSpanFormat(); 207 void TestTimeTicks(); 208 void TestParceRFC822(); 209 void TestDateParse(); 210 void TestTimeArithmetics(); 211 void TestDSTBug(); 212 void TestDateOnly(); 213 214 DECLARE_NO_COPY_CLASS(DateTimeTestCase) 215}; 216 217// register in the unnamed registry so that these tests are run by default 218CPPUNIT_TEST_SUITE_REGISTRATION( DateTimeTestCase ); 219 220// also include in it's own registry so that these tests can be run alone 221CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( DateTimeTestCase, "DateTimeTestCase" ); 222 223// ============================================================================ 224// implementation 225// ============================================================================ 226 227// test leap years detection 228void DateTimeTestCase::TestLeapYears() 229{ 230 static const struct LeapYearTestData 231 { 232 int year; 233 bool isLeap; 234 } years[] = 235 { 236 { 1900, false }, 237 { 1990, false }, 238 { 1976, true }, 239 { 2000, true }, 240 { 2030, false }, 241 { 1984, true }, 242 { 2100, false }, 243 { 2400, true }, 244 }; 245 246 for ( size_t n = 0; n < WXSIZEOF(years); n++ ) 247 { 248 const LeapYearTestData& y = years[n]; 249 250 CPPUNIT_ASSERT( wxDateTime::IsLeapYear(y.year) == y.isLeap ); 251 } 252} 253 254// test constructing wxDateTime objects 255void DateTimeTestCase::TestTimeSet() 256{ 257 for ( size_t n = 0; n < WXSIZEOF(testDates); n++ ) 258 { 259 const Date& d1 = testDates[n]; 260 wxDateTime dt = d1.DT(); 261 262 Date d2; 263 d2.Init(dt.GetTm()); 264 265 wxString s1 = d1.Format(), 266 s2 = d2.Format(); 267 268 CPPUNIT_ASSERT( s1 == s2 ); 269 } 270} 271 272// test conversions to JDN &c 273void DateTimeTestCase::TestTimeJDN() 274{ 275 for ( size_t n = 0; n < WXSIZEOF(testDates); n++ ) 276 { 277 const Date& d = testDates[n]; 278 wxDateTime dt(d.day, d.month, d.year, d.hour, d.min, d.sec); 279 280 // JDNs must be computed for UTC times 281 double jdn = dt.FromUTC().GetJulianDayNumber(); 282 283 CPPUNIT_ASSERT( jdn == d.jdn ); 284 285 dt.Set(jdn); 286 CPPUNIT_ASSERT( dt.GetJulianDayNumber() == jdn ); 287 } 288} 289 290// test week days computation 291void DateTimeTestCase::TestTimeWDays() 292{ 293 // test GetWeekDay() 294 size_t n; 295 for ( n = 0; n < WXSIZEOF(testDates); n++ ) 296 { 297 const Date& d = testDates[n]; 298 wxDateTime dt(d.day, d.month, d.year, d.hour, d.min, d.sec); 299 300 wxDateTime::WeekDay wday = dt.GetWeekDay(); 301 CPPUNIT_ASSERT( wday == d.wday ); 302 } 303 304 // test SetToWeekDay() 305 struct WeekDateTestData 306 { 307 Date date; // the real date (precomputed) 308 int nWeek; // its week index in the month 309 wxDateTime::WeekDay wday; // the weekday 310 wxDateTime::Month month; // the month 311 int year; // and the year 312 313 wxString Format() const 314 { 315 wxString s, which; 316 switch ( nWeek < -1 ? -nWeek : nWeek ) 317 { 318 case 1: which = _T("first"); break; 319 case 2: which = _T("second"); break; 320 case 3: which = _T("third"); break; 321 case 4: which = _T("fourth"); break; 322 case 5: which = _T("fifth"); break; 323 324 case -1: which = _T("last"); break; 325 } 326 327 if ( nWeek < -1 ) 328 { 329 which += _T(" from end"); 330 } 331 332 s.Printf(_T("The %s %s of %s in %d"), 333 which.c_str(), 334 wxDateTime::GetWeekDayName(wday).c_str(), 335 wxDateTime::GetMonthName(month).c_str(), 336 year); 337 338 return s; 339 } 340 }; 341 342 // the array data was generated by the following python program 343 /* 344from DateTime import * 345from whrandom import * 346from string import * 347 348monthNames = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] 349wdayNames = [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ] 350 351week = DateTimeDelta(7) 352 353for n in range(20): 354 year = randint(1900, 2100) 355 month = randint(1, 12) 356 day = randint(1, 28) 357 dt = DateTime(year, month, day) 358 wday = dt.day_of_week 359 360 countFromEnd = choice([-1, 1]) 361 weekNum = 0; 362 363 while dt.month is month: 364 dt = dt - countFromEnd * week 365 weekNum = weekNum + countFromEnd 366 367 data = { 'day': rjust(`day`, 2), 'month': monthNames[month - 1], 'year': year, 'weekNum': rjust(`weekNum`, 2), 'wday': wdayNames[wday] } 368 369 print "{ { %(day)s, wxDateTime::%(month)s, %(year)d }, %(weekNum)d, "\ 370 "wxDateTime::%(wday)s, wxDateTime::%(month)s, %(year)d }," % data 371 */ 372 373 static const WeekDateTestData weekDatesTestData[] = 374 { 375 { { 20, wxDateTime::Mar, 2045, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 3, wxDateTime::Mon, wxDateTime::Mar, 2045 }, 376 { { 5, wxDateTime::Jun, 1985, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, -4, wxDateTime::Wed, wxDateTime::Jun, 1985 }, 377 { { 12, wxDateTime::Nov, 1961, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, -3, wxDateTime::Sun, wxDateTime::Nov, 1961 }, 378 { { 27, wxDateTime::Feb, 2093, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, -1, wxDateTime::Fri, wxDateTime::Feb, 2093 }, 379 { { 4, wxDateTime::Jul, 2070, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, -4, wxDateTime::Fri, wxDateTime::Jul, 2070 }, 380 { { 2, wxDateTime::Apr, 1906, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, -5, wxDateTime::Mon, wxDateTime::Apr, 1906 }, 381 { { 19, wxDateTime::Jul, 2023, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, -2, wxDateTime::Wed, wxDateTime::Jul, 2023 }, 382 { { 5, wxDateTime::May, 1958, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, -4, wxDateTime::Mon, wxDateTime::May, 1958 }, 383 { { 11, wxDateTime::Aug, 1900, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 2, wxDateTime::Sat, wxDateTime::Aug, 1900 }, 384 { { 14, wxDateTime::Feb, 1945, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 2, wxDateTime::Wed, wxDateTime::Feb, 1945 }, 385 { { 25, wxDateTime::Jul, 1967, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, -1, wxDateTime::Tue, wxDateTime::Jul, 1967 }, 386 { { 9, wxDateTime::May, 1916, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, -4, wxDateTime::Tue, wxDateTime::May, 1916 }, 387 { { 20, wxDateTime::Jun, 1927, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 3, wxDateTime::Mon, wxDateTime::Jun, 1927 }, 388 { { 2, wxDateTime::Aug, 2000, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 1, wxDateTime::Wed, wxDateTime::Aug, 2000 }, 389 { { 20, wxDateTime::Apr, 2044, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 3, wxDateTime::Wed, wxDateTime::Apr, 2044 }, 390 { { 20, wxDateTime::Feb, 1932, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, -2, wxDateTime::Sat, wxDateTime::Feb, 1932 }, 391 { { 25, wxDateTime::Jul, 2069, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 4, wxDateTime::Thu, wxDateTime::Jul, 2069 }, 392 { { 3, wxDateTime::Apr, 1925, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 1, wxDateTime::Fri, wxDateTime::Apr, 1925 }, 393 { { 21, wxDateTime::Mar, 2093, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 3, wxDateTime::Sat, wxDateTime::Mar, 2093 }, 394 { { 3, wxDateTime::Dec, 2074, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, -5, wxDateTime::Mon, wxDateTime::Dec, 2074 } 395 }; 396 397 wxDateTime dt; 398 for ( n = 0; n < WXSIZEOF(weekDatesTestData); n++ ) 399 { 400 const WeekDateTestData& wd = weekDatesTestData[n]; 401 402 dt.SetToWeekDay(wd.wday, wd.nWeek, wd.month, wd.year); 403 404 const Date& d = wd.date; 405 CPPUNIT_ASSERT( d.SameDay(dt.GetTm()) ); 406 } 407} 408 409// test the computation of (ISO) week numbers 410void DateTimeTestCase::TestTimeWNumber() 411{ 412 struct WeekNumberTestData 413 { 414 Date date; // the date 415 wxDateTime::wxDateTime_t week; // the week number in the year 416 wxDateTime::wxDateTime_t wmon; // the week number in the month 417 wxDateTime::wxDateTime_t wmon2; // same but week starts with Sun 418 wxDateTime::wxDateTime_t dnum; // day number in the year 419 }; 420 421 // data generated with the following python script: 422 /* 423from DateTime import * 424from whrandom import * 425from string import * 426 427monthNames = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] 428wdayNames = [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ] 429 430def GetMonthWeek(dt): 431 weekNumMonth = dt.iso_week[1] - DateTime(dt.year, dt.month, 1).iso_week[1] + 1 432 if weekNumMonth < 0: 433 weekNumMonth = weekNumMonth + 53 434 return weekNumMonth 435 436def GetLastSundayBefore(dt): 437 if dt.iso_week[2] == 7: 438 return dt 439 else: 440 return dt - DateTimeDelta(dt.iso_week[2]) 441 442for n in range(20): 443 year = randint(1900, 2100) 444 month = randint(1, 12) 445 day = randint(1, 28) 446 dt = DateTime(year, month, day) 447 dayNum = dt.day_of_year 448 weekNum = dt.iso_week[1] 449 weekNumMonth = GetMonthWeek(dt) 450 451 weekNumMonth2 = 0 452 dtSunday = GetLastSundayBefore(dt) 453 454 while dtSunday >= GetLastSundayBefore(DateTime(dt.year, dt.month, 1)): 455 weekNumMonth2 = weekNumMonth2 + 1 456 dtSunday = dtSunday - DateTimeDelta(7) 457 458 data = { 'day': rjust(`day`, 2), \ 459 'month': monthNames[month - 1], \ 460 'year': year, \ 461 'weekNum': rjust(`weekNum`, 2), \ 462 'weekNumMonth': weekNumMonth, \ 463 'weekNumMonth2': weekNumMonth2, \ 464 'dayNum': rjust(`dayNum`, 3) } 465 466 print " { { %(day)s, "\ 467 "wxDateTime::%(month)s, "\ 468 "%(year)d }, "\ 469 "%(weekNum)s, "\ 470 "%(weekNumMonth)s, "\ 471 "%(weekNumMonth2)s, "\ 472 "%(dayNum)s }," % data 473 474 */ 475 static const WeekNumberTestData weekNumberTestDates[] = 476 { 477 { { 27, wxDateTime::Dec, 1966, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 52, 5, 5, 361 }, 478 { { 22, wxDateTime::Jul, 1926, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 29, 4, 4, 203 }, 479 { { 22, wxDateTime::Oct, 2076, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 43, 4, 4, 296 }, 480 { { 1, wxDateTime::Jul, 1967, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 26, 1, 1, 182 }, 481 { { 8, wxDateTime::Nov, 2004, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 46, 2, 2, 313 }, 482 { { 21, wxDateTime::Mar, 1920, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 12, 3, 4, 81 }, 483 { { 7, wxDateTime::Jan, 1965, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 1, 2, 2, 7 }, 484 { { 19, wxDateTime::Oct, 1999, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 42, 4, 4, 292 }, 485 { { 13, wxDateTime::Aug, 1955, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 32, 2, 2, 225 }, 486 { { 18, wxDateTime::Jul, 2087, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 29, 3, 3, 199 }, 487 { { 2, wxDateTime::Sep, 2028, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 35, 1, 1, 246 }, 488 { { 28, wxDateTime::Jul, 1945, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 30, 5, 4, 209 }, 489 { { 15, wxDateTime::Jun, 1901, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 24, 3, 3, 166 }, 490 { { 10, wxDateTime::Oct, 1939, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 41, 3, 2, 283 }, 491 { { 3, wxDateTime::Dec, 1965, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 48, 1, 1, 337 }, 492 { { 23, wxDateTime::Feb, 1940, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 8, 4, 4, 54 }, 493 { { 2, wxDateTime::Jan, 1987, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 1, 1, 1, 2 }, 494 { { 11, wxDateTime::Aug, 2079, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 32, 2, 2, 223 }, 495 { { 2, wxDateTime::Feb, 2063, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 5, 1, 1, 33 }, 496 { { 16, wxDateTime::Oct, 1942, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 42, 3, 3, 289 }, 497 { { 30, wxDateTime::Dec, 2003, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 1, 5, 5, 364 }, 498 { { 2, wxDateTime::Jan, 2004, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 1, 1, 1, 2 }, 499 }; 500 501 for ( size_t n = 0; n < WXSIZEOF(weekNumberTestDates); n++ ) 502 { 503 const WeekNumberTestData& wn = weekNumberTestDates[n]; 504 const Date& d = wn.date; 505 506 wxDateTime dt = d.DT(); 507 508 wxDateTime::wxDateTime_t 509 week = dt.GetWeekOfYear(wxDateTime::Monday_First), 510 wmon = dt.GetWeekOfMonth(wxDateTime::Monday_First), 511 wmon2 = dt.GetWeekOfMonth(wxDateTime::Sunday_First), 512 dnum = dt.GetDayOfYear(); 513 514 CPPUNIT_ASSERT( dnum == wn.dnum ); 515 CPPUNIT_ASSERT( wmon == wn.wmon ); 516 CPPUNIT_ASSERT( wmon2 == wn.wmon2 ); 517 CPPUNIT_ASSERT( week == wn.week ); 518 519 int year = d.year; 520 if ( week == 1 && d.month != wxDateTime::Jan ) 521 { 522 // this means we're in the first week of the next year 523 year++; 524 } 525 526 wxDateTime 527 dt2 = wxDateTime::SetToWeekOfYear(year, week, dt.GetWeekDay()); 528 CPPUNIT_ASSERT( dt2 == dt ); 529 } 530} 531 532// test DST applicability 533void DateTimeTestCase::TestTimeDST() 534{ 535 // taken from http://www.energy.ca.gov/daylightsaving.html 536 static const Date datesDST[2][2004 - 1900 + 1] = 537 { 538 { 539 { 1, wxDateTime::Apr, 1990, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 540 { 7, wxDateTime::Apr, 1991, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 541 { 5, wxDateTime::Apr, 1992, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 542 { 4, wxDateTime::Apr, 1993, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 543 { 3, wxDateTime::Apr, 1994, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 544 { 2, wxDateTime::Apr, 1995, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 545 { 7, wxDateTime::Apr, 1996, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 546 { 6, wxDateTime::Apr, 1997, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 547 { 5, wxDateTime::Apr, 1998, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 548 { 4, wxDateTime::Apr, 1999, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 549 { 2, wxDateTime::Apr, 2000, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 550 { 1, wxDateTime::Apr, 2001, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 551 { 7, wxDateTime::Apr, 2002, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 552 { 6, wxDateTime::Apr, 2003, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 553 { 4, wxDateTime::Apr, 2004, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 554 }, 555 { 556 { 28, wxDateTime::Oct, 1990, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 557 { 27, wxDateTime::Oct, 1991, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 558 { 25, wxDateTime::Oct, 1992, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 559 { 31, wxDateTime::Oct, 1993, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 560 { 30, wxDateTime::Oct, 1994, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 561 { 29, wxDateTime::Oct, 1995, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 562 { 27, wxDateTime::Oct, 1996, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 563 { 26, wxDateTime::Oct, 1997, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 564 { 25, wxDateTime::Oct, 1998, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 565 { 31, wxDateTime::Oct, 1999, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 566 { 29, wxDateTime::Oct, 2000, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 567 { 28, wxDateTime::Oct, 2001, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 568 { 27, wxDateTime::Oct, 2002, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 569 { 26, wxDateTime::Oct, 2003, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 570 { 31, wxDateTime::Oct, 2004, 0, 0, 0, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, 571 } 572 }; 573 574 for ( int year = 1990; year < 2005; year++ ) 575 { 576 wxDateTime dtBegin = wxDateTime::GetBeginDST(year, wxDateTime::USA), 577 dtEnd = wxDateTime::GetEndDST(year, wxDateTime::USA); 578 579 size_t n = year - 1990; 580 const Date& dBegin = datesDST[0][n]; 581 const Date& dEnd = datesDST[1][n]; 582 583 CPPUNIT_ASSERT( dBegin.SameDay(dtBegin.GetTm()) ); 584 CPPUNIT_ASSERT( dEnd.SameDay(dtEnd.GetTm()) ); 585 } 586} 587 588// test wxDateTime -> text conversion 589void DateTimeTestCase::TestTimeFormat() 590{ 591 // some information may be lost during conversion, so store what kind 592 // of info should we recover after a round trip 593 enum CompareKind 594 { 595 CompareNone, // don't try comparing 596 CompareBoth, // dates and times should be identical 597 CompareYear, // don't compare centuries (fails for 2 digit years) 598 CompareDate, // dates only 599 CompareTime // time only 600 }; 601 602 static const struct 603 { 604 CompareKind compareKind; 605 const wxChar *format; 606 } formatTestFormats[] = 607 { 608 { CompareYear, _T("---> %c") }, // %c could use 2 digit years 609 { CompareDate, _T("Date is %A, %d of %B, in year %Y") }, 610 { CompareYear, _T("Date is %x, time is %X") }, // %x could use 2 digits 611 { CompareTime, _T("Time is %H:%M:%S or %I:%M:%S %p") }, 612 { CompareNone, _T("The day of year: %j, the week of year: %W") }, 613 { CompareDate, _T("ISO date without separators: %Y%m%d") }, 614 }; 615 616 static const Date formatTestDates[] = 617 { 618 { 29, wxDateTime::May, 1976, 18, 30, 00, 0.0, wxDateTime::Inv_WeekDay }, 619 { 31, wxDateTime::Dec, 1999, 23, 30, 00, 0.0, wxDateTime::Inv_WeekDay }, 620 { 6, wxDateTime::Feb, 1937, 23, 30, 00, 0.0, wxDateTime::Inv_WeekDay }, 621 { 6, wxDateTime::Feb, 1856, 23, 30, 00, 0.0, wxDateTime::Inv_WeekDay }, 622 { 6, wxDateTime::Feb, 1857, 23, 30, 00, 0.0, wxDateTime::Inv_WeekDay }, 623 { 29, wxDateTime::May, 2076, 18, 30, 00, 0.0, wxDateTime::Inv_WeekDay }, 624 { 29, wxDateTime::Feb, 2400, 02, 15, 25, 0.0, wxDateTime::Inv_WeekDay }, 625#if 0 626 // Need to add support for BCE dates. 627 { 01, wxDateTime::Jan, -52, 03, 16, 47, 0.0, wxDateTime::Inv_WeekDay }, 628#endif 629 }; 630 631 for ( size_t d = 0; d < WXSIZEOF(formatTestDates); d++ ) 632 { 633 wxDateTime dt = formatTestDates[d].DT(); 634 for ( size_t n = 0; n < WXSIZEOF(formatTestFormats); n++ ) 635 { 636 const wxChar *fmt = formatTestFormats[n].format; 637 wxString s = dt.Format(fmt); 638 639 // what can we recover? 640 CompareKind kind = formatTestFormats[n].compareKind; 641 642 // convert back 643 wxDateTime dt2; 644 const wxChar *result = dt2.ParseFormat(s, fmt); 645 if ( !result ) 646 { 647 // converion failed - should it have? 648 CPPUNIT_ASSERT( kind == CompareNone ); 649 } 650 else // conversion succeeded 651 { 652 // should have parsed the entire string 653 CPPUNIT_ASSERT( !*result ); 654 655 switch ( kind ) 656 { 657 case CompareYear: 658 if ( dt2.GetCentury() != dt.GetCentury() ) 659 { 660 CPPUNIT_ASSERT_EQUAL(dt.GetYear() % 100, 661 dt2.GetYear() % 100); 662 663 dt2.SetYear(dt.GetYear()); 664 } 665 // fall through and compare everything 666 667 case CompareBoth: 668 CPPUNIT_ASSERT_EQUAL( dt, dt2 ); 669 break; 670 671 case CompareDate: 672 CPPUNIT_ASSERT( dt.IsSameDate(dt2) ); 673 break; 674 675 case CompareTime: 676 CPPUNIT_ASSERT( dt.IsSameTime(dt2) ); 677 break; 678 679 case CompareNone: 680 wxFAIL_MSG( _T("unexpected") ); 681 break; 682 } 683 } 684 } 685 } 686} 687 688void DateTimeTestCase::TestTimeSpanFormat() 689{ 690 static const struct TimeSpanFormatTestData 691 { 692 long h, min, sec, msec; 693 const wxChar *fmt; 694 const wxChar *result; 695 } testSpans[] = 696 { 697 { 12, 34, 56, 789, _T("%H:%M:%S.%l"), _T("12:34:56.789") }, 698 { 1, 2, 3, 0, _T("%H:%M:%S"), _T("01:02:03") }, 699 { 1, 2, 3, 0, _T("%S"), _T("3723") }, 700 { -1, -2, -3, 0, _T("%S"), _T("-3723") }, 701 { -1, -2, -3, 0, _T("%H:%M:%S"), _T("-01:02:03") }, 702 { 26, 0, 0, 0, _T("%H"), _T("26") }, 703 { 26, 0, 0, 0, _T("%D, %H"), _T("1, 02") }, 704 { -26, 0, 0, 0, _T("%H"), _T("-26") }, 705 { -26, 0, 0, 0, _T("%D, %H"), _T("-1, 02") }, 706 { 219, 0, 0, 0, _T("%H"), _T("219") }, 707 { 219, 0, 0, 0, _T("%D, %H"), _T("9, 03") }, 708 { 219, 0, 0, 0, _T("%E, %D, %H"), _T("1, 2, 03") }, 709 { 0, -1, 0, 0, _T("%H:%M:%S"), _T("-00:01:00") }, 710 { 0, 0, -1, 0, _T("%H:%M:%S"), _T("-00:00:01") }, 711 }; 712 713 for ( size_t n = 0; n < WXSIZEOF(testSpans); n++ ) 714 { 715 const TimeSpanFormatTestData& td = testSpans[n]; 716 wxTimeSpan ts(td.h, td.min, td.sec, td.msec); 717 CPPUNIT_ASSERT_EQUAL( wxString(td.result), ts.Format(td.fmt) ); 718 } 719} 720 721void DateTimeTestCase::TestTimeTicks() 722{ 723 static const wxDateTime::TimeZone TZ_LOCAL(wxDateTime::Local); 724 static const wxDateTime::TimeZone TZ_TEST(wxDateTime::NZST); 725 726 // this offset is needed to make the test work in any time zone when we 727 // only have expected test results in UTC in testDates 728 static const long tzOffset = TZ_LOCAL.GetOffset() - TZ_TEST.GetOffset(); 729 730 for ( size_t n = 0; n < WXSIZEOF(testDates); n++ ) 731 { 732 const Date& d = testDates[n]; 733 if ( d.gmticks == -1 ) 734 continue; 735 736 wxDateTime dt = d.DT().MakeTimezone(TZ_TEST, true /* no DST */); 737 738 // GetValue() returns internal UTC-based representation, we need to 739 // convert it to local TZ before comparing 740 long ticks = (dt.GetValue() / 1000).ToLong() + TZ_LOCAL.GetOffset(); 741 if ( dt.IsDST() ) 742 ticks += 3600; 743 WX_ASSERT_TIME_T_EQUAL( d.gmticks, ticks + tzOffset ); 744 745 dt = d.DT().FromTimezone(wxDateTime::UTC); 746 ticks = (dt.GetValue() / 1000).ToLong(); 747 WX_ASSERT_TIME_T_EQUAL( d.gmticks, ticks ); 748 } 749} 750 751// test parsing dates in RFC822 format 752void DateTimeTestCase::TestParceRFC822() 753{ 754 static const struct ParseTestData 755 { 756 const wxChar *rfc822; 757 Date date; // NB: this should be in UTC 758 bool good; 759 } parseTestDates[] = 760 { 761 { 762 _T("Sat, 18 Dec 1999 00:46:40 +0100"), 763 { 17, wxDateTime::Dec, 1999, 23, 46, 40 }, 764 true 765 }, 766 { 767 _T("Wed, 1 Dec 1999 05:17:20 +0300"), 768 { 1, wxDateTime::Dec, 1999, 2, 17, 20 }, 769 true 770 }, 771 { 772 _T("Sun, 28 Aug 2005 03:31:30 +0200"), 773 { 28, wxDateTime::Aug, 2005, 1, 31, 30 }, 774 true 775 }, 776 777 { 778 _T("Sat, 18 Dec 1999 10:48:30 -0500"), 779 { 18, wxDateTime::Dec, 1999, 15, 48, 30 }, 780 true 781 }, 782 }; 783 784 for ( size_t n = 0; n < WXSIZEOF(parseTestDates); n++ ) 785 { 786 wxDateTime dt; 787 if ( dt.ParseRfc822Date(parseTestDates[n].rfc822) ) 788 { 789 CPPUNIT_ASSERT( parseTestDates[n].good ); 790 791 wxDateTime dtReal = parseTestDates[n].date.DT().FromUTC(); 792 CPPUNIT_ASSERT_EQUAL( dtReal, dt ); 793 } 794 else // failed to parse 795 { 796 CPPUNIT_ASSERT( !parseTestDates[n].good ); 797 } 798 } 799} 800 801// test parsing dates in free format 802void DateTimeTestCase::TestDateParse() 803{ 804 static const struct ParseTestData 805 { 806 const wxChar *str; 807 Date date; // NB: this should be in UTC 808 bool good; 809 } parseTestDates[] = 810 { 811 { _T("21 Mar 2006"), { 21, wxDateTime::Mar, 2006 }, true }, 812 { _T("29 Feb 1976"), { 29, wxDateTime::Feb, 1976 }, true }, 813 { _T("Feb 29 1976"), { 29, wxDateTime::Feb, 1976 }, true }, 814 { _T("31/03/06"), { 31, wxDateTime::Mar, 6 }, true }, 815 { _T("31/03/2006"), { 31, wxDateTime::Mar, 2006 }, true }, 816 817 // some invalid ones too 818 { _T("29 Feb 2006") }, 819 { _T("31/04/06") }, 820 { _T("bloordyblop") } 821 }; 822 823 // special cases 824 wxDateTime dt; 825 CPPUNIT_ASSERT( dt.ParseDate(_T("today")) ); 826 CPPUNIT_ASSERT_EQUAL( wxDateTime::Today(), dt ); 827 828 for ( size_t n = 0; n < WXSIZEOF(parseTestDates); n++ ) 829 { 830 wxDateTime dt; 831 if ( dt.ParseDate(parseTestDates[n].str) ) 832 { 833 CPPUNIT_ASSERT( parseTestDates[n].good ); 834 835 CPPUNIT_ASSERT_EQUAL( parseTestDates[n].date.DT(), dt ); 836 } 837 else // failed to parse 838 { 839 CPPUNIT_ASSERT( !parseTestDates[n].good ); 840 } 841 } 842} 843 844void DateTimeTestCase::TestTimeArithmetics() 845{ 846 static const wxDateSpan testArithmData[] = 847 { 848 wxDateSpan::Day(), 849 wxDateSpan::Week(), 850 wxDateSpan::Month(), 851 wxDateSpan::Year(), 852 }; 853 854 // the test will *not* work with arbitrary date! 855 wxDateTime dt(2, wxDateTime::Dec, 1999), 856 dt1, 857 dt2; 858 859 for ( size_t n = 0; n < WXSIZEOF(testArithmData); n++ ) 860 { 861 const wxDateSpan& span = testArithmData[n]; 862 dt1 = dt + span; 863 dt2 = dt - span; 864 865 CPPUNIT_ASSERT( dt1 - span == dt ); 866 CPPUNIT_ASSERT( dt2 + span == dt ); 867 CPPUNIT_ASSERT( dt2 + 2*span == dt1 ); 868 } 869} 870 871void DateTimeTestCase::TestDSTBug() 872{ 873 ///////////////////////// 874 // Test GetEndDST() 875 wxDateTime dt = wxDateTime::GetEndDST(2004); 876 CPPUNIT_ASSERT_EQUAL(31, (int)dt.GetDay()); 877 CPPUNIT_ASSERT_EQUAL(wxDateTime::Oct, dt.GetMonth()); 878 CPPUNIT_ASSERT_EQUAL(2004, (int)dt.GetYear()); 879 CPPUNIT_ASSERT_EQUAL(2, (int)dt.GetHour()); 880 CPPUNIT_ASSERT_EQUAL(0, (int)dt.GetMinute()); 881 CPPUNIT_ASSERT_EQUAL(0, (int)dt.GetSecond()); 882 CPPUNIT_ASSERT_EQUAL(0, (int)dt.GetMillisecond()); 883 884 ///////////////////////// 885 // Test ResetTime() 886 dt.SetHour(5); 887 CPPUNIT_ASSERT_EQUAL(5, (int)dt.GetHour()); 888 dt.ResetTime(); 889 CPPUNIT_ASSERT_EQUAL(31, (int)dt.GetDay()); 890 CPPUNIT_ASSERT_EQUAL(wxDateTime::Oct, dt.GetMonth()); 891 CPPUNIT_ASSERT_EQUAL(2004, (int)dt.GetYear()); 892 CPPUNIT_ASSERT_EQUAL(0, (int)dt.GetHour()); 893 CPPUNIT_ASSERT_EQUAL(0, (int)dt.GetMinute()); 894 CPPUNIT_ASSERT_EQUAL(0, (int)dt.GetSecond()); 895 CPPUNIT_ASSERT_EQUAL(0, (int)dt.GetMillisecond()); 896 897 dt.Set(1, 0, 0, 0); 898 CPPUNIT_ASSERT_EQUAL(1, (int)dt.GetHour()); 899 900 ///////////////////////// 901 // Test Today() 902#ifdef CHANGE_SYSTEM_DATE 903 { 904 DateChanger change(2004, 10, 31, 5, 0, 0); 905 dt = wxDateTime::Today(); 906 } 907 908 CPPUNIT_ASSERT_EQUAL(31, (int)dt.GetDay()); 909 CPPUNIT_ASSERT_EQUAL(wxDateTime::Oct, dt.GetMonth()); 910 CPPUNIT_ASSERT_EQUAL(2004, (int)dt.GetYear()); 911 CPPUNIT_ASSERT_EQUAL(0, (int)dt.GetHour()); 912 CPPUNIT_ASSERT_EQUAL(0, (int)dt.GetMinute()); 913 CPPUNIT_ASSERT_EQUAL(0, (int)dt.GetSecond()); 914 CPPUNIT_ASSERT_EQUAL(0, (int)dt.GetMillisecond()); 915 916 ///////////////////////// 917 // Test Set(hour, minute, second, milli) 918 wxDateTime dt2; 919 { 920 DateChanger change(2004, 10, 31, 5, 0, 0); 921 dt.Set(1, 30, 0, 0); 922 dt2.Set(5, 30, 0, 0); 923 } 924 925 CPPUNIT_ASSERT_EQUAL(31, (int)dt.GetDay()); 926 CPPUNIT_ASSERT_EQUAL(wxDateTime::Oct, dt.GetMonth()); 927 CPPUNIT_ASSERT_EQUAL(2004, (int)dt.GetYear()); 928 CPPUNIT_ASSERT_EQUAL(1, (int)dt.GetHour()); 929 CPPUNIT_ASSERT_EQUAL(30, (int)dt.GetMinute()); 930 CPPUNIT_ASSERT_EQUAL(0, (int)dt.GetSecond()); 931 CPPUNIT_ASSERT_EQUAL(0, (int)dt.GetMillisecond()); 932 933 CPPUNIT_ASSERT_EQUAL(31, (int)dt2.GetDay()); 934 CPPUNIT_ASSERT_EQUAL(wxDateTime::Oct, dt2.GetMonth()); 935 CPPUNIT_ASSERT_EQUAL(2004, (int)dt2.GetYear()); 936 CPPUNIT_ASSERT_EQUAL(5, (int)dt2.GetHour()); 937 CPPUNIT_ASSERT_EQUAL(30, (int)dt2.GetMinute()); 938 CPPUNIT_ASSERT_EQUAL(0, (int)dt2.GetSecond()); 939 CPPUNIT_ASSERT_EQUAL(0, (int)dt2.GetMillisecond()); 940#endif // CHANGE_SYSTEM_DATE 941} 942 943void DateTimeTestCase::TestDateOnly() 944{ 945 wxDateTime dt(19, wxDateTime::Jan, 2007, 15, 01, 00); 946 947 static const wxDateTime::wxDateTime_t DATE_ZERO = 0; 948 CPPUNIT_ASSERT_EQUAL( DATE_ZERO, dt.GetDateOnly().GetHour() ); 949 CPPUNIT_ASSERT_EQUAL( DATE_ZERO, dt.GetDateOnly().GetMinute() ); 950 CPPUNIT_ASSERT_EQUAL( DATE_ZERO, dt.GetDateOnly().GetSecond() ); 951 CPPUNIT_ASSERT_EQUAL( DATE_ZERO, dt.GetDateOnly().GetMillisecond() ); 952 953 dt.ResetTime(); 954 CPPUNIT_ASSERT_EQUAL( wxDateTime(19, wxDateTime::Jan, 2007), dt ); 955 956 CPPUNIT_ASSERT_EQUAL( wxDateTime::Today(), wxDateTime::Now().GetDateOnly() ); 957} 958 959#endif // wxUSE_DATETIME 960