Used the remaining 2 bits from SilcTime structure.
[silc.git] / lib / silcutil / silctime.c
1 /*
2
3   silctime.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2003 - 2005 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 {
32   if (year > (1 << 15))
33     return FALSE;
34   if (month < 1 || month > 12)
35     return FALSE;
36   if (day < 1 || day > 31)
37     return FALSE;
38   if (hour > 23)
39     return FALSE;
40   if (minute > 60)
41     return FALSE;
42   if (second > 61)
43     return FALSE;
44
45   time->year = year;
46   time->month = month;
47   time->day = day;
48   time->hour = hour;
49   time->minute = minute;
50   time->second = second;
51
52   return TRUE;
53 }
54
55 /* Return time since Epoch */
56
57 SilcInt64 silc_time(void)
58 {
59   return (SilcInt64)time(NULL);
60 }
61
62 /* Returns time as string */
63
64 const char *silc_time_string(SilcInt64 timeval)
65 {
66   time_t curtime;
67   char *return_time;
68
69   if (!timeval)
70     curtime = time(NULL);
71   else
72     curtime = (time_t)timeval;
73   return_time = ctime(&curtime);
74   if (!return_time)
75     return NULL;
76   return_time[strlen(return_time) - 1] = '\0';
77
78   return (const char *)return_time;
79 }
80
81 /* Returns time as SilcTime structure */
82
83 SilcBool silc_time_value(SilcInt64 timeval, SilcTime ret_time)
84 {
85   struct tm *time;
86
87   if (!ret_time)
88     return TRUE;
89
90   if (!timeval)
91     timeval = silc_time();
92
93   time = localtime((time_t *)&timeval);
94   if (!time)
95     return FALSE;
96
97   memset(ret_time, 0, sizeof(*ret_time));
98   if (!silc_time_fill(ret_time, time->tm_year + 1900, time->tm_mon + 1,
99                       time->tm_mday, time->tm_hour, time->tm_min,
100                       time->tm_sec))
101     return FALSE;
102
103   ret_time->dst        = time->tm_isdst ? 1 : 0;
104 #ifdef SILC_WIN32
105   ret_time->utc_east   = _timezone < 0 ? 1 : 0;
106   ret_time->utc_hour   = (ret_time->utc_east ? (-(_timezone)) / 3600 :
107                           _timezone / 3600);
108   ret_time->utc_minute = (ret_time->utc_east ? (-(_timezone)) % 3600 :
109                           _timezone % 3600);
110 #else
111 #if defined(HAVE_TZSET)
112   ret_time->utc_east   = timezone < 0 ? 1 : 0;
113   ret_time->utc_hour   = (ret_time->utc_east ? (-(timezone)) / 3600 :
114                           timezone / 3600);
115   ret_time->utc_minute = (ret_time->utc_east ? (-(timezone)) % 3600 :
116                           timezone % 3600);
117 #endif /* HAVE_TZSET */
118 #endif /* SILC_WIN32 */
119
120   return TRUE;
121 }
122
123 /* Returns time from universal time string into SilcTime */
124
125 SilcBool silc_time_universal(const char *universal_time, SilcTime ret_time)
126 {
127   int ret;
128   unsigned int year, month, day, hour = 0, minute = 0, second = 0;
129   unsigned char z = 0;
130
131   if (!ret_time)
132     return TRUE;
133   memset(ret_time, 0, sizeof(*ret_time));
134
135   /* Parse the time string */
136   ret = sscanf(universal_time, "%02u%02u%02u%02u%02u%02u%c", &year, &month,
137                &day, &hour, &minute, &second, &z);
138   if (ret < 3) {
139     SILC_LOG_DEBUG(("Invalid UTC time string"));
140     return FALSE;
141   }
142
143   /* Fill the SilcTime structure */
144   ret = silc_time_fill(ret_time, year, month, day, hour, minute, second);
145   if (!ret) {
146     SILC_LOG_DEBUG(("Incorrect values in UTC time string"));
147     return FALSE;
148   }
149
150   /* Check timezone */
151   if (z == '-' || z == '+') {
152     ret = sscanf(universal_time + (ret * 2) + 1, "%02u%02u", &hour, &minute);
153     if (ret != 2) {
154       SILC_LOG_DEBUG(("Malformed UTC time string"));
155       return FALSE;
156     }
157
158     if (hour < 0 || hour > 23)
159       return FALSE;
160     if (minute < 0 || minute > 60)
161       return FALSE;
162
163     ret_time->utc_hour   = hour;
164     ret_time->utc_minute = minute;
165     ret_time->utc_east   = (z == '-') ? 0 : 1;
166   } else if (z != 'Z') {
167     SILC_LOG_DEBUG(("Invalid timezone"));
168     return FALSE;
169   }
170
171   /* UTC year must be fixed since it's represented only as YY not YYYY. */
172   ret_time->year += 1900;
173   if (ret_time->year < 1950)
174     ret_time->year += 100;
175
176   return TRUE;
177 }
178
179 /* Encode universal time string. */
180
181 SilcBool silc_time_universal_string(SilcTime timeval, char *ret_string,
182                                     SilcUInt32 ret_string_size)
183 {
184   int ret, len = 0;
185   memset(ret_string, 0, ret_string_size);
186   ret = snprintf(ret_string, ret_string_size - 1,
187                  "%02u%02u%02u%02u%02u%02u",
188                  timeval->year % 100, timeval->month, timeval->day,
189                  timeval->hour, timeval->minute, timeval->second);
190   if (ret < 0)
191     return FALSE;
192   len += ret;
193
194   if (!timeval->utc_hour && !timeval->utc_minute) {
195     ret = snprintf(ret_string + len, ret_string_size - 1 - len, "Z");
196     if (ret < 0)
197       return FALSE;
198     len += ret;
199   } else {
200     ret = snprintf(ret_string + len, ret_string_size - 1 - len,
201                    "%c%02u%02u", timeval->utc_east ? '+' : '-',
202                    timeval->utc_hour, timeval->utc_minute);
203     if (ret < 0)
204       return FALSE;
205     len += ret;
206   }
207
208   return TRUE;
209 }
210
211 /* Returns time from generalized time string into SilcTime */
212
213 SilcBool silc_time_generalized(const char *generalized_time, SilcTime ret_time)
214 {
215   int ret, i;
216   unsigned int year, month, day, hour = 0, minute = 0, second = 0;
217   unsigned int msecond = 0;
218   unsigned char z = 0;
219
220   if (!ret_time)
221     return TRUE;
222   memset(ret_time, 0, sizeof(*ret_time));
223
224   /* Parse the time string */
225   ret = sscanf(generalized_time, "%04u%02u%02u%02u%02u%02u", &year, &month,
226                &day, &hour, &minute, &second);
227   if (ret < 3) {
228     SILC_LOG_DEBUG(("Invalid generalized time string"));
229     return FALSE;
230   }
231
232   /* Fill the SilcTime structure */
233   ret = silc_time_fill(ret_time, year, month, day, hour, minute, second);
234   if (!ret) {
235     SILC_LOG_DEBUG(("Incorrect values in generalized time string"));
236     return FALSE;
237   }
238
239   /* Check fractions of second and/or timezone */
240   i = ret * 4;
241   ret = sscanf(generalized_time + i, "%c", &z);
242   if (ret != 1) {
243     SILC_LOG_DEBUG(("Malformed generalized time string"));
244     return FALSE;
245   }
246
247   if (z == '.') {
248     /* Take fractions of second */
249     int l;
250     i++;
251     ret = sscanf(generalized_time + i, "%u%n", &msecond, &l);
252     if (ret != 1) {
253       SILC_LOG_DEBUG(("Malformed generalized time string"));
254       return FALSE;
255     }
256     while (l > 4) {
257       msecond /= 10;
258       l--;
259     }
260     ret_time->msecond = msecond;
261     i += l;
262
263     /* Read optional timezone */
264     if (strlen(generalized_time) < i)
265       sscanf(generalized_time + i, "%c", &z);
266   }
267
268   /* Check timezone if present */
269   if (z == '-' || z == '+') {
270     ret = sscanf(generalized_time + i + 1, "%02u%02u", &hour, &minute);
271     if (ret != 2) {
272       SILC_LOG_DEBUG(("Malformed UTC time string"));
273       return FALSE;
274     }
275
276     if (hour < 0 || hour > 23)
277       return FALSE;
278     if (minute < 0 || minute > 60)
279       return FALSE;
280
281     ret_time->utc_hour   = hour;
282     ret_time->utc_minute = minute;
283     ret_time->utc_east   = (z == '-') ? 0 : 1;
284   }
285
286   return TRUE;
287 }
288
289 /* Encode generalized time string */
290
291 SilcBool silc_time_generalized_string(SilcTime timeval, char *ret_string,
292                                       SilcUInt32 ret_string_size)
293 {
294   int len = 0, ret;
295   memset(ret_string, 0, ret_string_size);
296   ret = snprintf(ret_string, ret_string_size - 1,
297                  "%04u%02u%02u%02u%02u%02u",
298                  timeval->year, timeval->month, timeval->day, timeval->hour,
299                  timeval->minute, timeval->second);
300   if (ret < 0)
301     return FALSE;
302   len += ret;
303
304   if (timeval->msecond) {
305     ret = snprintf(ret_string + len, ret_string_size - 1 - len,
306                    ".%lu", (unsigned long)timeval->msecond);
307     if (ret < 0)
308       return FALSE;
309     len += ret;
310   }
311
312   if (!timeval->utc_hour && !timeval->utc_minute) {
313     ret = snprintf(ret_string + len, ret_string_size - 1 - len, "Z");
314     if (ret < 0)
315       return FALSE;
316     len += ret;
317   } else {
318     ret = snprintf(ret_string + len, ret_string_size - 1 - len,
319                    "%c%02u%02u", timeval->utc_east ? '+' : '-',
320                    timeval->utc_hour, timeval->utc_minute);
321     if (ret < 0)
322       return FALSE;
323     len += ret;
324   }
325
326   return TRUE;
327 }