diff options
-rw-r--r-- | i18npool/inc/calendarImpl.hxx | 8 | ||||
-rw-r--r-- | i18npool/inc/calendar_gregorian.hxx | 1 | ||||
-rw-r--r-- | i18npool/source/calendar/calendarImpl.cxx | 36 | ||||
-rw-r--r-- | i18npool/source/calendar/calendar_gregorian.cxx | 21 | ||||
-rw-r--r-- | include/unotools/calendarwrapper.hxx | 26 | ||||
-rw-r--r-- | offapi/com/sun/star/i18n/XCalendar4.idl | 24 | ||||
-rw-r--r-- | unotools/source/i18n/calendarwrapper.cxx | 8 |
7 files changed, 110 insertions, 14 deletions
diff --git a/i18npool/inc/calendarImpl.hxx b/i18npool/inc/calendarImpl.hxx index b35decb0f750..299bfe79bae2 100644 --- a/i18npool/inc/calendarImpl.hxx +++ b/i18npool/inc/calendarImpl.hxx @@ -84,6 +84,8 @@ public: // XCalendar4 virtual void SAL_CALL setLocalDateTime(double TimeInDays) override; virtual double SAL_CALL getLocalDateTime() override; + virtual void SAL_CALL loadDefaultCalendarTZ(const css::lang::Locale& rLocale, const OUString& rTimeZone) override; + virtual void SAL_CALL loadCalendarTZ(const OUString& uniqueID, const css::lang::Locale& rLocale, const OUString& rTimeZone) override; //XServiceInfo virtual OUString SAL_CALL getImplementationName() override; @@ -92,9 +94,9 @@ public: private: struct lookupTableItem { - lookupTableItem(const OUString& _uniqueID, css::uno::Reference < css::i18n::XCalendar4 > const & _xCalendar) - : uniqueID(_uniqueID), xCalendar(_xCalendar) {} - OUString uniqueID; + lookupTableItem(const OUString& rCacheID, css::uno::Reference < css::i18n::XCalendar4 > const & _xCalendar) + : m_aCacheID(rCacheID), xCalendar(_xCalendar) {} + OUString m_aCacheID; css::uno::Reference < css::i18n::XCalendar4 > xCalendar; }; std::vector<lookupTableItem> lookupTable; diff --git a/i18npool/inc/calendar_gregorian.hxx b/i18npool/inc/calendar_gregorian.hxx index 894f2246e30b..aa9a02965d79 100644 --- a/i18npool/inc/calendar_gregorian.hxx +++ b/i18npool/inc/calendar_gregorian.hxx @@ -54,6 +54,7 @@ public: Calendar_gregorian(); Calendar_gregorian(const Era *_eraArray); void init(const Era *_eraArray); + bool setTimeZone( const OUString& rTimeZone ); /** * Destructor 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() diff --git a/include/unotools/calendarwrapper.hxx b/include/unotools/calendarwrapper.hxx index 149393bb7e84..a81186ae4ba5 100644 --- a/include/unotools/calendarwrapper.hxx +++ b/include/unotools/calendarwrapper.hxx @@ -54,8 +54,30 @@ public: // wrapper implementations of XCalendar - void loadDefaultCalendar( const css::lang::Locale& rLocale ); - void loadCalendar( const OUString& rUniqueID, const css::lang::Locale& rLocale ); + /** Load the default calendar of a locale. + + This adds a bool bTimeZoneUTC parameter which is not part of the UNO API to + facilitate handling of non time zone aware data. + + @param bTimeZoneUTC + Default <TRUE/>. If <FALSE/>, the system's timezone is assigned + to the calendar, including all DST quirks like not existing + times on DST transition dates when switching to/from DST. As + current implementations and number parser/formatter don't store + or convert or calculate with time zones it is safer to use UTC, + which is not DST afflicted, otherwise surprises are lurking + (for example tdf#92503). + */ + void loadDefaultCalendar( const css::lang::Locale& rLocale, bool bTimeZoneUTC = true ); + /// This adds a bTimeZoneUTC parameter which is not part of the API. + void loadCalendar( const OUString& rUniqueID, const css::lang::Locale& rLocale, bool bTimeZoneUTC = true ); + + /* XXX NOTE: the time zone taking UNO API functions are not implemented as + * wrapper interface as they are not necessary/used so far. These are: + void loadDefaultCalendarTZ( const css::lang::Locale& rLocale, const OUString& rTimeZone ); + void loadCalendarTZ( const OUString& rUniqueID, const css::lang::Locale& rLocale, const OUString& rTimeZone ); + */ + css::uno::Sequence< OUString > getAllCalendars( const css::lang::Locale& rLocale ) const; OUString getUniqueID() const; /// set UTC date/time diff --git a/offapi/com/sun/star/i18n/XCalendar4.idl b/offapi/com/sun/star/i18n/XCalendar4.idl index 1303dc0aed11..94686b4a0031 100644 --- a/offapi/com/sun/star/i18n/XCalendar4.idl +++ b/offapi/com/sun/star/i18n/XCalendar4.idl @@ -49,6 +49,30 @@ interface XCalendar4 : com::sun::star::i18n::XCalendar3 */ double getLocalDateTime(); + /** Load the default calendar for the given locale with a given time zone. + + @param TimeZone + If empty, the system's time zone is used. + Else specified as "Region/City" name like "Europe/Berlin", + or a custom time zone ID such as "UTC" or "GMT-8:00". + + @since LibreOffice 6.3 + */ + void loadDefaultCalendarTZ( [in] ::com::sun::star::lang::Locale rLocale, [in] string TimeZone ); + + /** Load a specific calendar for the given locale with a given time zone. + + @param TimeZone + If empty, the system's time zone is used. + Else specified as "Region/City" name like "Europe/Berlin", + or a custom time zone ID such as "UTC" or "GMT-8:00". + + @since LibreOffice 6.3 + */ + void loadCalendarTZ( [in] string uniqueID, + [in] ::com::sun::star::lang::Locale rLocale, + [in] string TimeZone ); + }; }; }; }; }; diff --git a/unotools/source/i18n/calendarwrapper.cxx b/unotools/source/i18n/calendarwrapper.cxx index 6e755be3bd36..5f4a1669bccf 100644 --- a/unotools/source/i18n/calendarwrapper.cxx +++ b/unotools/source/i18n/calendarwrapper.cxx @@ -39,12 +39,12 @@ CalendarWrapper::~CalendarWrapper() { } -void CalendarWrapper::loadDefaultCalendar( const css::lang::Locale& rLocale ) +void CalendarWrapper::loadDefaultCalendar( const css::lang::Locale& rLocale, bool bTimeZoneUTC ) { try { if ( xC.is() ) - xC->loadDefaultCalendar( rLocale ); + xC->loadDefaultCalendarTZ( rLocale, (bTimeZoneUTC ? "UTC" : OUString())); } catch (const Exception&) { @@ -52,12 +52,12 @@ void CalendarWrapper::loadDefaultCalendar( const css::lang::Locale& rLocale ) } } -void CalendarWrapper::loadCalendar( const OUString& rUniqueID, const css::lang::Locale& rLocale ) +void CalendarWrapper::loadCalendar( const OUString& rUniqueID, const css::lang::Locale& rLocale, bool bTimeZoneUTC ) { try { if ( xC.is() ) - xC->loadCalendar( rUniqueID, rLocale ); + xC->loadCalendarTZ( rUniqueID, rLocale, (bTimeZoneUTC ? "UTC" : OUString())); } catch (const Exception&) { |