X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/7d3e806d86a159f3af1db1744a75de162e0e1a8d..ca6a9a148646fb024d76b3e19c940ace56eec1f8:/src/common/datetime.cpp diff --git a/src/common/datetime.cpp b/src/common/datetime.cpp index 7f1f7924a0..d3e20b66d5 100644 --- a/src/common/datetime.cpp +++ b/src/common/datetime.cpp @@ -127,6 +127,18 @@ wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter , wxFromStringCon #undef HAVE_STRPTIME #endif // broken strptime() +#if defined(HAVE_STRPTIME) && defined(__DARWIN__) && defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS + // configure detects strptime as linkable because it's in the OS X + // System library but MSL headers don't declare it. + +// char *strptime(const char *, const char *, struct tm *); + // However, we DON'T want to just provide it here because we would + // crash and/or overwrite data when strptime from OS X tries + // to fill in MW's struct tm which is two fields shorter (no TZ stuff) + // So for now let's just say we don't have strptime + #undef HAVE_STRPTIME +#endif + #if defined(__MWERKS__) && wxUSE_UNICODE #include #endif @@ -364,13 +376,18 @@ static long GetTruncatedJDN(wxDateTime::wxDateTime_t day, static wxString CallStrftime(const wxChar *format, const tm* tm) { wxChar buf[4096]; + // Create temp wxString here to work around mingw/cygwin bug 1046059 + // http://sourceforge.net/tracker/?func=detail&atid=102435&aid=1046059&group_id=2435 + wxString s; + if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) ) { // buffer is too small? wxFAIL_MSG(_T("strftime() failed")); } - return wxString(buf); + s = buf; + return s; } #endif @@ -675,7 +692,7 @@ wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz) case wxDateTime::A_CST: // Central Standard Time in use in Australia = UTC + 9.5 - m_offset = 60l*(9*60 + 30); + m_offset = 60l*(9*MIN_PER_HOUR + MIN_PER_HOUR/2); break; default: @@ -1388,20 +1405,9 @@ wxDateTime& wxDateTime::Set(double jdn) // EPOCH_JDN + 0.5 jdn -= EPOCH_JDN + 0.5; - jdn *= MILLISECONDS_PER_DAY; - - m_time.Assign(jdn); - - // JDNs always suppose an UTC date, so bring it back to local time zone - // (also see GetJulianDayNumber() implementation) - long tzDiff = GetTimeZone(); - if ( IsDST() == 1 ) - { - // FIXME: again, we suppose that DST is always one hour - tzDiff -= 3600; - } + m_time.Assign(jdn*MILLISECONDS_PER_DAY); - m_time += tzDiff*1000; // tzDiff is in seconds + // JDNs always are in UTC, so we don't need any adjustments for time zone return *this; } @@ -1620,14 +1626,14 @@ wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const timeOnly -= tm.msec; timeOnly /= 1000; // now we have time in seconds - tm.sec = (wxDateTime_t)(timeOnly % 60); + tm.sec = (wxDateTime_t)(timeOnly % SEC_PER_MIN); timeOnly -= tm.sec; - timeOnly /= 60; // now we have time in minutes + timeOnly /= SEC_PER_MIN; // now we have time in minutes - tm.min = (wxDateTime_t)(timeOnly % 60); + tm.min = (wxDateTime_t)(timeOnly % MIN_PER_HOUR); timeOnly -= tm.min; - tm.hour = (wxDateTime_t)(timeOnly / 60); + tm.hour = (wxDateTime_t)(timeOnly / MIN_PER_HOUR); return tm; } @@ -2093,16 +2099,7 @@ wxDateTime& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday) double wxDateTime::GetJulianDayNumber() const { - // JDN are always expressed for the UTC dates - Tm tm(ToTimezone(UTC).GetTm(UTC)); - - double result = GetTruncatedJDN(tm.mday, tm.mon, tm.year); - - // add the part GetTruncatedJDN() neglected - result += 0.5; - - // and now add the time: 86400 sec = 1 JDN - return result + ((double)(60*(60*tm.hour + tm.min) + tm.sec)) / 86400; + return m_time.ToDouble() / MILLISECONDS_PER_DAY + EPOCH_JDN + 0.5; } double wxDateTime::GetRataDie() const @@ -2156,6 +2153,21 @@ wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST) secDiff -= 3600; } + return Add(wxTimeSpan::Seconds(secDiff)); +} + +wxDateTime& wxDateTime::MakeFromTimezone(const TimeZone& tz, bool noDST) +{ + long secDiff = GetTimeZone() + tz.GetOffset(); + + // we need to know whether DST is or not in effect for this date unless + // the test disabled by the caller + if ( !noDST && (IsDST() == 1) ) + { + // FIXME we assume that the DST is always shifted by 1 hour + secDiff -= 3600; + } + return Subtract(wxTimeSpan::Seconds(secDiff)); } @@ -2297,12 +2309,12 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const // find the YEAR which is a year in the strftime() range (1970 // - 2038) whose Jan 1 falls on the same week day as the Jan 1 // of the real year. Then make a copy of the format and - // replace all occurences of YEAR in it with some unique + // replace all occurrences of YEAR in it with some unique // string not appearing anywhere else in it, then use // strftime() to format the date in year YEAR and then replace // YEAR back by the real year and the unique replacement - // string back with YEAR. Notice that "all occurences of YEAR" - // means all occurences of 4 digit as well as 2 digit form! + // string back with YEAR. Notice that "all occurrences of YEAR" + // means all occurrences of 4 digit as well as 2 digit form! // // the bugs: we assume that neither of %c nor %x contains any // fields which may change between the YEAR and real year. For @@ -2360,8 +2372,8 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const strYear.Printf(_T("%d"), year); strYear2.Printf(_T("%d"), year % 100); - // find two strings not occuring in format (this is surely - // not optimal way of doing it... improvements welcome!) + // find two strings not occurring in format (this is surely + // not the optimal way of doing it... improvements welcome!) wxString fmt = format; wxString replacement = (wxChar)-1; while ( fmt.Find(replacement) != wxNOT_FOUND ) @@ -2375,7 +2387,7 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const replacement << (wxChar)-2; } - // replace all occurences of year with it + // replace all occurrences of year with it bool wasReplaced = fmt.Replace(strYear, replacement) > 0; if ( !wasReplaced ) wasReplaced = fmt.Replace(strYear2, replacement2) > 0; @@ -2402,14 +2414,14 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const : _T("%x"), &tmAdjusted); - // now replace the occurence of 1999 with the real year + // now replace the occurrence of 1999 with the real year wxString strYearReal, strYearReal2; strYearReal.Printf(_T("%04d"), yearReal); strYearReal2.Printf(_T("%02d"), yearReal % 100); str.Replace(strYear, strYearReal); str.Replace(strYear2, strYearReal2); - // and replace back all occurences of replacement string + // and replace back all occurrences of replacement string if ( wasReplaced ) { str.Replace(replacement2, strYear2); @@ -2754,7 +2766,7 @@ const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date) } // hours - offset = 60*(10*(*p - _T('0')) + (*(p + 1) - _T('0'))); + offset = MIN_PER_HOUR*(10*(*p - _T('0')) + (*(p + 1) - _T('0'))); p += 2; @@ -2832,24 +2844,26 @@ const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date) p += tz.length(); } - + // make it minutes - offset *= 60; + offset *= MIN_PER_HOUR; } - // the spec was correct + // the spec was correct, construct the date from the values we found Set(day, mon, year, hour, min, sec); - MakeTimezone((wxDateTime_t)(60*offset)); + MakeFromTimezone(TimeZone((wxDateTime_t)(offset*SEC_PER_MIN))); return p; } #ifdef __WINDOWS__ -// Get's current locale's date formatting string and stores it in fmt if -// the locale is set; otherwise or in case of failure, leaves fmt unchanged -static void GetLocaleDateFormat(wxString *fmt) +// returns the string containing strftime() format used for short dates in the +// current locale or an empty string +static wxString GetLocaleDateFormat() { + wxString fmtWX; + // there is no setlocale() under Windows CE, so just always query the // system there #ifndef __WXWINCE__ @@ -2865,45 +2879,139 @@ static void GetLocaleDateFormat(wxString *fmt) #else LCID lcid = GetThreadLocale(); #endif - wxChar delim[5]; // fields deliminer, 4 chars max - if ( GetLocaleInfo(lcid, LOCALE_SDATE, delim, 5) ) + // according to MSDN 80 chars is max allowed for short date format + wxChar fmt[81]; + if ( ::GetLocaleInfo(lcid, LOCALE_SSHORTDATE, fmt, WXSIZEOF(fmt)) ) { - wxChar centurybuf[2]; // use %y or %Y, 1 char max - wxChar century = 'y'; - if ( GetLocaleInfo(lcid, LOCALE_ICENTURY, centurybuf, 2) ) - { - if ( centurybuf[0] == _T('1') ) - century = 'Y'; - // else 'y' as above - } - - wxChar order[2]; // order code, 1 char max - if ( GetLocaleInfo(lcid, LOCALE_IDATE, order, 2) ) + wxChar chLast = _T('\0'); + size_t lastCount = 0; + for ( const wxChar *p = fmt; /* NUL handled inside */; p++ ) { - if ( order[0] == _T('0') ) // M-D-Y + if ( *p == chLast ) { - *fmt = wxString::Format(_T("%%m%s%%d%s%%%c"), - delim, delim, century); - } - else if ( order[0] == _T('1') ) // D-M-Y - { - *fmt = wxString::Format(_T("%%d%s%%m%s%%%c"), - delim, delim, century); - } - else if ( order[0] == _T('2') ) // Y-M-D - { - *fmt = wxString::Format(_T("%%%c%s%%m%s%%d"), - century, delim, delim); + lastCount++; + continue; } - else + + switch ( *p ) { - wxFAIL_MSG(_T("unexpected GetLocaleInfo return value")); + // these characters come in groups, start counting them + case _T('d'): + case _T('M'): + case _T('y'): + case _T('g'): + chLast = *p; + lastCount = 1; + break; + + default: + // first deal with any special characters we have had + if ( lastCount ) + { + switch ( chLast ) + { + case _T('d'): + switch ( lastCount ) + { + case 1: // d + case 2: // dd + // these two are the same as we + // don't distinguish between 1 and + // 2 digits for days + fmtWX += _T("%d"); + break; + + case 3: // ddd + fmtWX += _T("%a"); + break; + + case 4: // dddd + fmtWX += _T("%A"); + break; + + default: + wxFAIL_MSG( _T("too many 'd's") ); + } + break; + + case _T('M'): + switch ( lastCount ) + { + case 1: // M + case 2: // MM + // as for 'd' and 'dd' above + fmtWX += _T("%m"); + break; + + case 3: + fmtWX += _T("%b"); + break; + + case 4: + fmtWX += _T("%B"); + break; + + default: + wxFAIL_MSG( _T("too many 'M's") ); + } + break; + + case _T('y'): + switch ( lastCount ) + { + case 1: // y + case 2: // yy + fmtWX += _T("%y"); + break; + + case 4: // yyyy + fmtWX += _T("%Y"); + break; + + default: + wxFAIL_MSG( _T("wrong number of 'y's") ); + } + break; + + case _T('g'): + // strftime() doesn't have era string, + // ignore this format + wxASSERT_MSG( lastCount <= 2, + _T("too many 'g's") ); + break; + + default: + wxFAIL_MSG( _T("unreachable") ); + } + + chLast = _T('\0'); + lastCount = 0; + } + + // not a special character so must be just a separator, + // treat as is + if ( *p != _T('\0') ) + { + if ( *p == _T('%') ) + { + // this one needs to be escaped + fmtWX += _T('%'); + } + + fmtWX += *p; + } } + + if ( *p == _T('\0') ) + break; } } - // if we failed, leave fmtDate value unchanged and - // try our luck with the default set above + //else: GetLocaleInfo() failed, leave fmtDate value unchanged and + // try our luck with the default formats } + //else: default C locale, default formats should work + + return fmtWX; } #endif // __WINDOWS__ @@ -3270,30 +3378,32 @@ const wxChar *wxDateTime::ParseFormat(const wxChar *date, { wxDateTime dt; - - wxString fmtDate, fmtDateAlt; - - if ( IsWestEuropeanCountry(GetCountry()) || - GetCountry() == Russia ) - { - fmtDate = _T("%d/%m/%y"); - fmtDateAlt = _T("%m/%d/%y"); - } - else // assume USA - { - fmtDate = _T("%m/%d/%y"); - fmtDateAlt = _T("%d/%m/%y"); - } + wxString fmtDate, + fmtDateAlt; #ifdef __WINDOWS__ // The above doesn't work for all locales, try to query // Windows for the right way of formatting the date: - GetLocaleDateFormat(&fmtDate); + fmtDate = GetLocaleDateFormat(); + if ( fmtDate.empty() ) #endif + { + if ( IsWestEuropeanCountry(GetCountry()) || + GetCountry() == Russia ) + { + fmtDate = _T("%d/%m/%y"); + fmtDateAlt = _T("%m/%d/%y"); + } + else // assume USA + { + fmtDate = _T("%m/%d/%y"); + fmtDateAlt = _T("%d/%m/%y"); + } + } const wxChar *result = dt.ParseFormat(input, fmtDate); - if ( !result ) + if ( !result && !fmtDateAlt.empty() ) { // ok, be nice and try another one result = dt.ParseFormat(input, fmtDateAlt);