wxLongLong wxGetLocalTimeMillis() { return wxGetUTCTimeMillis() - wxGetTimeZone()*MILLISECONDS_PER_SECOND; }
// test wxDateTime -> text conversion void DateTimeTestCase::TestTimeFormat() { // some information may be lost during conversion, so store what kind // of info should we recover after a round trip enum CompareKind { CompareNone, // don't try comparing CompareBoth, // dates and times should be identical CompareYear, // don't compare centuries (fails for 2 digit years) CompareDate, // dates only CompareTime // time only }; static const struct { CompareKind compareKind; const char *format; } formatTestFormats[] = { { CompareYear, "---> %c" }, // %c could use 2 digit years { CompareDate, "Date is %A, %d of %B, in year %Y" }, { CompareYear, "Date is %x, time is %X" }, // %x could use 2 digits { CompareTime, "Time is %H:%M:%S or %I:%M:%S %p" }, { CompareNone, "The day of year: %j, the week of year: %W" }, { CompareDate, "ISO date without separators: %Y%m%d" }, { CompareBoth, "RFC 2822 string: %Y-%m-%d %H:%M:%S.%l %z" }, }; const long timeZonesOffsets[] = { wxDateTime::TimeZone(wxDateTime::Local).GetOffset(), // Fictitious TimeZone offsets to ensure time zone formating and // interpretation works -(3600 + 2*60), 3*3600 + 30*60 }; static const Date formatTestDates[] = { { 29, wxDateTime::May, 1976, 18, 30, 00, 0.0, wxDateTime::Inv_WeekDay }, { 31, wxDateTime::Dec, 1999, 23, 30, 00, 0.0, wxDateTime::Inv_WeekDay }, { 6, wxDateTime::Feb, 1937, 23, 30, 00, 0.0, wxDateTime::Inv_WeekDay }, { 6, wxDateTime::Feb, 1856, 23, 30, 00, 0.0, wxDateTime::Inv_WeekDay }, { 6, wxDateTime::Feb, 1857, 23, 30, 00, 0.0, wxDateTime::Inv_WeekDay }, { 29, wxDateTime::May, 2076, 18, 30, 00, 0.0, wxDateTime::Inv_WeekDay }, // FIXME: the test with 02:15:25 time doesn't pass because of DST // computation problems, we get back 03:15:25 { 29, wxDateTime::Feb, 2400, 04, 15, 25, 0.0, wxDateTime::Inv_WeekDay }, #if 0 // Need to add support for BCE dates. { 01, wxDateTime::Jan, -52, 03, 16, 47, 0.0, wxDateTime::Inv_WeekDay }, #endif }; for ( unsigned idxtz = 0; idxtz < WXSIZEOF(timeZonesOffsets); ++idxtz ) { wxDateTime::TimeZone tz(timeZonesOffsets[idxtz]); const bool isLocalTz = tz.GetOffset() == -wxGetTimeZone(); for ( size_t d = 0; d < WXSIZEOF(formatTestDates); d++ ) { wxDateTime dt = formatTestDates[d].DT(); for ( unsigned n = 0; n < WXSIZEOF(formatTestFormats); n++ ) { const char *fmt = formatTestFormats[n].format; // skip the check with %p for those locales which have empty AM/PM strings: // for those locales it's impossible to pass the test with %p... wxString am, pm; wxDateTime::GetAmPmStrings(&am, &pm); if (am.empty() && pm.empty() && wxStrstr(fmt, "%p") != NULL) continue; // what can we recover? CompareKind kind = formatTestFormats[n].compareKind; // When using a different time zone we must perform a time zone // conversion below which doesn't always work correctly, check // for the cases when it doesn't. if ( !isLocalTz ) { // DST computation doesn't work correctly for dates above // 2038 currently on the systems with 32 bit time_t. if ( dt.GetYear() >= 2038 ) continue; // We can't compare just dates nor just times when doing TZ // conversion as both are affected by the DST: for the // dates, the DST can switch midnight to 23:00 of the // previous day while for the times DST can be different // for the original date and today. if ( kind == CompareDate || kind == CompareTime ) continue; } // do convert date to string wxString s = dt.Format(fmt, tz); // convert back wxDateTime dt2; const char *result = dt2.ParseFormat(s, fmt); if ( !result ) { // conversion failed - should it have? WX_ASSERT_MESSAGE( ("Test #%u failed: failed to parse \"%s\"", n, s), kind == CompareNone ); } else // conversion succeeded { // currently ParseFormat() doesn't support "%Z" and so is // incapable of parsing time zone part used at the end of date // representations in many (but not "C") locales, compensate // for it ourselves by simply consuming and ignoring it while ( *result && (*result >= 'A' && *result <= 'Z') ) result++; WX_ASSERT_MESSAGE( ("Test #%u failed: \"%s\" was left unparsed in \"%s\"", n, result, s), !*result ); // Without "%z" we can't recover the time zone used in the // call to Format() so we need to call MakeFromTimezone() // explicitly. if ( !strstr(fmt, "%z") && !isLocalTz ) dt2.MakeFromTimezone(tz); switch ( kind ) { case CompareYear: if ( dt2.GetCentury() != dt.GetCentury() ) { CPPUNIT_ASSERT_EQUAL(dt.GetYear() % 100, dt2.GetYear() % 100); dt2.SetYear(dt.GetYear()); } // fall through and compare everything case CompareBoth: CPPUNIT_ASSERT_EQUAL( dt, dt2 ); break; case CompareDate: CPPUNIT_ASSERT( dt.IsSameDate(dt2) ); break; case CompareTime: CPPUNIT_ASSERT( dt.IsSameTime(dt2) ); break; case CompareNone: wxFAIL_MSG( wxT("unexpected") ); break; } } } } } wxDateTime dt; #if 0 // special case which was known to fail CPPUNIT_ASSERT( dt.ParseFormat("02/06/1856", "%x") ); CPPUNIT_ASSERT_EQUAL( 1856, dt.GetYear() ); #endif // also test %l separately CPPUNIT_ASSERT( dt.ParseFormat("12:23:45.678", "%H:%M:%S.%l") ); CPPUNIT_ASSERT_EQUAL( 678, dt.GetMillisecond() ); // test special case of %l matching 0 milliseconds CPPUNIT_ASSERT( dt.ParseFormat("12:23:45.000", "%H:%M:%S.%l") ); CPPUNIT_ASSERT_EQUAL( 0, dt.GetMillisecond() ); // test partially specified dates too wxDateTime dtDef(26, wxDateTime::Sep, 2008); CPPUNIT_ASSERT( dt.ParseFormat("17", "%d") ); CPPUNIT_ASSERT_EQUAL( 17, dt.GetDay() ); // test some degenerate cases CPPUNIT_ASSERT( !dt.ParseFormat("", "%z") ); CPPUNIT_ASSERT( !dt.ParseFormat("", "%%") ); // test compilation of some calls which should compile (and not result in // ambiguity because of char*<->wxCStrData<->wxString conversions) wxString s("foo"); CPPUNIT_ASSERT( !dt.ParseFormat("foo") ); CPPUNIT_ASSERT( !dt.ParseFormat(wxT("foo")) ); CPPUNIT_ASSERT( !dt.ParseFormat(s) ); dt.ParseFormat(s.c_str()); // Simply test compilation of this one. CPPUNIT_ASSERT( !dt.ParseFormat("foo", "%c") ); CPPUNIT_ASSERT( !dt.ParseFormat(wxT("foo"), "%c") ); CPPUNIT_ASSERT( !dt.ParseFormat(s, "%c") ); dt.ParseFormat(s.c_str(), "%c"); CPPUNIT_ASSERT( !dt.ParseFormat("foo", wxT("%c")) ); CPPUNIT_ASSERT( !dt.ParseFormat(wxT("foo"), wxT("%c")) ); CPPUNIT_ASSERT( !dt.ParseFormat(s, "%c") ); dt.ParseFormat(s.c_str(), wxT("%c")); wxString spec("%c"); CPPUNIT_ASSERT( !dt.ParseFormat("foo", spec) ); CPPUNIT_ASSERT( !dt.ParseFormat(wxT("foo"), spec) ); CPPUNIT_ASSERT( !dt.ParseFormat(s, spec) ); dt.ParseFormat(s.c_str(), spec); }