X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/dcbd3762e86258781ed81202977f680665190528..abb4f9c93715f5b00c526203af96ed2938a1a2c3:/src/common/datetime.cpp?ds=sidebyside diff --git a/src/common/datetime.cpp b/src/common/datetime.cpp index 9294901531..282fde1bae 100644 --- a/src/common/datetime.cpp +++ b/src/common/datetime.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////// -// Name: wx/datetime.h +// Name: src/common/datetime.cpp // Purpose: implementation of time/date related classes // Author: Vadim Zeitlin // Modified by: @@ -56,10 +56,6 @@ // headers // ---------------------------------------------------------------------------- -#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) - #pragma implementation "datetime.h" -#endif - // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" @@ -130,7 +126,13 @@ wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter , wxFromStringCon #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 *); + +// 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 @@ -229,11 +231,14 @@ static const long MILLISECONDS_PER_DAY = 86400000l; // (i.e. JDN(Jan 1, 1970) = 2440587.5) static const long EPOCH_JDN = 2440587l; +// used only in asserts +#ifdef __WXDEBUG__ // the date of JDN -0.5 (as we don't work with fractional parts, this is the // reference date for us) is Nov 24, 4714BC static const int JDN_0_YEAR = -4713; static const int JDN_0_MONTH = wxDateTime::Nov; static const int JDN_0_DAY = 24; +#endif // __WXDEBUG__ // the constants used for JDN calculations static const long JDN_OFFSET = 32046l; @@ -370,21 +375,26 @@ 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 #ifdef HAVE_STRPTIME -// glibc2 doesn't define this in the headers unless _XOPEN_SOURCE is defined -// which, unfortunately, wreaks havoc elsewhere -#if defined(__GLIBC__) && (__GLIBC__ == 2) +#if wxUSE_UNIX && !defined(HAVE_STRPTIME_DECL) + // configure detected that we had strptime() but not its declaration, + // provide it ourselves extern "C" char *strptime(const char *, const char *, struct tm *); #endif @@ -681,7 +691,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: @@ -1394,20 +1404,9 @@ wxDateTime& wxDateTime::Set(double jdn) // EPOCH_JDN + 0.5 jdn -= EPOCH_JDN + 0.5; - jdn *= MILLISECONDS_PER_DAY; + m_time.Assign(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 += tzDiff*1000; // tzDiff is in seconds + // JDNs always are in UTC, so we don't need any adjustments for time zone return *this; } @@ -1626,14 +1625,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; } @@ -2099,16 +2098,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 @@ -2162,6 +2152,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)); } @@ -2303,12 +2308,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 @@ -2366,25 +2371,25 @@ 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!) - wxString fmt = format; + // find two strings not occurring in format (this is surely + // not the optimal way of doing it... improvements welcome!) + wxString fmt2 = format; wxString replacement = (wxChar)-1; - while ( fmt.Find(replacement) != wxNOT_FOUND ) + while ( fmt2.Find(replacement) != wxNOT_FOUND ) { replacement << (wxChar)-1; } wxString replacement2 = (wxChar)-2; - while ( fmt.Find(replacement) != wxNOT_FOUND ) + while ( fmt2.Find(replacement) != wxNOT_FOUND ) { replacement << (wxChar)-2; } - // replace all occurences of year with it - bool wasReplaced = fmt.Replace(strYear, replacement) > 0; + // replace all occurrences of year with it + bool wasReplaced = fmt2.Replace(strYear, replacement) > 0; if ( !wasReplaced ) - wasReplaced = fmt.Replace(strYear2, replacement2) > 0; + wasReplaced = fmt2.Replace(strYear2, replacement2) > 0; // use strftime() to format the same date but in supported // year @@ -2408,14 +2413,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); @@ -2760,7 +2765,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; @@ -2840,22 +2845,24 @@ const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date) } // 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__ @@ -2871,45 +2878,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__ @@ -3276,30 +3377,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); @@ -3589,12 +3692,12 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date) for ( size_t n = 0; n < WXSIZEOF(literalDates); n++ ) { - wxString date = wxGetTranslation(literalDates[n].str); - size_t len = date.length(); + const wxString dateStr = wxGetTranslation(literalDates[n].str); + size_t len = dateStr.length(); if ( wxStrlen(p) >= len ) { wxString str(p, len); - if ( str.CmpNoCase(date) == 0 ) + if ( str.CmpNoCase(dateStr) == 0 ) { // nothing can follow this, so stop here p += len; @@ -4177,7 +4280,7 @@ wxString wxTimeSpan::Format(const wxChar *format) const #include "wx/arrimpl.cpp" -WX_DEFINE_OBJARRAY(wxDateTimeArray); +WX_DEFINE_OBJARRAY(wxDateTimeArray) static int wxCMPFUNC_CONV wxDateTimeCompareFunc(wxDateTime **first, wxDateTime **second) @@ -4219,8 +4322,8 @@ wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime& dtStart, holidays.Clear(); - size_t count = ms_authorities.size(); - for ( size_t nAuth = 0; nAuth < count; nAuth++ ) + const size_t countAuth = ms_authorities.size(); + for ( size_t nAuth = 0; nAuth < countAuth; nAuth++ ) { ms_authorities[nAuth]->DoGetHolidaysInRange(dtStart, dtEnd, hol);