intl/locale/DateTimeFormatICU.cpp
author Gregory Moore <olucafont6@yahoo.com>
Mon, 06 Feb 2017 14:02:38 -0800
changeset 390165 27775393a94ea9215fcc9a5bd15181aca2cad7d6
parent 374374 7e747433d1c9fae8a0acb4090f61335fcc165e4c
permissions -rw-r--r--
Bug 1331466 - Added functionality to FormatPRExplodedTime to take into account timezone information given in the PRExplodedTime parameter. r=emk MozReview-Commit-ID: HsRi1Uwfdgq

/* -*- 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 "nsILocaleService.h"

namespace mozilla {

nsCString* DateTimeFormat::mLocale = nullptr;

/*static*/ nsresult
DateTimeFormat::Initialize()
{
  nsAutoString localeStr;
  nsresult rv = NS_OK;

  if (!mLocale) {
    mLocale = new nsCString();
  } else if (!mLocale->IsEmpty()) {
    return NS_OK;
  }

  nsCOMPtr<nsILocaleService> localeService =
           do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
  if (NS_SUCCEEDED(rv)) {
    nsCOMPtr<nsILocale> appLocale;
    rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
    if (NS_SUCCEEDED(rv)) {
      rv = appLocale->GetCategory(NS_LITERAL_STRING("NSILOCALE_TIME"), localeStr);
      if (NS_SUCCEEDED(rv) && !localeStr.IsEmpty()) {
        *mLocale = NS_LossyConvertUTF16toASCII(localeStr); // cache locale name
      }
    }
  }

  return rv;
}

// performs a locale sensitive date formatting operation on the time_t parameter
/*static*/ nsresult
DateTimeFormat::FormatTime(const nsDateFormatSelector aDateFormatSelector,
                           const nsTimeFormatSelector aTimeFormatSelector,
                           const time_t aTimetTime,
                           nsAString& aStringOut)
{
  return FormatPRTime(aDateFormatSelector, aTimeFormatSelector, (aTimetTime * PR_USEC_PER_SEC), aStringOut);
}

// 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)
{
#define 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:
  UDateFormatStyle dateStyle;
  switch (aDateFormatSelector) {
    case kDateFormatLong:
      dateStyle = UDAT_LONG;
      break;
    case kDateFormatShort:
      dateStyle = UDAT_SHORT;
      break;
    case kDateFormatNone:
      dateStyle = UDAT_NONE;
      break;
    default:
      NS_ERROR("Unknown nsDateFormatSelector");
      return NS_ERROR_ILLEGAL_VALUE;
  }

  // Get the time style for the formatter:
  UDateFormatStyle timeStyle;
  switch (aTimeFormatSelector) {
    case kTimeFormatSeconds:
      timeStyle = UDAT_MEDIUM;
      break;
    case kTimeFormatNoSeconds:
      timeStyle = UDAT_SHORT;
      break;
    case kTimeFormatNone:
      timeStyle = UDAT_NONE;
      break;
    default:
      NS_ERROR("Unknown nsTimeFormatSelector");
      return NS_ERROR_ILLEGAL_VALUE;
  }

  // generate date/time string

  UErrorCode status = U_ZERO_ERROR;

  UDateFormat* dateTimeFormat;
  if (aTimeParameters) {
    nsAutoString timeZoneID(u"GMT");

    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);
    }

    dateTimeFormat = udat_open(timeStyle, dateStyle, mLocale->get(), reinterpret_cast<const UChar*>(timeZoneID.BeginReading()), timeZoneID.Length(), nullptr, -1, &status);
  } else {
    dateTimeFormat = udat_open(timeStyle, dateStyle, mLocale->get(), nullptr, -1, nullptr, -1, &status);
  }

  if (U_SUCCESS(status) && dateTimeFormat) {
    aStringOut.SetLength(DATETIME_FORMAT_INITIAL_LEN);
    dateTimeLen = udat_format(dateTimeFormat, aUDateTime, reinterpret_cast<UChar*>(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<UChar*>(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;
  }
}

}