285 lines
9.8 KiB
C++
285 lines
9.8 KiB
C++
/* -*- 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 <locale.h>
|
|
#include "plstr.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsDateTimeFormatUnix.h"
|
|
#include "nsIComponentManager.h"
|
|
#include "nsILocaleService.h"
|
|
#include "nsIPlatformCharset.h"
|
|
#include "nsPosixLocale.h"
|
|
#include "nsCRT.h"
|
|
#include "nsReadableUtils.h"
|
|
#include "nsUnicharUtils.h"
|
|
#include "mozilla/dom/EncodingUtils.h"
|
|
|
|
using mozilla::dom::EncodingUtils;
|
|
|
|
NS_IMPL_ISUPPORTS(nsDateTimeFormatUnix, nsIDateTimeFormat)
|
|
|
|
// init this interface to a specified locale
|
|
nsresult nsDateTimeFormatUnix::Initialize(nsILocale* locale)
|
|
{
|
|
nsAutoString localeStr;
|
|
NS_NAMED_LITERAL_STRING(aCategory, "NSILOCALE_TIME##PLATFORM");
|
|
nsresult res = NS_OK;
|
|
|
|
// use cached info if match with stored locale
|
|
if (!locale) {
|
|
if (!mLocale.IsEmpty() &&
|
|
mLocale.Equals(mAppLocale, nsCaseInsensitiveStringComparator())) {
|
|
return NS_OK;
|
|
}
|
|
}
|
|
else {
|
|
res = locale->GetCategory(aCategory, localeStr);
|
|
if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
|
|
if (!mLocale.IsEmpty() &&
|
|
mLocale.Equals(localeStr,
|
|
nsCaseInsensitiveStringComparator())) {
|
|
return NS_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
mCharset.AssignLiteral("windows-1252");
|
|
mPlatformLocale.AssignLiteral("en_US");
|
|
|
|
// get locale name string, use app default if no locale specified
|
|
if (!locale) {
|
|
nsCOMPtr<nsILocaleService> localeService =
|
|
do_GetService(NS_LOCALESERVICE_CONTRACTID, &res);
|
|
if (NS_SUCCEEDED(res)) {
|
|
nsCOMPtr<nsILocale> appLocale;
|
|
res = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
|
|
if (NS_SUCCEEDED(res)) {
|
|
res = appLocale->GetCategory(aCategory, localeStr);
|
|
if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
|
|
NS_ASSERTION(NS_SUCCEEDED(res), "failed to get app locale info");
|
|
mAppLocale = localeStr; // cache app locale name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
res = locale->GetCategory(aCategory, localeStr);
|
|
NS_ASSERTION(NS_SUCCEEDED(res), "failed to get locale info");
|
|
}
|
|
|
|
if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
|
|
mLocale = localeStr; // cache locale name
|
|
|
|
nsPosixLocale::GetPlatformLocale(mLocale, mPlatformLocale);
|
|
|
|
nsCOMPtr <nsIPlatformCharset> platformCharset = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &res);
|
|
if (NS_SUCCEEDED(res)) {
|
|
nsAutoCString mappedCharset;
|
|
res = platformCharset->GetDefaultCharsetForLocale(mLocale, mappedCharset);
|
|
if (NS_SUCCEEDED(res)) {
|
|
mCharset = mappedCharset;
|
|
}
|
|
}
|
|
}
|
|
|
|
mDecoder = EncodingUtils::DecoderForEncoding(mCharset);
|
|
|
|
LocalePreferred24hour();
|
|
|
|
return res;
|
|
}
|
|
|
|
void nsDateTimeFormatUnix::LocalePreferred24hour()
|
|
{
|
|
char str[100];
|
|
time_t tt;
|
|
struct tm *tmc;
|
|
int i;
|
|
|
|
tt = time(nullptr);
|
|
tmc = localtime(&tt);
|
|
|
|
tmc->tm_hour=22; // put the test sample hour to 22:00 which is 10PM
|
|
tmc->tm_min=0; // set the min & sec other number than '2'
|
|
tmc->tm_sec=0;
|
|
|
|
char *temp = setlocale(LC_TIME, mPlatformLocale.get());
|
|
strftime(str, (size_t)99, "%X", (struct tm *)tmc);
|
|
|
|
(void) setlocale(LC_TIME, temp);
|
|
|
|
mLocalePreferred24hour = false;
|
|
for (i=0; str[i]; i++) {
|
|
if (str[i] == '2') { // if there is any '2', that locale use 0-23 time format
|
|
mLocalePreferred24hour = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
mLocaleAMPMfirst = true;
|
|
if (mLocalePreferred24hour == false) {
|
|
if (str[0] && str[0] == '1') { // if the first character is '1' of 10:00,
|
|
// AMPM string is located after 10:00
|
|
mLocaleAMPMfirst = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
nsresult nsDateTimeFormatUnix::FormatTime(nsILocale* locale,
|
|
const nsDateFormatSelector dateFormatSelector,
|
|
const nsTimeFormatSelector timeFormatSelector,
|
|
const time_t timetTime,
|
|
nsAString& stringOut)
|
|
{
|
|
struct tm tmTime;
|
|
memcpy(&tmTime, localtime(&timetTime), sizeof(struct tm));
|
|
return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut);
|
|
}
|
|
|
|
// performs a locale sensitive date formatting operation on the struct tm parameter
|
|
nsresult nsDateTimeFormatUnix::FormatTMTime(nsILocale* locale,
|
|
const nsDateFormatSelector dateFormatSelector,
|
|
const nsTimeFormatSelector timeFormatSelector,
|
|
const struct tm* tmTime,
|
|
nsAString& stringOut)
|
|
{
|
|
#define NSDATETIME_FORMAT_BUFFER_LEN 80
|
|
char strOut[NSDATETIME_FORMAT_BUFFER_LEN*2]; // buffer for date and time
|
|
char fmtD[NSDATETIME_FORMAT_BUFFER_LEN], fmtT[NSDATETIME_FORMAT_BUFFER_LEN];
|
|
nsresult rv;
|
|
|
|
|
|
// set up locale data
|
|
(void) Initialize(locale);
|
|
NS_ENSURE_TRUE(mDecoder, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
// set date format
|
|
if (dateFormatSelector == kDateFormatLong && timeFormatSelector == kTimeFormatSeconds) {
|
|
PL_strncpy(fmtD, "%c", NSDATETIME_FORMAT_BUFFER_LEN);
|
|
PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN);
|
|
} else {
|
|
|
|
switch (dateFormatSelector) {
|
|
case kDateFormatNone:
|
|
PL_strncpy(fmtD, "", NSDATETIME_FORMAT_BUFFER_LEN);
|
|
break;
|
|
case kDateFormatLong:
|
|
case kDateFormatShort:
|
|
PL_strncpy(fmtD, "%x", NSDATETIME_FORMAT_BUFFER_LEN);
|
|
break;
|
|
case kDateFormatYearMonth:
|
|
PL_strncpy(fmtD, "%Y/%m", NSDATETIME_FORMAT_BUFFER_LEN);
|
|
break;
|
|
case kDateFormatWeekday:
|
|
PL_strncpy(fmtD, "%a", NSDATETIME_FORMAT_BUFFER_LEN);
|
|
break;
|
|
default:
|
|
PL_strncpy(fmtD, "", NSDATETIME_FORMAT_BUFFER_LEN);
|
|
}
|
|
|
|
// set time format
|
|
switch (timeFormatSelector) {
|
|
case kTimeFormatNone:
|
|
PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN);
|
|
break;
|
|
case kTimeFormatSeconds:
|
|
PL_strncpy(fmtT, "%X", NSDATETIME_FORMAT_BUFFER_LEN);
|
|
break;
|
|
case kTimeFormatNoSeconds:
|
|
PL_strncpy(fmtT,
|
|
mLocalePreferred24hour ? "%H:%M" : mLocaleAMPMfirst ? "%p %I:%M" : "%I:%M %p",
|
|
NSDATETIME_FORMAT_BUFFER_LEN);
|
|
break;
|
|
case kTimeFormatSecondsForce24Hour:
|
|
PL_strncpy(fmtT, "%H:%M:%S", NSDATETIME_FORMAT_BUFFER_LEN);
|
|
break;
|
|
case kTimeFormatNoSecondsForce24Hour:
|
|
PL_strncpy(fmtT, "%H:%M", NSDATETIME_FORMAT_BUFFER_LEN);
|
|
break;
|
|
default:
|
|
PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN);
|
|
}
|
|
}
|
|
|
|
// generate data/time string
|
|
char *old_locale = setlocale(LC_TIME, nullptr);
|
|
(void) setlocale(LC_TIME, mPlatformLocale.get());
|
|
if (strlen(fmtD) && strlen(fmtT)) {
|
|
PL_strncat(fmtD, " ", NSDATETIME_FORMAT_BUFFER_LEN);
|
|
PL_strncat(fmtD, fmtT, NSDATETIME_FORMAT_BUFFER_LEN);
|
|
strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN, fmtD, tmTime);
|
|
}
|
|
else if (strlen(fmtD) && !strlen(fmtT)) {
|
|
strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN, fmtD, tmTime);
|
|
}
|
|
else if (!strlen(fmtD) && strlen(fmtT)) {
|
|
strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN, fmtT, tmTime);
|
|
}
|
|
else {
|
|
PL_strncpy(strOut, "", NSDATETIME_FORMAT_BUFFER_LEN);
|
|
}
|
|
(void) setlocale(LC_TIME, old_locale);
|
|
|
|
// convert result to unicode
|
|
int32_t srcLength = (int32_t) strlen(strOut);
|
|
int32_t unicharLength = NSDATETIME_FORMAT_BUFFER_LEN*2;
|
|
char16_t unichars[NSDATETIME_FORMAT_BUFFER_LEN*2]; // buffer for date and time
|
|
|
|
rv = mDecoder->Convert(strOut, &srcLength, unichars, &unicharLength);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
stringOut.Assign(unichars, unicharLength);
|
|
|
|
return rv;
|
|
}
|
|
|
|
// performs a locale sensitive date formatting operation on the PRTime parameter
|
|
nsresult nsDateTimeFormatUnix::FormatPRTime(nsILocale* locale,
|
|
const nsDateFormatSelector dateFormatSelector,
|
|
const nsTimeFormatSelector timeFormatSelector,
|
|
const PRTime prTime,
|
|
nsAString& stringOut)
|
|
{
|
|
PRExplodedTime explodedTime;
|
|
PR_ExplodeTime(prTime, PR_LocalTimeParameters, &explodedTime);
|
|
|
|
return FormatPRExplodedTime(locale, dateFormatSelector, timeFormatSelector, &explodedTime, stringOut);
|
|
}
|
|
|
|
// performs a locale sensitive date formatting operation on the PRExplodedTime parameter
|
|
nsresult nsDateTimeFormatUnix::FormatPRExplodedTime(nsILocale* locale,
|
|
const nsDateFormatSelector dateFormatSelector,
|
|
const nsTimeFormatSelector timeFormatSelector,
|
|
const PRExplodedTime* explodedTime,
|
|
nsAString& stringOut)
|
|
{
|
|
struct tm tmTime;
|
|
/* be safe and set all members of struct tm to zero
|
|
*
|
|
* there are other fields in the tm struct that we aren't setting
|
|
* (tm_isdst, tm_gmtoff, tm_zone, should we set these?) and since
|
|
* tmTime is on the stack, it may be filled with garbage, but
|
|
* the garbage may vary. (this may explain why some saw bug #10412, and
|
|
* others did not.
|
|
*
|
|
* when tmTime is passed to strftime() with garbage bad things may happen.
|
|
* see bug #10412
|
|
*/
|
|
memset( &tmTime, 0, sizeof(tmTime) );
|
|
|
|
tmTime.tm_yday = explodedTime->tm_yday;
|
|
tmTime.tm_wday = explodedTime->tm_wday;
|
|
tmTime.tm_year = explodedTime->tm_year;
|
|
tmTime.tm_year -= 1900;
|
|
tmTime.tm_mon = explodedTime->tm_month;
|
|
tmTime.tm_mday = explodedTime->tm_mday;
|
|
tmTime.tm_hour = explodedTime->tm_hour;
|
|
tmTime.tm_min = explodedTime->tm_min;
|
|
tmTime.tm_sec = explodedTime->tm_sec;
|
|
|
|
return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut);
|
|
}
|
|
|