[Libreoffice-commits] core.git: i18npool/inc i18npool/source include/unotools offapi/com unotools/source

Eike Rathke (via logerrit) logerrit at kemper.freedesktop.org
Wed Jun 19 23:58:18 UTC 2019


 i18npool/inc/calendarImpl.hxx                   |    8 +++--
 i18npool/inc/calendar_gregorian.hxx             |    1 
 i18npool/source/calendar/calendarImpl.cxx       |   36 ++++++++++++++++++++----
 i18npool/source/calendar/calendar_gregorian.cxx |   21 ++++++++++++++
 include/unotools/calendarwrapper.hxx            |   26 ++++++++++++++++-
 offapi/com/sun/star/i18n/XCalendar4.idl         |   24 ++++++++++++++++
 unotools/source/i18n/calendarwrapper.cxx        |    8 ++---
 7 files changed, 110 insertions(+), 14 deletions(-)

New commits:
commit 942de6a01ba990e5f3bc55ce4ab3737a03f67f39
Author:     Eike Rathke <erack at redhat.com>
AuthorDate: Wed Jun 19 23:03:49 2019 +0200
Commit:     Eike Rathke <erack at redhat.com>
CommitDate: Thu Jun 20 01:57:20 2019 +0200

    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 at redhat.com>
    Tested-by: Jenkins

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&)
     {


More information about the Libreoffice-commits mailing list