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