michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include "nsIServiceManager.h" michael@0: #include "nsDateTimeFormatMac.h" michael@0: #include michael@0: #include "nsIComponentManager.h" michael@0: #include "nsILocaleService.h" michael@0: #include "nsCRT.h" michael@0: #include "plstr.h" michael@0: #include "nsUnicharUtils.h" michael@0: #include "nsTArray.h" michael@0: michael@0: michael@0: NS_IMPL_ISUPPORTS(nsDateTimeFormatMac, nsIDateTimeFormat) michael@0: michael@0: nsresult nsDateTimeFormatMac::Initialize(nsILocale* locale) michael@0: { michael@0: nsAutoString localeStr; michael@0: nsAutoString category(NS_LITERAL_STRING("NSILOCALE_TIME")); michael@0: nsresult res; michael@0: michael@0: // use cached info if match with stored locale michael@0: if (nullptr == locale) { michael@0: if (!mLocale.IsEmpty() && michael@0: mLocale.Equals(mAppLocale, nsCaseInsensitiveStringComparator())) { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: else { michael@0: res = locale->GetCategory(category, localeStr); michael@0: if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) { michael@0: if (!mLocale.IsEmpty() && michael@0: mLocale.Equals(localeStr, michael@0: nsCaseInsensitiveStringComparator())) { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // get application locale michael@0: nsCOMPtr localeService = michael@0: do_GetService(NS_LOCALESERVICE_CONTRACTID, &res); michael@0: if (NS_SUCCEEDED(res)) { michael@0: nsCOMPtr appLocale; michael@0: res = localeService->GetApplicationLocale(getter_AddRefs(appLocale)); michael@0: if (NS_SUCCEEDED(res)) { michael@0: res = appLocale->GetCategory(category, localeStr); michael@0: if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) { michael@0: mAppLocale = localeStr; // cache app locale name michael@0: } michael@0: } michael@0: } michael@0: michael@0: // use app default if no locale specified michael@0: if (nullptr == locale) { michael@0: mUseDefaultLocale = true; michael@0: } michael@0: else { michael@0: mUseDefaultLocale = false; michael@0: res = locale->GetCategory(category, localeStr); michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) { michael@0: mLocale.Assign(localeStr); // cache locale name michael@0: } michael@0: michael@0: return res; michael@0: } michael@0: michael@0: // performs a locale sensitive date formatting operation on the time_t parameter michael@0: nsresult nsDateTimeFormatMac::FormatTime(nsILocale* locale, michael@0: const nsDateFormatSelector dateFormatSelector, michael@0: const nsTimeFormatSelector timeFormatSelector, michael@0: const time_t timetTime, michael@0: nsAString& stringOut) michael@0: { michael@0: struct tm tmTime; michael@0: return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, localtime_r(&timetTime, &tmTime), stringOut); michael@0: } michael@0: michael@0: // performs a locale sensitive date formatting operation on the struct tm parameter michael@0: nsresult nsDateTimeFormatMac::FormatTMTime(nsILocale* locale, michael@0: const nsDateFormatSelector dateFormatSelector, michael@0: const nsTimeFormatSelector timeFormatSelector, michael@0: const struct tm* tmTime, michael@0: nsAString& stringOut) michael@0: { michael@0: nsresult res = NS_OK; michael@0: michael@0: // set up locale data michael@0: (void) Initialize(locale); michael@0: michael@0: // return, nothing to format michael@0: if (dateFormatSelector == kDateFormatNone && timeFormatSelector == kTimeFormatNone) { michael@0: stringOut.Truncate(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_ASSERTION(tmTime->tm_mon >= 0, "tm is not set correctly"); michael@0: NS_ASSERTION(tmTime->tm_mday >= 1, "tm is not set correctly"); michael@0: NS_ASSERTION(tmTime->tm_hour >= 0, "tm is not set correctly"); michael@0: NS_ASSERTION(tmTime->tm_min >= 0, "tm is not set correctly"); michael@0: NS_ASSERTION(tmTime->tm_sec >= 0, "tm is not set correctly"); michael@0: NS_ASSERTION(tmTime->tm_wday >= 0, "tm is not set correctly"); michael@0: michael@0: // Got the locale for the formatter: michael@0: CFLocaleRef formatterLocale; michael@0: if (!locale) { michael@0: formatterLocale = CFLocaleCopyCurrent(); michael@0: } else { michael@0: CFStringRef localeStr = CFStringCreateWithCharacters(nullptr, michael@0: reinterpret_cast(mLocale.get()), michael@0: mLocale.Length()); michael@0: formatterLocale = CFLocaleCreate(nullptr, localeStr); michael@0: CFRelease(localeStr); michael@0: } michael@0: michael@0: // Get the date style for the formatter: michael@0: CFDateFormatterStyle dateStyle; michael@0: switch (dateFormatSelector) { michael@0: case kDateFormatLong: michael@0: dateStyle = kCFDateFormatterLongStyle; michael@0: break; michael@0: case kDateFormatShort: michael@0: dateStyle = kCFDateFormatterShortStyle; michael@0: break; michael@0: case kDateFormatYearMonth: michael@0: case kDateFormatWeekday: michael@0: dateStyle = kCFDateFormatterNoStyle; // formats handled below michael@0: break; michael@0: case kDateFormatNone: michael@0: dateStyle = kCFDateFormatterNoStyle; michael@0: break; michael@0: default: michael@0: NS_ERROR("Unknown nsDateFormatSelector"); michael@0: res = NS_ERROR_FAILURE; michael@0: dateStyle = kCFDateFormatterNoStyle; michael@0: } michael@0: michael@0: // Get the time style for the formatter: michael@0: CFDateFormatterStyle timeStyle; michael@0: switch (timeFormatSelector) { michael@0: case kTimeFormatSeconds: michael@0: case kTimeFormatSecondsForce24Hour: // 24 hour part fixed below michael@0: timeStyle = kCFDateFormatterMediumStyle; michael@0: break; michael@0: case kTimeFormatNoSeconds: michael@0: case kTimeFormatNoSecondsForce24Hour: // 24 hour part fixed below michael@0: timeStyle = kCFDateFormatterShortStyle; michael@0: break; michael@0: case kTimeFormatNone: michael@0: timeStyle = kCFDateFormatterNoStyle; michael@0: break; michael@0: default: michael@0: NS_ERROR("Unknown nsTimeFormatSelector"); michael@0: res = NS_ERROR_FAILURE; michael@0: timeStyle = kCFDateFormatterNoStyle; michael@0: } michael@0: michael@0: // Create the formatter and fix up its formatting as necessary: michael@0: CFDateFormatterRef formatter = michael@0: CFDateFormatterCreate(nullptr, formatterLocale, dateStyle, timeStyle); michael@0: michael@0: CFRelease(formatterLocale); michael@0: michael@0: if (dateFormatSelector == kDateFormatYearMonth || michael@0: dateFormatSelector == kDateFormatWeekday) { michael@0: CFStringRef dateFormat = michael@0: dateFormatSelector == kDateFormatYearMonth ? CFSTR("yyyy/MM ") : CFSTR("EEE "); michael@0: michael@0: CFStringRef oldFormat = CFDateFormatterGetFormat(formatter); michael@0: CFMutableStringRef newFormat = CFStringCreateMutableCopy(nullptr, 0, oldFormat); michael@0: CFStringInsert(newFormat, 0, dateFormat); michael@0: CFDateFormatterSetFormat(formatter, newFormat); michael@0: CFRelease(newFormat); // note we don't own oldFormat michael@0: } michael@0: michael@0: if (timeFormatSelector == kTimeFormatSecondsForce24Hour || michael@0: timeFormatSelector == kTimeFormatNoSecondsForce24Hour) { michael@0: // Replace "h" with "H", and remove "a": michael@0: CFStringRef oldFormat = CFDateFormatterGetFormat(formatter); michael@0: CFMutableStringRef newFormat = CFStringCreateMutableCopy(nullptr, 0, oldFormat); michael@0: CFIndex replaceCount = CFStringFindAndReplace(newFormat, michael@0: CFSTR("h"), CFSTR("H"), michael@0: CFRangeMake(0, CFStringGetLength(newFormat)), michael@0: 0); michael@0: NS_ASSERTION(replaceCount == 1, "Unexpected number of \"h\" occurrences"); michael@0: replaceCount = CFStringFindAndReplace(newFormat, michael@0: CFSTR("a"), CFSTR(""), michael@0: CFRangeMake(0, CFStringGetLength(newFormat)), michael@0: 0); michael@0: NS_ASSERTION(replaceCount == 1, "Unexpected number of \"a\" occurrences"); michael@0: CFDateFormatterSetFormat(formatter, newFormat); michael@0: CFRelease(newFormat); // note we don't own oldFormat michael@0: } michael@0: michael@0: // Now get the formatted date: michael@0: CFGregorianDate date; michael@0: date.second = tmTime->tm_sec; michael@0: date.minute = tmTime->tm_min; michael@0: date.hour = tmTime->tm_hour; michael@0: date.day = tmTime->tm_mday; // Mac is 1-based, tm is 1-based michael@0: date.month = tmTime->tm_mon + 1; // Mac is 1-based, tm is 0-based michael@0: date.year = tmTime->tm_year + 1900; michael@0: michael@0: CFTimeZoneRef timeZone = CFTimeZoneCopySystem(); // tmTime is in local time michael@0: CFAbsoluteTime absTime = CFGregorianDateGetAbsoluteTime(date, timeZone); michael@0: CFRelease(timeZone); michael@0: michael@0: CFStringRef formattedDate = CFDateFormatterCreateStringWithAbsoluteTime(nullptr, michael@0: formatter, michael@0: absTime); michael@0: michael@0: CFIndex stringLen = CFStringGetLength(formattedDate); michael@0: michael@0: nsAutoTArray stringBuffer; michael@0: stringBuffer.SetLength(stringLen + 1); michael@0: CFStringGetCharacters(formattedDate, CFRangeMake(0, stringLen), stringBuffer.Elements()); michael@0: stringOut.Assign(reinterpret_cast(stringBuffer.Elements()), stringLen); michael@0: michael@0: CFRelease(formattedDate); michael@0: CFRelease(formatter); michael@0: michael@0: return res; michael@0: } michael@0: michael@0: // performs a locale sensitive date formatting operation on the PRTime parameter michael@0: nsresult nsDateTimeFormatMac::FormatPRTime(nsILocale* locale, michael@0: const nsDateFormatSelector dateFormatSelector, michael@0: const nsTimeFormatSelector timeFormatSelector, michael@0: const PRTime prTime, michael@0: nsAString& stringOut) michael@0: { michael@0: PRExplodedTime explodedTime; michael@0: PR_ExplodeTime(prTime, PR_LocalTimeParameters, &explodedTime); michael@0: michael@0: return FormatPRExplodedTime(locale, dateFormatSelector, timeFormatSelector, &explodedTime, stringOut); michael@0: } michael@0: michael@0: // performs a locale sensitive date formatting operation on the PRExplodedTime parameter michael@0: nsresult nsDateTimeFormatMac::FormatPRExplodedTime(nsILocale* locale, michael@0: const nsDateFormatSelector dateFormatSelector, michael@0: const nsTimeFormatSelector timeFormatSelector, michael@0: const PRExplodedTime* explodedTime, michael@0: nsAString& stringOut) michael@0: { michael@0: struct tm tmTime; michael@0: memset( &tmTime, 0, sizeof(tmTime) ); michael@0: michael@0: tmTime.tm_yday = explodedTime->tm_yday; michael@0: tmTime.tm_wday = explodedTime->tm_wday; michael@0: tmTime.tm_year = explodedTime->tm_year; michael@0: tmTime.tm_year -= 1900; michael@0: tmTime.tm_mon = explodedTime->tm_month; michael@0: tmTime.tm_mday = explodedTime->tm_mday; michael@0: tmTime.tm_hour = explodedTime->tm_hour; michael@0: tmTime.tm_min = explodedTime->tm_min; michael@0: tmTime.tm_sec = explodedTime->tm_sec; michael@0: michael@0: return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut); michael@0: } michael@0: