Added support for checking timezone and tm_gmtoff. Fixed
[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     return FALSE;
35   if (month < 1 || month > 12)
36     return FALSE;
37   if (day < 1 || day > 31)
38     return FALSE;
39   if (hour > 23)
40     return FALSE;
41   if (minute > 60)
42     return FALSE;
43   if (second > 61)
44     return FALSE;
45   if (msec > 1000)
46     return FALSE;
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
59 /* Return time since Epoch */
60
61 SilcInt64 silc_time(void)
62 {
63   return (SilcInt64)time(NULL);
64 }
65
66 /* Return time since Epoch in milliseconds */
67
68 SilcInt64 silc_time_msec(void)
69 {
70   struct timeval curtime;
71   silc_gettimeofday(&curtime);
72   return (curtime.tv_sec * (SilcUInt64)1000) +
73     (curtime.tv_usec / (SilcUInt64)1000);
74 }
75
76 /* Return time since Epoch in microseconds */
77
78 SilcInt64 silc_time_usec(void)
79 {
80   struct timeval curtime;
81   silc_gettimeofday(&curtime);
82   return (curtime.tv_sec * (SilcUInt64)1000000) + curtime.tv_usec;
83 }
84
85 /* Returns time as string */
86
87 const char *silc_time_string(SilcInt64 time_val)
88 {
89   time_t curtime;
90   char *return_time;
91
92   if (!time_val)
93     curtime = silc_time();
94   else
95     curtime = (time_t)time_val;
96   return_time = ctime(&curtime);
97   if (!return_time)
98     return NULL;
99   return_time[strlen(return_time) - 1] = '\0';
100
101   return (const char *)return_time;
102 }
103
104 /* Returns time as SilcTime structure */
105
106 SilcBool silc_time_value(SilcInt64 time_val, SilcTime ret_time)
107 {
108   struct tm *t;
109   unsigned int msec = 0;
110   time_t timeval;
111
112   if (!ret_time)
113     return TRUE;
114
115   if (!time_val)
116     time_val = silc_time_msec();
117
118   msec = (SilcUInt64)time_val % (SilcUInt64)1000;
119   timeval = (time_t)((SilcUInt64)time_val / (SilcUInt64)1000);
120
121   t = localtime(&timeval);
122   if (!t)
123     return FALSE;
124
125   memset(ret_time, 0, sizeof(*ret_time));
126   if (!silc_time_fill(ret_time, t->tm_year + 1900, t->tm_mon + 1,
127                       t->tm_mday, t->tm_hour, t->tm_min,
128                       t->tm_sec, msec))
129     return FALSE;
130
131   ret_time->dst        = t->tm_isdst ? 1 : 0;
132
133 #ifdef SILC_WIN32
134   ret_time->utc_east   = _timezone < 0 ? 1 : 0;
135   ret_time->utc_hour   = (ret_time->utc_east ? (-(_timezone)) / 3600 :
136                           _timezone / 3600);
137   ret_time->utc_minute = (ret_time->utc_east ? (-(_timezone)) % 3600 :
138                           _timezone % 3600);
139 #else
140 #if defined(HAVE_TIMEZONE)
141   ret_time->utc_east   = timezone < 0 ? 1 : 0;
142 #elif defined(HAVE_TM_GMTOFF)
143   ret_time->utc_east   = t->tm_gmtoff > 0 ? 1 : 0;
144 #elif defined(HAVE___TM_GMTOFF)
145   ret_time->utc_east   = t->__tm_gmtoff > 0 ? 1 : 0;
146 #elif defined(HAVE___TM_GMTOFF__)
147   ret_time->utc_east   = t->__tm_gmtoff__ > 0 ? 1 : 0;
148 #endif /* HAVE_TIMEZONE */
149
150 #if defined(HAVE_TIMEZONE)
151   ret_time->utc_hour   = (ret_time->utc_east ? (-(timezone)) / 3600 :
152                           timezone / 3600);
153   if (ret_time->dst)
154     ret_time->utc_hour++;
155   ret_time->utc_minute = (ret_time->utc_east ? (-(timezone)) % 3600 :
156                           timezone % 3600);
157 #elif defined(HAVE_GMTIME)
158   t = gmtime(&timeval);
159   if (t) {
160     ret_time->utc_hour   = (ret_time->utc_east
161                             ? ret_time->hour - t->tm_hour
162                             : ret_time->hour + t->tm_hour);
163     ret_time->utc_minute = (ret_time->utc_east
164                             ? ret_time->minute - t->tm_min
165                             : ret_time->minute + t->tm_min);
166   }
167 #endif /* HAVE_TIMEZONE */
168 #endif /* SILC_WIN32 */
169
170   return TRUE;
171 }
172
173 /* Returns timezone */
174
175 SilcBool silc_timezone(char *timezone, SilcUInt32 timezone_size)
176 {
177   SilcTimeStruct curtime;
178
179   if (timezone_size < 6)
180     return FALSE;
181
182   if (!silc_time_value(0, &curtime))
183     return FALSE;
184
185   if (!curtime.utc_hour && curtime.utc_minute)
186     silc_snprintf(timezone, timezone_size, "Z");
187   else if (curtime.utc_minute)
188     silc_snprintf(timezone, timezone_size, "%c%02d:%02d",
189                   curtime.utc_east ? '+' : '-', curtime.utc_hour,
190                   curtime.utc_minute);
191   else
192     silc_snprintf(timezone, timezone_size, "%c%02d",
193                   curtime.utc_east ? '+' : '-', curtime.utc_hour);
194
195   return TRUE;
196 }
197
198 /* Returns time from universal time string into SilcTime */
199
200 SilcBool silc_time_universal(const char *universal_time, SilcTime ret_time)
201 {
202   int ret;
203   unsigned int year, month, day, hour = 0, minute = 0, second = 0;
204   unsigned char z = 0;
205
206   if (!ret_time)
207     return TRUE;
208   memset(ret_time, 0, sizeof(*ret_time));
209
210   /* Parse the time string */
211   ret = sscanf(universal_time, "%02u%02u%02u%02u%02u%02u%c", &year, &month,
212                &day, &hour, &minute, &second, &z);
213   if (ret < 3) {
214     SILC_LOG_DEBUG(("Invalid UTC time string"));
215     return FALSE;
216   }
217
218   /* Fill the SilcTime structure */
219   ret = silc_time_fill(ret_time, year, month, day, hour, minute, second, 0);
220   if (!ret) {
221     SILC_LOG_DEBUG(("Incorrect values in UTC time string"));
222     return FALSE;
223   }
224
225   /* Check timezone */
226   if (z == '-' || z == '+') {
227     ret = sscanf(universal_time + (ret * 2) + 1, "%02u%02u", &hour, &minute);
228     if (ret != 2) {
229       SILC_LOG_DEBUG(("Malformed UTC time string"));
230       return FALSE;
231     }
232
233     if (hour < 0 || hour > 23)
234       return FALSE;
235     if (minute < 0 || minute > 60)
236       return FALSE;
237
238     ret_time->utc_hour   = hour;
239     ret_time->utc_minute = minute;
240     ret_time->utc_east   = (z == '-') ? 0 : 1;
241   } else if (z != 'Z') {
242     SILC_LOG_DEBUG(("Invalid timezone"));
243     return FALSE;
244   }
245
246   /* UTC year must be fixed since it's represented only as YY not YYYY. */
247   ret_time->year += 1900;
248   if (ret_time->year < 1950)
249     ret_time->year += 100;
250
251   return TRUE;
252 }
253
254 /* Encode universal time string. */
255
256 SilcBool silc_time_universal_string(SilcTime time_val, char *ret_string,
257                                     SilcUInt32 ret_string_size)
258 {
259   int ret, len = 0;
260   memset(ret_string, 0, ret_string_size);
261   ret = silc_snprintf(ret_string, ret_string_size - 1,
262                  "%02u%02u%02u%02u%02u%02u",
263                  time_val->year % 100, time_val->month, time_val->day,
264                  time_val->hour, time_val->minute, time_val->second);
265   if (ret < 0)
266     return FALSE;
267   len += ret;
268
269   if (!time_val->utc_hour && !time_val->utc_minute) {
270     ret = silc_snprintf(ret_string + len, ret_string_size - 1 - len, "Z");
271     if (ret < 0)
272       return FALSE;
273     len += ret;
274   } else {
275     ret = silc_snprintf(ret_string + len, ret_string_size - 1 - len,
276                    "%c%02u%02u", time_val->utc_east ? '+' : '-',
277                    time_val->utc_hour, time_val->utc_minute);
278     if (ret < 0)
279       return FALSE;
280     len += ret;
281   }
282
283   return TRUE;
284 }
285
286 /* Returns time from generalized time string into SilcTime */
287
288 SilcBool silc_time_generalized(const char *generalized_time, SilcTime ret_time)
289 {
290   int ret, i;
291   unsigned int year, month, day, hour = 0, minute = 0, second = 0;
292   unsigned int msecond = 0;
293   unsigned char z = 0;
294
295   if (!ret_time)
296     return TRUE;
297   memset(ret_time, 0, sizeof(*ret_time));
298
299   /* Parse the time string */
300   ret = sscanf(generalized_time, "%04u%02u%02u%02u%02u%02u", &year, &month,
301                &day, &hour, &minute, &second);
302   if (ret < 3) {
303     SILC_LOG_DEBUG(("Invalid generalized time string"));
304     return FALSE;
305   }
306
307   /* Fill the SilcTime structure */
308   ret = silc_time_fill(ret_time, year, month, day, hour, minute, second, 0);
309   if (!ret) {
310     SILC_LOG_DEBUG(("Incorrect values in generalized time string"));
311     return FALSE;
312   }
313
314   /* Check fractions of second and/or timezone */
315   i = ret * 4;
316   ret = sscanf(generalized_time + i, "%c", &z);
317   if (ret != 1) {
318     SILC_LOG_DEBUG(("Malformed generalized time string"));
319     return FALSE;
320   }
321
322   if (z == '.') {
323     /* Take fractions of second */
324     int l;
325     i++;
326     ret = sscanf(generalized_time + i, "%u%n", &msecond, &l);
327     if (ret != 1) {
328       SILC_LOG_DEBUG(("Malformed generalized time string"));
329       return FALSE;
330     }
331     while (l > 4) {
332       msecond /= 10;
333       l--;
334     }
335     ret_time->msecond = msecond;
336     i += l;
337
338     /* Read optional timezone */
339     if (strlen(generalized_time) < i)
340       sscanf(generalized_time + i, "%c", &z);
341   }
342
343   /* Check timezone if present */
344   if (z == '-' || z == '+') {
345     ret = sscanf(generalized_time + i + 1, "%02u%02u", &hour, &minute);
346     if (ret != 2) {
347       SILC_LOG_DEBUG(("Malformed UTC time string"));
348       return FALSE;
349     }
350
351     if (hour < 0 || hour > 23)
352       return FALSE;
353     if (minute < 0 || minute > 60)
354       return FALSE;
355
356     ret_time->utc_hour   = hour;
357     ret_time->utc_minute = minute;
358     ret_time->utc_east   = (z == '-') ? 0 : 1;
359   }
360
361   return TRUE;
362 }
363
364 /* Encode generalized time string */
365
366 SilcBool silc_time_generalized_string(SilcTime time_val, char *ret_string,
367                                       SilcUInt32 ret_string_size)
368 {
369   int len = 0, ret;
370   memset(ret_string, 0, ret_string_size);
371   ret = silc_snprintf(ret_string, ret_string_size - 1,
372                  "%04u%02u%02u%02u%02u%02u",
373                  time_val->year, time_val->month, time_val->day, time_val->hour,
374                  time_val->minute, time_val->second);
375   if (ret < 0)
376     return FALSE;
377   len += ret;
378
379   if (time_val->msecond) {
380     ret = silc_snprintf(ret_string + len, ret_string_size - 1 - len,
381                    ".%lu", (unsigned long)time_val->msecond);
382     if (ret < 0)
383       return FALSE;
384     len += ret;
385   }
386
387   if (!time_val->utc_hour && !time_val->utc_minute) {
388     ret = silc_snprintf(ret_string + len, ret_string_size - 1 - len, "Z");
389     if (ret < 0)
390       return FALSE;
391     len += ret;
392   } else {
393     ret = silc_snprintf(ret_string + len, ret_string_size - 1 - len,
394                    "%c%02u%02u", time_val->utc_east ? '+' : '-',
395                    time_val->utc_hour, time_val->utc_minute);
396     if (ret < 0)
397       return FALSE;
398     len += ret;
399   }
400
401   return TRUE;
402 }
403
404 /* Return TRUE if `smaller' is smaller than `bigger'. */
405
406 int silc_compare_timeval(struct timeval *t1, struct timeval *t2)
407 {
408   SilcInt32 s = t1->tv_sec - t2->tv_sec;
409   if (!s)
410     return t1->tv_usec - t2->tv_usec;
411   return s;
412 }