X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/1696c17801e10ea87246508ff7da37aa2f6d79ff..236a9de39afa090fdee3cf91cb5364ceca69e3f8:/src/common/datetime.cpp?ds=sidebyside diff --git a/src/common/datetime.cpp b/src/common/datetime.cpp index b50431a134..11c5c78824 100644 --- a/src/common/datetime.cpp +++ b/src/common/datetime.cpp @@ -67,14 +67,16 @@ #include "wx/string.h" #include "wx/intl.h" #include "wx/log.h" - #include "wx/module.h" #endif // WX_PRECOMP #include "wx/thread.h" #include "wx/tokenzr.h" +#include "wx/module.h" #define wxDEFINE_TIME_CONSTANTS // before including datetime.h +#include + #include "wx/datetime.h" // ---------------------------------------------------------------------------- @@ -95,6 +97,9 @@ #ifndef WX_TIMEZONE #if defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__) #define WX_TIMEZONE _timezone + #elif defined(__MWERKS__) + long wxmw_timezone = 28800; + #define WX_TIMEZONE wxmw_timezone ; #else // unknown platform - try timezone #define WX_TIMEZONE timezone #endif @@ -117,6 +122,7 @@ public: virtual void OnExit() { wxDateTimeHolidayAuthority::ClearAllAuthorities(); + wxDateTimeHolidayAuthority::ms_authorities.Clear(); } private: @@ -190,7 +196,7 @@ wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown; #ifdef __WXDEBUG__ extern const wxChar *wxDumpDate(const wxDateTime* dt) { - static wxChar buf[20]; + static wxChar buf[128]; wxStrcpy(buf, dt->Format(_T("%Y-%m-%d (%a) %H:%M:%S"))); @@ -1258,18 +1264,18 @@ wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const wxASSERT_MSG( jdn > -2, _T("JDN out of range") ); // calculate the century - int temp = (jdn + JDN_OFFSET) * 4 - 1; - int century = temp / DAYS_PER_400_YEARS; + long temp = (jdn + JDN_OFFSET) * 4 - 1; + long century = temp / DAYS_PER_400_YEARS; // then the year and day of year (1 <= dayOfYear <= 366) temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3; - int year = (century * 100) + (temp / DAYS_PER_4_YEARS); - int dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1; + long year = (century * 100) + (temp / DAYS_PER_4_YEARS); + long dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1; // and finally the month and day of the month temp = dayOfYear * 5 - 3; - int month = temp / DAYS_PER_5_MONTHS; - int day = (temp % DAYS_PER_5_MONTHS) / 5 + 1; + long month = temp / DAYS_PER_5_MONTHS; + long day = (temp % DAYS_PER_5_MONTHS) / 5 + 1; // month is counted from March - convert to normal if ( month < 10 ) @@ -1296,18 +1302,18 @@ wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const tm.year = (int)year; tm.mon = (Month)(month - 1); // algorithm yields 1 for January, not 0 tm.mday = (wxDateTime_t)day; - tm.msec = timeOnly % 1000; + tm.msec = (wxDateTime_t)(timeOnly % 1000); timeOnly -= tm.msec; timeOnly /= 1000; // now we have time in seconds - tm.sec = timeOnly % 60; + tm.sec = (wxDateTime_t)(timeOnly % 60); timeOnly -= tm.sec; timeOnly /= 60; // now we have time in minutes - tm.min = timeOnly % 60; + tm.min = (wxDateTime_t)(timeOnly % 60); timeOnly -= tm.min; - tm.hour = timeOnly / 60; + tm.hour = (wxDateTime_t)(timeOnly / 60); return tm; } @@ -1636,20 +1642,15 @@ wxDateTime::wxDateTime_t wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags, { Tm tm = GetTm(tz); wxDateTime dtMonthStart = wxDateTime(1, tm.mon, tm.year); - size_t nWeek = GetWeekOfYear(flags) - dtMonthStart.GetWeekOfYear(flags) + 1; -#ifdef __VMS__ // nWeek is unsigned so avoid the warning - int nweek2 = (int) nWeek; - if ( nweek2 < 0 ) -#else + int nWeek = GetWeekOfYear(flags) - dtMonthStart.GetWeekOfYear(flags) + 1; if ( nWeek < 0 ) -#endif { // this may happen for January when Jan, 1 is the last week of the // previous year nWeek += IsLeapYear(tm.year - 1) ? 53 : 52; } - return nWeek; + return (wxDateTime::wxDateTime_t)nWeek; } wxDateTime& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday) @@ -1734,7 +1735,7 @@ int wxDateTime::IsDST(wxDateTime::Country country) const wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST) { - int secDiff = GetTimeZone() + tz.GetOffset(); + 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 @@ -1770,7 +1771,7 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const } else { - time += tz.GetOffset(); + time += (int)tz.GetOffset(); #ifdef __VMS__ // time is unsigned so avoid the warning int time2 = (int) time; @@ -1843,257 +1844,264 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const fmt = _T("%02d"); } -restart: - // start of the format specification - switch ( *p ) + bool restart = TRUE; + while ( restart ) { - case _T('a'): // a weekday name - case _T('A'): - // second parameter should be TRUE for abbreviated names - res += GetWeekDayName(tm.GetWeekDay(), - *p == _T('a') ? Name_Abbr : Name_Full); - break; + restart = FALSE; - case _T('b'): // a month name - case _T('B'): - res += GetMonthName(tm.mon, - *p == _T('b') ? Name_Abbr : Name_Full); - break; + // start of the format specification + switch ( *p ) + { + case _T('a'): // a weekday name + case _T('A'): + // second parameter should be TRUE for abbreviated names + res += GetWeekDayName(tm.GetWeekDay(), + *p == _T('a') ? Name_Abbr : Name_Full); + break; - case _T('c'): // locale default date and time representation - case _T('x'): // locale default date representation - // - // the problem: there is no way to know what do these format - // specifications correspond to for the current locale. - // - // the solution: use a hack and still use strftime(): first - // 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 - // 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! - // - // the bugs: we assume that neither of %c nor %x contains any - // fields which may change between the YEAR and real year. For - // example, the week number (%U, %W) and the day number (%j) - // will change if one of these years is leap and the other one - // is not! - { - // find the YEAR: normally, for any year X, Jan 1 or the - // year X + 28 is the same weekday as Jan 1 of X (because - // the weekday advances by 1 for each normal X and by 2 - // for each leap X, hence by 5 every 4 years or by 35 - // which is 0 mod 7 every 28 years) but this rule breaks - // down if there are years between X and Y which are - // divisible by 4 but not leap (i.e. divisible by 100 but - // not 400), hence the correction. - - int yearReal = GetYear(tz); - int mod28 = yearReal % 28; - - // be careful to not go too far - we risk to leave the - // supported range - int year; - if ( mod28 < 10 ) - { - year = 1988 + mod28; // 1988 == 0 (mod 28) - } - else + case _T('b'): // a month name + case _T('B'): + res += GetMonthName(tm.mon, + *p == _T('b') ? Name_Abbr : Name_Full); + break; + + case _T('c'): // locale default date and time representation + case _T('x'): // locale default date representation + // + // the problem: there is no way to know what do these format + // specifications correspond to for the current locale. + // + // the solution: use a hack and still use strftime(): first + // 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 + // 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! + // + // the bugs: we assume that neither of %c nor %x contains any + // fields which may change between the YEAR and real year. For + // example, the week number (%U, %W) and the day number (%j) + // will change if one of these years is leap and the other one + // is not! { - year = 1970 + mod28 - 10; // 1970 == 10 (mod 28) - } + // find the YEAR: normally, for any year X, Jan 1 or the + // year X + 28 is the same weekday as Jan 1 of X (because + // the weekday advances by 1 for each normal X and by 2 + // for each leap X, hence by 5 every 4 years or by 35 + // which is 0 mod 7 every 28 years) but this rule breaks + // down if there are years between X and Y which are + // divisible by 4 but not leap (i.e. divisible by 100 but + // not 400), hence the correction. + + int yearReal = GetYear(tz); + int mod28 = yearReal % 28; + + // be careful to not go too far - we risk to leave the + // supported range + int year; + if ( mod28 < 10 ) + { + year = 1988 + mod28; // 1988 == 0 (mod 28) + } + else + { + year = 1970 + mod28 - 10; // 1970 == 10 (mod 28) + } - int nCentury = year / 100, - nCenturyReal = yearReal / 100; + int nCentury = year / 100, + nCenturyReal = yearReal / 100; - // need to adjust for the years divisble by 400 which are - // not leap but are counted like leap ones if we just take - // the number of centuries in between for nLostWeekDays - int nLostWeekDays = (nCentury - nCenturyReal) - - (nCentury / 4 - nCenturyReal / 4); + // need to adjust for the years divisble by 400 which are + // not leap but are counted like leap ones if we just take + // the number of centuries in between for nLostWeekDays + int nLostWeekDays = (nCentury - nCenturyReal) - + (nCentury / 4 - nCenturyReal / 4); - // we have to gain back the "lost" weekdays: note that the - // effect of this loop is to not do anything to - // nLostWeekDays (which we won't use any more), but to - // (indirectly) set the year correctly - while ( (nLostWeekDays % 7) != 0 ) - { - nLostWeekDays += year++ % 4 ? 1 : 2; - } + // we have to gain back the "lost" weekdays: note that the + // effect of this loop is to not do anything to + // nLostWeekDays (which we won't use any more), but to + // (indirectly) set the year correctly + while ( (nLostWeekDays % 7) != 0 ) + { + nLostWeekDays += year++ % 4 ? 1 : 2; + } - // at any rate, we couldn't go further than 1988 + 9 + 28! - wxASSERT_MSG( year < 2030, - _T("logic error in wxDateTime::Format") ); + // at any rate, we couldn't go further than 1988 + 9 + 28! + wxASSERT_MSG( year < 2030, + _T("logic error in wxDateTime::Format") ); - wxString strYear, strYear2; - strYear.Printf(_T("%d"), year); - strYear2.Printf(_T("%d"), year % 100); + wxString strYear, strYear2; + 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; - wxString replacement = (wxChar)-1; - while ( fmt.Find(replacement) != wxNOT_FOUND ) - { - replacement << (wxChar)-1; - } + // find two strings not occuring in format (this is surely + // not optimal way of doing it... improvements welcome!) + wxString fmt = format; + wxString replacement = (wxChar)-1; + while ( fmt.Find(replacement) != wxNOT_FOUND ) + { + replacement << (wxChar)-1; + } - wxString replacement2 = (wxChar)-2; - while ( fmt.Find(replacement) != wxNOT_FOUND ) - { - replacement << (wxChar)-2; - } + wxString replacement2 = (wxChar)-2; + while ( fmt.Find(replacement) != wxNOT_FOUND ) + { + replacement << (wxChar)-2; + } - // replace all occurences of year with it - bool wasReplaced = fmt.Replace(strYear, replacement) > 0; - if ( !wasReplaced ) - wasReplaced = fmt.Replace(strYear2, replacement2) > 0; + // replace all occurences of year with it + bool wasReplaced = fmt.Replace(strYear, replacement) > 0; + if ( !wasReplaced ) + wasReplaced = fmt.Replace(strYear2, replacement2) > 0; + + // use strftime() to format the same date but in supported + // year + // + // NB: we assume that strftime() doesn't check for the + // date validity and will happily format the date + // corresponding to Feb 29 of a non leap year (which + // may happen if yearReal was leap and year is not) + struct tm tmAdjusted; + InitTm(tmAdjusted); + tmAdjusted.tm_hour = tm.hour; + tmAdjusted.tm_min = tm.min; + tmAdjusted.tm_sec = tm.sec; + tmAdjusted.tm_wday = tm.GetWeekDay(); + tmAdjusted.tm_yday = GetDayOfYear(); + tmAdjusted.tm_mday = tm.mday; + tmAdjusted.tm_mon = tm.mon; + tmAdjusted.tm_year = year - 1900; + tmAdjusted.tm_isdst = 0; // no DST, already adjusted + wxString str = CallStrftime(*p == _T('c') ? _T("%c") + : _T("%x"), + &tmAdjusted); + + // now replace the occurence 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 + if ( wasReplaced ) + { + str.Replace(replacement2, strYear2); + str.Replace(replacement, strYear); + } - // use strftime() to format the same date but in supported - // year - // - // NB: we assume that strftime() doesn't check for the - // date validity and will happily format the date - // corresponding to Feb 29 of a non leap year (which - // may happen if yearReal was leap and year is not) - struct tm tmAdjusted; - InitTm(tmAdjusted); - tmAdjusted.tm_hour = tm.hour; - tmAdjusted.tm_min = tm.min; - tmAdjusted.tm_sec = tm.sec; - tmAdjusted.tm_wday = tm.GetWeekDay(); - tmAdjusted.tm_yday = GetDayOfYear(); - tmAdjusted.tm_mday = tm.mday; - tmAdjusted.tm_mon = tm.mon; - tmAdjusted.tm_year = year - 1900; - tmAdjusted.tm_isdst = 0; // no DST, already adjusted - wxString str = CallStrftime(*p == _T('c') ? _T("%c") - : _T("%x"), - &tmAdjusted); - - // now replace the occurence 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 - if ( wasReplaced ) - { - str.Replace(replacement2, strYear2); - str.Replace(replacement, strYear); + res += str; } + break; - res += str; - } - break; + case _T('d'): // day of a month (01-31) + res += wxString::Format(fmt, tm.mday); + break; - case _T('d'): // day of a month (01-31) - res += wxString::Format(fmt, tm.mday); - break; + case _T('H'): // hour in 24h format (00-23) + res += wxString::Format(fmt, tm.hour); + break; - case _T('H'): // hour in 24h format (00-23) - res += wxString::Format(fmt, tm.hour); - break; + case _T('I'): // hour in 12h format (01-12) + { + // 24h -> 12h, 0h -> 12h too + int hour12 = tm.hour > 12 ? tm.hour - 12 + : tm.hour ? tm.hour : 12; + res += wxString::Format(fmt, hour12); + } + break; - case _T('I'): // hour in 12h format (01-12) - { - // 24h -> 12h, 0h -> 12h too - int hour12 = tm.hour > 12 ? tm.hour - 12 - : tm.hour ? tm.hour : 12; - res += wxString::Format(fmt, hour12); - } - break; + case _T('j'): // day of the year + res += wxString::Format(fmt, GetDayOfYear(tz)); + break; - case _T('j'): // day of the year - res += wxString::Format(fmt, GetDayOfYear(tz)); - break; + case _T('m'): // month as a number (01-12) + res += wxString::Format(fmt, tm.mon + 1); + break; - case _T('m'): // month as a number (01-12) - res += wxString::Format(fmt, tm.mon + 1); - break; + case _T('M'): // minute as a decimal number (00-59) + res += wxString::Format(fmt, tm.min); + break; - case _T('M'): // minute as a decimal number (00-59) - res += wxString::Format(fmt, tm.min); - break; + case _T('p'): // AM or PM string + res += CallStrftime(_T("%p"), &tmTimeOnly); + break; - case _T('p'): // AM or PM string - res += CallStrftime(_T("%p"), &tmTimeOnly); - break; + case _T('S'): // second as a decimal number (00-61) + res += wxString::Format(fmt, tm.sec); + break; - case _T('S'): // second as a decimal number (00-61) - res += wxString::Format(fmt, tm.sec); - break; + case _T('U'): // week number in the year (Sunday 1st week day) + res += wxString::Format(fmt, GetWeekOfYear(Sunday_First, tz)); + break; - case _T('U'): // week number in the year (Sunday 1st week day) - res += wxString::Format(fmt, GetWeekOfYear(Sunday_First, tz)); - break; + case _T('W'): // week number in the year (Monday 1st week day) + res += wxString::Format(fmt, GetWeekOfYear(Monday_First, tz)); + break; - case _T('W'): // week number in the year (Monday 1st week day) - res += wxString::Format(fmt, GetWeekOfYear(Monday_First, tz)); - break; + case _T('w'): // weekday as a number (0-6), Sunday = 0 + res += wxString::Format(fmt, tm.GetWeekDay()); + break; - case _T('w'): // weekday as a number (0-6), Sunday = 0 - res += wxString::Format(fmt, tm.GetWeekDay()); - break; + // case _T('x'): -- handled with "%c" - // case _T('x'): -- handled with "%c" + case _T('X'): // locale default time representation + // just use strftime() to format the time for us + res += CallStrftime(_T("%X"), &tmTimeOnly); + break; - case _T('X'): // locale default time representation - // just use strftime() to format the time for us - res += CallStrftime(_T("%X"), &tmTimeOnly); - break; + case _T('y'): // year without century (00-99) + res += wxString::Format(fmt, tm.year % 100); + break; - case _T('y'): // year without century (00-99) - res += wxString::Format(fmt, tm.year % 100); - break; + case _T('Y'): // year with century + res += wxString::Format(fmt, tm.year); + break; - case _T('Y'): // year with century - res += wxString::Format(fmt, tm.year); - break; + case _T('Z'): // timezone name + res += CallStrftime(_T("%Z"), &tmTimeOnly); + break; - case _T('Z'): // timezone name - res += CallStrftime(_T("%Z"), &tmTimeOnly); - break; + default: + // is it the format width? + fmt.Empty(); + while ( *p == _T('-') || *p == _T('+') || + *p == _T(' ') || wxIsdigit(*p) ) + { + fmt += *p; + } - default: - // is it the format width? - fmt.Empty(); - while ( *p == _T('-') || *p == _T('+') || - *p == _T(' ') || wxIsdigit(*p) ) - { - fmt += *p; - } + if ( !fmt.IsEmpty() ) + { + // we've only got the flags and width so far in fmt + fmt.Prepend(_T('%')); + fmt.Append(_T('d')); - if ( !fmt.IsEmpty() ) - { - // we've only got the flags and width so far in fmt - fmt.Prepend(_T('%')); - fmt.Append(_T('d')); + restart = TRUE; - goto restart; - } + break; + } - // no, it wasn't the width - wxFAIL_MSG(_T("unknown format specificator")); + // no, it wasn't the width + wxFAIL_MSG(_T("unknown format specificator")); - // fall through and just copy it nevertheless + // fall through and just copy it nevertheless - case _T('%'): // a percent sign - res += *p; - break; + case _T('%'): // a percent sign + res += *p; + break; - case 0: // the end of string - wxFAIL_MSG(_T("missing format at the end of string")); + case 0: // the end of string + wxFAIL_MSG(_T("missing format at the end of string")); - // just put the '%' which was the last char in format - res += _T('%'); - break; + // just put the '%' which was the last char in format + res += _T('%'); + break; + } } } @@ -2531,7 +2539,7 @@ const wxChar *wxDateTime::ParseFormat(const wxChar *date, break; case _T('d'): // day of a month (01-31) - if ( !GetNumericToken(input, &num) || (num > 31) ) + if ( !GetNumericToken(input, &num) || (num > 31) || (num < 1) ) { // no match return (wxChar *)NULL; @@ -2563,7 +2571,7 @@ const wxChar *wxDateTime::ParseFormat(const wxChar *date, haveHour = TRUE; hourIsIn12hFormat = TRUE; - hour = num % 12; // 12 should be 0 + hour = (wxDateTime_t)(num % 12); // 12 should be 0 break; case _T('j'): // day of the year @@ -2819,7 +2827,7 @@ const wxChar *wxDateTime::ParseFormat(const wxChar *date, } haveYear = TRUE; - year = 1900 + num; + year = 1900 + (wxDateTime_t)num; break; case _T('Y'): // year with century @@ -2963,16 +2971,36 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date) while ( wxIsspace(*p) ) p++; - wxString today = _T("today"); - size_t len = today.length(); - if ( wxString(p, len).CmpNoCase(today) == 0 ) + // some special cases + static struct + { + const wxChar *str; + int dayDiffFromToday; + } literalDates[] = { - // nothing can follow this, so stop here - p += len; + { wxTRANSLATE("today"), 0 }, + { wxTRANSLATE("yesterday"), -1 }, + { wxTRANSLATE("tomorrow"), 1 }, + }; - *this = Today(); + for ( size_t n = 0; n < WXSIZEOF(literalDates); n++ ) + { + wxString date = wxGetTranslation(literalDates[n].str); + size_t len = date.length(); + if ( wxString(p, len).CmpNoCase(date) == 0 ) + { + // nothing can follow this, so stop here + p += len; - return p; + int dayDiffFromToday = literalDates[n].dayDiffFromToday; + *this = Today(); + if ( dayDiffFromToday ) + { + *this += wxDateSpan::Days(dayDiffFromToday); + } + + return p; + } } // what do we have? @@ -3059,7 +3087,7 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date) haveYear = TRUE; // no roll over - 99 means 99, not 1999 for us - year = val; + year = (wxDateTime_t)val; } else if ( isMonth ) { @@ -3103,7 +3131,7 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date) haveDay = TRUE; - day = val; + day = (wxDateTime_t)val; } } else // not a number @@ -3461,9 +3489,9 @@ wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime& dtStart, holidays.Empty(); size_t count = ms_authorities.GetCount(); - for ( size_t n = 0; n < count; n++ ) + for ( size_t nAuth = 0; nAuth < count; nAuth++ ) { - ms_authorities[n]->DoGetHolidaysInRange(dtStart, dtEnd, hol); + ms_authorities[nAuth]->DoGetHolidaysInRange(dtStart, dtEnd, hol); WX_APPEND_ARRAY(holidays, hol); } @@ -3529,3 +3557,5 @@ size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime& dtStart, return holidays.GetCount(); } + +