Added silc_time_epoch, converting SilcTime to Epoch time value
[runtime.git] / lib / silcutil / silctime.c
1 /*
2
3   silctime.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2003 - 2008 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 #include "silcruntime.h"
21
22 /* Fills the SilcTime structure with correct values */
23
24 static SilcBool silc_time_fill(SilcTime time,
25                                unsigned int year,
26                                unsigned int month,
27                                unsigned int day,
28                                unsigned int hour,
29                                unsigned int minute,
30                                unsigned int second,
31                                unsigned int msec)
32 {
33   if (year > (1 << 15))
34     goto err;
35   if (month < 1 || month > 12)
36     goto err;
37   if (day < 1 || day > 31)
38     goto err;
39   if (hour > 23)
40     goto err;
41   if (minute > 60)
42     goto err;
43   if (second > 61)
44     goto err;
45   if (msec > 1000)
46     goto err;
47
48   time->year = year;
49   time->month = month;
50   time->day = day;
51   time->hour = hour;
52   time->minute = minute;
53   time->second = second;
54   time->msecond = msec;
55
56   return TRUE;
57
58  err:
59   silc_set_errno(SILC_ERR_BAD_TIME);
60   return FALSE;
61 }
62
63 /* Return time since Epoch */
64
65 SilcInt64 silc_time(void)
66 {
67   return (SilcInt64)time(NULL);
68 }
69
70 /* Return time since Epoch in milliseconds */
71
72 SilcInt64 silc_time_msec(void)
73 {
74   struct timeval curtime;
75   silc_gettimeofday(&curtime);
76   return (curtime.tv_sec * (SilcUInt64)1000) +
77     (curtime.tv_usec / (SilcUInt64)1000);
78 }
79
80 /* Return time since Epoch in microseconds */
81
82 SilcInt64 silc_time_usec(void)
83 {
84   struct timeval curtime;
85   if (silc_gettimeofday(&curtime))
86     silc_set_errno_posix(errno);
87   return (curtime.tv_sec * (SilcUInt64)1000000) + curtime.tv_usec;
88 }
89
90 /* Returns time as string */
91
92 const char *silc_time_string(SilcInt64 time_val)
93 {
94   time_t curtime;
95   char *return_time;
96
97   if (!time_val)
98     curtime = silc_time();
99   else
100     curtime = (time_t)time_val;
101   return_time = ctime(&curtime);
102   if (!return_time) {
103     silc_set_errno(SILC_ERR_BAD_TIME);
104     return NULL;
105   }
106   return_time[strlen(return_time) - 1] = '\0';
107
108   return (const char *)return_time;
109 }
110
111 /* Returns time as SilcTime structure */
112
113 SilcBool silc_time_value(SilcInt64 time_val, SilcTime ret_time)
114 {
115   struct tm *t;
116   unsigned int msec = 0;
117   time_t timeval;
118   SilcInt32 ctz = 0;
119
120   if (!ret_time)
121     return TRUE;
122
123   if (!time_val)
124     time_val = silc_time_msec();
125
126   msec = (SilcUInt64)time_val % (SilcUInt64)1000;
127   timeval = (time_t)((SilcUInt64)time_val / (SilcUInt64)1000);
128
129   t = localtime(&timeval);
130   if (!t) {
131     silc_set_errno(SILC_ERR_BAD_TIME);
132     return FALSE;
133   }
134
135   memset(ret_time, 0, sizeof(*ret_time));
136   if (!silc_time_fill(ret_time, t->tm_year + 1900, t->tm_mon + 1,
137                       t->tm_mday, t->tm_hour, t->tm_min,
138                       t->tm_sec, msec))
139     return FALSE;
140
141   ret_time->dst        = t->tm_isdst ? 1 : 0;
142
143 #ifdef SILC_WIN32
144   ret_time->utc_east   = _timezone < 0 ? 1 : 0;
145   ret_time->utc_hour   = (ret_time->utc_east ? (-(_timezone)) / 3600 :
146                           _timezone / 3600);
147   ret_time->utc_minute = (ret_time->utc_east ? (-(_timezone)) % 3600 :
148                           _timezone % 3600);
149 #else
150 #if defined(HAVE_TIMEZONE)
151   ret_time->utc_east   = timezone < 0 ? 1 : 0;
152   ctz = timezone;
153   if (ret_time->dst)
154     ctz -= 3600;
155 #elif defined(HAVE_TM_GMTOFF)
156   ret_time->utc_east   = t->tm_gmtoff > 0 ? 1 : 0;
157   ctz = -t->tm_gmtoff;
158 #elif defined(HAVE___TM_GMTOFF)
159   ret_time->utc_east   = t->__tm_gmtoff > 0 ? 1 : 0;
160   ctz = -t->__tm_gmtoff;
161 #elif defined(HAVE___TM_GMTOFF__)
162   ret_time->utc_east   = t->__tm_gmtoff__ > 0 ? 1 : 0;
163   ctz = -t->__tm_gmtoff__;
164 #endif /* HAVE_TIMEZONE */
165
166   ret_time->utc_hour   = (ret_time->utc_east ? (-(ctz)) / 3600 : ctz / 3600);
167   ret_time->utc_minute = (ret_time->utc_east ? (-(ctz)) % 3600 : ctz % 3600);
168 #endif /* SILC_WIN32 */
169
170   if (ret_time->utc_minute)
171     ret_time->utc_minute /= 60;
172
173   return TRUE;
174 }
175
176 /* SilcTime to epoch */
177
178 SilcUInt64 silc_time_epoch(SilcTime timeval)
179 {
180   struct tm val;
181
182   if (!timeval)
183     return 0;
184
185   val.tm_sec = timeval->second;
186   val.tm_min = timeval->minute;
187   val.tm_hour = timeval->hour;
188   val.tm_mday = timeval->day;
189   val.tm_mon = timeval->month - 1;
190   val.tm_year = timeval->year - 1900;
191   val.tm_isdst = timeval->dst;
192
193   return (SilcUInt64)mktime(&val);
194 }
195
196 /* Returns timezone */
197
198 SilcBool silc_timezone(char *timezone, SilcUInt32 timezone_size)
199 {
200   SilcTimeStruct curtime;
201
202   if (timezone_size < 6) {
203     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
204     return FALSE;
205   }
206
207   if (!silc_time_value(0, &curtime))
208     return FALSE;
209
210   if (!curtime.utc_hour && curtime.utc_minute)
211     silc_snprintf(timezone, timezone_size, "Z");
212   else if (curtime.utc_minute)
213     silc_snprintf(timezone, timezone_size, "%c%02d:%02d",
214                   curtime.utc_east ? '+' : '-', curtime.utc_hour,
215                   curtime.utc_minute);
216   else
217     silc_snprintf(timezone, timezone_size, "%c%02d",
218                   curtime.utc_east ? '+' : '-', curtime.utc_hour);
219
220   return TRUE;
221 }
222
223 /* Returns time from universal time string into SilcTime */
224
225 SilcBool silc_time_universal(const char *universal_time, SilcTime ret_time)
226 {
227   int ret;
228   unsigned int year, month, day, hour = 0, minute = 0, second = 0;
229   unsigned char z = 0;
230
231   if (!ret_time)
232     return TRUE;
233   memset(ret_time, 0, sizeof(*ret_time));
234
235   /* Parse the time string */
236   ret = sscanf(universal_time, "%02u%02u%02u%02u%02u%02u%c", &year, &month,
237                &day, &hour, &minute, &second, &z);
238   if (ret < 3) {
239     SILC_LOG_DEBUG(("Invalid UTC time string"));
240     silc_set_errno_reason(SILC_ERR_BAD_TIME, "Invalid UTC time string");
241     return FALSE;
242   }
243
244   /* Fill the SilcTime structure */
245   ret = silc_time_fill(ret_time, year, month, day, hour, minute, second, 0);
246   if (!ret) {
247     SILC_LOG_DEBUG(("Incorrect values in UTC time string"));
248     return FALSE;
249   }
250
251   /* Check timezone */
252   if (z == '-' || z == '+') {
253     ret = sscanf(universal_time + (ret * 2) + 1, "%02u%02u", &hour, &minute);
254     if (ret != 2) {
255       SILC_LOG_DEBUG(("Malformed UTC time string"));
256       silc_set_errno_reason(SILC_ERR_BAD_TIME, "Malformed UTC time string");
257       return FALSE;
258     }
259
260     if (hour > 23 || minute > 60) {
261       silc_set_errno(SILC_ERR_BAD_TIME);
262       return FALSE;
263     }
264
265     ret_time->utc_hour   = hour;
266     ret_time->utc_minute = minute;
267     ret_time->utc_east   = (z == '-') ? 0 : 1;
268   } else if (z != 'Z') {
269     SILC_LOG_DEBUG(("Invalid timezone"));
270     silc_set_errno_reason(SILC_ERR_BAD_TIME, "Invalid timezone");
271     return FALSE;
272   }
273
274   /* UTC year must be fixed since it's represented only as YY not YYYY. */
275   ret_time->year += 1900;
276   if (ret_time->year < 1950)
277     ret_time->year += 100;
278
279   return TRUE;
280 }
281
282 /* Encode universal time string. */
283
284 SilcBool silc_time_universal_string(SilcTime time_val, char *ret_string,
285                                     SilcUInt32 ret_string_size)
286 {
287   int ret, len = 0;
288
289   memset(ret_string, 0, ret_string_size);
290   ret = silc_snprintf(ret_string, ret_string_size - 1,
291                       "%02u%02u%02u%02u%02u%02u",
292                       time_val->year % 100, time_val->month, time_val->day,
293                       time_val->hour, time_val->minute, time_val->second);
294   if (ret < 0)
295     return FALSE;
296   len += ret;
297
298   if (!time_val->utc_hour && !time_val->utc_minute) {
299     ret = silc_snprintf(ret_string + len, ret_string_size - 1 - len, "Z");
300     if (ret < 0)
301       return FALSE;
302     len += ret;
303   } else {
304     ret = silc_snprintf(ret_string + len, ret_string_size - 1 - len,
305                         "%c%02u%02u", time_val->utc_east ? '+' : '-',
306                         time_val->utc_hour, time_val->utc_minute);
307     if (ret < 0)
308       return FALSE;
309     len += ret;
310   }
311
312   return TRUE;
313 }
314
315 /* Returns time from generalized time string into SilcTime */
316
317 SilcBool silc_time_generalized(const char *generalized_time, SilcTime ret_time)
318 {
319   int ret, i;
320   unsigned int year, month, day, hour = 0, minute = 0, second = 0;
321   unsigned int msecond = 0;
322   unsigned char z = 0;
323
324   if (!ret_time)
325     return TRUE;
326   memset(ret_time, 0, sizeof(*ret_time));
327
328   /* Parse the time string */
329   ret = sscanf(generalized_time, "%04u%02u%02u%02u%02u%02u", &year, &month,
330                &day, &hour, &minute, &second);
331   if (ret < 3) {
332     SILC_LOG_DEBUG(("Invalid generalized time string"));
333     silc_set_errno_reason(SILC_ERR_BAD_TIME, "Invalid generalized time string");
334     return FALSE;
335   }
336
337   /* Fill the SilcTime structure */
338   ret = silc_time_fill(ret_time, year, month, day, hour, minute, second, 0);
339   if (!ret) {
340     SILC_LOG_DEBUG(("Incorrect values in generalized time string"));
341     return FALSE;
342   }
343
344   /* Check fractions of second and/or timezone */
345   i = ret * 4;
346   ret = sscanf(generalized_time + i, "%c", &z);
347   if (ret != 1) {
348     SILC_LOG_DEBUG(("Malformed generalized time string"));
349     silc_set_errno_reason(SILC_ERR_BAD_TIME,
350                           "Malformed generalized time string");
351     return FALSE;
352   }
353
354   if (z == '.') {
355     /* Take fractions of second */
356     int l;
357     i++;
358     ret = sscanf(generalized_time + i, "%u%n", &msecond, &l);
359     if (ret != 1) {
360       SILC_LOG_DEBUG(("Malformed generalized time string"));
361       silc_set_errno_reason(SILC_ERR_BAD_TIME,
362                             "Malformed generalized time string");
363       return FALSE;
364     }
365     while (l > 4) {
366       msecond /= 10;
367       l--;
368     }
369     ret_time->msecond = msecond;
370     i += l;
371
372     /* Read optional timezone */
373     if (strlen(generalized_time) < i)
374       sscanf(generalized_time + i, "%c", &z);
375   }
376
377   /* Check timezone if present */
378   if (z == '-' || z == '+') {
379     ret = sscanf(generalized_time + i + 1, "%02u%02u", &hour, &minute);
380     if (ret != 2) {
381       SILC_LOG_DEBUG(("Malformed generalized time string"));
382       silc_set_errno_reason(SILC_ERR_BAD_TIME,
383                             "Malformed generalized time string");
384       return FALSE;
385     }
386
387     if (hour > 23 || minute > 60) {
388       silc_set_errno(SILC_ERR_BAD_TIME);
389       return FALSE;
390     }
391
392     ret_time->utc_hour   = hour;
393     ret_time->utc_minute = minute;
394     ret_time->utc_east   = (z == '-') ? 0 : 1;
395   }
396
397   return TRUE;
398 }
399
400 /* Encode generalized time string */
401
402 SilcBool silc_time_generalized_string(SilcTime time_val, char *ret_string,
403                                       SilcUInt32 ret_string_size)
404 {
405   int len = 0, ret;
406   memset(ret_string, 0, ret_string_size);
407   ret = silc_snprintf(ret_string, ret_string_size - 1,
408                       "%04u%02u%02u%02u%02u%02u",
409                       time_val->year, time_val->month,
410                       time_val->day, time_val->hour,
411                       time_val->minute, time_val->second);
412   if (ret < 0)
413     return FALSE;
414   len += ret;
415
416   if (time_val->msecond) {
417     ret = silc_snprintf(ret_string + len, ret_string_size - 1 - len,
418                         ".%lu", (unsigned long)time_val->msecond);
419     if (ret < 0)
420       return FALSE;
421     len += ret;
422   }
423
424   if (!time_val->utc_hour && !time_val->utc_minute) {
425     ret = silc_snprintf(ret_string + len, ret_string_size - 1 - len, "Z");
426     if (ret < 0)
427       return FALSE;
428     len += ret;
429   } else {
430     ret = silc_snprintf(ret_string + len, ret_string_size - 1 - len,
431                         "%c%02u%02u", time_val->utc_east ? '+' : '-',
432                         time_val->utc_hour, time_val->utc_minute);
433     if (ret < 0)
434       return FALSE;
435     len += ret;
436   }
437
438   return TRUE;
439 }
440
441 /* Return TRUE if `smaller' is smaller than `bigger'. */
442
443 int silc_compare_timeval(struct timeval *t1, struct timeval *t2)
444 {
445   SilcInt32 s = t1->tv_sec - t2->tv_sec;
446   if (!s)
447     return t1->tv_usec - t2->tv_usec;
448   return s;
449 }