/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Pierre Phaneuf * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsIServiceManager.h" #include "nsDateTimeFormatMac.h" #include #include #include #include #include #include "nsIComponentManager.h" #include "nsLocaleCID.h" #include "nsILocaleService.h" #include "nsIPlatformCharset.h" #include "nsIMacLocale.h" #include "nsCRT.h" #include "plstr.h" #include "prmem.h" #include "nsUnicharUtils.h" //////////////////////////////////////////////////////////////////////////////// static Intl1Hndl GetItl1Resource(short scriptcode, short regioncode) { long itl1num; if (smRoman == scriptcode) { itl1num = regioncode; // if smRoman, use regioncode to differenciate } else { // get itlb from currenty system script ItlbRecord **ItlbRecordHandle; ItlbRecordHandle = (ItlbRecord **)::GetResource('itlb', scriptcode); // get itl1 number from itlb resource, if possible // otherwise, use the one return from script manager, // (Script manager won't update itl1 number when the change on the fly ) if(ItlbRecordHandle != NULL) { if(*ItlbRecordHandle == NULL) ::LoadResource((Handle)ItlbRecordHandle); if(*ItlbRecordHandle != NULL) itl1num = (*ItlbRecordHandle)->itlbDate; else itl1num = ::GetScriptVariable(scriptcode, smScriptDate); } else { // Use this as fallback itl1num = ::GetScriptVariable(scriptcode, smScriptDate); } } // get itl1 resource Intl1Hndl Itl1RecordHandle; Itl1RecordHandle = (Intl1Hndl)::GetResource('itl1', itl1num); NS_ASSERTION(Itl1RecordHandle, "failed to get itl1 handle"); return Itl1RecordHandle; } static Intl0Hndl GetItl0Resource(short scriptcode, short regioncode) { long itl0num; if (smRoman == scriptcode) { itl0num = regioncode; // if smRoman, use regioncode to differenciate } else { // get itlb from currenty system script ItlbRecord **ItlbRecordHandle; ItlbRecordHandle = (ItlbRecord **)::GetResource('itlb', scriptcode); // get itl0 number from itlb resource, if possible // otherwise, use the one return from script manager, // (Script manager won't update itl1 number when the change on the fly ) if(ItlbRecordHandle != NULL) { if(*ItlbRecordHandle == NULL) ::LoadResource((Handle)ItlbRecordHandle); if(*ItlbRecordHandle != NULL) itl0num = (*ItlbRecordHandle)->itlbNumber; else itl0num = ::GetScriptVariable(scriptcode, smScriptNumber); } else { // Use this as fallback itl0num = ::GetScriptVariable(scriptcode, smScriptNumber); } } // get itl0 resource Intl0Hndl Itl0RecordHandle; Itl0RecordHandle = (Intl0Hndl)::GetResource('itl0', itl0num); NS_ASSERTION(Itl0RecordHandle, "failed to get itl0 handle"); return Itl0RecordHandle; } static void AbbrevWeekdayString(DateTimeRec &dateTime, Str255 weekdayString, Intl1Hndl Itl1RecordHandle ) { Boolean gotit = false; // If we can get itl1Resource, exam it. if(Itl1RecordHandle != NULL ) { if(*Itl1RecordHandle == NULL) ::LoadResource((Handle)Itl1RecordHandle); if(*Itl1RecordHandle == NULL) { weekdayString[0] = 0; return; } // if itl1 resource is in the itl1ExtRec format // look at the additional table // See IM-Text Appendix B for details if((unsigned short)((*Itl1RecordHandle)->localRtn[0]) == 0xA89F) { // use itl1ExtRect Itl1ExtRec **Itl1ExtRecHandle; Itl1ExtRecHandle = (Itl1ExtRec **) Itl1RecordHandle; // check abbrevDaysTableLength and abbrevDaysTableOffset if(((*Itl1ExtRecHandle)->abbrevDaysTableLength != 0) && ((*Itl1ExtRecHandle)->abbrevDaysTableOffset != 0)) { // use the additional table for abbreviation weekday name // Japanese use it. // Start Pointer access to Handle, no HLock since we don't // call any API here. // Be careful when you debug- don't move memory :) Ptr abTablePt; short itemlen; // Ok, change it back to range [0-6] short weekday = dateTime.dayOfWeek - 1; abTablePt = (Ptr)(*Itl1ExtRecHandle); abTablePt += (*Itl1ExtRecHandle)->abbrevDaysTableOffset; // first 2 byte in the table should be the count. itemlen = (short) *((short*)abTablePt); abTablePt += 2; if(weekday < itemlen) { unsigned char len; short i; // iterate till we hit the weekday name we want for(i = 0 ; i < weekday ; i++) { len = *abTablePt; // shift to the next one. don't forget the len byte itself. abTablePt += len + 1; } // Ok, we got it, let's copy it. len = *abTablePt; ::BlockMoveData(abTablePt,&weekdayString[0] ,len+1); gotit = true; } } } // didn't get it. Either it is not in itl1ExtRect format or it don't have // additional abbreviation table. // use itl1Rect instead. if(! gotit) { // get abbreviation length // not the length is not always 3. Some country use longer (say 4) // abbreviation. short abbrLen = (*Itl1RecordHandle)->abbrLen; // Fix Traditional Chinese problem if(((((*Itl1RecordHandle)->intl1Vers) >> 8) == verTaiwan ) && (abbrLen == 4) && ((*Itl1RecordHandle)->days[0][0] == 6) && ((*Itl1RecordHandle)->days[1][0] == 6) && ((*Itl1RecordHandle)->days[2][0] == 6) && ((*Itl1RecordHandle)->days[3][0] == 6) && ((*Itl1RecordHandle)->days[4][0] == 6) && ((*Itl1RecordHandle)->days[5][0] == 6) && ((*Itl1RecordHandle)->days[6][0] == 6)) { abbrLen = 6; } weekdayString[0] = abbrLen; // copy the weekday name with that abbreviation length ::BlockMoveData(&((*Itl1RecordHandle)->days[dateTime.dayOfWeek-1][1]), &weekdayString[1] , abbrLen); gotit = true; } } else { // cannot get itl1 resource, return with null string. weekdayString[0] = 0; } } //////////////////////////////////////////////////////////////////////////////// NS_IMPL_THREADSAFE_ISUPPORTS1(nsDateTimeFormatMac, nsIDateTimeFormat) nsresult nsDateTimeFormatMac::Initialize(nsILocale* locale) { nsAutoString localeStr; nsAutoString category(NS_LITERAL_STRING("NSILOCALE_TIME")); nsresult res; // use cached info if match with stored locale if (nsnull == locale) { if (!mLocale.IsEmpty() && mLocale.Equals(mAppLocale, nsCaseInsensitiveStringComparator())) { return NS_OK; } } else { res = locale->GetCategory(category, localeStr); if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) { if (!mLocale.IsEmpty() && mLocale.Equals(localeStr, nsCaseInsensitiveStringComparator())) { return NS_OK; } } } mScriptcode = smSystemScript; mLangcode = langEnglish; mRegioncode = verUS; mCharset.AssignLiteral("x-mac-roman"); // get application locale nsCOMPtr localeService = do_GetService(NS_LOCALESERVICE_CONTRACTID, &res); if (NS_SUCCEEDED(res)) { nsCOMPtr appLocale; res = localeService->GetApplicationLocale(getter_AddRefs(appLocale)); if (NS_SUCCEEDED(res)) { res = appLocale->GetCategory(category, localeStr); if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) { mAppLocale = localeStr; // cache app locale name } } } // use app default if no locale specified if (nsnull == locale) { mUseDefaultLocale = true; } else { mUseDefaultLocale = false; res = locale->GetCategory(category, localeStr); } // Get a script code and charset name from locale, if available if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) { mLocale.Assign(localeStr); // cache locale name nsCOMPtr macLocale = do_GetService(NS_MACLOCALE_CONTRACTID, &res); if (NS_SUCCEEDED(res)) { res = macLocale->GetPlatformLocale(mLocale, &mScriptcode, &mLangcode, &mRegioncode); } nsCOMPtr platformCharset = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &res); if (NS_SUCCEEDED(res)) { nsCAutoString mappedCharset; res = platformCharset->GetDefaultCharsetForLocale(mLocale, mappedCharset); if (NS_SUCCEEDED(res)) { mCharset = mappedCharset; } } } // Initialize unicode decoder nsCOMPtr charsetAtom; nsCOMPtr charsetConverterManager; charsetConverterManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &res); if (NS_SUCCEEDED(res)) { res = charsetConverterManager->GetUnicodeDecoder(mCharset.get(), getter_AddRefs(mDecoder)); } return res; } // performs a locale sensitive date formatting operation on the time_t parameter nsresult nsDateTimeFormatMac::FormatTime(nsILocale* locale, const nsDateFormatSelector dateFormatSelector, const nsTimeFormatSelector timeFormatSelector, const time_t timetTime, nsAString& stringOut) { return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, localtime(&timetTime), stringOut); } #if !TARGET_CARBON static void CopyPascalStringToC(StringPtr aSrc, char* aDest) { int len = aSrc[0]; ::BlockMoveData(aSrc + 1, aDest, len); aDest[len] = '\0'; } #endif // performs a locale sensitive date formatting operation on the struct tm parameter nsresult nsDateTimeFormatMac::FormatTMTime(nsILocale* locale, const nsDateFormatSelector dateFormatSelector, const nsTimeFormatSelector timeFormatSelector, const struct tm* tmTime, nsAString& stringOut) { DateTimeRec macDateTime; Str255 timeString, dateString; int32 dateTime; nsresult res; // set up locale data (void) Initialize(locale); // return, nothing to format if (dateFormatSelector == kDateFormatNone && timeFormatSelector == kTimeFormatNone) { stringOut.Truncate(); return NS_OK; } // set the default string, in case for API/conversion errors CopyASCIItoUTF16(nsDependentCString(asctime(tmTime)), stringOut); // convert struct tm to input format of mac toolbox call NS_ASSERTION(tmTime->tm_mon >= 0, "tm is not set correctly"); NS_ASSERTION(tmTime->tm_mday >= 1, "tm is not set correctly"); NS_ASSERTION(tmTime->tm_hour >= 0, "tm is not set correctly"); NS_ASSERTION(tmTime->tm_min >= 0, "tm is not set correctly"); NS_ASSERTION(tmTime->tm_sec >= 0, "tm is not set correctly"); NS_ASSERTION(tmTime->tm_wday >= 0, "tm is not set correctly"); macDateTime.year = tmTime->tm_year + 1900; // Mac use 1 for Jan and 12 for Dec // tm use 0 for Jan and 11 for Dec macDateTime.month = tmTime->tm_mon + 1; macDateTime.day = tmTime->tm_mday; macDateTime.hour = tmTime->tm_hour; macDateTime.minute = tmTime->tm_min; macDateTime.second = tmTime->tm_sec; // Mac use 1 for sunday 7 for saturday // tm use 0 for sunday 6 for saturday macDateTime.dayOfWeek = tmTime->tm_wday +1 ; ::DateToSeconds( &macDateTime, (unsigned long *) &dateTime); // specify itl if not using a default locale Handle itl1Handle = mUseDefaultLocale ? nil : (Handle) GetItl1Resource(mScriptcode, mRegioncode); Handle itl0Handle = mUseDefaultLocale ? nil : (Handle) GetItl0Resource(mScriptcode, mRegioncode); // get time string if (timeFormatSelector != kTimeFormatNone) { // modify itl0 to force 24 hour time cycle ! if ( itl0Handle && (timeFormatSelector == kTimeFormatSecondsForce24Hour || timeFormatSelector == kTimeFormatNoSecondsForce24Hour)) { Intl0Hndl itl0HandleToModify = (Intl0Hndl) itl0Handle; UInt8 timeCycle = (**itl0HandleToModify).timeCycle; (**itl0HandleToModify).timeCycle = timeCycle24; ::TimeString(dateTime, (timeFormatSelector == kTimeFormatSeconds), timeString, itl0Handle); (**itl0HandleToModify).timeCycle = timeCycle; } else { ::TimeString(dateTime, (timeFormatSelector == kTimeFormatSeconds), timeString, itl0Handle); } } // get date string switch (dateFormatSelector) { case kDateFormatLong: ::DateString(dateTime, abbrevDate, dateString, itl1Handle); break; case kDateFormatShort: ::DateString(dateTime, shortDate, dateString, itl0Handle); break; case kDateFormatYearMonth: dateString[0] = strftime((char*)&dateString[1],254,"%y/%m",tmTime); break; case kDateFormatWeekday: AbbrevWeekdayString(macDateTime, dateString, (Intl1Hndl)itl1Handle); // try fallback if it return with null string. if(dateString[0] == 0) { // cannot get weekdayString from itl1 , try fallback dateString[0] = strftime((char*)&dateString[1],254,"%a",tmTime); } break; } // construct a C string char localBuffer[2 * 255 + 2 + 1]; // 2 255-byte-max pascal strings, plus // a space and a null. if (dateFormatSelector != kDateFormatNone && timeFormatSelector != kTimeFormatNone) { CopyPascalStringToC(dateString, localBuffer); localBuffer[dateString[0]] = ' '; CopyPascalStringToC(timeString, &(localBuffer[dateString[0] + 1])); } else if (dateFormatSelector != kDateFormatNone) { CopyPascalStringToC(dateString, localBuffer); } else if (timeFormatSelector != kTimeFormatNone) { CopyPascalStringToC(timeString, localBuffer); } // convert result to unicode if (mDecoder) { PRInt32 srcLength = (PRInt32) PL_strlen(localBuffer); PRInt32 unicharLength = sizeof(Str255)*2; PRUnichar unichars[sizeof(Str255)*2]; // buffer for combined date and time res = mDecoder->Convert(localBuffer, &srcLength, unichars, &unicharLength); if (NS_SUCCEEDED(res)) { stringOut.Assign(unichars, unicharLength); } } return res; } // performs a locale sensitive date formatting operation on the PRTime parameter nsresult nsDateTimeFormatMac::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 nsDateTimeFormatMac::FormatPRExplodedTime(nsILocale* locale, const nsDateFormatSelector dateFormatSelector, const nsTimeFormatSelector timeFormatSelector, const PRExplodedTime* explodedTime, nsAString& stringOut) { struct tm tmTime; 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); }