/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "DateTimeFormat.h" #include "nsCOMPtr.h" #include "nsIServiceManager.h" #include "mozilla/intl/LocaleService.h" #include "OSPreferences.h" #include "mozIOSPreferences.h" #include "unicode/udatpg.h" namespace mozilla { using namespace mozilla::intl; nsCString* DateTimeFormat::mLocale = nullptr; /*static*/ nsresult DateTimeFormat::Initialize() { if (mLocale) { return NS_OK; } mLocale = new nsCString(); AutoTArray regionalPrefsLocales; intl::LocaleService::GetInstance()->GetRegionalPrefsLocales(regionalPrefsLocales); mLocale->Assign(regionalPrefsLocales[0]); return NS_OK; } // performs a locale sensitive date formatting operation on the PRTime parameter /*static*/ nsresult DateTimeFormat::FormatPRTime(const nsDateFormatSelector aDateFormatSelector, const nsTimeFormatSelector aTimeFormatSelector, const PRTime aPrTime, nsAString& aStringOut) { return FormatUDateTime(aDateFormatSelector, aTimeFormatSelector, (aPrTime / PR_USEC_PER_MSEC), nullptr, aStringOut); } // performs a locale sensitive date formatting operation on the PRExplodedTime parameter /*static*/ nsresult DateTimeFormat::FormatPRExplodedTime(const nsDateFormatSelector aDateFormatSelector, const nsTimeFormatSelector aTimeFormatSelector, const PRExplodedTime* aExplodedTime, nsAString& aStringOut) { return FormatUDateTime(aDateFormatSelector, aTimeFormatSelector, (PR_ImplodeTime(aExplodedTime) / PR_USEC_PER_MSEC), &(aExplodedTime->tm_params), aStringOut); } // performs a locale sensitive date formatting operation on the UDate parameter /*static*/ nsresult DateTimeFormat::FormatUDateTime(const nsDateFormatSelector aDateFormatSelector, const nsTimeFormatSelector aTimeFormatSelector, const UDate aUDateTime, const PRTimeParameters* aTimeParameters, nsAString& aStringOut) { const int32_t DATETIME_FORMAT_INITIAL_LEN = 127; int32_t dateTimeLen = 0; nsresult rv = NS_OK; // return, nothing to format if (aDateFormatSelector == kDateFormatNone && aTimeFormatSelector == kTimeFormatNone) { aStringOut.Truncate(); return NS_OK; } // set up locale data rv = Initialize(); if (NS_FAILED(rv)) { return rv; } // Get the date style for the formatter. nsAutoString skeletonDate; nsAutoString patternDate; bool haveSkeleton = true; switch (aDateFormatSelector) { case kDateFormatLong: rv = OSPreferences::GetInstance()->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleLong, mozIOSPreferences::dateTimeFormatStyleNone, nsDependentCString(mLocale->get()), patternDate); NS_ENSURE_SUCCESS(rv, rv); haveSkeleton = false; break; case kDateFormatShort: rv = OSPreferences::GetInstance()->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleShort, mozIOSPreferences::dateTimeFormatStyleNone, nsDependentCString(mLocale->get()), patternDate); NS_ENSURE_SUCCESS(rv, rv); haveSkeleton = false; break; case kDateFormatYearMonth: skeletonDate.AssignLiteral("yyyyMM"); break; case kDateFormatYearMonthLong: skeletonDate.AssignLiteral("yyyyMMMM"); break; case kDateFormatMonthLong: skeletonDate.AssignLiteral("MMMM"); break; case kDateFormatWeekday: skeletonDate.AssignLiteral("EEE"); break; case kDateFormatNone: haveSkeleton = false; break; default: NS_ERROR("Unknown nsDateFormatSelector"); return NS_ERROR_ILLEGAL_VALUE; } UErrorCode status = U_ZERO_ERROR; if (haveSkeleton) { // Get pattern for skeleton. UDateTimePatternGenerator* patternGenerator = udatpg_open(mLocale->get(), &status); if (U_SUCCESS(status)) { int32_t patternLength; patternDate.SetLength(DATETIME_FORMAT_INITIAL_LEN); patternLength = udatpg_getBestPattern(patternGenerator, reinterpret_cast(skeletonDate.BeginReading()), skeletonDate.Length(), reinterpret_cast(patternDate.BeginWriting()), DATETIME_FORMAT_INITIAL_LEN, &status); patternDate.SetLength(patternLength); if (status == U_BUFFER_OVERFLOW_ERROR) { status = U_ZERO_ERROR; udatpg_getBestPattern(patternGenerator, reinterpret_cast(skeletonDate.BeginReading()), skeletonDate.Length(), reinterpret_cast(patternDate.BeginWriting()), patternLength, &status); } } udatpg_close(patternGenerator); } // Get the time style for the formatter. nsAutoString patternTime; switch (aTimeFormatSelector) { case kTimeFormatSeconds: rv = OSPreferences::GetInstance()->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleNone, mozIOSPreferences::dateTimeFormatStyleLong, nsDependentCString(mLocale->get()), patternTime); NS_ENSURE_SUCCESS(rv, rv); break; case kTimeFormatNoSeconds: rv = OSPreferences::GetInstance()->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleNone, mozIOSPreferences::dateTimeFormatStyleShort, nsDependentCString(mLocale->get()), patternTime); NS_ENSURE_SUCCESS(rv, rv); break; case kTimeFormatNone: break; default: NS_ERROR("Unknown nsTimeFormatSelector"); return NS_ERROR_ILLEGAL_VALUE; } nsAutoString pattern; if (patternTime.Length() == 0) { pattern.Assign(patternDate); } else if (patternDate.Length() == 0) { pattern.Assign(patternTime); } else { OSPreferences::GetDateTimeConnectorPattern(nsDependentCString(mLocale->get()), pattern); int32_t index = pattern.Find("{1}"); if (index != kNotFound) pattern.Replace(index, 3, patternDate); index = pattern.Find("{0}"); if (index != kNotFound) pattern.Replace(index, 3, patternTime); } // Generate date/time string. nsAutoString timeZoneID(u"GMT"); if (aTimeParameters) { int32_t totalOffsetMinutes = (aTimeParameters->tp_gmt_offset + aTimeParameters->tp_dst_offset) / 60; if (totalOffsetMinutes != 0) { char sign = totalOffsetMinutes < 0 ? '-' : '+'; int32_t hours = abs(totalOffsetMinutes) / 60; int32_t minutes = abs(totalOffsetMinutes) % 60; timeZoneID.AppendPrintf("%c%02d:%02d", sign, hours, minutes); } } UDateFormat* dateTimeFormat; if (aTimeParameters) { dateTimeFormat = udat_open(UDAT_PATTERN, UDAT_PATTERN, mLocale->get(), reinterpret_cast(timeZoneID.BeginReading()), timeZoneID.Length(), reinterpret_cast(pattern.BeginReading()), pattern.Length(), &status); } else { dateTimeFormat = udat_open(UDAT_PATTERN, UDAT_PATTERN, mLocale->get(), nullptr, -1, reinterpret_cast(pattern.BeginReading()), pattern.Length(), &status); } if (U_SUCCESS(status) && dateTimeFormat) { aStringOut.SetLength(DATETIME_FORMAT_INITIAL_LEN); dateTimeLen = udat_format(dateTimeFormat, aUDateTime, reinterpret_cast(aStringOut.BeginWriting()), DATETIME_FORMAT_INITIAL_LEN, nullptr, &status); aStringOut.SetLength(dateTimeLen); if (status == U_BUFFER_OVERFLOW_ERROR) { status = U_ZERO_ERROR; udat_format(dateTimeFormat, aUDateTime, reinterpret_cast(aStringOut.BeginWriting()), dateTimeLen, nullptr, &status); } } if (U_FAILURE(status)) { rv = NS_ERROR_FAILURE; } if (dateTimeFormat) { udat_close(dateTimeFormat); } return rv; } /*static*/ void DateTimeFormat::Shutdown() { if (mLocale) { delete mLocale; } } }