Added SILC Thread Queue API
[silc.git] / lib / silcutil / silctime.c
1 /*
2
3   silctime.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2003 - 2007 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 "silc.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 /* Returns timezone */
177
178 SilcBool silc_timezone(char *timezone, SilcUInt32 timezone_size)
179 {
180   SilcTimeStruct curtime;
181
182   if (timezone_size < 6) {
183     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
184     return FALSE;
185   }
186
187   if (!silc_time_value(0, &curtime))
188     return FALSE;
189
190   if (!curtime.utc_hour && curtime.utc_minute)
191     silc_snprintf(timezone, timezone_size, "Z");
192   else if (curtime.utc_minute)
193     silc_snprintf(timezone, timezone_size, "%c%02d:%02d",
194                   curtime.utc_east ? '+' : '-', curtime.utc_hour,
195                   curtime.utc_minute);
196   else
197     silc_snprintf(timezone, timezone_size, "%c%02d",
198                   curtime.utc_east ? '+' : '-', curtime.utc_hour);
199
200   return TRUE;
201 }
202
203 /* Returns time from universal time string into SilcTime */
204
205 SilcBool silc_time_universal(const char *universal_time, SilcTime ret_time)
206 {
207   int ret;
208   unsigned int year, month, day, hour = 0, minute = 0, second = 0;
209   unsigned char z = 0;
210
211   if (!ret_time)
212     return TRUE;
213   memset(ret_time, 0, sizeof(*ret_time));
214
215   /* Parse the time string */
216   ret = sscanf(universal_time, "%02u%02u%02u%02u%02u%02u%c", &year, &month,
217                &day, &hour, &minute, &second, &z);
218   if (ret < 3) {
219     SILC_LOG_DEBUG(("Invalid UTC time string"));
220     silc_set_errno_reason(SILC_ERR_BAD_TIME, "Invalid UTC time string");
221     return FALSE;
222   }
223
224   /* Fill the SilcTime structure */
225   ret = silc_time_fill(ret_time, year, month, day, hour, minute, second, 0);
226   if (!ret) {
227     SILC_LOG_DEBUG(("Incorrect values in UTC time string"));
228     return FALSE;
229   }
230
231   /* Check timezone */
232   if (z == '-' || z == '+') {
233     ret = sscanf(universal_time + (ret * 2) + 1, "%02u%02u", &hour, &minute);
234     if (ret != 2) {
235       SILC_LOG_DEBUG(("Malformed UTC time string"));
236       silc_set_errno_reason(SILC_ERR_BAD_TIME, "Malformed UTC time string");
237       return FALSE;
238     }
239
240     if (hour > 23 || minute > 60) {
241       silc_set_errno(SILC_ERR_BAD_TIME);
242       return FALSE;
243     }
244
245     ret_time->utc_hour   = hour;
246     ret_time->utc_minute = minute;
247     ret_time->utc_east   = (z == '-') ? 0 : 1;
248   } else if (z != 'Z') {
249     SILC_LOG_DEBUG(("Invalid timezone"));
250     silc_set_errno_reason(SILC_ERR_BAD_TIME, "Invalid timezone");
251     return FALSE;
252   }
253
254   /* UTC year must be fixed since it's represented only as YY not YYYY. */
255   ret_time->year += 1900;
256   if (ret_time->year < 1950)
257     ret_time->year += 100;
258
259   return TRUE;
260 }
261
262 /* Encode universal time string. */
263
264 SilcBool silc_time_universal_string(SilcTime time_val, char *ret_string,
265                                     SilcUInt32 ret_string_size)
266 {
267   int ret, len = 0;
268
269   memset(ret_string, 0, ret_string_size);
270   ret = silc_snprintf(ret_string, ret_string_size - 1,
271                       "%02u%02u%02u%02u%02u%02u",
272                       time_val->year % 100, time_val->month, time_val->day,
273                       time_val->hour, time_val->minute, time_val->second);
274   if (ret < 0)
275     return FALSE;
276   len += ret;
277
278   if (!time_val->utc_hour && !time_val->utc_minute) {
279     ret = silc_snprintf(ret_string + len, ret_string_size - 1 - len, "Z");
280     if (ret < 0)
281       return FALSE;
282     len += ret;
283   } else {
284     ret = silc_snprintf(ret_string + len, ret_string_size - 1 - len,
285                         "%c%02u%02u", time_val->utc_east ? '+' : '-',
286                         time_val->utc_hour, time_val->utc_minute);
287     if (ret < 0)
288       return FALSE;
289     len += ret;
290   }
291
292   return TRUE;
293 }
294
295 /* Returns time from generalized time string into SilcTime */
296
297 SilcBool silc_time_generalized(const char *generalized_time, SilcTime ret_time)
298 {
299   int ret, i;
300   unsigned int year, month, day, hour = 0, minute = 0, second = 0;
301   unsigned int msecond = 0;
302   unsigned char z = 0;
303
304   if (!ret_time)
305     return TRUE;
306   memset(ret_time, 0, sizeof(*ret_time));
307
308   /* Parse the time string */
309   ret = sscanf(generalized_time, "%04u%02u%02u%02u%02u%02u", &year, &month,
310                &day, &hour, &minute, &second);
311   if (ret < 3) {
312     SILC_LOG_DEBUG(("Invalid generalized time string"));
313     silc_set_errno_reason(SILC_ERR_BAD_TIME, "Invalid generalized time string");
314     return FALSE;
315   }
316
317   /* Fill the SilcTime structure */
318   ret = silc_time_fill(ret_time, year, month, day, hour, minute, second, 0);
319   if (!ret) {
320     SILC_LOG_DEBUG(("Incorrect values in generalized time string"));
321     return FALSE;
322   }
323
324   /* Check fractions of second and/or timezone */
325   i = ret * 4;
326   ret = sscanf(generalized_time + i, "%c", &z);
327   if (ret != 1) {
328     SILC_LOG_DEBUG(("Malformed generalized time string"));
329     silc_set_errno_reason(SILC_ERR_BAD_TIME,
330                           "Malformed generalized time string");
331     return FALSE;
332   }
333
334   if (z == '.') {
335     /* Take fractions of second */
336     int l;
337     i++;
338     ret = sscanf(generalized_time + i, "%u%n", &msecond, &l);
339     if (ret != 1) {
340       SILC_LOG_DEBUG(("Malformed generalized time string"));
341       silc_set_errno_reason(SILC_ERR_BAD_TIME,
342                             "Malformed generalized time string");
343       return FALSE;
344     }
345     while (l > 4) {
346       msecond /= 10;
347       l--;
348     }
349     ret_time->msecond = msecond;
350     i += l;
351
352     /* Read optional timezone */
353     if (strlen(generalized_time) < i)
354       sscanf(generalized_time + i, "%c", &z);
355   }
356
357   /* Check timezone if present */
358   if (z == '-' || z == '+') {
359     ret = sscanf(generalized_time + i + 1, "%02u%02u", &hour, &minute);
360     if (ret != 2) {
361       SILC_LOG_DEBUG(("Malformed generalized time string"));
362       silc_set_errno_reason(SILC_ERR_BAD_TIME,
363                             "Malformed generalized time string");
364       return FALSE;
365     }
366
367     if (hour > 23 || minute > 60) {
368       silc_set_errno(SILC_ERR_BAD_TIME);
369       return FALSE;
370     }
371
372     ret_time->utc_hour   = hour;
373     ret_time->utc_minute = minute;
374     ret_time->utc_east   = (z == '-') ? 0 : 1;
375   }
376
377   return TRUE;
378 }
379
380 /* Encode generalized time string */
381
382 SilcBool silc_time_generalized_string(SilcTime time_val, char *ret_string,
383                                       SilcUInt32 ret_string_size)
384 {
385   int len = 0, ret;
386   memset(ret_string, 0, ret_string_size);
387   ret = silc_snprintf(ret_string, ret_string_size - 1,
388                       "%04u%02u%02u%02u%02u%02u",
389                       time_val->year, time_val->month,
390                       time_val->day, time_val->hour,
391                       time_val->minute, time_val->second);
392   if (ret < 0)
393     return FALSE;
394   len += ret;
395
396   if (time_val->msecond) {
397     ret = silc_snprintf(ret_string + len, ret_string_size - 1 - len,
398                         ".%lu", (unsigned long)time_val->msecond);
399     if (ret < 0)
400       return FALSE;
401     len += ret;
402   }
403
404   if (!time_val->utc_hour && !time_val->utc_minute) {
405     ret = silc_snprintf(ret_string + len, ret_string_size - 1 - len, "Z");
406     if (ret < 0)
407       return FALSE;
408     len += ret;
409   } else {
410     ret = silc_snprintf(ret_string + len, ret_string_size - 1 - len,
411                         "%c%02u%02u", time_val->utc_east ? '+' : '-',
412                         time_val->utc_hour, time_val->utc_minute);
413     if (ret < 0)
414       return FALSE;
415     len += ret;
416   }
417
418   return TRUE;
419 }
420
421 /* Return TRUE if `smaller' is smaller than `bigger'. */
422
423 int silc_compare_timeval(struct timeval *t1, struct timeval *t2)
424 {
425   SilcInt32 s = t1->tv_sec - t2->tv_sec;
426   if (!s)
427     return t1->tv_usec - t2->tv_usec;
428   return s;
429 }