blob: 6d7b4228c6d71d02925ab3ae6c2ca9129ad0cb46 [file] [edit]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/i18n/timezone.h"
#include <memory>
#include <string>
#include "base/check.h"
#include "base/i18n/icu4c_tag_converter.h" // nogncheck
#include "base/i18n/language_tag.h"
#include "base/i18n/unicodestring.h"
#include "base/memory/ptr_util.h"
#include "base/notreached.h"
#include "third_party/icu/source/common/unicode/locid.h"
#include "third_party/icu/source/common/unicode/unistr.h"
#include "third_party/icu/source/i18n/unicode/timezone.h"
namespace base::i18n {
namespace {
icu::TimeZone::EDisplayType ToIcuDisplayType(TimeZone::DisplayType style) {
switch (style) {
case TimeZone::kShort:
return icu::TimeZone::SHORT;
case TimeZone::kLong:
return icu::TimeZone::LONG;
case TimeZone::kShortGeneric:
return icu::TimeZone::SHORT_GENERIC;
case TimeZone::kLongGeneric:
return icu::TimeZone::LONG_GENERIC;
case TimeZone::kGenericLocation:
return icu::TimeZone::GENERIC_LOCATION;
case TimeZone::kShortGMT:
return icu::TimeZone::SHORT_GMT;
case TimeZone::kLongGMT:
return icu::TimeZone::LONG_GMT;
case TimeZone::kShortCommonlyUsed:
return icu::TimeZone::SHORT_COMMONLY_USED;
}
NOTREACHED();
}
std::string GetIdFromIcuTimeZone(const icu::TimeZone& zone) {
icu::UnicodeString id;
std::string res;
zone.getID(id).toUTF8String(res);
return res;
}
} // namespace
struct TimeZone::Impl {
explicit Impl(std::unique_ptr<icu::TimeZone> zone)
: icu_timezone(std::move(zone)) {
DCHECK(icu_timezone);
id = GetIdFromIcuTimeZone(*icu_timezone);
}
std::string id;
std::unique_ptr<icu::TimeZone> icu_timezone;
};
TimeZone::TimeZone()
: impl_(std::make_unique<Impl>(
base::WrapUnique(icu::TimeZone::createDefault()))) {}
TimeZone::TimeZone(const TimeZone& other)
: impl_(std::make_unique<Impl>(
base::WrapUnique(other.impl_->icu_timezone->clone()))) {}
TimeZone& TimeZone::operator=(const TimeZone& other) {
if (this != &other) {
impl_ = std::make_unique<Impl>(
base::WrapUnique(other.impl_->icu_timezone->clone()));
}
return *this;
}
TimeZone::TimeZone(TimeZone&& other) noexcept = default;
TimeZone& TimeZone::operator=(TimeZone&& other) noexcept = default;
TimeZone::~TimeZone() = default;
// static
TimeZone TimeZone::Default() {
return TimeZone();
}
// static
TimeZone TimeZone::FromString(std::string_view id) {
return TimeZone(std::make_unique<Impl>(base::WrapUnique(
icu::TimeZone::createTimeZone(icu::UnicodeString::fromUTF8(id)))));
}
// static
TimeZone TimeZone::GMT() {
return FromString("GMT");
}
// static
TimeZone TimeZone::Unknown() {
return FromString("Etc/Unknown");
}
// static
TimeZone TimeZone::DetectHostTimeZone() {
std::unique_ptr<icu::TimeZone> zone(icu::TimeZone::detectHostTimeZone());
return TimeZone(std::make_unique<Impl>(std::move(zone)));
}
std::string_view TimeZone::GetID() const {
return impl_->id;
}
std::string TimeZone::GetRegion() const {
char region_code[4];
UErrorCode status = U_ZERO_ERROR;
int length = icu::TimeZone::getRegion(icu::UnicodeString::fromUTF8(impl_->id),
region_code, 4, status);
// Return an empty string if region_code is a 3-digit numeric code such
// as 001 (World).
if (U_SUCCESS(status) && length == 2) {
return std::string(region_code, static_cast<size_t>(length));
}
return std::string();
}
// static
TimeZone::DisplayNameOptions TimeZone::CreateDefaultDisplayNameOptions() {
return DisplayNameOptions{};
}
std::u16string TimeZone::GetDisplayName(
const LanguageTag& language_tag,
const DisplayNameOptions& options) const {
icu::UnicodeString name;
icu::Locale locale =
IcuLocaleConverter::GetInstance().FromLanguageTag(language_tag);
impl_->icu_timezone->getDisplayName(
options.is_day_light, ToIcuDisplayType(options.style), locale, name);
return UnicodeStringToString16(name);
}
std::u16string TimeZone::GetDisplayName(
const DisplayNameOptions& options) const {
icu::UnicodeString name;
impl_->icu_timezone->getDisplayName(options.is_day_light,
ToIcuDisplayType(options.style),
icu::Locale::getDefault(), name);
return UnicodeStringToString16(name);
}
base::TimeDelta TimeZone::GetRawOffset() const {
return base::Milliseconds(impl_->icu_timezone->getRawOffset());
}
void TimeZone::GetOffset(base::Time time,
bool is_local,
base::TimeDelta& raw_offset,
base::TimeDelta& dst_offset) const {
int32_t raw_ms = 0;
int32_t dst_ms = 0;
UErrorCode status = U_ZERO_ERROR;
impl_->icu_timezone->getOffset(time.InMillisecondsFSinceUnixEpoch(), is_local,
raw_ms, dst_ms, status);
DCHECK(U_SUCCESS(status));
raw_offset = base::Milliseconds(raw_ms);
dst_offset = base::Milliseconds(dst_ms);
}
bool TimeZone::UseDaylightTime() const {
return impl_->icu_timezone->useDaylightTime();
}
bool TimeZone::InDaylightTime(base::Time time) const {
UErrorCode status = U_ZERO_ERROR;
bool in_daylight = impl_->icu_timezone->inDaylightTime(
time.InMillisecondsFSinceUnixEpoch(), status);
DCHECK(U_SUCCESS(status));
return in_daylight;
}
bool TimeZone::operator==(const TimeZone& other) const {
if (impl_->id == other.impl_->id) {
return true;
}
return *impl_->icu_timezone == *other.impl_->icu_timezone;
}
bool TimeZone::operator!=(const TimeZone& other) const {
return !(*this == other);
}
TimeZone::TimeZone(std::unique_ptr<Impl> impl) : impl_(std::move(impl)) {
DCHECK(impl_);
}
std::string CountryCodeForCurrentTimezone() {
TimeZone zone = TimeZone::Default();
// ICU returns '001' (world) for Etc/GMT. Preserve the old behavior
// only for Etc/GMT while returning an empty string for Etc/UTC and
// Etc/UCT because they're less likely to be chosen by mistake in UK in
// place of Europe/London (Briitish Time).
if (zone.GetID() == "Etc/GMT") {
return "GB";
}
return zone.GetRegion();
}
} // namespace base::i18n