|
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 <locale.h> |
|
7 #include "plstr.h" |
|
8 #include "nsIServiceManager.h" |
|
9 #include "nsDateTimeFormatUnix.h" |
|
10 #include "nsIComponentManager.h" |
|
11 #include "nsILocaleService.h" |
|
12 #include "nsIPlatformCharset.h" |
|
13 #include "nsPosixLocale.h" |
|
14 #include "nsCRT.h" |
|
15 #include "nsReadableUtils.h" |
|
16 #include "nsUnicharUtils.h" |
|
17 |
|
18 NS_IMPL_ISUPPORTS(nsDateTimeFormatUnix, nsIDateTimeFormat) |
|
19 |
|
20 // init this interface to a specified locale |
|
21 nsresult nsDateTimeFormatUnix::Initialize(nsILocale* locale) |
|
22 { |
|
23 nsAutoString localeStr; |
|
24 NS_NAMED_LITERAL_STRING(aCategory, "NSILOCALE_TIME##PLATFORM"); |
|
25 nsresult res = NS_OK; |
|
26 |
|
27 // use cached info if match with stored locale |
|
28 if (!locale) { |
|
29 if (!mLocale.IsEmpty() && |
|
30 mLocale.Equals(mAppLocale, nsCaseInsensitiveStringComparator())) { |
|
31 return NS_OK; |
|
32 } |
|
33 } |
|
34 else { |
|
35 res = locale->GetCategory(aCategory, localeStr); |
|
36 if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) { |
|
37 if (!mLocale.IsEmpty() && |
|
38 mLocale.Equals(localeStr, |
|
39 nsCaseInsensitiveStringComparator())) { |
|
40 return NS_OK; |
|
41 } |
|
42 } |
|
43 } |
|
44 |
|
45 mCharset.AssignLiteral("ISO-8859-1"); |
|
46 mPlatformLocale.Assign("en_US"); |
|
47 |
|
48 // get locale name string, use app default if no locale specified |
|
49 if (!locale) { |
|
50 nsCOMPtr<nsILocaleService> localeService = |
|
51 do_GetService(NS_LOCALESERVICE_CONTRACTID, &res); |
|
52 if (NS_SUCCEEDED(res)) { |
|
53 nsCOMPtr<nsILocale> appLocale; |
|
54 res = localeService->GetApplicationLocale(getter_AddRefs(appLocale)); |
|
55 if (NS_SUCCEEDED(res)) { |
|
56 res = appLocale->GetCategory(aCategory, localeStr); |
|
57 if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) { |
|
58 NS_ASSERTION(NS_SUCCEEDED(res), "failed to get app locale info"); |
|
59 mAppLocale = localeStr; // cache app locale name |
|
60 } |
|
61 } |
|
62 } |
|
63 } |
|
64 else { |
|
65 res = locale->GetCategory(aCategory, localeStr); |
|
66 NS_ASSERTION(NS_SUCCEEDED(res), "failed to get locale info"); |
|
67 } |
|
68 |
|
69 if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) { |
|
70 mLocale = localeStr; // cache locale name |
|
71 |
|
72 nsPosixLocale::GetPlatformLocale(mLocale, mPlatformLocale); |
|
73 |
|
74 nsCOMPtr <nsIPlatformCharset> platformCharset = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &res); |
|
75 if (NS_SUCCEEDED(res)) { |
|
76 nsAutoCString mappedCharset; |
|
77 res = platformCharset->GetDefaultCharsetForLocale(mLocale, mappedCharset); |
|
78 if (NS_SUCCEEDED(res)) { |
|
79 mCharset = mappedCharset; |
|
80 } |
|
81 } |
|
82 } |
|
83 |
|
84 // Initialize unicode decoder |
|
85 nsCOMPtr <nsICharsetConverterManager> charsetConverterManager; |
|
86 charsetConverterManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &res); |
|
87 if (NS_SUCCEEDED(res)) { |
|
88 res = charsetConverterManager->GetUnicodeDecoder(mCharset.get(), getter_AddRefs(mDecoder)); |
|
89 } |
|
90 |
|
91 LocalePreferred24hour(); |
|
92 |
|
93 return res; |
|
94 } |
|
95 |
|
96 void nsDateTimeFormatUnix::LocalePreferred24hour() |
|
97 { |
|
98 char str[100]; |
|
99 time_t tt; |
|
100 struct tm *tmc; |
|
101 int i; |
|
102 |
|
103 tt = time(nullptr); |
|
104 tmc = localtime(&tt); |
|
105 |
|
106 tmc->tm_hour=22; // put the test sample hour to 22:00 which is 10PM |
|
107 tmc->tm_min=0; // set the min & sec other number than '2' |
|
108 tmc->tm_sec=0; |
|
109 |
|
110 char *temp = setlocale(LC_TIME, mPlatformLocale.get()); |
|
111 strftime(str, (size_t)99, "%X", (struct tm *)tmc); |
|
112 |
|
113 (void) setlocale(LC_TIME, temp); |
|
114 |
|
115 mLocalePreferred24hour = false; |
|
116 for (i=0; str[i]; i++) { |
|
117 if (str[i] == '2') { // if there is any '2', that locale use 0-23 time format |
|
118 mLocalePreferred24hour = true; |
|
119 break; |
|
120 } |
|
121 } |
|
122 |
|
123 mLocaleAMPMfirst = true; |
|
124 if (mLocalePreferred24hour == false) { |
|
125 if (str[0] && str[0] == '1') { // if the first character is '1' of 10:00, |
|
126 // AMPM string is located after 10:00 |
|
127 mLocaleAMPMfirst = false; |
|
128 } |
|
129 } |
|
130 } |
|
131 |
|
132 nsresult nsDateTimeFormatUnix::FormatTime(nsILocale* locale, |
|
133 const nsDateFormatSelector dateFormatSelector, |
|
134 const nsTimeFormatSelector timeFormatSelector, |
|
135 const time_t timetTime, |
|
136 nsAString& stringOut) |
|
137 { |
|
138 struct tm tmTime; |
|
139 memcpy(&tmTime, localtime(&timetTime), sizeof(struct tm)); |
|
140 return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut); |
|
141 } |
|
142 |
|
143 // performs a locale sensitive date formatting operation on the struct tm parameter |
|
144 nsresult nsDateTimeFormatUnix::FormatTMTime(nsILocale* locale, |
|
145 const nsDateFormatSelector dateFormatSelector, |
|
146 const nsTimeFormatSelector timeFormatSelector, |
|
147 const struct tm* tmTime, |
|
148 nsAString& stringOut) |
|
149 { |
|
150 #define NSDATETIME_FORMAT_BUFFER_LEN 80 |
|
151 char strOut[NSDATETIME_FORMAT_BUFFER_LEN*2]; // buffer for date and time |
|
152 char fmtD[NSDATETIME_FORMAT_BUFFER_LEN], fmtT[NSDATETIME_FORMAT_BUFFER_LEN]; |
|
153 nsresult rv; |
|
154 |
|
155 |
|
156 // set up locale data |
|
157 (void) Initialize(locale); |
|
158 NS_ENSURE_TRUE(mDecoder, NS_ERROR_NOT_INITIALIZED); |
|
159 |
|
160 // set date format |
|
161 if (dateFormatSelector == kDateFormatLong && timeFormatSelector == kTimeFormatSeconds) { |
|
162 PL_strncpy(fmtD, "%c", NSDATETIME_FORMAT_BUFFER_LEN); |
|
163 PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN); |
|
164 } else { |
|
165 |
|
166 switch (dateFormatSelector) { |
|
167 case kDateFormatNone: |
|
168 PL_strncpy(fmtD, "", NSDATETIME_FORMAT_BUFFER_LEN); |
|
169 break; |
|
170 case kDateFormatLong: |
|
171 case kDateFormatShort: |
|
172 PL_strncpy(fmtD, "%x", NSDATETIME_FORMAT_BUFFER_LEN); |
|
173 break; |
|
174 case kDateFormatYearMonth: |
|
175 PL_strncpy(fmtD, "%Y/%m", NSDATETIME_FORMAT_BUFFER_LEN); |
|
176 break; |
|
177 case kDateFormatWeekday: |
|
178 PL_strncpy(fmtD, "%a", NSDATETIME_FORMAT_BUFFER_LEN); |
|
179 break; |
|
180 default: |
|
181 PL_strncpy(fmtD, "", NSDATETIME_FORMAT_BUFFER_LEN); |
|
182 } |
|
183 |
|
184 // set time format |
|
185 switch (timeFormatSelector) { |
|
186 case kTimeFormatNone: |
|
187 PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN); |
|
188 break; |
|
189 case kTimeFormatSeconds: |
|
190 PL_strncpy(fmtT, "%X", NSDATETIME_FORMAT_BUFFER_LEN); |
|
191 break; |
|
192 case kTimeFormatNoSeconds: |
|
193 PL_strncpy(fmtT, |
|
194 mLocalePreferred24hour ? "%H:%M" : mLocaleAMPMfirst ? "%p %I:%M" : "%I:%M %p", |
|
195 NSDATETIME_FORMAT_BUFFER_LEN); |
|
196 break; |
|
197 case kTimeFormatSecondsForce24Hour: |
|
198 PL_strncpy(fmtT, "%H:%M:%S", NSDATETIME_FORMAT_BUFFER_LEN); |
|
199 break; |
|
200 case kTimeFormatNoSecondsForce24Hour: |
|
201 PL_strncpy(fmtT, "%H:%M", NSDATETIME_FORMAT_BUFFER_LEN); |
|
202 break; |
|
203 default: |
|
204 PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN); |
|
205 } |
|
206 } |
|
207 |
|
208 // generate data/time string |
|
209 char *old_locale = setlocale(LC_TIME, nullptr); |
|
210 (void) setlocale(LC_TIME, mPlatformLocale.get()); |
|
211 if (strlen(fmtD) && strlen(fmtT)) { |
|
212 PL_strncat(fmtD, " ", NSDATETIME_FORMAT_BUFFER_LEN); |
|
213 PL_strncat(fmtD, fmtT, NSDATETIME_FORMAT_BUFFER_LEN); |
|
214 strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN, fmtD, tmTime); |
|
215 } |
|
216 else if (strlen(fmtD) && !strlen(fmtT)) { |
|
217 strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN, fmtD, tmTime); |
|
218 } |
|
219 else if (!strlen(fmtD) && strlen(fmtT)) { |
|
220 strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN, fmtT, tmTime); |
|
221 } |
|
222 else { |
|
223 PL_strncpy(strOut, "", NSDATETIME_FORMAT_BUFFER_LEN); |
|
224 } |
|
225 (void) setlocale(LC_TIME, old_locale); |
|
226 |
|
227 // convert result to unicode |
|
228 int32_t srcLength = (int32_t) strlen(strOut); |
|
229 int32_t unicharLength = NSDATETIME_FORMAT_BUFFER_LEN*2; |
|
230 char16_t unichars[NSDATETIME_FORMAT_BUFFER_LEN*2]; // buffer for date and time |
|
231 |
|
232 rv = mDecoder->Convert(strOut, &srcLength, unichars, &unicharLength); |
|
233 if (NS_FAILED(rv)) |
|
234 return rv; |
|
235 stringOut.Assign(unichars, unicharLength); |
|
236 |
|
237 return rv; |
|
238 } |
|
239 |
|
240 // performs a locale sensitive date formatting operation on the PRTime parameter |
|
241 nsresult nsDateTimeFormatUnix::FormatPRTime(nsILocale* locale, |
|
242 const nsDateFormatSelector dateFormatSelector, |
|
243 const nsTimeFormatSelector timeFormatSelector, |
|
244 const PRTime prTime, |
|
245 nsAString& stringOut) |
|
246 { |
|
247 PRExplodedTime explodedTime; |
|
248 PR_ExplodeTime(prTime, PR_LocalTimeParameters, &explodedTime); |
|
249 |
|
250 return FormatPRExplodedTime(locale, dateFormatSelector, timeFormatSelector, &explodedTime, stringOut); |
|
251 } |
|
252 |
|
253 // performs a locale sensitive date formatting operation on the PRExplodedTime parameter |
|
254 nsresult nsDateTimeFormatUnix::FormatPRExplodedTime(nsILocale* locale, |
|
255 const nsDateFormatSelector dateFormatSelector, |
|
256 const nsTimeFormatSelector timeFormatSelector, |
|
257 const PRExplodedTime* explodedTime, |
|
258 nsAString& stringOut) |
|
259 { |
|
260 struct tm tmTime; |
|
261 /* be safe and set all members of struct tm to zero |
|
262 * |
|
263 * there are other fields in the tm struct that we aren't setting |
|
264 * (tm_isdst, tm_gmtoff, tm_zone, should we set these?) and since |
|
265 * tmTime is on the stack, it may be filled with garbage, but |
|
266 * the garbage may vary. (this may explain why some saw bug #10412, and |
|
267 * others did not. |
|
268 * |
|
269 * when tmTime is passed to strftime() with garbage bad things may happen. |
|
270 * see bug #10412 |
|
271 */ |
|
272 memset( &tmTime, 0, sizeof(tmTime) ); |
|
273 |
|
274 tmTime.tm_yday = explodedTime->tm_yday; |
|
275 tmTime.tm_wday = explodedTime->tm_wday; |
|
276 tmTime.tm_year = explodedTime->tm_year; |
|
277 tmTime.tm_year -= 1900; |
|
278 tmTime.tm_mon = explodedTime->tm_month; |
|
279 tmTime.tm_mday = explodedTime->tm_mday; |
|
280 tmTime.tm_hour = explodedTime->tm_hour; |
|
281 tmTime.tm_min = explodedTime->tm_min; |
|
282 tmTime.tm_sec = explodedTime->tm_sec; |
|
283 |
|
284 return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut); |
|
285 } |
|
286 |