diff options
Diffstat (limited to 'intl/locale/unix/nsDateTimeFormatUnix.cpp')
-rw-r--r-- | intl/locale/unix/nsDateTimeFormatUnix.cpp | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/intl/locale/unix/nsDateTimeFormatUnix.cpp b/intl/locale/unix/nsDateTimeFormatUnix.cpp new file mode 100644 index 000000000..a70b54102 --- /dev/null +++ b/intl/locale/unix/nsDateTimeFormatUnix.cpp @@ -0,0 +1,284 @@ +/* -*- 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); +} + |