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.
This commit is contained in:
Jeff Genovy 2019-02-20 19:03:11 -08:00
parent 3ebd817504
commit 1188394d74
4 changed files with 49 additions and 27 deletions

View file

@ -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

View file

@ -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;

View file

@ -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()`.
*
* <p>This function is not thread safe.</p>
*
* @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

View file

@ -105,7 +105,7 @@
* <p>
* <strong>Note:</strong> 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 <em>and</em> day-of-week in some cases.
*
* <p>
@ -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