Back to index

lightning-sunbird  0.9+nobinonly
nsDateTimeFormatUnix.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 <locale.h>
00040 #include "plstr.h"
00041 #include "nsIServiceManager.h"
00042 #include "nsDateTimeFormatUnix.h"
00043 #include "nsIComponentManager.h"
00044 #include "nsLocaleCID.h"
00045 #include "nsILocaleService.h"
00046 #include "nsIPlatformCharset.h"
00047 #include "nsIPosixLocale.h"
00048 #include "nsCRT.h"
00049 #include "nsReadableUtils.h"
00050 #include "nsUnicharUtils.h"
00051 
00052 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDateTimeFormatUnix, nsIDateTimeFormat)
00053 
00054 // init this interface to a specified locale
00055 nsresult nsDateTimeFormatUnix::Initialize(nsILocale* locale)
00056 {
00057   nsAutoString localeStr;
00058   NS_NAMED_LITERAL_STRING(aCategory, "NSILOCALE_TIME##PLATFORM");
00059   nsresult res = NS_OK;
00060 
00061   // use cached info if match with stored locale
00062   if (NULL == locale) {
00063     if (!mLocale.IsEmpty() &&
00064         mLocale.Equals(mAppLocale, nsCaseInsensitiveStringComparator())) {
00065       return NS_OK;
00066     }
00067   }
00068   else {
00069     res = locale->GetCategory(aCategory, localeStr);
00070     if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
00071       if (!mLocale.IsEmpty() &&
00072           mLocale.Equals(localeStr,
00073                          nsCaseInsensitiveStringComparator())) {
00074         return NS_OK;
00075       }
00076     }
00077   }
00078 
00079   mCharset.AssignLiteral("ISO-8859-1");
00080   mPlatformLocale.Assign("en_US");
00081 
00082   // get locale name string, use app default if no locale specified
00083   if (NULL == locale) {
00084     nsCOMPtr<nsILocaleService> localeService = 
00085              do_GetService(NS_LOCALESERVICE_CONTRACTID, &res);
00086     if (NS_SUCCEEDED(res)) {
00087       nsCOMPtr<nsILocale> appLocale;
00088       res = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
00089       if (NS_SUCCEEDED(res)) {
00090         res = appLocale->GetCategory(aCategory, localeStr);
00091         if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
00092           NS_ASSERTION(NS_SUCCEEDED(res), "failed to get app locale info");
00093           mAppLocale = localeStr; // cache app locale name
00094         }
00095       }
00096     }
00097   }
00098   else {
00099     res = locale->GetCategory(aCategory, localeStr);
00100     NS_ASSERTION(NS_SUCCEEDED(res), "failed to get locale info");
00101   }
00102 
00103   if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
00104     mLocale = localeStr; // cache locale name
00105 
00106     nsCOMPtr <nsIPosixLocale> posixLocale = do_GetService(NS_POSIXLOCALE_CONTRACTID, &res);
00107     if (NS_SUCCEEDED(res)) {
00108       res = posixLocale->GetPlatformLocale(mLocale, mPlatformLocale);
00109     }
00110 
00111     nsCOMPtr <nsIPlatformCharset> platformCharset = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &res);
00112     if (NS_SUCCEEDED(res)) {
00113       nsCAutoString mappedCharset;
00114       res = platformCharset->GetDefaultCharsetForLocale(mLocale, mappedCharset);
00115       if (NS_SUCCEEDED(res)) {
00116         mCharset = mappedCharset;
00117       }
00118     }
00119   }
00120 
00121   // Initialize unicode decoder
00122   nsCOMPtr <nsICharsetConverterManager>  charsetConverterManager;
00123   charsetConverterManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &res);
00124   if (NS_SUCCEEDED(res)) {
00125     res = charsetConverterManager->GetUnicodeDecoder(mCharset.get(), getter_AddRefs(mDecoder));
00126   }
00127 
00128   LocalePreferred24hour();
00129 
00130   return res;
00131 }
00132 
00133 void nsDateTimeFormatUnix::LocalePreferred24hour()
00134 {
00135   char str[100];
00136   time_t tt;
00137   struct tm *tmc;
00138   int i;
00139 
00140   tt = time((time_t)NULL);
00141   tmc = localtime(&tt);
00142 
00143   tmc->tm_hour=22;    // put the test sample hour to 22:00 which is 10PM
00144   tmc->tm_min=0;      // set the min & sec other number than '2'
00145   tmc->tm_sec=0;
00146 
00147   char *temp = setlocale(LC_TIME, mPlatformLocale.get());
00148   strftime(str, (size_t)99, "%X", (struct tm *)tmc);
00149 
00150   (void) setlocale(LC_TIME, temp);
00151 
00152   mLocalePreferred24hour = PR_FALSE;
00153   for (i=0; str[i]; i++) {
00154     if (str[i] == '2') {    // if there is any '2', that locale use 0-23 time format
00155         mLocalePreferred24hour = PR_TRUE;
00156         break;
00157     }
00158   }
00159 
00160   mLocaleAMPMfirst = PR_TRUE;
00161   if (mLocalePreferred24hour == PR_FALSE) {
00162     if (str[0] && str[0] == '1') { // if the first character is '1' of 10:00,
00163                                 // AMPM string is located after 10:00
00164       mLocaleAMPMfirst = PR_FALSE;
00165     }
00166   }
00167 }
00168 
00169 nsresult nsDateTimeFormatUnix::FormatTime(nsILocale* locale, 
00170                                       const nsDateFormatSelector  dateFormatSelector, 
00171                                       const nsTimeFormatSelector timeFormatSelector, 
00172                                       const time_t  timetTime, 
00173                                       nsString& stringOut) 
00174 {
00175   struct tm tmTime;
00176   memcpy(&tmTime, localtime(&timetTime), sizeof(struct tm));
00177   return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut);
00178 }
00179 
00180 // performs a locale sensitive date formatting operation on the struct tm parameter
00181 nsresult nsDateTimeFormatUnix::FormatTMTime(nsILocale* locale, 
00182                                         const nsDateFormatSelector  dateFormatSelector, 
00183                                         const nsTimeFormatSelector timeFormatSelector, 
00184                                         const struct tm*  tmTime, 
00185                                         nsString& stringOut) 
00186 {
00187 #define NSDATETIME_FORMAT_BUFFER_LEN  80
00188   char strOut[NSDATETIME_FORMAT_BUFFER_LEN*2];  // buffer for date and time
00189   char fmtD[NSDATETIME_FORMAT_BUFFER_LEN], fmtT[NSDATETIME_FORMAT_BUFFER_LEN];
00190   nsresult rv;
00191 
00192   
00193   // set up locale data
00194   (void) Initialize(locale);
00195   NS_ENSURE_TRUE(mDecoder, NS_ERROR_NOT_INITIALIZED);
00196 
00197   // set date format
00198   switch (dateFormatSelector) {
00199     case kDateFormatNone:
00200       PL_strncpy(fmtD, "", NSDATETIME_FORMAT_BUFFER_LEN);
00201       break; 
00202     case kDateFormatLong:
00203     case kDateFormatShort:
00204       PL_strncpy(fmtD, "%x", NSDATETIME_FORMAT_BUFFER_LEN);
00205       break; 
00206     case kDateFormatYearMonth:
00207       PL_strncpy(fmtD, "%y/%m", NSDATETIME_FORMAT_BUFFER_LEN);
00208       break; 
00209     case kDateFormatWeekday:
00210       PL_strncpy(fmtD, "%a", NSDATETIME_FORMAT_BUFFER_LEN);
00211       break;
00212     default:
00213       PL_strncpy(fmtD, "", NSDATETIME_FORMAT_BUFFER_LEN); 
00214   }
00215 
00216   // set time format
00217   switch (timeFormatSelector) {
00218     case kTimeFormatNone:
00219       PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN); 
00220       break;
00221     case kTimeFormatSeconds:
00222       PL_strncpy(fmtT, 
00223                  mLocalePreferred24hour ? "%H:%M:%S" : mLocaleAMPMfirst ? "%p %I:%M:%S" : "%I:%M:%S %p", 
00224                  NSDATETIME_FORMAT_BUFFER_LEN);
00225       break;
00226     case kTimeFormatNoSeconds:
00227       PL_strncpy(fmtT, 
00228                  mLocalePreferred24hour ? "%H:%M" : mLocaleAMPMfirst ? "%p %I:%M" : "%I:%M %p", 
00229                  NSDATETIME_FORMAT_BUFFER_LEN);
00230       break;
00231     case kTimeFormatSecondsForce24Hour:
00232       PL_strncpy(fmtT, "%H:%M:%S", NSDATETIME_FORMAT_BUFFER_LEN);
00233       break;
00234     case kTimeFormatNoSecondsForce24Hour:
00235       PL_strncpy(fmtT, "%H:%M", NSDATETIME_FORMAT_BUFFER_LEN);
00236       break;
00237     default:
00238       PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN); 
00239   }
00240 
00241   // generate data/time string
00242   char *old_locale = setlocale(LC_TIME, NULL);
00243   (void) setlocale(LC_TIME, mPlatformLocale.get());
00244   if (PL_strlen(fmtD) && PL_strlen(fmtT)) {
00245     PL_strncat(fmtD, " ", NSDATETIME_FORMAT_BUFFER_LEN);
00246     PL_strncat(fmtD, fmtT, NSDATETIME_FORMAT_BUFFER_LEN);
00247     strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN*2, fmtD, tmTime);
00248   }
00249   else if (PL_strlen(fmtD) && !PL_strlen(fmtT)) {
00250     strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN*2, fmtD, tmTime);
00251   }
00252   else if (!PL_strlen(fmtD) && PL_strlen(fmtT)) {
00253     strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN*2, fmtT, tmTime);
00254   }
00255   else {
00256     PL_strncpy(strOut, "", NSDATETIME_FORMAT_BUFFER_LEN*2);
00257   }
00258   (void) setlocale(LC_TIME, old_locale);
00259 
00260   // convert result to unicode
00261   PRInt32 srcLength = (PRInt32) PL_strlen(strOut);
00262   PRInt32 unicharLength = NSDATETIME_FORMAT_BUFFER_LEN*2;
00263   PRUnichar unichars[NSDATETIME_FORMAT_BUFFER_LEN*2];   // buffer for date and time
00264 
00265   rv = mDecoder->Convert(strOut, &srcLength, unichars, &unicharLength);
00266   if (NS_FAILED(rv))
00267     return rv;
00268   stringOut.Assign(unichars, unicharLength);
00269 
00270   return rv;
00271 }
00272 
00273 // performs a locale sensitive date formatting operation on the PRTime parameter
00274 nsresult nsDateTimeFormatUnix::FormatPRTime(nsILocale* locale, 
00275                                            const nsDateFormatSelector  dateFormatSelector, 
00276                                            const nsTimeFormatSelector timeFormatSelector, 
00277                                            const PRTime  prTime, 
00278                                            nsString& stringOut)
00279 {
00280   PRExplodedTime explodedTime;
00281   PR_ExplodeTime(prTime, PR_LocalTimeParameters, &explodedTime);
00282 
00283   return FormatPRExplodedTime(locale, dateFormatSelector, timeFormatSelector, &explodedTime, stringOut);
00284 }
00285 
00286 // performs a locale sensitive date formatting operation on the PRExplodedTime parameter
00287 nsresult nsDateTimeFormatUnix::FormatPRExplodedTime(nsILocale* locale, 
00288                                                    const nsDateFormatSelector  dateFormatSelector, 
00289                                                    const nsTimeFormatSelector timeFormatSelector, 
00290                                                    const PRExplodedTime*  explodedTime, 
00291                                                    nsString& stringOut)
00292 {
00293   struct tm  tmTime;
00294   /* be safe and set all members of struct tm to zero
00295    *
00296    * there are other fields in the tm struct that we aren't setting
00297    * (tm_isdst, tm_gmtoff, tm_zone, should we set these?) and since
00298    * tmTime is on the stack, it may be filled with garbage, but
00299    * the garbage may vary.  (this may explain why some saw bug #10412, and
00300    * others did not.
00301    *
00302    * when tmTime is passed to strftime() with garbage bad things may happen. 
00303    * see bug #10412
00304    */
00305   memset( &tmTime, 0, sizeof(tmTime) );
00306 
00307   tmTime.tm_yday = explodedTime->tm_yday;
00308   tmTime.tm_wday = explodedTime->tm_wday;
00309   tmTime.tm_year = explodedTime->tm_year;
00310   tmTime.tm_year -= 1900;
00311   tmTime.tm_mon = explodedTime->tm_month;
00312   tmTime.tm_mday = explodedTime->tm_mday;
00313   tmTime.tm_hour = explodedTime->tm_hour;
00314   tmTime.tm_min = explodedTime->tm_min;
00315   tmTime.tm_sec = explodedTime->tm_sec;
00316 
00317   return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut);
00318 }
00319