1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/locale/src/mac/nsDateTimeFormatMac.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,266 @@ 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 <CoreFoundation/CoreFoundation.h> 1.10 +#include "nsIServiceManager.h" 1.11 +#include "nsDateTimeFormatMac.h" 1.12 +#include <CoreFoundation/CFDateFormatter.h> 1.13 +#include "nsIComponentManager.h" 1.14 +#include "nsILocaleService.h" 1.15 +#include "nsCRT.h" 1.16 +#include "plstr.h" 1.17 +#include "nsUnicharUtils.h" 1.18 +#include "nsTArray.h" 1.19 + 1.20 + 1.21 +NS_IMPL_ISUPPORTS(nsDateTimeFormatMac, nsIDateTimeFormat) 1.22 + 1.23 +nsresult nsDateTimeFormatMac::Initialize(nsILocale* locale) 1.24 +{ 1.25 + nsAutoString localeStr; 1.26 + nsAutoString category(NS_LITERAL_STRING("NSILOCALE_TIME")); 1.27 + nsresult res; 1.28 + 1.29 + // use cached info if match with stored locale 1.30 + if (nullptr == locale) { 1.31 + if (!mLocale.IsEmpty() && 1.32 + mLocale.Equals(mAppLocale, nsCaseInsensitiveStringComparator())) { 1.33 + return NS_OK; 1.34 + } 1.35 + } 1.36 + else { 1.37 + res = locale->GetCategory(category, localeStr); 1.38 + if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) { 1.39 + if (!mLocale.IsEmpty() && 1.40 + mLocale.Equals(localeStr, 1.41 + nsCaseInsensitiveStringComparator())) { 1.42 + return NS_OK; 1.43 + } 1.44 + } 1.45 + } 1.46 + 1.47 + // get application locale 1.48 + nsCOMPtr<nsILocaleService> localeService = 1.49 + do_GetService(NS_LOCALESERVICE_CONTRACTID, &res); 1.50 + if (NS_SUCCEEDED(res)) { 1.51 + nsCOMPtr<nsILocale> appLocale; 1.52 + res = localeService->GetApplicationLocale(getter_AddRefs(appLocale)); 1.53 + if (NS_SUCCEEDED(res)) { 1.54 + res = appLocale->GetCategory(category, localeStr); 1.55 + if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) { 1.56 + mAppLocale = localeStr; // cache app locale name 1.57 + } 1.58 + } 1.59 + } 1.60 + 1.61 + // use app default if no locale specified 1.62 + if (nullptr == locale) { 1.63 + mUseDefaultLocale = true; 1.64 + } 1.65 + else { 1.66 + mUseDefaultLocale = false; 1.67 + res = locale->GetCategory(category, localeStr); 1.68 + } 1.69 + 1.70 + if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) { 1.71 + mLocale.Assign(localeStr); // cache locale name 1.72 + } 1.73 + 1.74 + return res; 1.75 +} 1.76 + 1.77 +// performs a locale sensitive date formatting operation on the time_t parameter 1.78 +nsresult nsDateTimeFormatMac::FormatTime(nsILocale* locale, 1.79 + const nsDateFormatSelector dateFormatSelector, 1.80 + const nsTimeFormatSelector timeFormatSelector, 1.81 + const time_t timetTime, 1.82 + nsAString& stringOut) 1.83 +{ 1.84 + struct tm tmTime; 1.85 + return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, localtime_r(&timetTime, &tmTime), stringOut); 1.86 +} 1.87 + 1.88 +// performs a locale sensitive date formatting operation on the struct tm parameter 1.89 +nsresult nsDateTimeFormatMac::FormatTMTime(nsILocale* locale, 1.90 + const nsDateFormatSelector dateFormatSelector, 1.91 + const nsTimeFormatSelector timeFormatSelector, 1.92 + const struct tm* tmTime, 1.93 + nsAString& stringOut) 1.94 +{ 1.95 + nsresult res = NS_OK; 1.96 + 1.97 + // set up locale data 1.98 + (void) Initialize(locale); 1.99 + 1.100 + // return, nothing to format 1.101 + if (dateFormatSelector == kDateFormatNone && timeFormatSelector == kTimeFormatNone) { 1.102 + stringOut.Truncate(); 1.103 + return NS_OK; 1.104 + } 1.105 + 1.106 + NS_ASSERTION(tmTime->tm_mon >= 0, "tm is not set correctly"); 1.107 + NS_ASSERTION(tmTime->tm_mday >= 1, "tm is not set correctly"); 1.108 + NS_ASSERTION(tmTime->tm_hour >= 0, "tm is not set correctly"); 1.109 + NS_ASSERTION(tmTime->tm_min >= 0, "tm is not set correctly"); 1.110 + NS_ASSERTION(tmTime->tm_sec >= 0, "tm is not set correctly"); 1.111 + NS_ASSERTION(tmTime->tm_wday >= 0, "tm is not set correctly"); 1.112 + 1.113 + // Got the locale for the formatter: 1.114 + CFLocaleRef formatterLocale; 1.115 + if (!locale) { 1.116 + formatterLocale = CFLocaleCopyCurrent(); 1.117 + } else { 1.118 + CFStringRef localeStr = CFStringCreateWithCharacters(nullptr, 1.119 + reinterpret_cast<const UniChar*>(mLocale.get()), 1.120 + mLocale.Length()); 1.121 + formatterLocale = CFLocaleCreate(nullptr, localeStr); 1.122 + CFRelease(localeStr); 1.123 + } 1.124 + 1.125 + // Get the date style for the formatter: 1.126 + CFDateFormatterStyle dateStyle; 1.127 + switch (dateFormatSelector) { 1.128 + case kDateFormatLong: 1.129 + dateStyle = kCFDateFormatterLongStyle; 1.130 + break; 1.131 + case kDateFormatShort: 1.132 + dateStyle = kCFDateFormatterShortStyle; 1.133 + break; 1.134 + case kDateFormatYearMonth: 1.135 + case kDateFormatWeekday: 1.136 + dateStyle = kCFDateFormatterNoStyle; // formats handled below 1.137 + break; 1.138 + case kDateFormatNone: 1.139 + dateStyle = kCFDateFormatterNoStyle; 1.140 + break; 1.141 + default: 1.142 + NS_ERROR("Unknown nsDateFormatSelector"); 1.143 + res = NS_ERROR_FAILURE; 1.144 + dateStyle = kCFDateFormatterNoStyle; 1.145 + } 1.146 + 1.147 + // Get the time style for the formatter: 1.148 + CFDateFormatterStyle timeStyle; 1.149 + switch (timeFormatSelector) { 1.150 + case kTimeFormatSeconds: 1.151 + case kTimeFormatSecondsForce24Hour: // 24 hour part fixed below 1.152 + timeStyle = kCFDateFormatterMediumStyle; 1.153 + break; 1.154 + case kTimeFormatNoSeconds: 1.155 + case kTimeFormatNoSecondsForce24Hour: // 24 hour part fixed below 1.156 + timeStyle = kCFDateFormatterShortStyle; 1.157 + break; 1.158 + case kTimeFormatNone: 1.159 + timeStyle = kCFDateFormatterNoStyle; 1.160 + break; 1.161 + default: 1.162 + NS_ERROR("Unknown nsTimeFormatSelector"); 1.163 + res = NS_ERROR_FAILURE; 1.164 + timeStyle = kCFDateFormatterNoStyle; 1.165 + } 1.166 + 1.167 + // Create the formatter and fix up its formatting as necessary: 1.168 + CFDateFormatterRef formatter = 1.169 + CFDateFormatterCreate(nullptr, formatterLocale, dateStyle, timeStyle); 1.170 + 1.171 + CFRelease(formatterLocale); 1.172 + 1.173 + if (dateFormatSelector == kDateFormatYearMonth || 1.174 + dateFormatSelector == kDateFormatWeekday) { 1.175 + CFStringRef dateFormat = 1.176 + dateFormatSelector == kDateFormatYearMonth ? CFSTR("yyyy/MM ") : CFSTR("EEE "); 1.177 + 1.178 + CFStringRef oldFormat = CFDateFormatterGetFormat(formatter); 1.179 + CFMutableStringRef newFormat = CFStringCreateMutableCopy(nullptr, 0, oldFormat); 1.180 + CFStringInsert(newFormat, 0, dateFormat); 1.181 + CFDateFormatterSetFormat(formatter, newFormat); 1.182 + CFRelease(newFormat); // note we don't own oldFormat 1.183 + } 1.184 + 1.185 + if (timeFormatSelector == kTimeFormatSecondsForce24Hour || 1.186 + timeFormatSelector == kTimeFormatNoSecondsForce24Hour) { 1.187 + // Replace "h" with "H", and remove "a": 1.188 + CFStringRef oldFormat = CFDateFormatterGetFormat(formatter); 1.189 + CFMutableStringRef newFormat = CFStringCreateMutableCopy(nullptr, 0, oldFormat); 1.190 + CFIndex replaceCount = CFStringFindAndReplace(newFormat, 1.191 + CFSTR("h"), CFSTR("H"), 1.192 + CFRangeMake(0, CFStringGetLength(newFormat)), 1.193 + 0); 1.194 + NS_ASSERTION(replaceCount == 1, "Unexpected number of \"h\" occurrences"); 1.195 + replaceCount = CFStringFindAndReplace(newFormat, 1.196 + CFSTR("a"), CFSTR(""), 1.197 + CFRangeMake(0, CFStringGetLength(newFormat)), 1.198 + 0); 1.199 + NS_ASSERTION(replaceCount == 1, "Unexpected number of \"a\" occurrences"); 1.200 + CFDateFormatterSetFormat(formatter, newFormat); 1.201 + CFRelease(newFormat); // note we don't own oldFormat 1.202 + } 1.203 + 1.204 + // Now get the formatted date: 1.205 + CFGregorianDate date; 1.206 + date.second = tmTime->tm_sec; 1.207 + date.minute = tmTime->tm_min; 1.208 + date.hour = tmTime->tm_hour; 1.209 + date.day = tmTime->tm_mday; // Mac is 1-based, tm is 1-based 1.210 + date.month = tmTime->tm_mon + 1; // Mac is 1-based, tm is 0-based 1.211 + date.year = tmTime->tm_year + 1900; 1.212 + 1.213 + CFTimeZoneRef timeZone = CFTimeZoneCopySystem(); // tmTime is in local time 1.214 + CFAbsoluteTime absTime = CFGregorianDateGetAbsoluteTime(date, timeZone); 1.215 + CFRelease(timeZone); 1.216 + 1.217 + CFStringRef formattedDate = CFDateFormatterCreateStringWithAbsoluteTime(nullptr, 1.218 + formatter, 1.219 + absTime); 1.220 + 1.221 + CFIndex stringLen = CFStringGetLength(formattedDate); 1.222 + 1.223 + nsAutoTArray<UniChar, 256> stringBuffer; 1.224 + stringBuffer.SetLength(stringLen + 1); 1.225 + CFStringGetCharacters(formattedDate, CFRangeMake(0, stringLen), stringBuffer.Elements()); 1.226 + stringOut.Assign(reinterpret_cast<char16_t*>(stringBuffer.Elements()), stringLen); 1.227 + 1.228 + CFRelease(formattedDate); 1.229 + CFRelease(formatter); 1.230 + 1.231 + return res; 1.232 +} 1.233 + 1.234 +// performs a locale sensitive date formatting operation on the PRTime parameter 1.235 +nsresult nsDateTimeFormatMac::FormatPRTime(nsILocale* locale, 1.236 + const nsDateFormatSelector dateFormatSelector, 1.237 + const nsTimeFormatSelector timeFormatSelector, 1.238 + const PRTime prTime, 1.239 + nsAString& stringOut) 1.240 +{ 1.241 + PRExplodedTime explodedTime; 1.242 + PR_ExplodeTime(prTime, PR_LocalTimeParameters, &explodedTime); 1.243 + 1.244 + return FormatPRExplodedTime(locale, dateFormatSelector, timeFormatSelector, &explodedTime, stringOut); 1.245 +} 1.246 + 1.247 +// performs a locale sensitive date formatting operation on the PRExplodedTime parameter 1.248 +nsresult nsDateTimeFormatMac::FormatPRExplodedTime(nsILocale* locale, 1.249 + const nsDateFormatSelector dateFormatSelector, 1.250 + const nsTimeFormatSelector timeFormatSelector, 1.251 + const PRExplodedTime* explodedTime, 1.252 + nsAString& stringOut) 1.253 +{ 1.254 + struct tm tmTime; 1.255 + memset( &tmTime, 0, sizeof(tmTime) ); 1.256 + 1.257 + tmTime.tm_yday = explodedTime->tm_yday; 1.258 + tmTime.tm_wday = explodedTime->tm_wday; 1.259 + tmTime.tm_year = explodedTime->tm_year; 1.260 + tmTime.tm_year -= 1900; 1.261 + tmTime.tm_mon = explodedTime->tm_month; 1.262 + tmTime.tm_mday = explodedTime->tm_mday; 1.263 + tmTime.tm_hour = explodedTime->tm_hour; 1.264 + tmTime.tm_min = explodedTime->tm_min; 1.265 + tmTime.tm_sec = explodedTime->tm_sec; 1.266 + 1.267 + return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut); 1.268 +} 1.269 +