From: Jeff Genovy <29107334+jefgen@users.noreply.github.com> Date: Thu, 21 Feb 2019 03:03:11 +0000 (-0800) Subject: ICU-13820 ICU4C should use "Etc/Unknown" zone when host TZ detection fails. X-Git-Tag: release-64-rc~38 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1188394d744e5a56f8465fd1be4e3b886a51b801;p=icu ICU-13820 ICU4C should use "Etc/Unknown" zone when host TZ detection fails. Update API docs comments to clarify what is returned in failure cases. --- diff --git a/icu4c/source/common/putil.cpp b/icu4c/source/common/putil.cpp index cdfaed9936b..9ed85c00a05 100644 --- a/icu4c/source/common/putil.cpp +++ b/icu4c/source/common/putil.cpp @@ -1068,7 +1068,7 @@ uprv_tzname(int n) // the other code path returns a pointer to a heap location. // If we don't have a name already, then tzname wouldn't be any // better, so just fall back. - return uprv_strdup("Etc/UTC"); + return uprv_strdup(""); #endif // !U_TZNAME #else diff --git a/icu4c/source/i18n/timezone.cpp b/icu4c/source/i18n/timezone.cpp index b8fe13a3685..f8711e47ade 100644 --- a/icu4c/source/i18n/timezone.cpp +++ b/icu4c/source/i18n/timezone.cpp @@ -456,10 +456,11 @@ TimeZone::createTimeZone(const UnicodeString& ID) TimeZone* U_EXPORT2 TimeZone::detectHostTimeZone() { - // We access system timezone data through TPlatformUtilities, - // including tzset(), timezone, and tzname[]. + // We access system timezone data through uprv_tzset(), uprv_tzname(), and others, + // which have platform specific implementations in putil.cpp int32_t rawOffset = 0; const char *hostID; + UBool hostDetectionSucceeded = TRUE; // First, try to create a system timezone, based // on the string ID in tzname[0]. @@ -470,8 +471,7 @@ TimeZone::detectHostTimeZone() // Get the timezone ID from the host. This function should do // any required host-specific remapping; e.g., on Windows this - // function maps the Date and Time control panel setting to an - // ICU timezone ID. + // function maps the Windows Time Zone name to an ICU timezone ID. hostID = uprv_tzname(0); // Invert sign because UNIX semantics are backwards @@ -479,10 +479,15 @@ TimeZone::detectHostTimeZone() TimeZone* hostZone = NULL; - /* Make sure that the string is NULL terminated to prevent BoundsChecker/Purify warnings. */ UnicodeString hostStrID(hostID, -1, US_INV); - hostStrID.append((UChar)0); - hostStrID.truncate(hostStrID.length()-1); + + if (hostStrID.length() == 0) { + // The host time zone detection (or remapping) above has failed and + // we have no name at all. Fallback to using the Unknown zone. + hostStrID = UnicodeString(TRUE, UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH); + hostDetectionSucceeded = FALSE; + } + hostZone = createSystemTimeZone(hostStrID); #if U_PLATFORM_USES_ONLY_WIN32_API @@ -502,19 +507,19 @@ TimeZone::detectHostTimeZone() // Construct a fixed standard zone with the host's ID // and raw offset. - if (hostZone == NULL) { + if (hostZone == NULL && hostDetectionSucceeded) { hostZone = new SimpleTimeZone(rawOffset, hostStrID); } - // If we _still_ don't have a time zone, use GMT. + // If we _still_ don't have a time zone, use the Unknown zone. // // Note: This is extremely unlikely situation. If // new SimpleTimeZone(...) above fails, the following // code may also fail. if (hostZone == NULL) { - const TimeZone* temptz = TimeZone::getGMT(); - // GMT zone uses staticly allocated memory, so creation of it can never fail due to OOM. - hostZone = temptz->clone(); + // Unknown zone uses static allocated memory, so it must always exist. + // However, clone() allocates memory and can fail. + hostZone = TimeZone::getUnknown().clone(); } return hostZone; diff --git a/icu4c/source/i18n/unicode/timezone.h b/icu4c/source/i18n/unicode/timezone.h index bc15bbf5fc4..de558f91ac9 100644 --- a/icu4c/source/i18n/unicode/timezone.h +++ b/icu4c/source/i18n/unicode/timezone.h @@ -277,17 +277,25 @@ public: /** * Creates an instance of TimeZone detected from the current host - * system configuration. Note that ICU4C does not change the default - * time zone unless TimeZone::adoptDefault(TimeZone*) or - * TimeZone::setDefault(const TimeZone&) is explicitly called by a + * system configuration. If the host system detection routines fail, + * or if they specify a TimeZone or TimeZone offset which is not + * recognized, then the special TimeZone "Etc/Unknown" is returned. + * + * Note that ICU4C does not change the default time zone unless + * `TimeZone::adoptDefault(TimeZone*)` or + * `TimeZone::setDefault(const TimeZone&)` is explicitly called by a * user. This method does not update the current ICU's default, * and may return a different TimeZone from the one returned by - * TimeZone::createDefault(). + * `TimeZone::createDefault()`. * *
This function is not thread safe.
* * @return A new instance of TimeZone detected from the current host system * configuration. + * @see adoptDefault + * @see setDefault + * @see createDefault + * @see getUnknown * @stable ICU 55 */ static TimeZone* U_EXPORT2 detectHostTimeZone(); @@ -295,13 +303,14 @@ public: /** * Creates a new copy of the default TimeZone for this host. Unless the default time * zone has already been set using adoptDefault() or setDefault(), the default is - * determined by querying the system using methods in TPlatformUtilities. If the - * system routines fail, or if they specify a TimeZone or TimeZone offset which is not - * recognized, the TimeZone indicated by the ID kLastResortID is instantiated - * and made the default. + * determined by querying the host system configuration. If the host system detection + * routines fail, or if they specify a TimeZone or TimeZone offset which is not + * recognized, then the special TimeZone "Etc/Unknown" is instantiated and made the + * default. * * @return A default TimeZone. Clients are responsible for deleting the time zone * object returned. + * @see getUnknown * @stable ICU 2.0 */ static TimeZone* U_EXPORT2 createDefault(void); @@ -676,7 +685,7 @@ public: * @param locale the locale in which to supply the display name. * @param result the human-readable name of this time zone in the given locale * or in the default locale if the given locale is not recognized. - * @return A refence to 'result'. + * @return A reference to 'result'. * @stable ICU 2.0 */ UnicodeString& getDisplayName(UBool inDaylight, EDisplayType style, const Locale& locale, UnicodeString& result) const; @@ -926,7 +935,7 @@ private: UErrorCode& status); /** - * Returns the normalized custome time zone ID for the given offset fields. + * Returns the normalized custom time zone ID for the given offset fields. * @param hour offset hours * @param min offset minutes * @param sec offset seconds diff --git a/icu4c/source/i18n/unicode/ucal.h b/icu4c/source/i18n/unicode/ucal.h index fb7c387c2d7..f31a25732dc 100644 --- a/icu4c/source/i18n/unicode/ucal.h +++ b/icu4c/source/i18n/unicode/ucal.h @@ -105,7 +105,7 @@ ** Note: for some non-Gregorian calendars, different * fields may be necessary for complete disambiguation. For example, a full - * specification of the historial Arabic astronomical calendar requires year, + * specification of the historical Arabic astronomical calendar requires year, * month, day-of-month and day-of-week in some cases. * *
@@ -157,6 +157,7 @@ /** * The time zone ID reserved for unknown time zone. + * It behaves like the GMT/UTC time zone but has the special ID "Etc/Unknown". * @stable ICU 4.8 */ #define UCAL_UNKNOWN_ZONE_ID "Etc/Unknown" @@ -620,8 +621,13 @@ ucal_openCountryTimeZones(const char* country, UErrorCode* ec); /** * Return the default time zone. The default is determined initially - * by querying the host operating system. It may be changed with - * ucal_setDefaultTimeZone() or with the C++ TimeZone API. + * by querying the host operating system. If the host system detection + * routines fail, or if they specify a TimeZone or TimeZone offset + * which is not recognized, then the special TimeZone "Etc/Unknown" + * is returned. + * + * The default may be changed with `ucal_setDefaultTimeZone()` or with + * the C++ TimeZone API, `TimeZone::adoptDefault(TimeZone*)`. * * @param result A buffer to receive the result, or NULL * @@ -631,7 +637,9 @@ ucal_openCountryTimeZones(const char* country, UErrorCode* ec); * * @return The result string length, not including the terminating * null - * + * + * @see #UCAL_UNKNOWN_ZONE_ID + * * @stable ICU 2.6 */ U_STABLE int32_t U_EXPORT2