1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Pierre Phaneuf <pp@ludusdesign.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include <locale.h>
40 : #include "plstr.h"
41 : #include "nsIServiceManager.h"
42 : #include "nsDateTimeFormatUnix.h"
43 : #include "nsIComponentManager.h"
44 : #include "nsILocaleService.h"
45 : #include "nsIPlatformCharset.h"
46 : #include "nsPosixLocale.h"
47 : #include "nsCRT.h"
48 : #include "nsReadableUtils.h"
49 : #include "nsUnicharUtils.h"
50 :
51 7123 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsDateTimeFormatUnix, nsIDateTimeFormat)
52 :
53 : // init this interface to a specified locale
54 13 : nsresult nsDateTimeFormatUnix::Initialize(nsILocale* locale)
55 : {
56 26 : nsAutoString localeStr;
57 26 : NS_NAMED_LITERAL_STRING(aCategory, "NSILOCALE_TIME##PLATFORM");
58 13 : nsresult res = NS_OK;
59 :
60 : // use cached info if match with stored locale
61 13 : if (NULL == locale) {
62 13 : if (!mLocale.IsEmpty() &&
63 0 : mLocale.Equals(mAppLocale, nsCaseInsensitiveStringComparator())) {
64 0 : return NS_OK;
65 : }
66 : }
67 : else {
68 0 : res = locale->GetCategory(aCategory, localeStr);
69 0 : if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
70 0 : if (!mLocale.IsEmpty() &&
71 : mLocale.Equals(localeStr,
72 0 : nsCaseInsensitiveStringComparator())) {
73 0 : return NS_OK;
74 : }
75 : }
76 : }
77 :
78 13 : mCharset.AssignLiteral("ISO-8859-1");
79 13 : mPlatformLocale.Assign("en_US");
80 :
81 : // get locale name string, use app default if no locale specified
82 13 : if (NULL == locale) {
83 : nsCOMPtr<nsILocaleService> localeService =
84 26 : do_GetService(NS_LOCALESERVICE_CONTRACTID, &res);
85 13 : if (NS_SUCCEEDED(res)) {
86 26 : nsCOMPtr<nsILocale> appLocale;
87 13 : res = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
88 13 : if (NS_SUCCEEDED(res)) {
89 13 : res = appLocale->GetCategory(aCategory, localeStr);
90 13 : if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
91 13 : NS_ASSERTION(NS_SUCCEEDED(res), "failed to get app locale info");
92 13 : mAppLocale = localeStr; // cache app locale name
93 : }
94 : }
95 : }
96 : }
97 : else {
98 0 : res = locale->GetCategory(aCategory, localeStr);
99 0 : NS_ASSERTION(NS_SUCCEEDED(res), "failed to get locale info");
100 : }
101 :
102 13 : if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
103 13 : mLocale = localeStr; // cache locale name
104 :
105 13 : nsPosixLocale::GetPlatformLocale(mLocale, mPlatformLocale);
106 :
107 26 : nsCOMPtr <nsIPlatformCharset> platformCharset = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &res);
108 13 : if (NS_SUCCEEDED(res)) {
109 26 : nsCAutoString mappedCharset;
110 13 : res = platformCharset->GetDefaultCharsetForLocale(mLocale, mappedCharset);
111 13 : if (NS_SUCCEEDED(res)) {
112 13 : mCharset = mappedCharset;
113 : }
114 : }
115 : }
116 :
117 : // Initialize unicode decoder
118 26 : nsCOMPtr <nsICharsetConverterManager> charsetConverterManager;
119 13 : charsetConverterManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &res);
120 13 : if (NS_SUCCEEDED(res)) {
121 13 : res = charsetConverterManager->GetUnicodeDecoder(mCharset.get(), getter_AddRefs(mDecoder));
122 : }
123 :
124 13 : LocalePreferred24hour();
125 :
126 13 : return res;
127 : }
128 :
129 13 : void nsDateTimeFormatUnix::LocalePreferred24hour()
130 : {
131 : char str[100];
132 : time_t tt;
133 : struct tm *tmc;
134 : int i;
135 :
136 13 : tt = time((time_t)NULL);
137 13 : tmc = localtime(&tt);
138 :
139 13 : tmc->tm_hour=22; // put the test sample hour to 22:00 which is 10PM
140 13 : tmc->tm_min=0; // set the min & sec other number than '2'
141 13 : tmc->tm_sec=0;
142 :
143 13 : char *temp = setlocale(LC_TIME, mPlatformLocale.get());
144 13 : strftime(str, (size_t)99, "%X", (struct tm *)tmc);
145 :
146 13 : (void) setlocale(LC_TIME, temp);
147 :
148 13 : mLocalePreferred24hour = false;
149 156 : for (i=0; str[i]; i++) {
150 143 : if (str[i] == '2') { // if there is any '2', that locale use 0-23 time format
151 0 : mLocalePreferred24hour = true;
152 0 : break;
153 : }
154 : }
155 :
156 13 : mLocaleAMPMfirst = true;
157 13 : if (mLocalePreferred24hour == false) {
158 13 : if (str[0] && str[0] == '1') { // if the first character is '1' of 10:00,
159 : // AMPM string is located after 10:00
160 13 : mLocaleAMPMfirst = false;
161 : }
162 : }
163 13 : }
164 :
165 13 : nsresult nsDateTimeFormatUnix::FormatTime(nsILocale* locale,
166 : const nsDateFormatSelector dateFormatSelector,
167 : const nsTimeFormatSelector timeFormatSelector,
168 : const time_t timetTime,
169 : nsAString& stringOut)
170 : {
171 : struct tm tmTime;
172 13 : memcpy(&tmTime, localtime(&timetTime), sizeof(struct tm));
173 13 : return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut);
174 : }
175 :
176 : // performs a locale sensitive date formatting operation on the struct tm parameter
177 13 : nsresult nsDateTimeFormatUnix::FormatTMTime(nsILocale* locale,
178 : const nsDateFormatSelector dateFormatSelector,
179 : const nsTimeFormatSelector timeFormatSelector,
180 : const struct tm* tmTime,
181 : nsAString& stringOut)
182 : {
183 : #define NSDATETIME_FORMAT_BUFFER_LEN 80
184 : char strOut[NSDATETIME_FORMAT_BUFFER_LEN*2]; // buffer for date and time
185 : char fmtD[NSDATETIME_FORMAT_BUFFER_LEN], fmtT[NSDATETIME_FORMAT_BUFFER_LEN];
186 : nsresult rv;
187 :
188 :
189 : // set up locale data
190 13 : (void) Initialize(locale);
191 13 : NS_ENSURE_TRUE(mDecoder, NS_ERROR_NOT_INITIALIZED);
192 :
193 : // set date format
194 13 : if (dateFormatSelector == kDateFormatLong && timeFormatSelector == kTimeFormatSeconds) {
195 0 : PL_strncpy(fmtD, "%c", NSDATETIME_FORMAT_BUFFER_LEN);
196 0 : PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN);
197 : } else {
198 :
199 13 : switch (dateFormatSelector) {
200 : case kDateFormatNone:
201 4 : PL_strncpy(fmtD, "", NSDATETIME_FORMAT_BUFFER_LEN);
202 4 : break;
203 : case kDateFormatLong:
204 : case kDateFormatShort:
205 9 : PL_strncpy(fmtD, "%x", NSDATETIME_FORMAT_BUFFER_LEN);
206 9 : break;
207 : case kDateFormatYearMonth:
208 0 : PL_strncpy(fmtD, "%Y/%m", NSDATETIME_FORMAT_BUFFER_LEN);
209 0 : break;
210 : case kDateFormatWeekday:
211 0 : PL_strncpy(fmtD, "%a", NSDATETIME_FORMAT_BUFFER_LEN);
212 0 : break;
213 : default:
214 0 : PL_strncpy(fmtD, "", NSDATETIME_FORMAT_BUFFER_LEN);
215 : }
216 :
217 : // set time format
218 13 : switch (timeFormatSelector) {
219 : case kTimeFormatNone:
220 0 : PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN);
221 0 : break;
222 : case kTimeFormatSeconds:
223 0 : PL_strncpy(fmtT, "%X", NSDATETIME_FORMAT_BUFFER_LEN);
224 0 : break;
225 : case kTimeFormatNoSeconds:
226 : PL_strncpy(fmtT,
227 : mLocalePreferred24hour ? "%H:%M" : mLocaleAMPMfirst ? "%p %I:%M" : "%I:%M %p",
228 13 : NSDATETIME_FORMAT_BUFFER_LEN);
229 13 : break;
230 : case kTimeFormatSecondsForce24Hour:
231 0 : PL_strncpy(fmtT, "%H:%M:%S", NSDATETIME_FORMAT_BUFFER_LEN);
232 0 : break;
233 : case kTimeFormatNoSecondsForce24Hour:
234 0 : PL_strncpy(fmtT, "%H:%M", NSDATETIME_FORMAT_BUFFER_LEN);
235 0 : break;
236 : default:
237 0 : PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN);
238 : }
239 : }
240 :
241 : // generate data/time string
242 13 : char *old_locale = setlocale(LC_TIME, NULL);
243 13 : (void) setlocale(LC_TIME, mPlatformLocale.get());
244 13 : if (PL_strlen(fmtD) && PL_strlen(fmtT)) {
245 9 : PL_strncat(fmtD, " ", NSDATETIME_FORMAT_BUFFER_LEN);
246 9 : PL_strncat(fmtD, fmtT, NSDATETIME_FORMAT_BUFFER_LEN);
247 9 : strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN, fmtD, tmTime);
248 : }
249 4 : else if (PL_strlen(fmtD) && !PL_strlen(fmtT)) {
250 0 : strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN, fmtD, tmTime);
251 : }
252 4 : else if (!PL_strlen(fmtD) && PL_strlen(fmtT)) {
253 4 : strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN, fmtT, tmTime);
254 : }
255 : else {
256 0 : PL_strncpy(strOut, "", NSDATETIME_FORMAT_BUFFER_LEN);
257 : }
258 13 : (void) setlocale(LC_TIME, old_locale);
259 :
260 : // convert result to unicode
261 13 : PRInt32 srcLength = (PRInt32) PL_strlen(strOut);
262 13 : PRInt32 unicharLength = NSDATETIME_FORMAT_BUFFER_LEN*2;
263 : PRUnichar unichars[NSDATETIME_FORMAT_BUFFER_LEN*2]; // buffer for date and time
264 :
265 13 : rv = mDecoder->Convert(strOut, &srcLength, unichars, &unicharLength);
266 13 : if (NS_FAILED(rv))
267 0 : return rv;
268 13 : stringOut.Assign(unichars, unicharLength);
269 :
270 13 : return rv;
271 : }
272 :
273 : // performs a locale sensitive date formatting operation on the PRTime parameter
274 0 : nsresult nsDateTimeFormatUnix::FormatPRTime(nsILocale* locale,
275 : const nsDateFormatSelector dateFormatSelector,
276 : const nsTimeFormatSelector timeFormatSelector,
277 : const PRTime prTime,
278 : nsAString& stringOut)
279 : {
280 : PRExplodedTime explodedTime;
281 0 : PR_ExplodeTime(prTime, PR_LocalTimeParameters, &explodedTime);
282 :
283 0 : return FormatPRExplodedTime(locale, dateFormatSelector, timeFormatSelector, &explodedTime, stringOut);
284 : }
285 :
286 : // performs a locale sensitive date formatting operation on the PRExplodedTime parameter
287 0 : nsresult nsDateTimeFormatUnix::FormatPRExplodedTime(nsILocale* locale,
288 : const nsDateFormatSelector dateFormatSelector,
289 : const nsTimeFormatSelector timeFormatSelector,
290 : const PRExplodedTime* explodedTime,
291 : nsAString& stringOut)
292 : {
293 : struct tm tmTime;
294 : /* be safe and set all members of struct tm to zero
295 : *
296 : * there are other fields in the tm struct that we aren't setting
297 : * (tm_isdst, tm_gmtoff, tm_zone, should we set these?) and since
298 : * tmTime is on the stack, it may be filled with garbage, but
299 : * the garbage may vary. (this may explain why some saw bug #10412, and
300 : * others did not.
301 : *
302 : * when tmTime is passed to strftime() with garbage bad things may happen.
303 : * see bug #10412
304 : */
305 0 : memset( &tmTime, 0, sizeof(tmTime) );
306 :
307 0 : tmTime.tm_yday = explodedTime->tm_yday;
308 0 : tmTime.tm_wday = explodedTime->tm_wday;
309 0 : tmTime.tm_year = explodedTime->tm_year;
310 0 : tmTime.tm_year -= 1900;
311 0 : tmTime.tm_mon = explodedTime->tm_month;
312 0 : tmTime.tm_mday = explodedTime->tm_mday;
313 0 : tmTime.tm_hour = explodedTime->tm_hour;
314 0 : tmTime.tm_min = explodedTime->tm_min;
315 0 : tmTime.tm_sec = explodedTime->tm_sec;
316 :
317 0 : return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut);
318 : }
319 :
|