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