xref: /haiku/src/system/libroot/add-ons/icu/ICUTimeConversion.cpp (revision 675ffabd70492a962f8c0288a32208c22ce5de18)
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