Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
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/. */
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"
18 NS_IMPL_ISUPPORTS(nsDateTimeFormatUnix, nsIDateTimeFormat)
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;
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 }
45 mCharset.AssignLiteral("ISO-8859-1");
46 mPlatformLocale.Assign("en_US");
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 }
69 if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
70 mLocale = localeStr; // cache locale name
72 nsPosixLocale::GetPlatformLocale(mLocale, mPlatformLocale);
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 }
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 }
91 LocalePreferred24hour();
93 return res;
94 }
96 void nsDateTimeFormatUnix::LocalePreferred24hour()
97 {
98 char str[100];
99 time_t tt;
100 struct tm *tmc;
101 int i;
103 tt = time(nullptr);
104 tmc = localtime(&tt);
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;
110 char *temp = setlocale(LC_TIME, mPlatformLocale.get());
111 strftime(str, (size_t)99, "%X", (struct tm *)tmc);
113 (void) setlocale(LC_TIME, temp);
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 }
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 }
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 }
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;
156 // set up locale data
157 (void) Initialize(locale);
158 NS_ENSURE_TRUE(mDecoder, NS_ERROR_NOT_INITIALIZED);
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 {
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 }
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 }
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);
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
232 rv = mDecoder->Convert(strOut, &srcLength, unichars, &unicharLength);
233 if (NS_FAILED(rv))
234 return rv;
235 stringOut.Assign(unichars, unicharLength);
237 return rv;
238 }
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);
250 return FormatPRExplodedTime(locale, dateFormatSelector, timeFormatSelector, &explodedTime, stringOut);
251 }
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) );
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;
284 return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut);
285 }