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