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 <CoreFoundation/CoreFoundation.h>
7 #include "nsIServiceManager.h"
8 #include "nsDateTimeFormatMac.h"
9 #include <CoreFoundation/CFDateFormatter.h>
10 #include "nsIComponentManager.h"
11 #include "nsILocaleService.h"
14 #include "nsUnicharUtils.h"
18 NS_IMPL_ISUPPORTS(nsDateTimeFormatMac
, nsIDateTimeFormat
)
20 nsresult
nsDateTimeFormatMac::Initialize(nsILocale
* locale
)
22 nsAutoString localeStr
;
23 nsAutoString
category(NS_LITERAL_STRING("NSILOCALE_TIME"));
26 // use cached info if match with stored locale
27 if (nullptr == locale
) {
28 if (!mLocale
.IsEmpty() &&
29 mLocale
.Equals(mAppLocale
, nsCaseInsensitiveStringComparator())) {
34 res
= locale
->GetCategory(category
, localeStr
);
35 if (NS_SUCCEEDED(res
) && !localeStr
.IsEmpty()) {
36 if (!mLocale
.IsEmpty() &&
37 mLocale
.Equals(localeStr
,
38 nsCaseInsensitiveStringComparator())) {
44 // get application locale
45 nsCOMPtr
<nsILocaleService
> localeService
=
46 do_GetService(NS_LOCALESERVICE_CONTRACTID
, &res
);
47 if (NS_SUCCEEDED(res
)) {
48 nsCOMPtr
<nsILocale
> appLocale
;
49 res
= localeService
->GetApplicationLocale(getter_AddRefs(appLocale
));
50 if (NS_SUCCEEDED(res
)) {
51 res
= appLocale
->GetCategory(category
, localeStr
);
52 if (NS_SUCCEEDED(res
) && !localeStr
.IsEmpty()) {
53 mAppLocale
= localeStr
; // cache app locale name
58 // use app default if no locale specified
59 if (nullptr == locale
) {
60 mUseDefaultLocale
= true;
63 mUseDefaultLocale
= false;
64 res
= locale
->GetCategory(category
, localeStr
);
67 if (NS_SUCCEEDED(res
) && !localeStr
.IsEmpty()) {
68 mLocale
.Assign(localeStr
); // cache locale name
74 // performs a locale sensitive date formatting operation on the time_t parameter
75 nsresult
nsDateTimeFormatMac::FormatTime(nsILocale
* locale
,
76 const nsDateFormatSelector dateFormatSelector
,
77 const nsTimeFormatSelector timeFormatSelector
,
78 const time_t timetTime
,
82 return FormatTMTime(locale
, dateFormatSelector
, timeFormatSelector
, localtime_r(&timetTime
, &tmTime
), stringOut
);
85 // performs a locale sensitive date formatting operation on the struct tm parameter
86 nsresult
nsDateTimeFormatMac::FormatTMTime(nsILocale
* locale
,
87 const nsDateFormatSelector dateFormatSelector
,
88 const nsTimeFormatSelector timeFormatSelector
,
89 const struct tm
* tmTime
,
95 (void) Initialize(locale
);
97 // return, nothing to format
98 if (dateFormatSelector
== kDateFormatNone
&& timeFormatSelector
== kTimeFormatNone
) {
103 NS_ASSERTION(tmTime
->tm_mon
>= 0, "tm is not set correctly");
104 NS_ASSERTION(tmTime
->tm_mday
>= 1, "tm is not set correctly");
105 NS_ASSERTION(tmTime
->tm_hour
>= 0, "tm is not set correctly");
106 NS_ASSERTION(tmTime
->tm_min
>= 0, "tm is not set correctly");
107 NS_ASSERTION(tmTime
->tm_sec
>= 0, "tm is not set correctly");
108 NS_ASSERTION(tmTime
->tm_wday
>= 0, "tm is not set correctly");
110 // Got the locale for the formatter:
111 CFLocaleRef formatterLocale
;
113 formatterLocale
= CFLocaleCopyCurrent();
115 CFStringRef localeStr
= CFStringCreateWithCharacters(nullptr,
116 reinterpret_cast<const UniChar
*>(mLocale
.get()),
118 formatterLocale
= CFLocaleCreate(nullptr, localeStr
);
119 CFRelease(localeStr
);
122 // Get the date style for the formatter:
123 CFDateFormatterStyle dateStyle
;
124 switch (dateFormatSelector
) {
125 case kDateFormatLong
:
126 dateStyle
= kCFDateFormatterLongStyle
;
128 case kDateFormatShort
:
129 dateStyle
= kCFDateFormatterShortStyle
;
131 case kDateFormatYearMonth
:
132 case kDateFormatWeekday
:
133 dateStyle
= kCFDateFormatterNoStyle
; // formats handled below
135 case kDateFormatNone
:
136 dateStyle
= kCFDateFormatterNoStyle
;
139 NS_ERROR("Unknown nsDateFormatSelector");
140 res
= NS_ERROR_FAILURE
;
141 dateStyle
= kCFDateFormatterNoStyle
;
144 // Get the time style for the formatter:
145 CFDateFormatterStyle timeStyle
;
146 switch (timeFormatSelector
) {
147 case kTimeFormatSeconds
:
148 case kTimeFormatSecondsForce24Hour
: // 24 hour part fixed below
149 timeStyle
= kCFDateFormatterMediumStyle
;
151 case kTimeFormatNoSeconds
:
152 case kTimeFormatNoSecondsForce24Hour
: // 24 hour part fixed below
153 timeStyle
= kCFDateFormatterShortStyle
;
155 case kTimeFormatNone
:
156 timeStyle
= kCFDateFormatterNoStyle
;
159 NS_ERROR("Unknown nsTimeFormatSelector");
160 res
= NS_ERROR_FAILURE
;
161 timeStyle
= kCFDateFormatterNoStyle
;
164 // Create the formatter and fix up its formatting as necessary:
165 CFDateFormatterRef formatter
=
166 CFDateFormatterCreate(nullptr, formatterLocale
, dateStyle
, timeStyle
);
168 CFRelease(formatterLocale
);
170 if (dateFormatSelector
== kDateFormatYearMonth
||
171 dateFormatSelector
== kDateFormatWeekday
) {
172 CFStringRef dateFormat
=
173 dateFormatSelector
== kDateFormatYearMonth
? CFSTR("yyyy/MM ") : CFSTR("EEE ");
175 CFStringRef oldFormat
= CFDateFormatterGetFormat(formatter
);
176 CFMutableStringRef newFormat
= CFStringCreateMutableCopy(nullptr, 0, oldFormat
);
177 CFStringInsert(newFormat
, 0, dateFormat
);
178 CFDateFormatterSetFormat(formatter
, newFormat
);
179 CFRelease(newFormat
); // note we don't own oldFormat
182 if (timeFormatSelector
== kTimeFormatSecondsForce24Hour
||
183 timeFormatSelector
== kTimeFormatNoSecondsForce24Hour
) {
184 // Replace "h" with "H", and remove "a":
185 CFStringRef oldFormat
= CFDateFormatterGetFormat(formatter
);
186 CFMutableStringRef newFormat
= CFStringCreateMutableCopy(nullptr, 0, oldFormat
);
187 CFIndex replaceCount
= CFStringFindAndReplace(newFormat
,
188 CFSTR("h"), CFSTR("H"),
189 CFRangeMake(0, CFStringGetLength(newFormat
)),
191 NS_ASSERTION(replaceCount
<= 2, "Unexpected number of \"h\" occurrences");
192 replaceCount
= CFStringFindAndReplace(newFormat
,
193 CFSTR("a"), CFSTR(""),
194 CFRangeMake(0, CFStringGetLength(newFormat
)),
196 NS_ASSERTION(replaceCount
<= 1, "Unexpected number of \"a\" occurrences");
197 CFDateFormatterSetFormat(formatter
, newFormat
);
198 CFRelease(newFormat
); // note we don't own oldFormat
201 // Now get the formatted date:
202 CFGregorianDate date
;
203 date
.second
= tmTime
->tm_sec
;
204 date
.minute
= tmTime
->tm_min
;
205 date
.hour
= tmTime
->tm_hour
;
206 date
.day
= tmTime
->tm_mday
; // Mac is 1-based, tm is 1-based
207 date
.month
= tmTime
->tm_mon
+ 1; // Mac is 1-based, tm is 0-based
208 date
.year
= tmTime
->tm_year
+ 1900;
210 CFTimeZoneRef timeZone
= CFTimeZoneCopySystem(); // tmTime is in local time
211 CFAbsoluteTime absTime
= CFGregorianDateGetAbsoluteTime(date
, timeZone
);
214 CFStringRef formattedDate
= CFDateFormatterCreateStringWithAbsoluteTime(nullptr,
218 CFIndex stringLen
= CFStringGetLength(formattedDate
);
220 nsAutoTArray
<UniChar
, 256> stringBuffer
;
221 stringBuffer
.SetLength(stringLen
+ 1);
222 CFStringGetCharacters(formattedDate
, CFRangeMake(0, stringLen
), stringBuffer
.Elements());
223 stringOut
.Assign(reinterpret_cast<char16_t
*>(stringBuffer
.Elements()), stringLen
);
225 CFRelease(formattedDate
);
226 CFRelease(formatter
);
231 // performs a locale sensitive date formatting operation on the PRTime parameter
232 nsresult
nsDateTimeFormatMac::FormatPRTime(nsILocale
* locale
,
233 const nsDateFormatSelector dateFormatSelector
,
234 const nsTimeFormatSelector timeFormatSelector
,
236 nsAString
& stringOut
)
238 PRExplodedTime explodedTime
;
239 PR_ExplodeTime(prTime
, PR_LocalTimeParameters
, &explodedTime
);
241 return FormatPRExplodedTime(locale
, dateFormatSelector
, timeFormatSelector
, &explodedTime
, stringOut
);
244 // performs a locale sensitive date formatting operation on the PRExplodedTime parameter
245 nsresult
nsDateTimeFormatMac::FormatPRExplodedTime(nsILocale
* locale
,
246 const nsDateFormatSelector dateFormatSelector
,
247 const nsTimeFormatSelector timeFormatSelector
,
248 const PRExplodedTime
* explodedTime
,
249 nsAString
& stringOut
)
252 memset( &tmTime
, 0, sizeof(tmTime
) );
254 tmTime
.tm_yday
= explodedTime
->tm_yday
;
255 tmTime
.tm_wday
= explodedTime
->tm_wday
;
256 tmTime
.tm_year
= explodedTime
->tm_year
;
257 tmTime
.tm_year
-= 1900;
258 tmTime
.tm_mon
= explodedTime
->tm_month
;
259 tmTime
.tm_mday
= explodedTime
->tm_mday
;
260 tmTime
.tm_hour
= explodedTime
->tm_hour
;
261 tmTime
.tm_min
= explodedTime
->tm_min
;
262 tmTime
.tm_sec
= explodedTime
->tm_sec
;
264 return FormatTMTime(locale
, dateFormatSelector
, timeFormatSelector
, &tmTime
, stringOut
);