Blame intl/locale/DateTimeFormat.cpp

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