Back to index

lightning-sunbird  0.9+nobinonly
nsDateTimeFormatMac.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsIServiceManager.h"
00040 #include "nsDateTimeFormatMac.h"
00041 #include <Resources.h>
00042 #include <IntlResources.h>
00043 #include <DateTimeUtils.h>
00044 #include <Script.h>
00045 #include <TextUtils.h>
00046 #include "nsIComponentManager.h"
00047 #include "nsLocaleCID.h"
00048 #include "nsILocaleService.h"
00049 #include "nsIPlatformCharset.h"
00050 #include "nsIMacLocale.h"
00051 #include "nsCRT.h"
00052 #include "plstr.h"
00053 #include "prmem.h"
00054 #include "nsUnicharUtils.h"
00055 
00057 
00058 static Intl1Hndl GetItl1Resource(short scriptcode, short regioncode)
00059 {
00060        long itl1num;
00061 
00062        if (smRoman == scriptcode)
00063        {
00064               itl1num = regioncode;       // if smRoman, use regioncode to differenciate
00065        } else {
00066               // get itlb from currenty system script
00067               ItlbRecord **ItlbRecordHandle;
00068               ItlbRecordHandle = (ItlbRecord **)::GetResource('itlb', scriptcode);
00069               
00070               // get itl1 number from itlb resource, if possible
00071               // otherwise, use the one return from script manager, 
00072               // (Script manager won't update itl1 number when the change on the fly )
00073               if(ItlbRecordHandle != NULL)
00074               {
00075                      if(*ItlbRecordHandle == NULL)
00076                             ::LoadResource((Handle)ItlbRecordHandle);
00077                             
00078                      if(*ItlbRecordHandle != NULL)
00079                             itl1num = (*ItlbRecordHandle)->itlbDate;
00080                      else
00081                             itl1num = ::GetScriptVariable(scriptcode, smScriptDate);
00082               } else {      // Use this as fallback
00083                      itl1num = ::GetScriptVariable(scriptcode, smScriptDate);
00084               }
00085        }
00086        
00087        // get itl1 resource 
00088        Intl1Hndl Itl1RecordHandle;
00089        Itl1RecordHandle = (Intl1Hndl)::GetResource('itl1', itl1num);
00090        NS_ASSERTION(Itl1RecordHandle, "failed to get itl1 handle");
00091        return Itl1RecordHandle;
00092 }
00093 
00094 static Intl0Hndl GetItl0Resource(short scriptcode, short regioncode)
00095 {
00096        long itl0num;
00097 
00098        if (smRoman == scriptcode)
00099        {
00100               itl0num = regioncode;       // if smRoman, use regioncode to differenciate
00101        } else {
00102               // get itlb from currenty system script
00103               ItlbRecord **ItlbRecordHandle;
00104               ItlbRecordHandle = (ItlbRecord **)::GetResource('itlb', scriptcode);
00105               
00106               // get itl0 number from itlb resource, if possible
00107               // otherwise, use the one return from script manager, 
00108               // (Script manager won't update itl1 number when the change on the fly )
00109               if(ItlbRecordHandle != NULL)
00110               {
00111                      if(*ItlbRecordHandle == NULL)
00112                             ::LoadResource((Handle)ItlbRecordHandle);
00113                             
00114                      if(*ItlbRecordHandle != NULL)
00115                             itl0num = (*ItlbRecordHandle)->itlbNumber;
00116                      else
00117                             itl0num = ::GetScriptVariable(scriptcode, smScriptNumber);
00118               } else {      // Use this as fallback
00119                      itl0num = ::GetScriptVariable(scriptcode, smScriptNumber);
00120               }
00121        }
00122        
00123        // get itl0 resource 
00124        Intl0Hndl Itl0RecordHandle;
00125        Itl0RecordHandle = (Intl0Hndl)::GetResource('itl0', itl0num);
00126        NS_ASSERTION(Itl0RecordHandle, "failed to get itl0 handle");
00127        return Itl0RecordHandle;
00128 }
00129 
00130 static void AbbrevWeekdayString(DateTimeRec &dateTime, Str255 weekdayString, Intl1Hndl Itl1RecordHandle )
00131 {
00132        Boolean gotit = false;
00133        
00134        // If we can get itl1Resource, exam it.
00135        if(Itl1RecordHandle != NULL )
00136        {
00137               if(*Itl1RecordHandle == NULL)
00138                      ::LoadResource((Handle)Itl1RecordHandle);
00139 
00140               if(*Itl1RecordHandle == NULL)
00141               {
00142                      weekdayString[0] = 0;
00143                      return;
00144               }
00145               // if itl1 resource is in the itl1ExtRec format 
00146               // look at the additional table
00147               // See IM-Text Appendix B for details
00148               if((unsigned short)((*Itl1RecordHandle)->localRtn[0]) == 0xA89F)
00149               {      // use itl1ExtRect
00150                      Itl1ExtRec **Itl1ExtRecHandle;
00151                      Itl1ExtRecHandle = (Itl1ExtRec **) Itl1RecordHandle;
00152                      
00153                      // check abbrevDaysTableLength and abbrevDaysTableOffset
00154                      if(((*Itl1ExtRecHandle)->abbrevDaysTableLength != 0) &&
00155                         ((*Itl1ExtRecHandle)->abbrevDaysTableOffset != 0))
00156                      {      
00157                             // use the additional table for abbreviation weekday name
00158                             // Japanese use it.
00159                             // Start Pointer access to Handle, no HLock since we don't
00160                             // call any API here.
00161                             // Be careful when you debug- don't move memory :)
00162                             Ptr abTablePt;
00163                             short itemlen;
00164                             
00165                             // Ok, change it back to range [0-6]
00166                             short weekday = dateTime.dayOfWeek - 1;   
00167                             
00168                             abTablePt = (Ptr)(*Itl1ExtRecHandle);
00169                             abTablePt +=  (*Itl1ExtRecHandle)->abbrevDaysTableOffset;
00170                             
00171                             // first 2 byte in the table should be the count.
00172                             itemlen = (short) *((short*)abTablePt);   
00173                             abTablePt += 2;      
00174                             
00175                             if(weekday < itemlen)
00176                             {
00177                                    unsigned char len;
00178                                    short i;
00179                                    // iterate till we hit the weekday name we want
00180                                    for(i = 0 ; i < weekday ; i++)     
00181                                    {
00182                                           len = *abTablePt;
00183                                           // shift to the next one. don't forget the len byte itself.
00184                                           abTablePt += len + 1;       
00185                                    }
00186                                    // Ok, we got it, let's copy it.
00187                                    len = *abTablePt;
00188                                    ::BlockMoveData(abTablePt,&weekdayString[0] ,len+1);
00189                                    gotit = true;
00190                             }
00191                      }
00192               }
00193               
00194               // didn't get it. Either it is not in itl1ExtRect format or it don't have
00195               // additional abbreviation table. 
00196               // use itl1Rect instead.
00197               if(! gotit)
00198               {      
00199                      // get abbreviation length
00200                      // not the length is not always 3. Some country use longer (say 4)
00201                      // abbreviation.
00202                      short abbrLen = (*Itl1RecordHandle)->abbrLen;
00203                      // Fix  Traditional Chinese problem
00204                      if(((((*Itl1RecordHandle)->intl1Vers) >> 8) == verTaiwan ) &&
00205                         (abbrLen == 4) &&
00206                         ((*Itl1RecordHandle)->days[0][0] == 6) && 
00207                         ((*Itl1RecordHandle)->days[1][0] == 6) && 
00208                         ((*Itl1RecordHandle)->days[2][0] == 6) && 
00209                         ((*Itl1RecordHandle)->days[3][0] == 6) && 
00210                         ((*Itl1RecordHandle)->days[4][0] == 6) && 
00211                         ((*Itl1RecordHandle)->days[5][0] == 6) && 
00212                         ((*Itl1RecordHandle)->days[6][0] == 6))
00213                      {
00214                             abbrLen = 6;
00215                      }
00216                      weekdayString[0] = abbrLen;
00217                      // copy the weekday name with that abbreviation length
00218                      ::BlockMoveData(&((*Itl1RecordHandle)->days[dateTime.dayOfWeek-1][1]),
00219                              &weekdayString[1] , abbrLen);
00220                      gotit = true;
00221               }
00222        }
00223        else 
00224        {      // cannot get itl1 resource, return with null string.
00225               weekdayString[0] = 0;
00226        }
00227 }
00228 
00229 
00231 
00232 
00233 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDateTimeFormatMac, nsIDateTimeFormat)
00234 
00235 nsresult nsDateTimeFormatMac::Initialize(nsILocale* locale)
00236 {
00237   nsAutoString localeStr;
00238   nsAutoString category(NS_LITERAL_STRING("NSILOCALE_TIME"));
00239   nsresult res;
00240 
00241   // use cached info if match with stored locale
00242   if (nsnull == locale) {
00243     if (!mLocale.IsEmpty() &&
00244         mLocale.Equals(mAppLocale, nsCaseInsensitiveStringComparator())) {
00245       return NS_OK;
00246     }
00247   }
00248   else {
00249     res = locale->GetCategory(category, localeStr);
00250     if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
00251       if (!mLocale.IsEmpty() &&
00252           mLocale.Equals(localeStr,
00253                          nsCaseInsensitiveStringComparator())) {
00254         return NS_OK;
00255       }
00256     }
00257   }
00258 
00259   mScriptcode = smSystemScript;
00260   mLangcode = langEnglish;
00261   mRegioncode = verUS;
00262   mCharset.AssignLiteral("x-mac-roman");
00263   
00264 
00265   // get application locale
00266   nsCOMPtr<nsILocaleService> localeService = 
00267            do_GetService(NS_LOCALESERVICE_CONTRACTID, &res);
00268   if (NS_SUCCEEDED(res)) {
00269     nsCOMPtr<nsILocale> appLocale;
00270     res = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
00271     if (NS_SUCCEEDED(res)) {
00272       res = appLocale->GetCategory(category, localeStr);
00273       if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
00274         mAppLocale = localeStr; // cache app locale name
00275       }
00276     }
00277   }
00278   
00279   // use app default if no locale specified
00280   if (nsnull == locale) {
00281     mUseDefaultLocale = true;
00282   }
00283   else {
00284     mUseDefaultLocale = false;
00285     res = locale->GetCategory(category, localeStr);
00286   }
00287     
00288   // Get a script code and charset name from locale, if available
00289   if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
00290     mLocale.Assign(localeStr); // cache locale name
00291 
00292     nsCOMPtr <nsIMacLocale> macLocale = do_GetService(NS_MACLOCALE_CONTRACTID, &res);
00293     if (NS_SUCCEEDED(res)) {
00294       res = macLocale->GetPlatformLocale(mLocale, &mScriptcode, &mLangcode, &mRegioncode);
00295     }
00296 
00297     nsCOMPtr <nsIPlatformCharset> platformCharset = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &res);
00298     if (NS_SUCCEEDED(res)) {
00299       nsCAutoString  mappedCharset;
00300       res = platformCharset->GetDefaultCharsetForLocale(mLocale, mappedCharset);
00301       if (NS_SUCCEEDED(res)) {
00302         mCharset = mappedCharset;
00303       }
00304     }
00305   }
00306 
00307   // Initialize unicode decoder
00308   nsCOMPtr <nsIAtom>                      charsetAtom;
00309   nsCOMPtr <nsICharsetConverterManager>  charsetConverterManager;
00310   charsetConverterManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &res);
00311   if (NS_SUCCEEDED(res)) {
00312     res = charsetConverterManager->GetUnicodeDecoder(mCharset.get(),
00313                                                      getter_AddRefs(mDecoder));
00314   }
00315   
00316   return res;
00317 }
00318 
00319 // performs a locale sensitive date formatting operation on the time_t parameter
00320 nsresult nsDateTimeFormatMac::FormatTime(nsILocale* locale, 
00321                                       const nsDateFormatSelector  dateFormatSelector, 
00322                                       const nsTimeFormatSelector timeFormatSelector, 
00323                                       const time_t  timetTime, 
00324                                       nsString& stringOut)
00325 {
00326   return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, localtime(&timetTime), stringOut);
00327 }
00328 
00329 #if !TARGET_CARBON
00330 static void CopyPascalStringToC(StringPtr aSrc, char* aDest)
00331 {
00332   int len = aSrc[0];
00333   ::BlockMoveData(aSrc + 1, aDest, len);
00334   aDest[len] = '\0';
00335 }
00336 #endif
00337 
00338 // performs a locale sensitive date formatting operation on the struct tm parameter
00339 nsresult nsDateTimeFormatMac::FormatTMTime(nsILocale* locale, 
00340                                            const nsDateFormatSelector  dateFormatSelector, 
00341                                            const nsTimeFormatSelector timeFormatSelector, 
00342                                            const struct tm*  tmTime, 
00343                                            nsString& stringOut)
00344 {
00345   DateTimeRec macDateTime;
00346   Str255 timeString, dateString;
00347   int32 dateTime;    
00348   nsresult res;
00349 
00350   // set up locale data
00351   (void) Initialize(locale);
00352   
00353   // return, nothing to format
00354   if (dateFormatSelector == kDateFormatNone && timeFormatSelector == kTimeFormatNone) {
00355     stringOut.SetLength(0);
00356     return NS_OK;
00357   }
00358 
00359   stringOut.AssignWithConversion(asctime(tmTime));      // set the default string, in case for API/conversion errors
00360   
00361   // convert struct tm to input format of mac toolbox call
00362   NS_ASSERTION(tmTime->tm_mon >= 0, "tm is not set correctly");
00363   NS_ASSERTION(tmTime->tm_mday >= 1, "tm is not set correctly");
00364   NS_ASSERTION(tmTime->tm_hour >= 0, "tm is not set correctly");
00365   NS_ASSERTION(tmTime->tm_min >= 0, "tm is not set correctly");
00366   NS_ASSERTION(tmTime->tm_sec >= 0, "tm is not set correctly");
00367   NS_ASSERTION(tmTime->tm_wday >= 0, "tm is not set correctly");
00368 
00369   macDateTime.year = tmTime->tm_year + 1900;     
00370 
00371   // Mac use 1 for Jan and 12 for Dec
00372   // tm use 0 for Jan and 11 for Dec 
00373   macDateTime.month = tmTime->tm_mon + 1; 
00374   macDateTime.day = tmTime->tm_mday;
00375   macDateTime.hour = tmTime->tm_hour;
00376   macDateTime.minute = tmTime->tm_min;
00377   macDateTime.second = tmTime->tm_sec;
00378 
00379   // Mac use 1 for sunday 7 for saturday
00380   // tm use 0 for sunday 6 for saturday 
00381   macDateTime.dayOfWeek = tmTime->tm_wday +1 ; 
00382 
00383   ::DateToSeconds( &macDateTime, (unsigned long *) &dateTime);
00384   
00385   // specify itl if not using a default locale
00386   Handle itl1Handle = mUseDefaultLocale ? nil : (Handle) GetItl1Resource(mScriptcode, mRegioncode);
00387   Handle itl0Handle = mUseDefaultLocale ? nil : (Handle) GetItl0Resource(mScriptcode, mRegioncode);
00388 
00389   // get time string
00390   if (timeFormatSelector != kTimeFormatNone) {
00391     // modify itl0 to force 24 hour time cycle !
00392     if ( itl0Handle &&
00393        (timeFormatSelector == kTimeFormatSecondsForce24Hour || 
00394         timeFormatSelector == kTimeFormatNoSecondsForce24Hour)) {
00395       Intl0Hndl itl0HandleToModify = (Intl0Hndl) itl0Handle;
00396       UInt8 timeCycle = (**itl0HandleToModify).timeCycle;
00397       (**itl0HandleToModify).timeCycle = timeCycle24;
00398       ::TimeString(dateTime, (timeFormatSelector == kTimeFormatSeconds), timeString, itl0Handle);
00399       (**itl0HandleToModify).timeCycle = timeCycle;
00400     }
00401     else {
00402       ::TimeString(dateTime, (timeFormatSelector == kTimeFormatSeconds), timeString, itl0Handle);
00403     }
00404   }
00405   
00406   // get date string
00407   switch (dateFormatSelector) {
00408     case kDateFormatLong:
00409       ::DateString(dateTime, abbrevDate, dateString, itl1Handle);
00410       break;
00411     case kDateFormatShort:
00412       ::DateString(dateTime, shortDate, dateString, itl0Handle);
00413       break;
00414     case kDateFormatYearMonth:
00415       dateString[0] =  strftime((char*)&dateString[1],254,"%y/%m",tmTime);
00416       break;
00417     case kDateFormatWeekday:
00418       AbbrevWeekdayString(macDateTime, dateString, (Intl1Hndl)itl1Handle);
00419       // try fallback if it return with null string.
00420       if(dateString[0] == 0) {     //     cannot get weekdayString from itl1 , try fallback
00421         dateString[0] =  strftime((char*)&dateString[1],254,"%a",tmTime);
00422       }
00423       break;
00424   }
00425   
00426   // construct a C string
00427   char localBuffer[2 * 255 + 2 + 1]; // 2 255-byte-max pascal strings, plus
00428                                      // a space and a null.
00429   if (dateFormatSelector != kDateFormatNone && timeFormatSelector != kTimeFormatNone) {
00430     CopyPascalStringToC(dateString, localBuffer);
00431     localBuffer[dateString[0]] = ' ';
00432     CopyPascalStringToC(timeString, &(localBuffer[dateString[0] + 1]));
00433   }
00434   else if (dateFormatSelector != kDateFormatNone) {
00435     CopyPascalStringToC(dateString, localBuffer);
00436   }
00437   else if (timeFormatSelector != kTimeFormatNone) {
00438     CopyPascalStringToC(timeString, localBuffer);
00439   }
00440 
00441   // convert result to unicode
00442   if (mDecoder) {
00443     PRInt32 srcLength = (PRInt32) PL_strlen(localBuffer);
00444     PRInt32 unicharLength = sizeof(Str255)*2;
00445     PRUnichar unichars[sizeof(Str255)*2];   // buffer for combined date and time
00446 
00447     res = mDecoder->Convert(localBuffer, &srcLength, unichars, &unicharLength);
00448     if (NS_SUCCEEDED(res)) {
00449       stringOut.Assign(unichars, unicharLength);
00450     }
00451   }
00452   
00453   return res;
00454 }
00455 
00456 // performs a locale sensitive date formatting operation on the PRTime parameter
00457 nsresult nsDateTimeFormatMac::FormatPRTime(nsILocale* locale, 
00458                                            const nsDateFormatSelector  dateFormatSelector, 
00459                                            const nsTimeFormatSelector timeFormatSelector, 
00460                                            const PRTime  prTime, 
00461                                            nsString& stringOut)
00462 {
00463   PRExplodedTime explodedTime;
00464   PR_ExplodeTime(prTime, PR_LocalTimeParameters, &explodedTime);
00465 
00466   return FormatPRExplodedTime(locale, dateFormatSelector, timeFormatSelector, &explodedTime, stringOut);
00467 }
00468 
00469 // performs a locale sensitive date formatting operation on the PRExplodedTime parameter
00470 nsresult nsDateTimeFormatMac::FormatPRExplodedTime(nsILocale* locale, 
00471                                                    const nsDateFormatSelector  dateFormatSelector, 
00472                                                    const nsTimeFormatSelector timeFormatSelector, 
00473                                                    const PRExplodedTime*  explodedTime, 
00474                                                    nsString& stringOut)
00475 {
00476   struct tm  tmTime;
00477   memset( &tmTime, 0, sizeof(tmTime) );
00478 
00479   tmTime.tm_yday = explodedTime->tm_yday;
00480   tmTime.tm_wday = explodedTime->tm_wday;
00481   tmTime.tm_year = explodedTime->tm_year;
00482   tmTime.tm_year -= 1900;
00483   tmTime.tm_mon = explodedTime->tm_month;
00484   tmTime.tm_mday = explodedTime->tm_mday;
00485   tmTime.tm_hour = explodedTime->tm_hour;
00486   tmTime.tm_min = explodedTime->tm_min;
00487   tmTime.tm_sec = explodedTime->tm_sec;
00488 
00489   return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut);
00490 }
00491