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 19 20 ICUTimeConversion::ICUTimeConversion(const ICUTimeData& timeData) 21 : 22 fTimeData(timeData), 23 fDataBridge(NULL) 24 { 25 fTimeZoneID[0] = '\0'; 26 } 27 28 29 ICUTimeConversion::~ICUTimeConversion() 30 { 31 } 32 33 34 void 35 ICUTimeConversion::Initialize(TimeConversionDataBridge* dataBridge) 36 { 37 fDataBridge = dataBridge; 38 } 39 40 41 status_t 42 ICUTimeConversion::TZSet(const char* timeZoneID) 43 { 44 // nothing to do if the given name matches any name of the current timezone 45 if (strcasecmp(fTimeZoneID, timeZoneID) == 0) 46 return B_OK; 47 48 strlcpy(fTimeZoneID, timeZoneID, sizeof(fTimeZoneID)); 49 50 ObjectDeleter<TimeZone> icuTimeZone = TimeZone::createTimeZone(fTimeZoneID); 51 if (icuTimeZone.Get() == NULL) 52 return B_NO_MEMORY; 53 54 int32_t rawOffset; 55 int32_t dstOffset; 56 UDate nowMillis = 1000 * (UDate)time(NULL); 57 UErrorCode icuStatus = U_ZERO_ERROR; 58 icuTimeZone->getOffset(nowMillis, FALSE, rawOffset, dstOffset, icuStatus); 59 if (!U_SUCCESS(icuStatus)) { 60 *fDataBridge->addrOfTimezone = 0; 61 *fDataBridge->addrOfDaylight = false; 62 strcpy(fDataBridge->addrOfTZName[0], "GMT"); 63 strcpy(fDataBridge->addrOfTZName[1], "GMT"); 64 65 return B_ERROR; 66 } 67 68 *fDataBridge->addrOfTimezone = -1 * (rawOffset + dstOffset) / 1000; 69 // we want seconds, not the ms that ICU gives us 70 71 *fDataBridge->addrOfDaylight = icuTimeZone->useDaylightTime(); 72 73 for (int i = 0; i < 2; ++i) { 74 UnicodeString icuString; 75 icuTimeZone->getDisplayName(i == 1, TimeZone::SHORT_COMMONLY_USED, 76 fTimeData.ICULocale(), icuString); 77 CheckedArrayByteSink byteSink(fDataBridge->addrOfTZName[i], 78 sizeof(fTimeZoneID)); 79 icuString.toUTF8(byteSink); 80 81 // make sure to canonicalize "GMT+00:00" to just "GMT" 82 if (strcmp(fDataBridge->addrOfTZName[i], "GMT+00:00") == 0) 83 fDataBridge->addrOfTZName[i][3] = '\0'; 84 } 85 86 return B_OK; 87 } 88 89 90 status_t 91 ICUTimeConversion::Localtime(const time_t* inTime, struct tm* tmOut) 92 { 93 ObjectDeleter<TimeZone> icuTimeZone = TimeZone::createTimeZone(fTimeZoneID); 94 if (icuTimeZone.Get() == NULL) 95 return B_NO_MEMORY; 96 97 tmOut->tm_zone = fTimeZoneID; 98 return _FillTmValues(icuTimeZone.Get(), inTime, tmOut); 99 } 100 101 102 status_t 103 ICUTimeConversion::Gmtime(const time_t* inTime, struct tm* tmOut) 104 { 105 const TimeZone* icuTimeZone = TimeZone::getGMT(); 106 // no delete - doesn't belong to us 107 108 return _FillTmValues(icuTimeZone, inTime, tmOut); 109 } 110 111 112 status_t 113 ICUTimeConversion::Mktime(struct tm* inOutTm, time_t& timeOut) 114 { 115 ObjectDeleter<TimeZone> icuTimeZone = TimeZone::createTimeZone(fTimeZoneID); 116 if (icuTimeZone.Get() == NULL) 117 return B_NO_MEMORY; 118 119 UErrorCode icuStatus = U_ZERO_ERROR; 120 GregorianCalendar calendar(*icuTimeZone.Get(), fTimeData.ICULocale(), 121 icuStatus); 122 if (!U_SUCCESS(icuStatus)) 123 return B_ERROR; 124 125 calendar.setLenient(TRUE); 126 calendar.set(inOutTm->tm_year + 1900, inOutTm->tm_mon, inOutTm->tm_mday, 127 inOutTm->tm_hour, inOutTm->tm_min, inOutTm->tm_sec); 128 129 UDate timeInMillis = calendar.getTime(icuStatus); 130 if (!U_SUCCESS(icuStatus)) 131 return B_ERROR; 132 timeOut = (time_t)((int64_t)timeInMillis / 1000); 133 134 return _FillTmValues(icuTimeZone.Get(), &timeOut, inOutTm); 135 } 136 137 138 status_t 139 ICUTimeConversion::_FillTmValues(const TimeZone* icuTimeZone, 140 const time_t* inTime, struct tm* tmOut) 141 { 142 UErrorCode icuStatus = U_ZERO_ERROR; 143 GregorianCalendar calendar(*icuTimeZone, fTimeData.ICULocale(), icuStatus); 144 if (!U_SUCCESS(icuStatus)) 145 return B_ERROR; 146 147 calendar.setTime(1000 * (UDate)*inTime, icuStatus); 148 if (!U_SUCCESS(icuStatus)) 149 return B_ERROR; 150 151 tmOut->tm_sec = calendar.get(UCAL_SECOND, icuStatus); 152 if (!U_SUCCESS(icuStatus)) 153 return B_ERROR; 154 tmOut->tm_min = calendar.get(UCAL_MINUTE, icuStatus); 155 if (!U_SUCCESS(icuStatus)) 156 return B_ERROR; 157 tmOut->tm_hour = calendar.get(UCAL_HOUR_OF_DAY, icuStatus); 158 if (!U_SUCCESS(icuStatus)) 159 return B_ERROR; 160 tmOut->tm_mday = calendar.get(UCAL_DAY_OF_MONTH, icuStatus); 161 if (!U_SUCCESS(icuStatus)) 162 return B_ERROR; 163 tmOut->tm_mon = calendar.get(UCAL_MONTH, icuStatus); 164 if (!U_SUCCESS(icuStatus)) 165 return B_ERROR; 166 tmOut->tm_year = calendar.get(UCAL_YEAR, icuStatus) - 1900; 167 if (!U_SUCCESS(icuStatus)) 168 return B_ERROR; 169 tmOut->tm_wday = calendar.get(UCAL_DAY_OF_WEEK, icuStatus) - 1; 170 if (!U_SUCCESS(icuStatus)) 171 return B_ERROR; 172 tmOut->tm_yday = calendar.get(UCAL_DAY_OF_YEAR, icuStatus) - 1; 173 if (!U_SUCCESS(icuStatus)) 174 return B_ERROR; 175 tmOut->tm_isdst = calendar.inDaylightTime(icuStatus); 176 if (!U_SUCCESS(icuStatus)) 177 return B_ERROR; 178 tmOut->tm_gmtoff = (calendar.get(UCAL_ZONE_OFFSET, icuStatus) 179 + calendar.get(UCAL_DST_OFFSET, icuStatus)) / 1000; 180 if (!U_SUCCESS(icuStatus)) 181 return B_ERROR; 182 tmOut->tm_zone = fDataBridge->addrOfTZName[tmOut->tm_isdst ? 1 : 0]; 183 184 return B_OK; 185 } 186 187 188 } // namespace BPrivate 189