|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include <CoreFoundation/CoreFoundation.h> |
|
7 #include "nsIServiceManager.h" |
|
8 #include "nsDateTimeFormatMac.h" |
|
9 #include <CoreFoundation/CFDateFormatter.h> |
|
10 #include "nsIComponentManager.h" |
|
11 #include "nsILocaleService.h" |
|
12 #include "nsCRT.h" |
|
13 #include "plstr.h" |
|
14 #include "nsUnicharUtils.h" |
|
15 #include "nsTArray.h" |
|
16 |
|
17 |
|
18 NS_IMPL_ISUPPORTS(nsDateTimeFormatMac, nsIDateTimeFormat) |
|
19 |
|
20 nsresult nsDateTimeFormatMac::Initialize(nsILocale* locale) |
|
21 { |
|
22 nsAutoString localeStr; |
|
23 nsAutoString category(NS_LITERAL_STRING("NSILOCALE_TIME")); |
|
24 nsresult res; |
|
25 |
|
26 // use cached info if match with stored locale |
|
27 if (nullptr == locale) { |
|
28 if (!mLocale.IsEmpty() && |
|
29 mLocale.Equals(mAppLocale, nsCaseInsensitiveStringComparator())) { |
|
30 return NS_OK; |
|
31 } |
|
32 } |
|
33 else { |
|
34 res = locale->GetCategory(category, localeStr); |
|
35 if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) { |
|
36 if (!mLocale.IsEmpty() && |
|
37 mLocale.Equals(localeStr, |
|
38 nsCaseInsensitiveStringComparator())) { |
|
39 return NS_OK; |
|
40 } |
|
41 } |
|
42 } |
|
43 |
|
44 // get application locale |
|
45 nsCOMPtr<nsILocaleService> localeService = |
|
46 do_GetService(NS_LOCALESERVICE_CONTRACTID, &res); |
|
47 if (NS_SUCCEEDED(res)) { |
|
48 nsCOMPtr<nsILocale> appLocale; |
|
49 res = localeService->GetApplicationLocale(getter_AddRefs(appLocale)); |
|
50 if (NS_SUCCEEDED(res)) { |
|
51 res = appLocale->GetCategory(category, localeStr); |
|
52 if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) { |
|
53 mAppLocale = localeStr; // cache app locale name |
|
54 } |
|
55 } |
|
56 } |
|
57 |
|
58 // use app default if no locale specified |
|
59 if (nullptr == locale) { |
|
60 mUseDefaultLocale = true; |
|
61 } |
|
62 else { |
|
63 mUseDefaultLocale = false; |
|
64 res = locale->GetCategory(category, localeStr); |
|
65 } |
|
66 |
|
67 if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) { |
|
68 mLocale.Assign(localeStr); // cache locale name |
|
69 } |
|
70 |
|
71 return res; |
|
72 } |
|
73 |
|
74 // performs a locale sensitive date formatting operation on the time_t parameter |
|
75 nsresult nsDateTimeFormatMac::FormatTime(nsILocale* locale, |
|
76 const nsDateFormatSelector dateFormatSelector, |
|
77 const nsTimeFormatSelector timeFormatSelector, |
|
78 const time_t timetTime, |
|
79 nsAString& stringOut) |
|
80 { |
|
81 struct tm tmTime; |
|
82 return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, localtime_r(&timetTime, &tmTime), stringOut); |
|
83 } |
|
84 |
|
85 // performs a locale sensitive date formatting operation on the struct tm parameter |
|
86 nsresult nsDateTimeFormatMac::FormatTMTime(nsILocale* locale, |
|
87 const nsDateFormatSelector dateFormatSelector, |
|
88 const nsTimeFormatSelector timeFormatSelector, |
|
89 const struct tm* tmTime, |
|
90 nsAString& stringOut) |
|
91 { |
|
92 nsresult res = NS_OK; |
|
93 |
|
94 // set up locale data |
|
95 (void) Initialize(locale); |
|
96 |
|
97 // return, nothing to format |
|
98 if (dateFormatSelector == kDateFormatNone && timeFormatSelector == kTimeFormatNone) { |
|
99 stringOut.Truncate(); |
|
100 return NS_OK; |
|
101 } |
|
102 |
|
103 NS_ASSERTION(tmTime->tm_mon >= 0, "tm is not set correctly"); |
|
104 NS_ASSERTION(tmTime->tm_mday >= 1, "tm is not set correctly"); |
|
105 NS_ASSERTION(tmTime->tm_hour >= 0, "tm is not set correctly"); |
|
106 NS_ASSERTION(tmTime->tm_min >= 0, "tm is not set correctly"); |
|
107 NS_ASSERTION(tmTime->tm_sec >= 0, "tm is not set correctly"); |
|
108 NS_ASSERTION(tmTime->tm_wday >= 0, "tm is not set correctly"); |
|
109 |
|
110 // Got the locale for the formatter: |
|
111 CFLocaleRef formatterLocale; |
|
112 if (!locale) { |
|
113 formatterLocale = CFLocaleCopyCurrent(); |
|
114 } else { |
|
115 CFStringRef localeStr = CFStringCreateWithCharacters(nullptr, |
|
116 reinterpret_cast<const UniChar*>(mLocale.get()), |
|
117 mLocale.Length()); |
|
118 formatterLocale = CFLocaleCreate(nullptr, localeStr); |
|
119 CFRelease(localeStr); |
|
120 } |
|
121 |
|
122 // Get the date style for the formatter: |
|
123 CFDateFormatterStyle dateStyle; |
|
124 switch (dateFormatSelector) { |
|
125 case kDateFormatLong: |
|
126 dateStyle = kCFDateFormatterLongStyle; |
|
127 break; |
|
128 case kDateFormatShort: |
|
129 dateStyle = kCFDateFormatterShortStyle; |
|
130 break; |
|
131 case kDateFormatYearMonth: |
|
132 case kDateFormatWeekday: |
|
133 dateStyle = kCFDateFormatterNoStyle; // formats handled below |
|
134 break; |
|
135 case kDateFormatNone: |
|
136 dateStyle = kCFDateFormatterNoStyle; |
|
137 break; |
|
138 default: |
|
139 NS_ERROR("Unknown nsDateFormatSelector"); |
|
140 res = NS_ERROR_FAILURE; |
|
141 dateStyle = kCFDateFormatterNoStyle; |
|
142 } |
|
143 |
|
144 // Get the time style for the formatter: |
|
145 CFDateFormatterStyle timeStyle; |
|
146 switch (timeFormatSelector) { |
|
147 case kTimeFormatSeconds: |
|
148 case kTimeFormatSecondsForce24Hour: // 24 hour part fixed below |
|
149 timeStyle = kCFDateFormatterMediumStyle; |
|
150 break; |
|
151 case kTimeFormatNoSeconds: |
|
152 case kTimeFormatNoSecondsForce24Hour: // 24 hour part fixed below |
|
153 timeStyle = kCFDateFormatterShortStyle; |
|
154 break; |
|
155 case kTimeFormatNone: |
|
156 timeStyle = kCFDateFormatterNoStyle; |
|
157 break; |
|
158 default: |
|
159 NS_ERROR("Unknown nsTimeFormatSelector"); |
|
160 res = NS_ERROR_FAILURE; |
|
161 timeStyle = kCFDateFormatterNoStyle; |
|
162 } |
|
163 |
|
164 // Create the formatter and fix up its formatting as necessary: |
|
165 CFDateFormatterRef formatter = |
|
166 CFDateFormatterCreate(nullptr, formatterLocale, dateStyle, timeStyle); |
|
167 |
|
168 CFRelease(formatterLocale); |
|
169 |
|
170 if (dateFormatSelector == kDateFormatYearMonth || |
|
171 dateFormatSelector == kDateFormatWeekday) { |
|
172 CFStringRef dateFormat = |
|
173 dateFormatSelector == kDateFormatYearMonth ? CFSTR("yyyy/MM ") : CFSTR("EEE "); |
|
174 |
|
175 CFStringRef oldFormat = CFDateFormatterGetFormat(formatter); |
|
176 CFMutableStringRef newFormat = CFStringCreateMutableCopy(nullptr, 0, oldFormat); |
|
177 CFStringInsert(newFormat, 0, dateFormat); |
|
178 CFDateFormatterSetFormat(formatter, newFormat); |
|
179 CFRelease(newFormat); // note we don't own oldFormat |
|
180 } |
|
181 |
|
182 if (timeFormatSelector == kTimeFormatSecondsForce24Hour || |
|
183 timeFormatSelector == kTimeFormatNoSecondsForce24Hour) { |
|
184 // Replace "h" with "H", and remove "a": |
|
185 CFStringRef oldFormat = CFDateFormatterGetFormat(formatter); |
|
186 CFMutableStringRef newFormat = CFStringCreateMutableCopy(nullptr, 0, oldFormat); |
|
187 CFIndex replaceCount = CFStringFindAndReplace(newFormat, |
|
188 CFSTR("h"), CFSTR("H"), |
|
189 CFRangeMake(0, CFStringGetLength(newFormat)), |
|
190 0); |
|
191 NS_ASSERTION(replaceCount == 1, "Unexpected number of \"h\" occurrences"); |
|
192 replaceCount = CFStringFindAndReplace(newFormat, |
|
193 CFSTR("a"), CFSTR(""), |
|
194 CFRangeMake(0, CFStringGetLength(newFormat)), |
|
195 0); |
|
196 NS_ASSERTION(replaceCount == 1, "Unexpected number of \"a\" occurrences"); |
|
197 CFDateFormatterSetFormat(formatter, newFormat); |
|
198 CFRelease(newFormat); // note we don't own oldFormat |
|
199 } |
|
200 |
|
201 // Now get the formatted date: |
|
202 CFGregorianDate date; |
|
203 date.second = tmTime->tm_sec; |
|
204 date.minute = tmTime->tm_min; |
|
205 date.hour = tmTime->tm_hour; |
|
206 date.day = tmTime->tm_mday; // Mac is 1-based, tm is 1-based |
|
207 date.month = tmTime->tm_mon + 1; // Mac is 1-based, tm is 0-based |
|
208 date.year = tmTime->tm_year + 1900; |
|
209 |
|
210 CFTimeZoneRef timeZone = CFTimeZoneCopySystem(); // tmTime is in local time |
|
211 CFAbsoluteTime absTime = CFGregorianDateGetAbsoluteTime(date, timeZone); |
|
212 CFRelease(timeZone); |
|
213 |
|
214 CFStringRef formattedDate = CFDateFormatterCreateStringWithAbsoluteTime(nullptr, |
|
215 formatter, |
|
216 absTime); |
|
217 |
|
218 CFIndex stringLen = CFStringGetLength(formattedDate); |
|
219 |
|
220 nsAutoTArray<UniChar, 256> stringBuffer; |
|
221 stringBuffer.SetLength(stringLen + 1); |
|
222 CFStringGetCharacters(formattedDate, CFRangeMake(0, stringLen), stringBuffer.Elements()); |
|
223 stringOut.Assign(reinterpret_cast<char16_t*>(stringBuffer.Elements()), stringLen); |
|
224 |
|
225 CFRelease(formattedDate); |
|
226 CFRelease(formatter); |
|
227 |
|
228 return res; |
|
229 } |
|
230 |
|
231 // performs a locale sensitive date formatting operation on the PRTime parameter |
|
232 nsresult nsDateTimeFormatMac::FormatPRTime(nsILocale* locale, |
|
233 const nsDateFormatSelector dateFormatSelector, |
|
234 const nsTimeFormatSelector timeFormatSelector, |
|
235 const PRTime prTime, |
|
236 nsAString& stringOut) |
|
237 { |
|
238 PRExplodedTime explodedTime; |
|
239 PR_ExplodeTime(prTime, PR_LocalTimeParameters, &explodedTime); |
|
240 |
|
241 return FormatPRExplodedTime(locale, dateFormatSelector, timeFormatSelector, &explodedTime, stringOut); |
|
242 } |
|
243 |
|
244 // performs a locale sensitive date formatting operation on the PRExplodedTime parameter |
|
245 nsresult nsDateTimeFormatMac::FormatPRExplodedTime(nsILocale* locale, |
|
246 const nsDateFormatSelector dateFormatSelector, |
|
247 const nsTimeFormatSelector timeFormatSelector, |
|
248 const PRExplodedTime* explodedTime, |
|
249 nsAString& stringOut) |
|
250 { |
|
251 struct tm tmTime; |
|
252 memset( &tmTime, 0, sizeof(tmTime) ); |
|
253 |
|
254 tmTime.tm_yday = explodedTime->tm_yday; |
|
255 tmTime.tm_wday = explodedTime->tm_wday; |
|
256 tmTime.tm_year = explodedTime->tm_year; |
|
257 tmTime.tm_year -= 1900; |
|
258 tmTime.tm_mon = explodedTime->tm_month; |
|
259 tmTime.tm_mday = explodedTime->tm_mday; |
|
260 tmTime.tm_hour = explodedTime->tm_hour; |
|
261 tmTime.tm_min = explodedTime->tm_min; |
|
262 tmTime.tm_sec = explodedTime->tm_sec; |
|
263 |
|
264 return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut); |
|
265 } |
|
266 |