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