Bug 1919083 - [ci] Enable os-integration variant for more suites, r=jmaher
[gecko.git] / intl / locale / AppDateTimeFormat.cpp
blobd967d312a443ed5a7237d9ab55321ae56f17f884
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/. */
7 #include "nsCOMPtr.h"
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"
14 #ifdef DEBUG
15 # include "nsThreadManager.h"
16 #endif
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;
26 /*static*/
27 nsresult AppDateTimeFormat::Initialize() {
28 MOZ_ASSERT(NS_IsMainThread());
29 if (sLocale) {
30 return NS_OK;
33 sLocale = new nsCString();
34 AutoTArray<nsCString, 10> regionalPrefsLocales;
35 LocaleService::GetInstance()->GetRegionalPrefsLocales(regionalPrefsLocales);
36 sLocale->Assign(regionalPrefsLocales[0]);
38 return NS_OK;
41 // performs a locale sensitive date formatting operation on the PRTime parameter
42 /*static*/
43 nsresult AppDateTimeFormat::Format(const DateTimeFormat::StyleBag& aStyle,
44 const PRTime aPrTime,
45 nsAString& aStringOut) {
46 return AppDateTimeFormat::Format(
47 aStyle, (static_cast<double>(aPrTime) / PR_USEC_PER_MSEC), nullptr,
48 aStringOut);
51 // performs a locale sensitive date formatting operation on the PRExplodedTime
52 // parameter
53 /*static*/
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.
64 /*static*/
65 nsresult AppDateTimeFormat::Format(const DateTimeFormat::ComponentsBag& aBag,
66 const PRExplodedTime* aExplodedTime,
67 nsAString& aStringOut) {
68 // set up locale data
69 nsresult rv = Initialize();
70 if (NS_FAILED(rv)) {
71 return rv;
74 aStringOut.Truncate();
76 nsAutoCString str;
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();
89 double unixEpoch =
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(),
95 NS_ERROR_FAILURE);
97 return rv;
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()) {
106 switch (*aStyle) {
107 case DateTimeFormat::Style::Full:
108 return 1;
109 case DateTimeFormat::Style::Long:
110 return 2;
111 case DateTimeFormat::Style::Medium:
112 return 3;
113 case DateTimeFormat::Style::Short:
114 return 4;
117 return 0;
120 /*static*/
121 nsresult AppDateTimeFormat::Format(const DateTimeFormat::StyleBag& aStyle,
122 const double aUnixEpoch,
123 const PRTimeParameters* aTimeParameters,
124 nsAString& aStringOut) {
125 nsresult rv = NS_OK;
127 // return, nothing to format
128 if (aStyle.date.isNothing() && aStyle.time.isNothing()) {
129 aStringOut.Truncate();
130 return NS_OK;
133 // set up locale data
134 rv = Initialize();
136 if (NS_FAILED(rv)) {
137 return rv;
140 nsAutoCString key;
141 key.AppendInt(StyleToInt(aStyle.date));
142 key.Append(':');
143 key.AppendInt(StyleToInt(aStyle.time));
144 if (aTimeParameters) {
145 key.Append(':');
146 key.AppendInt(aTimeParameters->tp_gmt_offset);
147 key.Append(':');
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");
154 DeleteCache();
156 if (!sFormatCache) {
157 sFormatCache = new nsTHashMap<nsCStringHashKey, UniquePtr<DateTimeFormat>>(
158 kMaxCachedFormats);
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;
171 break;
172 case DateTimeFormat::Style::Medium:
173 case DateTimeFormat::Style::Short:
174 dateFormatStyle = mozIOSPreferences::dateTimeFormatStyleShort;
175 break;
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;
185 break;
186 case DateTimeFormat::Style::Medium:
187 case DateTimeFormat::Style::Short:
188 timeFormatStyle = mozIOSPreferences::dateTimeFormatStyleShort;
189 break;
193 nsAutoCString str;
194 rv = OSPreferences::GetInstance()->GetDateTimePattern(
195 dateFormatStyle, timeFormatStyle, nsDependentCString(sLocale->get()),
196 str);
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);
204 timeZoneOverride =
205 Some(Span<const char16_t>(timeZoneID.Data(), timeZoneID.Length()));
208 auto result = DateTimeFormat::TryCreateFromPattern(*sLocale, pattern,
209 timeZoneOverride);
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(),
219 NS_ERROR_FAILURE);
221 return rv;
224 /*static*/
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);
239 /*static*/
240 void AppDateTimeFormat::DeleteCache() {
241 MOZ_ASSERT(NS_IsMainThread());
242 if (sFormatCache) {
243 delete sFormatCache;
244 sFormatCache = nullptr;
248 /*static*/
249 void AppDateTimeFormat::Shutdown() {
250 MOZ_ASSERT(NS_IsMainThread());
251 DeleteCache();
252 delete sLocale;
255 /*static*/
256 void AppDateTimeFormat::ClearLocaleCache() {
257 MOZ_ASSERT(NS_IsMainThread());
258 DeleteCache();
259 delete sLocale;
260 sLocale = nullptr;
263 } // namespace mozilla::intl