diff options
author | Eike Rathke <erack@redhat.com> | 2019-06-19 23:03:49 +0200 |
---|---|---|
committer | Eike Rathke <erack@redhat.com> | 2019-06-20 01:57:20 +0200 |
commit | 942de6a01ba990e5f3bc55ce4ab3737a03f67f39 (patch) | |
tree | ba95c764124c7075fa8511a2788c8897ade95cc2 /i18npool/source | |
parent | 36fe1461c5ae7a7db175c77688aaff1a1d12551a (diff) |
Resolves: tdf#92503 introduce TimeZone to calendar loading and default to UTC
Without that, the system's time zone was used which on DST
transition dates leads to non-existent times when switching
to/from DST. As the calendar use and number parser/formatter nor
conversions or calculations are time zone aware, using not DST
afflicted UTC is the better choice.
Change-Id: I3303c6620d8c4b9d081555c8293954fb1bd67895
Reviewed-on: https://gerrit.libreoffice.org/74386
Reviewed-by: Eike Rathke <erack@redhat.com>
Tested-by: Jenkins
Diffstat (limited to 'i18npool/source')
-rw-r--r-- | i18npool/source/calendar/calendarImpl.cxx | 36 | ||||
-rw-r--r-- | i18npool/source/calendar/calendar_gregorian.cxx | 21 |
2 files changed, 52 insertions, 5 deletions
diff --git a/i18npool/source/calendar/calendarImpl.cxx b/i18npool/source/calendar/calendarImpl.cxx index 93ff9b16fa0b..f0962104a4b0 100644 --- a/i18npool/source/calendar/calendarImpl.cxx +++ b/i18npool/source/calendar/calendarImpl.cxx @@ -18,6 +18,7 @@ */ #include <calendarImpl.hxx> +#include <calendar_gregorian.hxx> #include <localedata.hxx> #include <cppuhelper/supportsservice.hxx> @@ -40,12 +41,12 @@ CalendarImpl::~CalendarImpl() } void SAL_CALL -CalendarImpl::loadDefaultCalendar( const Locale& rLocale ) +CalendarImpl::loadDefaultCalendarTZ( const Locale& rLocale, const OUString& rTimeZone ) { Sequence< Calendar2 > xC = LocaleDataImpl::get()->getAllCalendars2(rLocale); for (sal_Int32 i = 0; i < xC.getLength(); i++) { if (xC[i].Default) { - loadCalendar(xC[i].Name, rLocale); + loadCalendarTZ(xC[i].Name, rLocale, rTimeZone); return; } } @@ -53,14 +54,16 @@ CalendarImpl::loadDefaultCalendar( const Locale& rLocale ) } void SAL_CALL -CalendarImpl::loadCalendar(const OUString& uniqueID, const Locale& rLocale ) +CalendarImpl::loadCalendarTZ( const OUString& uniqueID, const Locale& rLocale, const OUString& rTimeZone ) { Reference < XCalendar4 > xOldCalendar( xCalendar ); // backup + const OUString aCacheID( uniqueID + "_" + rTimeZone); + bool bTimeZone = true; sal_Int32 i; for (i = 0; i < sal::static_int_cast<sal_Int32>(lookupTable.size()); i++) { lookupTableItem &listItem = lookupTable[i]; - if (uniqueID == listItem.uniqueID) { + if (aCacheID == listItem.m_aCacheID) { xCalendar = listItem.xCalendar; break; } @@ -85,7 +88,16 @@ CalendarImpl::loadCalendar(const OUString& uniqueID, const Locale& rLocale ) throw ERROR; xCalendar.set(xI, UNO_QUERY); - lookupTable.emplace_back( uniqueID, xCalendar ); + if (!rTimeZone.isEmpty()) + { + /* XXX NOTE: currently (2019-06-19) calendar implementations derive + * from Calendar_gregorian, even Hijri and Jewish. If that should + * change in future this should be adapted. */ + Calendar_gregorian* pCal = dynamic_cast<Calendar_gregorian*>(xCalendar.get()); + bTimeZone = (pCal && pCal->setTimeZone(rTimeZone)); + } + + lookupTable.emplace_back( aCacheID, xCalendar ); } if ( !xCalendar.is() ) @@ -103,6 +115,10 @@ CalendarImpl::loadCalendar(const OUString& uniqueID, const Locale& rLocale ) xCalendar = xOldCalendar; throw; } + + if (!bTimeZone) + // The calendar is usable but is not in the expected time zone. + throw ERROR; } Calendar2 SAL_CALL @@ -164,6 +180,16 @@ CalendarImpl::getLocalDateTime() return xCalendar->getLocalDateTime(); } +void SAL_CALL CalendarImpl::loadDefaultCalendar( const css::lang::Locale& rLocale ) +{ + loadDefaultCalendarTZ( rLocale, OUString()); +} + +void SAL_CALL CalendarImpl::loadCalendar( const OUString& uniqueID, const css::lang::Locale& rLocale ) +{ + loadCalendarTZ( uniqueID, rLocale, OUString()); +} + OUString SAL_CALL CalendarImpl::getUniqueID() { diff --git a/i18npool/source/calendar/calendar_gregorian.cxx b/i18npool/source/calendar/calendar_gregorian.cxx index 18676361027a..f3b228efc04e 100644 --- a/i18npool/source/calendar/calendar_gregorian.cxx +++ b/i18npool/source/calendar/calendar_gregorian.cxx @@ -167,6 +167,11 @@ Calendar_gregorian::init(const Era *_eraArray) * */ icu::Locale aIcuLocale( "", nullptr, nullptr, "calendar=gregorian"); + /* XXX: not specifying a timezone when creating a calendar assigns the + * system's timezone with all DST quirks, invalid times when switching + * to/from DST and so on. The XCalendar* interfaces are defined to support + * local time and UTC time so we can not override that here. + */ UErrorCode status = U_ZERO_ERROR; body.reset( icu::Calendar::createInstance( aIcuLocale, status) ); if (!body || !U_SUCCESS(status)) throw ERROR; @@ -369,6 +374,22 @@ Calendar_gregorian::getLocalDateTime() return (fTime + (nZoneOffset + nDSTOffset)) / U_MILLIS_PER_DAY; } +bool Calendar_gregorian::setTimeZone( const OUString& rTimeZone ) +{ + if (fieldSet) + { + setValue(); + getValue(); + } + const icu::UnicodeString aID( reinterpret_cast<const UChar*>(rTimeZone.getStr()), rTimeZone.getLength()); + const std::unique_ptr<const icu::TimeZone> pTZ( icu::TimeZone::createTimeZone(aID)); + if (!pTZ) + return false; + + body->setTimeZone(*pTZ); + return true; +} + // map field value from gregorian calendar to other calendar, it can be overwritten by derived class. // By using eraArray, it can take care Japanese and Taiwan ROC calendar. void Calendar_gregorian::mapFromGregorian() |