1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "mozilla/intl/AppDateTimeFormat.h"
9 #include "mozilla/intl/DateTimePatternGenerator.h"
10 #include "mozilla/intl/FormatBuffer.h"
11 #include "mozilla/intl/LocaleService.h"
12 #include "OSPreferences.h"
13 #include "mozIOSPreferences.h"
15 # include "nsThreadManager.h"
18 namespace mozilla::intl
{
20 nsCString
* AppDateTimeFormat::sLocale
= nullptr;
21 nsTHashMap
<nsCStringHashKey
, UniquePtr
<DateTimeFormat
>>*
22 AppDateTimeFormat::sFormatCache
;
24 static const int32_t DATETIME_FORMAT_INITIAL_LEN
= 127;
27 nsresult
AppDateTimeFormat::Initialize() {
28 MOZ_ASSERT(NS_IsMainThread());
33 sLocale
= new nsCString();
34 AutoTArray
<nsCString
, 10> regionalPrefsLocales
;
35 LocaleService::GetInstance()->GetRegionalPrefsLocales(regionalPrefsLocales
);
36 sLocale
->Assign(regionalPrefsLocales
[0]);
41 // performs a locale sensitive date formatting operation on the PRTime parameter
43 nsresult
AppDateTimeFormat::Format(const DateTimeFormat::StyleBag
& aStyle
,
45 nsAString
& aStringOut
) {
46 return AppDateTimeFormat::Format(
47 aStyle
, (static_cast<double>(aPrTime
) / PR_USEC_PER_MSEC
), nullptr,
51 // performs a locale sensitive date formatting operation on the PRExplodedTime
54 nsresult
AppDateTimeFormat::Format(const DateTimeFormat::StyleBag
& aStyle
,
55 const PRExplodedTime
* aExplodedTime
,
56 nsAString
& aStringOut
) {
57 return AppDateTimeFormat::Format(
58 aStyle
, (PR_ImplodeTime(aExplodedTime
) / PR_USEC_PER_MSEC
),
59 &(aExplodedTime
->tm_params
), aStringOut
);
62 // performs a locale sensitive date formatting operation on the PRExplodedTime
63 // parameter, using the specified options.
65 nsresult
AppDateTimeFormat::Format(const DateTimeFormat::ComponentsBag
& aBag
,
66 const PRExplodedTime
* aExplodedTime
,
67 nsAString
& aStringOut
) {
69 nsresult rv
= Initialize();
74 aStringOut
.Truncate();
77 nsAutoString timeZoneID
;
78 BuildTimeZoneString(aExplodedTime
->tm_params
, timeZoneID
);
80 auto genResult
= DateTimePatternGenerator::TryCreate(sLocale
->get());
81 NS_ENSURE_TRUE(genResult
.isOk(), NS_ERROR_FAILURE
);
82 auto dateTimePatternGenerator
= genResult
.unwrap();
84 auto result
= DateTimeFormat::TryCreateFromComponents(
85 *sLocale
, aBag
, dateTimePatternGenerator
.get(), Some(timeZoneID
));
86 NS_ENSURE_TRUE(result
.isOk(), NS_ERROR_FAILURE
);
87 auto dateTimeFormat
= result
.unwrap();
90 static_cast<float>((PR_ImplodeTime(aExplodedTime
) / PR_USEC_PER_MSEC
));
92 aStringOut
.SetLength(DATETIME_FORMAT_INITIAL_LEN
);
93 nsTStringToBufferAdapter
buffer(aStringOut
);
94 NS_ENSURE_TRUE(dateTimeFormat
->TryFormat(unixEpoch
, buffer
).isOk(),
101 * An internal utility function to serialize a Maybe<DateTimeFormat::Style> to
102 * an int, to be used as a caching key.
104 static int StyleToInt(const Maybe
<DateTimeFormat::Style
>& aStyle
) {
105 if (aStyle
.isSome()) {
107 case DateTimeFormat::Style::Full
:
109 case DateTimeFormat::Style::Long
:
111 case DateTimeFormat::Style::Medium
:
113 case DateTimeFormat::Style::Short
:
121 nsresult
AppDateTimeFormat::Format(const DateTimeFormat::StyleBag
& aStyle
,
122 const double aUnixEpoch
,
123 const PRTimeParameters
* aTimeParameters
,
124 nsAString
& aStringOut
) {
127 // return, nothing to format
128 if (aStyle
.date
.isNothing() && aStyle
.time
.isNothing()) {
129 aStringOut
.Truncate();
133 // set up locale data
141 key
.AppendInt(StyleToInt(aStyle
.date
));
143 key
.AppendInt(StyleToInt(aStyle
.time
));
144 if (aTimeParameters
) {
146 key
.AppendInt(aTimeParameters
->tp_gmt_offset
);
148 key
.AppendInt(aTimeParameters
->tp_dst_offset
);
151 if (sFormatCache
&& sFormatCache
->Count() == kMaxCachedFormats
) {
152 // Don't allow a pathological page to extend the cache unreasonably.
153 NS_WARNING("flushing DateTimeFormat cache");
157 sFormatCache
= new nsTHashMap
<nsCStringHashKey
, UniquePtr
<DateTimeFormat
>>(
161 UniquePtr
<DateTimeFormat
>& dateTimeFormat
= sFormatCache
->LookupOrInsert(key
);
163 if (!dateTimeFormat
) {
164 // We didn't have a cached formatter for this key, so create one.
165 int32_t dateFormatStyle
= mozIOSPreferences::dateTimeFormatStyleNone
;
166 if (aStyle
.date
.isSome()) {
167 switch (*aStyle
.date
) {
168 case DateTimeFormat::Style::Full
:
169 case DateTimeFormat::Style::Long
:
170 dateFormatStyle
= mozIOSPreferences::dateTimeFormatStyleLong
;
172 case DateTimeFormat::Style::Medium
:
173 case DateTimeFormat::Style::Short
:
174 dateFormatStyle
= mozIOSPreferences::dateTimeFormatStyleShort
;
179 int32_t timeFormatStyle
= mozIOSPreferences::dateTimeFormatStyleNone
;
180 if (aStyle
.time
.isSome()) {
181 switch (*aStyle
.time
) {
182 case DateTimeFormat::Style::Full
:
183 case DateTimeFormat::Style::Long
:
184 timeFormatStyle
= mozIOSPreferences::dateTimeFormatStyleLong
;
186 case DateTimeFormat::Style::Medium
:
187 case DateTimeFormat::Style::Short
:
188 timeFormatStyle
= mozIOSPreferences::dateTimeFormatStyleShort
;
194 rv
= OSPreferences::GetInstance()->GetDateTimePattern(
195 dateFormatStyle
, timeFormatStyle
, nsDependentCString(sLocale
->get()),
197 NS_ENSURE_SUCCESS(rv
, rv
);
198 nsAutoString pattern
= NS_ConvertUTF8toUTF16(str
);
200 Maybe
<Span
<const char16_t
>> timeZoneOverride
= Nothing();
201 nsAutoString timeZoneID
;
202 if (aTimeParameters
) {
203 BuildTimeZoneString(*aTimeParameters
, timeZoneID
);
205 Some(Span
<const char16_t
>(timeZoneID
.Data(), timeZoneID
.Length()));
208 auto result
= DateTimeFormat::TryCreateFromPattern(*sLocale
, pattern
,
210 NS_ENSURE_TRUE(result
.isOk(), NS_ERROR_FAILURE
);
211 dateTimeFormat
= result
.unwrap();
214 MOZ_ASSERT(dateTimeFormat
);
216 aStringOut
.SetLength(DATETIME_FORMAT_INITIAL_LEN
);
217 nsTStringToBufferAdapter
buffer(aStringOut
);
218 NS_ENSURE_TRUE(dateTimeFormat
->TryFormat(aUnixEpoch
, buffer
).isOk(),
225 void AppDateTimeFormat::BuildTimeZoneString(
226 const PRTimeParameters
& aTimeParameters
, nsAString
& aStringOut
) {
227 aStringOut
.Truncate();
228 aStringOut
.Append(u
"GMT");
229 int32_t totalOffsetMinutes
=
230 (aTimeParameters
.tp_gmt_offset
+ aTimeParameters
.tp_dst_offset
) / 60;
231 if (totalOffsetMinutes
!= 0) {
232 char sign
= totalOffsetMinutes
< 0 ? '-' : '+';
233 int32_t hours
= abs(totalOffsetMinutes
) / 60;
234 int32_t minutes
= abs(totalOffsetMinutes
) % 60;
235 aStringOut
.AppendPrintf("%c%02d:%02d", sign
, hours
, minutes
);
240 void AppDateTimeFormat::DeleteCache() {
241 MOZ_ASSERT(NS_IsMainThread());
244 sFormatCache
= nullptr;
249 void AppDateTimeFormat::Shutdown() {
250 MOZ_ASSERT(NS_IsMainThread());
256 void AppDateTimeFormat::ClearLocaleCache() {
257 MOZ_ASSERT(NS_IsMainThread());
263 } // namespace mozilla::intl