1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/locale/src/unix/nsDateTimeFormatUnix.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,286 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include <locale.h> 1.10 +#include "plstr.h" 1.11 +#include "nsIServiceManager.h" 1.12 +#include "nsDateTimeFormatUnix.h" 1.13 +#include "nsIComponentManager.h" 1.14 +#include "nsILocaleService.h" 1.15 +#include "nsIPlatformCharset.h" 1.16 +#include "nsPosixLocale.h" 1.17 +#include "nsCRT.h" 1.18 +#include "nsReadableUtils.h" 1.19 +#include "nsUnicharUtils.h" 1.20 + 1.21 +NS_IMPL_ISUPPORTS(nsDateTimeFormatUnix, nsIDateTimeFormat) 1.22 + 1.23 +// init this interface to a specified locale 1.24 +nsresult nsDateTimeFormatUnix::Initialize(nsILocale* locale) 1.25 +{ 1.26 + nsAutoString localeStr; 1.27 + NS_NAMED_LITERAL_STRING(aCategory, "NSILOCALE_TIME##PLATFORM"); 1.28 + nsresult res = NS_OK; 1.29 + 1.30 + // use cached info if match with stored locale 1.31 + if (!locale) { 1.32 + if (!mLocale.IsEmpty() && 1.33 + mLocale.Equals(mAppLocale, nsCaseInsensitiveStringComparator())) { 1.34 + return NS_OK; 1.35 + } 1.36 + } 1.37 + else { 1.38 + res = locale->GetCategory(aCategory, localeStr); 1.39 + if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) { 1.40 + if (!mLocale.IsEmpty() && 1.41 + mLocale.Equals(localeStr, 1.42 + nsCaseInsensitiveStringComparator())) { 1.43 + return NS_OK; 1.44 + } 1.45 + } 1.46 + } 1.47 + 1.48 + mCharset.AssignLiteral("ISO-8859-1"); 1.49 + mPlatformLocale.Assign("en_US"); 1.50 + 1.51 + // get locale name string, use app default if no locale specified 1.52 + if (!locale) { 1.53 + nsCOMPtr<nsILocaleService> localeService = 1.54 + do_GetService(NS_LOCALESERVICE_CONTRACTID, &res); 1.55 + if (NS_SUCCEEDED(res)) { 1.56 + nsCOMPtr<nsILocale> appLocale; 1.57 + res = localeService->GetApplicationLocale(getter_AddRefs(appLocale)); 1.58 + if (NS_SUCCEEDED(res)) { 1.59 + res = appLocale->GetCategory(aCategory, localeStr); 1.60 + if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) { 1.61 + NS_ASSERTION(NS_SUCCEEDED(res), "failed to get app locale info"); 1.62 + mAppLocale = localeStr; // cache app locale name 1.63 + } 1.64 + } 1.65 + } 1.66 + } 1.67 + else { 1.68 + res = locale->GetCategory(aCategory, localeStr); 1.69 + NS_ASSERTION(NS_SUCCEEDED(res), "failed to get locale info"); 1.70 + } 1.71 + 1.72 + if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) { 1.73 + mLocale = localeStr; // cache locale name 1.74 + 1.75 + nsPosixLocale::GetPlatformLocale(mLocale, mPlatformLocale); 1.76 + 1.77 + nsCOMPtr <nsIPlatformCharset> platformCharset = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &res); 1.78 + if (NS_SUCCEEDED(res)) { 1.79 + nsAutoCString mappedCharset; 1.80 + res = platformCharset->GetDefaultCharsetForLocale(mLocale, mappedCharset); 1.81 + if (NS_SUCCEEDED(res)) { 1.82 + mCharset = mappedCharset; 1.83 + } 1.84 + } 1.85 + } 1.86 + 1.87 + // Initialize unicode decoder 1.88 + nsCOMPtr <nsICharsetConverterManager> charsetConverterManager; 1.89 + charsetConverterManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &res); 1.90 + if (NS_SUCCEEDED(res)) { 1.91 + res = charsetConverterManager->GetUnicodeDecoder(mCharset.get(), getter_AddRefs(mDecoder)); 1.92 + } 1.93 + 1.94 + LocalePreferred24hour(); 1.95 + 1.96 + return res; 1.97 +} 1.98 + 1.99 +void nsDateTimeFormatUnix::LocalePreferred24hour() 1.100 +{ 1.101 + char str[100]; 1.102 + time_t tt; 1.103 + struct tm *tmc; 1.104 + int i; 1.105 + 1.106 + tt = time(nullptr); 1.107 + tmc = localtime(&tt); 1.108 + 1.109 + tmc->tm_hour=22; // put the test sample hour to 22:00 which is 10PM 1.110 + tmc->tm_min=0; // set the min & sec other number than '2' 1.111 + tmc->tm_sec=0; 1.112 + 1.113 + char *temp = setlocale(LC_TIME, mPlatformLocale.get()); 1.114 + strftime(str, (size_t)99, "%X", (struct tm *)tmc); 1.115 + 1.116 + (void) setlocale(LC_TIME, temp); 1.117 + 1.118 + mLocalePreferred24hour = false; 1.119 + for (i=0; str[i]; i++) { 1.120 + if (str[i] == '2') { // if there is any '2', that locale use 0-23 time format 1.121 + mLocalePreferred24hour = true; 1.122 + break; 1.123 + } 1.124 + } 1.125 + 1.126 + mLocaleAMPMfirst = true; 1.127 + if (mLocalePreferred24hour == false) { 1.128 + if (str[0] && str[0] == '1') { // if the first character is '1' of 10:00, 1.129 + // AMPM string is located after 10:00 1.130 + mLocaleAMPMfirst = false; 1.131 + } 1.132 + } 1.133 +} 1.134 + 1.135 +nsresult nsDateTimeFormatUnix::FormatTime(nsILocale* locale, 1.136 + const nsDateFormatSelector dateFormatSelector, 1.137 + const nsTimeFormatSelector timeFormatSelector, 1.138 + const time_t timetTime, 1.139 + nsAString& stringOut) 1.140 +{ 1.141 + struct tm tmTime; 1.142 + memcpy(&tmTime, localtime(&timetTime), sizeof(struct tm)); 1.143 + return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut); 1.144 +} 1.145 + 1.146 +// performs a locale sensitive date formatting operation on the struct tm parameter 1.147 +nsresult nsDateTimeFormatUnix::FormatTMTime(nsILocale* locale, 1.148 + const nsDateFormatSelector dateFormatSelector, 1.149 + const nsTimeFormatSelector timeFormatSelector, 1.150 + const struct tm* tmTime, 1.151 + nsAString& stringOut) 1.152 +{ 1.153 +#define NSDATETIME_FORMAT_BUFFER_LEN 80 1.154 + char strOut[NSDATETIME_FORMAT_BUFFER_LEN*2]; // buffer for date and time 1.155 + char fmtD[NSDATETIME_FORMAT_BUFFER_LEN], fmtT[NSDATETIME_FORMAT_BUFFER_LEN]; 1.156 + nsresult rv; 1.157 + 1.158 + 1.159 + // set up locale data 1.160 + (void) Initialize(locale); 1.161 + NS_ENSURE_TRUE(mDecoder, NS_ERROR_NOT_INITIALIZED); 1.162 + 1.163 + // set date format 1.164 + if (dateFormatSelector == kDateFormatLong && timeFormatSelector == kTimeFormatSeconds) { 1.165 + PL_strncpy(fmtD, "%c", NSDATETIME_FORMAT_BUFFER_LEN); 1.166 + PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN); 1.167 + } else { 1.168 + 1.169 + switch (dateFormatSelector) { 1.170 + case kDateFormatNone: 1.171 + PL_strncpy(fmtD, "", NSDATETIME_FORMAT_BUFFER_LEN); 1.172 + break; 1.173 + case kDateFormatLong: 1.174 + case kDateFormatShort: 1.175 + PL_strncpy(fmtD, "%x", NSDATETIME_FORMAT_BUFFER_LEN); 1.176 + break; 1.177 + case kDateFormatYearMonth: 1.178 + PL_strncpy(fmtD, "%Y/%m", NSDATETIME_FORMAT_BUFFER_LEN); 1.179 + break; 1.180 + case kDateFormatWeekday: 1.181 + PL_strncpy(fmtD, "%a", NSDATETIME_FORMAT_BUFFER_LEN); 1.182 + break; 1.183 + default: 1.184 + PL_strncpy(fmtD, "", NSDATETIME_FORMAT_BUFFER_LEN); 1.185 + } 1.186 + 1.187 + // set time format 1.188 + switch (timeFormatSelector) { 1.189 + case kTimeFormatNone: 1.190 + PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN); 1.191 + break; 1.192 + case kTimeFormatSeconds: 1.193 + PL_strncpy(fmtT, "%X", NSDATETIME_FORMAT_BUFFER_LEN); 1.194 + break; 1.195 + case kTimeFormatNoSeconds: 1.196 + PL_strncpy(fmtT, 1.197 + mLocalePreferred24hour ? "%H:%M" : mLocaleAMPMfirst ? "%p %I:%M" : "%I:%M %p", 1.198 + NSDATETIME_FORMAT_BUFFER_LEN); 1.199 + break; 1.200 + case kTimeFormatSecondsForce24Hour: 1.201 + PL_strncpy(fmtT, "%H:%M:%S", NSDATETIME_FORMAT_BUFFER_LEN); 1.202 + break; 1.203 + case kTimeFormatNoSecondsForce24Hour: 1.204 + PL_strncpy(fmtT, "%H:%M", NSDATETIME_FORMAT_BUFFER_LEN); 1.205 + break; 1.206 + default: 1.207 + PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN); 1.208 + } 1.209 + } 1.210 + 1.211 + // generate data/time string 1.212 + char *old_locale = setlocale(LC_TIME, nullptr); 1.213 + (void) setlocale(LC_TIME, mPlatformLocale.get()); 1.214 + if (strlen(fmtD) && strlen(fmtT)) { 1.215 + PL_strncat(fmtD, " ", NSDATETIME_FORMAT_BUFFER_LEN); 1.216 + PL_strncat(fmtD, fmtT, NSDATETIME_FORMAT_BUFFER_LEN); 1.217 + strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN, fmtD, tmTime); 1.218 + } 1.219 + else if (strlen(fmtD) && !strlen(fmtT)) { 1.220 + strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN, fmtD, tmTime); 1.221 + } 1.222 + else if (!strlen(fmtD) && strlen(fmtT)) { 1.223 + strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN, fmtT, tmTime); 1.224 + } 1.225 + else { 1.226 + PL_strncpy(strOut, "", NSDATETIME_FORMAT_BUFFER_LEN); 1.227 + } 1.228 + (void) setlocale(LC_TIME, old_locale); 1.229 + 1.230 + // convert result to unicode 1.231 + int32_t srcLength = (int32_t) strlen(strOut); 1.232 + int32_t unicharLength = NSDATETIME_FORMAT_BUFFER_LEN*2; 1.233 + char16_t unichars[NSDATETIME_FORMAT_BUFFER_LEN*2]; // buffer for date and time 1.234 + 1.235 + rv = mDecoder->Convert(strOut, &srcLength, unichars, &unicharLength); 1.236 + if (NS_FAILED(rv)) 1.237 + return rv; 1.238 + stringOut.Assign(unichars, unicharLength); 1.239 + 1.240 + return rv; 1.241 +} 1.242 + 1.243 +// performs a locale sensitive date formatting operation on the PRTime parameter 1.244 +nsresult nsDateTimeFormatUnix::FormatPRTime(nsILocale* locale, 1.245 + const nsDateFormatSelector dateFormatSelector, 1.246 + const nsTimeFormatSelector timeFormatSelector, 1.247 + const PRTime prTime, 1.248 + nsAString& stringOut) 1.249 +{ 1.250 + PRExplodedTime explodedTime; 1.251 + PR_ExplodeTime(prTime, PR_LocalTimeParameters, &explodedTime); 1.252 + 1.253 + return FormatPRExplodedTime(locale, dateFormatSelector, timeFormatSelector, &explodedTime, stringOut); 1.254 +} 1.255 + 1.256 +// performs a locale sensitive date formatting operation on the PRExplodedTime parameter 1.257 +nsresult nsDateTimeFormatUnix::FormatPRExplodedTime(nsILocale* locale, 1.258 + const nsDateFormatSelector dateFormatSelector, 1.259 + const nsTimeFormatSelector timeFormatSelector, 1.260 + const PRExplodedTime* explodedTime, 1.261 + nsAString& stringOut) 1.262 +{ 1.263 + struct tm tmTime; 1.264 + /* be safe and set all members of struct tm to zero 1.265 + * 1.266 + * there are other fields in the tm struct that we aren't setting 1.267 + * (tm_isdst, tm_gmtoff, tm_zone, should we set these?) and since 1.268 + * tmTime is on the stack, it may be filled with garbage, but 1.269 + * the garbage may vary. (this may explain why some saw bug #10412, and 1.270 + * others did not. 1.271 + * 1.272 + * when tmTime is passed to strftime() with garbage bad things may happen. 1.273 + * see bug #10412 1.274 + */ 1.275 + memset( &tmTime, 0, sizeof(tmTime) ); 1.276 + 1.277 + tmTime.tm_yday = explodedTime->tm_yday; 1.278 + tmTime.tm_wday = explodedTime->tm_wday; 1.279 + tmTime.tm_year = explodedTime->tm_year; 1.280 + tmTime.tm_year -= 1900; 1.281 + tmTime.tm_mon = explodedTime->tm_month; 1.282 + tmTime.tm_mday = explodedTime->tm_mday; 1.283 + tmTime.tm_hour = explodedTime->tm_hour; 1.284 + tmTime.tm_min = explodedTime->tm_min; 1.285 + tmTime.tm_sec = explodedTime->tm_sec; 1.286 + 1.287 + return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut); 1.288 +} 1.289 +