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