set mutex on gmt handle
[platform/core/pim/calendar-service.git] / common / cal_time.cpp
1 /*
2  * Calendar Service
3  *
4  * Copyright (c) 2012 - 2015 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  */
19
20 #include <string.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <unicode/utypes.h>
24 #include <unicode/ucal.h>
25 #include <unicode/uloc.h>
26 #include <unicode/calendar.h>
27 #include <unicode/timezone.h>
28 #include <unicode/gregocal.h>
29 #include <unicode/simpletz.h>
30 #include <unicode/ustring.h>
31 #include <unicode/strenum.h>
32 #include <unicode/ustdio.h>
33 #include <unicode/udat.h>
34 #include <unicode/rbtz.h>
35 #include <unicode/uclean.h>
36 #include <pthread.h>
37
38 #include "calendar.h"
39 #include "cal_internal.h"
40 #include "cal_typedef.h"
41 #include "cal_time.h"
42 #include "cal_utils.h"
43
44 #define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY)
45 #define uprv_strncpy(dst, src, size) U_STANDARD_CPP_NAMESPACE strncpy(dst, src, size)
46
47 #define ms2sec(ms) (long long int)(ms / 1000.0)
48 #define sec2ms(s) (s * 1000.0)
49
50 static pthread_mutex_t cal_mutex_gmt = PTHREAD_MUTEX_INITIALIZER;
51 static UCalendar *_g_ucal_gmt = NULL;
52
53 void cal_time_get_registered_tzid_with_offset(int offset, char *registered_tzid, int tzid_size)
54 {
55         UErrorCode ec = U_ZERO_ERROR;
56
57         RET_IF(NULL == registered_tzid);
58
59         StringEnumeration* s = TimeZone::createEnumeration(sec2ms(offset));
60         if (0 == s->count(ec)) {
61                 DBG("No tzid of offset(%d)sec", offset);
62                 return;
63         }
64
65         const UnicodeString *unicode_tzid = s->snext(ec);
66         unicode_tzid->extract(registered_tzid, tzid_size, NULL, ec);
67         delete s;
68 }
69
70 UCalendar *cal_time_open_ucal(int calendar_system_type, const char *tzid, int wkst)
71 {
72         UChar utf16_timezone[CAL_STR_SHORT_LEN64] = {0};
73         u_uastrncpy(utf16_timezone, tzid, sizeof(utf16_timezone));
74
75         UErrorCode status = U_ZERO_ERROR;
76         UCalendar *ucal = NULL;
77
78         char localeBuf[ULOC_LOCALE_IDENTIFIER_CAPACITY] = {0};
79
80         switch (calendar_system_type) {
81         case CALENDAR_SYSTEM_EAST_ASIAN_LUNISOLAR:
82                 uloc_setKeywordValue("calendar", "chinese", localeBuf, ULOC_LOCALE_IDENTIFIER_CAPACITY, &status);
83                 ucal = ucal_open(utf16_timezone, -1, localeBuf, UCAL_TRADITIONAL, &status);
84                 break;
85         default:
86                 ucal = ucal_open(utf16_timezone, -1, uloc_getDefault(), UCAL_GREGORIAN, &status);
87                 break;
88         }
89
90         if (U_FAILURE(status)) {
91                 ERR("ucal_open() Fail(%s)", u_errorName(status));
92                 return NULL;
93         }
94         if (CALENDAR_SUNDAY <= wkst && wkst <= CALENDAR_SATURDAY) {
95                 DBG("set wkst(%d)", wkst);
96                 ucal_setAttribute(ucal, UCAL_FIRST_DAY_OF_WEEK, wkst);
97         }
98         return ucal;
99 }
100
101 char* cal_time_convert_ltos(const char *tzid, long long int lli, int is_allday)
102 {
103         int y, mon, d, h, min, s;
104         char buf[CAL_STR_SHORT_LEN32] = {0};
105         UCalendar *ucal;
106         UErrorCode status = U_ZERO_ERROR;
107
108         if (NULL == tzid) {
109                 DBG("tzid is NULL so set gmt");
110                 tzid = CAL_TZID_GMT;
111         }
112
113         ucal = cal_time_open_ucal(-1, tzid, -1);
114         if (NULL == ucal) {
115                 ERR("cal_time_open_ucal() Fail");
116                 return NULL;
117         }
118         ucal_setMillis(ucal, sec2ms(lli), &status);
119         if (U_FAILURE(status)) {
120                 ERR("ucal_setMillFail (%s)", u_errorName(status));
121                 ucal_close(ucal);
122                 return NULL;
123         }
124
125         y = ucal_get(ucal, UCAL_YEAR, &status);
126         mon = ucal_get(ucal, UCAL_MONTH, &status) + 1;
127         d = ucal_get(ucal, UCAL_DATE, &status);
128         h = ucal_get(ucal, UCAL_HOUR_OF_DAY, &status);
129         min = ucal_get(ucal, UCAL_MINUTE, &status);
130         s = ucal_get(ucal, UCAL_SECOND, &status);
131
132         if (CAL_STRING_EQUAL == strncmp(tzid, CAL_TZID_GMT, strlen(CAL_TZID_GMT))) {
133                 snprintf(buf, sizeof(buf), CAL_DATETIME_FORMAT_YYYYMMDDTHHMMSS"%s",
134                                 y, mon, d, h, min, s, is_allday ? "" : "Z");
135         }
136         else {
137                 snprintf(buf, sizeof(buf), CAL_DATETIME_FORMAT_YYYYMMDDTHHMMSS, y, mon, d, h, min, s);
138         }
139
140         ucal_close(ucal);
141
142         return cal_strdup(buf);
143 }
144
145 long long int cal_time_convert_itol(const char *tzid, int y, int mon, int d, int h, int min, int s)
146 {
147         long long int lli;
148         UCalendar *ucal = NULL;
149         UErrorCode status = U_ZERO_ERROR;
150
151         ucal = cal_time_open_ucal(-1, tzid, -1);
152         if (NULL == ucal) {
153                 ERR("cal_time_open_ucal() Fail");
154                 return 0;
155         }
156         ucal_set(ucal, UCAL_YEAR, y);
157         ucal_set(ucal, UCAL_MONTH, mon -1);
158         ucal_set(ucal, UCAL_DATE, d);
159         ucal_set(ucal, UCAL_HOUR_OF_DAY, h);
160         ucal_set(ucal, UCAL_MINUTE, min);
161         ucal_set(ucal, UCAL_SECOND, s);
162         ucal_set(ucal, UCAL_MILLISECOND, 0);
163         lli = ms2sec(ucal_getMillis(ucal, &status));
164
165         ucal_close(ucal);
166         return lli;
167 }
168
169 long long int cal_time_get_now(void)
170 {
171         return ms2sec(ucal_getNow());
172 }
173
174 int cal_time_get_next_date(calendar_time_s *today, calendar_time_s *next)
175 {
176         RETV_IF(NULL == today, CALENDAR_ERROR_INVALID_PARAMETER);
177         RETV_IF(NULL == next, CALENDAR_ERROR_INVALID_PARAMETER);
178
179         UCalendar *ucal = NULL;
180         UErrorCode status = U_ZERO_ERROR;
181         UChar *utzid = NULL;
182         const char *tzid = CAL_TZID_GMT;
183
184         utzid = (UChar *)calloc(strlen(tzid) + 1, sizeof(UChar));
185         RETVM_IF(NULL == utzid, CALENDAR_ERROR_OUT_OF_MEMORY, "calloc() Fail");
186
187         u_uastrcpy(utzid, tzid);
188         ucal = ucal_open(utzid, u_strlen(utzid), "en_US", UCAL_TRADITIONAL, &status);
189         if (U_FAILURE(status)) {
190                 ERR("ucal_open() Fail(%s)", u_errorName(status));
191                 CAL_FREE(utzid);
192                 return status;
193         }
194
195         switch (today->type) {
196         case CALENDAR_TIME_UTIME:
197
198                 break;
199
200         case CALENDAR_TIME_LOCALTIME:
201                 ucal_setDateTime(ucal,
202                                 today->time.date.year,
203                                 today->time.date.month-1,
204                                 today->time.date.mday,
205                                 today->time.date.hour,
206                                 today->time.date.minute,
207                                 today->time.date.second,
208                                 &status);
209                 DBG("today %04d/%02d/%02d %02d:%02d:%02d",
210                                 today->time.date.year, today->time.date.month, today->time.date.mday,
211                                 today->time.date.hour, today->time.date.minute, today->time.date.second);
212                 ucal_add(ucal, UCAL_DAY_OF_YEAR, 1, &status);
213                 next->time.date.year = ucal_get(ucal, UCAL_YEAR, &status);
214                 next->time.date.month = ucal_get(ucal, UCAL_MONTH, &status) + 1;
215                 next->time.date.mday = ucal_get(ucal, UCAL_DATE, &status);
216                 next->time.date.hour = ucal_get(ucal, UCAL_HOUR_OF_DAY, &status);
217                 next->time.date.minute = ucal_get(ucal, UCAL_MINUTE, &status);
218                 next->time.date.second = ucal_get(ucal, UCAL_SECOND, &status);
219                 DBG("next %04d/%02d/%02d %02d:%02d:%02d",
220                                 next->time.date.year, next->time.date.month, next->time.date.mday,
221                                 next->time.date.hour, next->time.date.minute, next->time.date.second);
222                 break;
223         }
224
225         CAL_FREE(utzid);
226
227         return CALENDAR_ERROR_NONE;
228 }
229
230 void cal_time_u_cleanup(void)
231 {
232         u_cleanup();
233 }
234
235 void cal_time_get_tz_offset(const char *tz, time_t *zone_offset, time_t *dst_offset)
236 {
237         UErrorCode status = U_ZERO_ERROR;
238         UCalendar *ucal = NULL;
239
240         ucal = cal_time_open_ucal(-1, tz, -1);
241         if (NULL == ucal) {
242                 ERR("cal_time_open_ucal() Fail");
243                 return;
244         }
245         int32_t zone = ucal_get(ucal, UCAL_ZONE_OFFSET, &status);
246         int32_t dst = ucal_get(ucal, UCAL_DST_OFFSET, &status);
247         ucal_close(ucal);
248
249         if (zone_offset) *zone_offset = ms2sec(zone);
250         if (dst_offset) *dst_offset = ms2sec(dst);
251 }
252
253 bool cal_time_in_dst(const char *tz, long long int t)
254 {
255         UErrorCode status = U_ZERO_ERROR;
256         UCalendar *ucal = NULL;
257
258         ucal = cal_time_open_ucal(-1, tz, -1);
259         if (NULL == ucal) {
260                 ERR("cal_time_open_ucal() Fail");
261                 return false;
262         }
263         ucal_setMillis(ucal, sec2ms(t), &status);
264         bool is_dst = ucal_inDaylightTime(ucal, &status);
265         ucal_close(ucal);
266
267         return is_dst;
268 }
269
270 int cal_time_init(void)
271 {
272         UCalendar *ucal = NULL;
273
274         pthread_mutex_lock(&cal_mutex_gmt);
275         if (NULL == _g_ucal_gmt) {
276                 ucal = cal_time_open_ucal(-1, NULL, -1);
277                 RETVM_IF(NULL == ucal, CALENDAR_ERROR_SYSTEM, "cal_time_open_ucal() Fail");
278                 _g_ucal_gmt = ucal;
279         }
280         pthread_mutex_unlock(&cal_mutex_gmt);
281         return CALENDAR_ERROR_NONE;
282 }
283
284 void cal_time_fini(void)
285 {
286         pthread_mutex_lock(&cal_mutex_gmt);
287         if (_g_ucal_gmt) {
288                 ucal_close(_g_ucal_gmt);
289                 _g_ucal_gmt = NULL;
290         }
291         pthread_mutex_unlock(&cal_mutex_gmt);
292 }
293
294 static UCalendar* __get_gmt_ucal(void)
295 {
296         pthread_mutex_lock(&cal_mutex_gmt);
297         if (NULL == _g_ucal_gmt) {
298                 cal_time_init();
299         }
300         pthread_mutex_unlock(&cal_mutex_gmt);
301         return _g_ucal_gmt;
302 }
303
304 long long int cal_time_convert_lli(char *p)
305 {
306         UErrorCode status = U_ZERO_ERROR;
307
308         if (p && *p) {
309                 int y = 0, m = 0, d = 0;
310                 int h = 0, n = 0, s = 0;
311                 sscanf(p, CAL_DATETIME_FORMAT_YYYYMMDDTHHMMSSZ, &y, &m, &d, &h, &n, &s);
312
313                 UCalendar *ucal = __get_gmt_ucal();
314                 ucal_setDateTime(ucal, y, m -1, d, h, n, s, &status);
315                 return ms2sec(ucal_getMillis(ucal, &status));
316         }
317         return 0;
318 }
319
320 void cal_time_modify_caltime(calendar_time_s *caltime, long long int diff)
321 {
322         UErrorCode status = U_ZERO_ERROR;
323         RET_IF(NULL == caltime);
324
325         UCalendar *ucal = __get_gmt_ucal();
326         long long int lli = 0;
327         switch (caltime->type) {
328         case CALENDAR_TIME_UTIME:
329                 DBG("Before (%lld)", caltime->time.utime);
330                 caltime->time.utime += diff;
331                 DBG("After (%lld)", caltime->time.utime);
332                 break;
333
334         case CALENDAR_TIME_LOCALTIME:
335                 DBG("Before "CAL_DATETIME_FORMAT_YYYYMMDDTHHMMSS, caltime->time.date.year, caltime->time.date.month, caltime->time.date.mday,
336                                 caltime->time.date.hour, caltime->time.date.minute, caltime->time.date.second);
337
338                 ucal_setDateTime(ucal, caltime->time.date.year, caltime->time.date.month - 1, caltime->time.date.mday,
339                                 caltime->time.date.hour, caltime->time.date.minute, caltime->time.date.second, &status);
340                 lli = ms2sec(ucal_getMillis(ucal, &status));
341                 lli += diff;
342                 ucal_setMillis(ucal, sec2ms(lli), &status);
343                 caltime->time.date.year = ucal_get(ucal, UCAL_YEAR, &status);
344                 caltime->time.date.month = ucal_get(ucal, UCAL_MONTH, &status) + 1;
345                 caltime->time.date.mday = ucal_get(ucal, UCAL_DATE, &status);
346                 caltime->time.date.hour = ucal_get(ucal, UCAL_HOUR_OF_DAY, &status);
347                 caltime->time.date.minute = ucal_get(ucal, UCAL_MINUTE, &status);
348                 caltime->time.date.second = ucal_get(ucal, UCAL_SECOND, &status);
349
350                 DBG("After "CAL_DATETIME_FORMAT_YYYYMMDDTHHMMSS, caltime->time.date.year, caltime->time.date.month, caltime->time.date.mday,
351                                 caltime->time.date.hour, caltime->time.date.minute, caltime->time.date.second);
352                 break;
353         }
354 }
355
356 void cal_time_get_nth_wday(long long int t, int *nth, int *wday)
357 {
358         RET_IF(NULL == nth);
359         RET_IF(NULL == wday);
360
361         UErrorCode status = U_ZERO_ERROR;
362         UCalendar *ucal = __get_gmt_ucal();
363         ucal_setMillis(ucal, sec2ms(t), &status);
364         *wday = ucal_get(ucal, UCAL_DAY_OF_WEEK, &status);
365         /* check if nth is last */
366         int this_week = ucal_get(ucal, UCAL_DAY_OF_WEEK_IN_MONTH, &status);
367         ucal_add(ucal, UCAL_DAY_OF_YEAR, 7, &status);
368         int next_week = ucal_get(ucal, UCAL_DAY_OF_WEEK_IN_MONTH, &status);
369         *nth = next_week == 1 ? -1 : this_week;
370         DBG("nth(%d) wday(%d)", *nth, *wday);
371 }
372
373 void cal_time_get_datetime(long long int t, int *y, int *m, int *d, int *h, int *n, int *s)
374 {
375         UErrorCode status = U_ZERO_ERROR;
376         UCalendar *ucal = __get_gmt_ucal();
377         if (NULL == ucal) {
378                 ERR("__get_gmt_ucal() Fail");
379                 return;
380         }
381         ucal_setMillis(ucal, sec2ms(t), &status);
382         if (y) *y = ucal_get(ucal, UCAL_YEAR, &status);
383         if (m) *m = ucal_get(ucal, UCAL_MONTH, &status) + 1;
384         if (d) *d = ucal_get(ucal, UCAL_DATE, &status);
385         if (h) *h = ucal_get(ucal, UCAL_HOUR_OF_DAY, &status);
386         if (n) *n = ucal_get(ucal, UCAL_MINUTE, &status);
387         if (s) *s = ucal_get(ucal, UCAL_SECOND, &status);
388         ucal_close(ucal);
389 }
390
391 void cal_time_get_local_datetime(char *tzid, long long int t, int *y, int *m, int *d, int *h, int *n, int *s)
392 {
393         UErrorCode status = U_ZERO_ERROR;
394         UCalendar *ucal = cal_time_open_ucal(-1, tzid, 0);
395         if (NULL == ucal) {
396                 ERR("__get_gmt_ucal() Fail");
397                 return;
398         }
399         ucal_setMillis(ucal, sec2ms(t), &status);
400         if (y) *y = ucal_get(ucal, UCAL_YEAR, &status);
401         if (m) *m = ucal_get(ucal, UCAL_MONTH, &status) + 1;
402         if (d) *d = ucal_get(ucal, UCAL_DATE, &status);
403         if (h) *h = ucal_get(ucal, UCAL_HOUR_OF_DAY, &status);
404         if (n) *n = ucal_get(ucal, UCAL_MINUTE, &status);
405         if (s) *s = ucal_get(ucal, UCAL_SECOND, &status);
406         ucal_close(ucal);
407 }
408
409 bool cal_time_is_available_tzid(char *tzid)
410 {
411         UErrorCode ec = U_ZERO_ERROR;
412         StringEnumeration* s = TimeZone::createEnumeration();
413         int32_t s_count = s->count(ec);
414
415         int len = strlen(tzid);
416         int i, j;
417         for (i = 0; i < s_count; i++) {
418                 char buf[CAL_STR_SHORT_LEN32] = {0};
419                 const UnicodeString *unicode_tzid = s->snext(ec);
420                 for (j = 0; j < unicode_tzid->length(); j++) {
421                         buf[j] = unicode_tzid->charAt(j);
422                 }
423                 buf[j] = '\0';
424                 if (CAL_STRING_EQUAL == strncmp(buf, tzid, len)) {
425                         return true;
426                 }
427         }
428         return false;
429 }
430