1 /* 2 * Copyright 2010, Oliver Tappe, zooey@hirschkaefer.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "ICUTimeConversion.h" 8 9 #include <math.h> 10 #include <strings.h> 11 12 #include <unicode/gregocal.h> 13 14 #include <AutoDeleter.h> 15 16 17 namespace BPrivate { 18 namespace Libroot { 19 20 21 ICUTimeConversion::ICUTimeConversion(const ICUTimeData& timeData) 22 : 23 fTimeData(timeData), 24 fDataBridge(NULL), 25 fTimeZone(NULL) 26 { 27 fTimeZoneID[0] = '\0'; 28 } 29 30 31 ICUTimeConversion::~ICUTimeConversion() 32 { 33 delete fTimeZone; 34 } 35 36 37 void 38 ICUTimeConversion::Initialize(TimeConversionDataBridge* dataBridge) 39 { 40 fDataBridge = dataBridge; 41 } 42 43 44 status_t 45 ICUTimeConversion::TZSet(const char* timeZoneID, const char* tz) 46 { 47 bool offsetHasBeenSet = false; 48 49 // The given TZ environment variable's content overrides the default 50 // system timezone. 51 if (tz != NULL) { 52 // If the value given in the TZ env-var starts with a colon, that 53 // value is implementation specific, we expect a full timezone ID. 54 if (*tz == ':') { 55 // nothing to do if the given name matches the current timezone 56 if (strcasecmp(fTimeZoneID, tz + 1) == 0) 57 return B_OK; 58 59 strlcpy(fTimeZoneID, tz + 1, sizeof(fTimeZoneID)); 60 } else { 61 // note timezone name 62 strlcpy(fTimeZoneID, tz, sizeof(fTimeZoneID)); 63 64 // nothing to do if the given name matches the current timezone 65 if (strcasecmp(fTimeZoneID, fDataBridge->addrOfTZName[0]) == 0) 66 return B_OK; 67 68 // parse TZ variable (only <std> and <offset> supported) 69 const char* tzNameEnd = tz; 70 while(isalpha(*tzNameEnd)) 71 ++tzNameEnd; 72 if (*tzNameEnd == '-' || *tzNameEnd == '+') { 73 int hours = 0; 74 int minutes = 0; 75 int seconds = 0; 76 sscanf(tzNameEnd + 1, "%2d:%2d:%2d", &hours, &minutes, 77 &seconds); 78 hours = min_c(24, max_c(0, hours)); 79 minutes = min_c(59, max_c(0, minutes)); 80 seconds = min_c(59, max_c(0, seconds)); 81 82 *fDataBridge->addrOfTimezone = (*tzNameEnd == '-' ? -1 : 1) 83 * (hours * 3600 + minutes * 60 + seconds); 84 offsetHasBeenSet = true; 85 } 86 } 87 } else { 88 // nothing to do if the given name matches the current timezone 89 if (strcasecmp(fTimeZoneID, timeZoneID) == 0) 90 return B_OK; 91 92 strlcpy(fTimeZoneID, timeZoneID, sizeof(fTimeZoneID)); 93 } 94 95 delete fTimeZone; 96 fTimeZone = TimeZone::createTimeZone(fTimeZoneID); 97 if (fTimeZone == NULL) 98 return B_NO_MEMORY; 99 100 if (offsetHasBeenSet) { 101 fTimeZone->setRawOffset(*fDataBridge->addrOfTimezone * -1 * 1000); 102 } else { 103 int32_t rawOffset; 104 int32_t dstOffset; 105 UDate nowMillis = 1000 * (UDate)time(NULL); 106 UErrorCode icuStatus = U_ZERO_ERROR; 107 fTimeZone->getOffset(nowMillis, FALSE, rawOffset, dstOffset, icuStatus); 108 if (!U_SUCCESS(icuStatus)) { 109 *fDataBridge->addrOfTimezone = 0; 110 *fDataBridge->addrOfDaylight = false; 111 strcpy(fDataBridge->addrOfTZName[0], "GMT"); 112 strcpy(fDataBridge->addrOfTZName[1], "GMT"); 113 114 return B_ERROR; 115 } 116 *fDataBridge->addrOfTimezone = -1 * (rawOffset + dstOffset) / 1000; 117 // we want seconds, not the ms that ICU gives us 118 } 119 120 *fDataBridge->addrOfDaylight = fTimeZone->useDaylightTime(); 121 122 for (int i = 0; i < 2; ++i) { 123 if (tz != NULL && *tz != ':' && i == 0) { 124 strcpy(fDataBridge->addrOfTZName[0], fTimeZoneID); 125 } else { 126 UnicodeString icuString; 127 fTimeZone->getDisplayName(i == 1, TimeZone::SHORT_COMMONLY_USED, 128 fTimeData.ICULocale(), icuString); 129 CheckedArrayByteSink byteSink(fDataBridge->addrOfTZName[i], 130 sizeof(fTimeZoneID)); 131 icuString.toUTF8(byteSink); 132 133 // make sure to canonicalize "GMT+00:00" to just "GMT" 134 if (strcmp(fDataBridge->addrOfTZName[i], "GMT+00:00") == 0) 135 fDataBridge->addrOfTZName[i][3] = '\0'; 136 } 137 } 138 139 return B_OK; 140 } 141 142 143 status_t 144 ICUTimeConversion::Localtime(const time_t* inTime, struct tm* tmOut) 145 { 146 if (fTimeZone == NULL) 147 return B_NO_INIT; 148 149 tmOut->tm_zone = fTimeZoneID; 150 return _FillTmValues(fTimeZone, inTime, tmOut); 151 } 152 153 154 status_t 155 ICUTimeConversion::Gmtime(const time_t* inTime, struct tm* tmOut) 156 { 157 const TimeZone* icuTimeZone = TimeZone::getGMT(); 158 // no delete - doesn't belong to us 159 160 return _FillTmValues(icuTimeZone, inTime, tmOut); 161 } 162 163 164 status_t 165 ICUTimeConversion::Mktime(struct tm* inOutTm, time_t& timeOut) 166 { 167 if (fTimeZone == NULL) 168 return B_NO_INIT; 169 170 UErrorCode icuStatus = U_ZERO_ERROR; 171 GregorianCalendar calendar(*fTimeZone, fTimeData.ICULocale(), 172 icuStatus); 173 if (!U_SUCCESS(icuStatus)) 174 return B_ERROR; 175 176 calendar.setLenient(TRUE); 177 calendar.set(inOutTm->tm_year + 1900, inOutTm->tm_mon, inOutTm->tm_mday, 178 inOutTm->tm_hour, inOutTm->tm_min, inOutTm->tm_sec); 179 180 UDate timeInMillis = calendar.getTime(icuStatus); 181 if (!U_SUCCESS(icuStatus)) 182 return B_ERROR; 183 timeOut = (time_t)((int64_t)timeInMillis / 1000); 184 185 return _FillTmValues(fTimeZone, &timeOut, inOutTm); 186 } 187 188 189 status_t 190 ICUTimeConversion::_FillTmValues(const TimeZone* icuTimeZone, 191 const time_t* inTime, struct tm* tmOut) 192 { 193 UErrorCode icuStatus = U_ZERO_ERROR; 194 GregorianCalendar calendar(*icuTimeZone, fTimeData.ICULocale(), icuStatus); 195 if (!U_SUCCESS(icuStatus)) 196 return B_ERROR; 197 198 calendar.setTime(1000 * (UDate)*inTime, icuStatus); 199 if (!U_SUCCESS(icuStatus)) 200 return B_ERROR; 201 202 tmOut->tm_sec = calendar.get(UCAL_SECOND, icuStatus); 203 if (!U_SUCCESS(icuStatus)) 204 return B_ERROR; 205 tmOut->tm_min = calendar.get(UCAL_MINUTE, icuStatus); 206 if (!U_SUCCESS(icuStatus)) 207 return B_ERROR; 208 tmOut->tm_hour = calendar.get(UCAL_HOUR_OF_DAY, icuStatus); 209 if (!U_SUCCESS(icuStatus)) 210 return B_ERROR; 211 tmOut->tm_mday = calendar.get(UCAL_DAY_OF_MONTH, icuStatus); 212 if (!U_SUCCESS(icuStatus)) 213 return B_ERROR; 214 tmOut->tm_mon = calendar.get(UCAL_MONTH, icuStatus); 215 if (!U_SUCCESS(icuStatus)) 216 return B_ERROR; 217 tmOut->tm_year = calendar.get(UCAL_YEAR, icuStatus) - 1900; 218 if (!U_SUCCESS(icuStatus)) 219 return B_ERROR; 220 tmOut->tm_wday = calendar.get(UCAL_DAY_OF_WEEK, icuStatus) - 1; 221 if (!U_SUCCESS(icuStatus)) 222 return B_ERROR; 223 tmOut->tm_yday = calendar.get(UCAL_DAY_OF_YEAR, icuStatus) - 1; 224 if (!U_SUCCESS(icuStatus)) 225 return B_ERROR; 226 tmOut->tm_isdst = calendar.inDaylightTime(icuStatus); 227 if (!U_SUCCESS(icuStatus)) 228 return B_ERROR; 229 tmOut->tm_gmtoff = (calendar.get(UCAL_ZONE_OFFSET, icuStatus) 230 + calendar.get(UCAL_DST_OFFSET, icuStatus)) / 1000; 231 if (!U_SUCCESS(icuStatus)) 232 return B_ERROR; 233 tmOut->tm_zone = fDataBridge->addrOfTZName[tmOut->tm_isdst ? 1 : 0]; 234 235 return B_OK; 236 } 237 238 239 } // namespace Libroot 240 } // namespace BPrivate 241