1 // This may look like C code, but it is really -*- C++ -*-
2
3 // ------------------------------------------------------------------
4 // The Goldware Library
5 // Copyright (C) 1990-1999 Odinn Sorensen
6 // Copyright (C) 1999-2000 Alexander S. Aganichev
7 // ------------------------------------------------------------------
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
12 //
13 // This library 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 GNU
16 // Library General Public License for more details.
17 //
18 // You should have received a copy of the GNU Library General Public
19 // License along with this program; if not, write to the Free
20 // Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21 // MA 02111-1307, USA
22 // ------------------------------------------------------------------
23 // $Id: gtimutil.cpp,v 1.13 2011/04/10 06:26:01 stas_degteff Exp $
24 // ------------------------------------------------------------------
25 // Time utility functions.
26 // ------------------------------------------------------------------
27
28 #include <cstdarg>
29 #include <cstdio>
30 #include <gmemall.h>
31 #include <gstrall.h>
32 #include <gtimall.h>
33 #include <gutlmisc.h>
34 #include <gutlmtsk.h>
35
36 #ifdef __OS2__
37 #define INCL_BASE
38 #include <os2.h>
39 #endif
40
41 #ifdef __WIN32__
42 #include <windows.h>
43 #endif
44
45
46 // ------------------------------------------------------------------
47
48 const char* gmonths[] = {
49 "ERR",
50 "Jan", "Feb", "Mar",
51 "Apr", "May", "Jun",
52 "Jul", "Aug", "Sep",
53 "Oct", "Nov", "Dec",
54 "ERR"
55 };
56
57
58 // ------------------------------------------------------------------
59 // Returns current timezone offset based on TZ environment variable.
60
tzoffset()61 int tzoffset()
62 {
63 time32_t t1 = gtime(NULL);
64 struct tm tp; ggmtime(&tp, &t1);
65 tp.tm_isdst = -1;
66 time32_t t2 = gmktime(&tp);
67 int dt = (int)(t1 - t2);
68 return (dt / 3600) * 100 + (dt % 3600) / 60;
69 }
70
71
72 // ------------------------------------------------------------------
73
74 char* __gampm[2] = {
75 "AM", "PM"
76 };
77
78 char* __gsweekday[7] = {
79 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
80 };
81
82 char* __glweekday[7] = {
83 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
84 };
85
86 char* __gsmonth[12] = {
87 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
88 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
89 };
90
91 char* __glmonth[12] = {
92 "January", "February", "March", "April", "May", "June",
93 "July", "August", "September", "October", "November", "December"
94 };
95
96 char* gampm[2] = {
97 "AM", "PM"
98 };
99
100 char* gsweekday[7] = {
101 NULL, NULL, NULL, NULL, NULL, NULL, NULL
102 };
103
104 char* glweekday[7] = {
105 NULL, NULL, NULL, NULL, NULL, NULL, NULL
106 };
107
108 char* gsmonth[12] = {
109 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
110 };
111
112 char* glmonth[12] = {
113 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
114 };
115
116
117 // ------------------------------------------------------------------
118 // written 6 september 1989 by jim nutt
119 // released into the public domain by jim nutt
120 // modified 21-Oct-89 by Rob Duff
121
122 static char buf[26];
123 static char format[] = "%?";
124
125 static int gpow[5] = { 1, 10, 100, 1000, 10000 };
126
127
128 // ------------------------------------------------------------------
129 // static void strfmt(char *str, char *fmt);
130 // simple sprintf for strftime
131 // each format descriptor is of the form %n
132 // where n goes from zero to four
133 // 0 -- string %s
134 // 1..4 -- int %?.?d
135
strfmt(char * str,const char * fmt,...)136 static void strfmt(char *str, const char *fmt, ...) {
137
138 int ival, ilen;
139 char *sval;
140 va_list vp;
141
142 va_start(vp, fmt);
143 while(*fmt) {
144 if(*fmt++ == '%') {
145 ilen = *fmt++ - '0';
146 if(ilen == 0) // zero means string arg
147 {
148 sval = va_arg(vp, char*);
149 while(*sval)
150 *str++ = *sval++;
151 }
152 else { // always leading zeros
153 if(ilen == ('-' - '0')) {
154 ilen = *fmt++ - '0';
155 ival = va_arg(vp, int);
156 bool padding = true;
157 while(ilen) {
158 ival %= gpow[ilen];
159 int cval = ival / gpow[ilen-1];
160 if(cval)
161 padding = false;
162 if(--ilen and padding)
163 cval = ' ' - '0';
164 *str++ = (char)('0' + cval);
165 }
166 }
167 else {
168 ival = va_arg(vp, int);
169 while(ilen) {
170 ival %= gpow[ilen--];
171 *str++ = (char)('0' + ival / gpow[ilen]);
172 }
173 }
174 }
175 }
176 else *str++ = fmt[-1];
177 }
178 *str = '\0';
179 va_end(vp);
180 }
181
182
183 // ------------------------------------------------------------------
184 // In differ to ANSI C strftime this function supports reloading of
185 // field names
186 // Return NULL if output truncated, otherwize return s.
187
188 #define setvar(internal,external,field) \
189 char **internal = external; \
190 if(internal[field] == NULL) internal = __##external;
191
strftimei(char * s,size_t maxs,const char * f,const struct tm * t)192 char *strftimei(char *s, size_t maxs, const char *f, const struct tm *t)
193 {
194 char *p, *q, *r;
195
196 setvar(aday, gsweekday, t->tm_wday);
197 setvar(day, glweekday, t->tm_wday);
198 setvar(amonth, gsmonth, t->tm_mon);
199 setvar(month, glmonth, t->tm_mon);
200
201 p = s;
202 q = s + maxs - 1;
203 while((*f != NUL)) {
204 if(*f++ == '%') {
205 r = buf;
206 switch(*f++) {
207
208 case '%':
209 format[1] = NUL;
210 r = format;
211 break;
212
213 case 'a':
214 r = aday[t->tm_wday];
215 break;
216
217 case 'A':
218 r = day[t->tm_wday];
219 break;
220
221 case 'b':
222 r = amonth[t->tm_mon];
223 break;
224
225 case 'B':
226 r = month[t->tm_mon];
227 break;
228
229 case 'C':
230 strfmt(r, "%0 %0 %-2 %2:%2:%2 %4",
231 aday[t->tm_wday], amonth[t->tm_mon],
232 t->tm_mday,t->tm_hour, t->tm_min,
233 t->tm_sec, t->tm_year+1900);
234 break;
235
236 case 'e':
237 strfmt(r, "%-2", t->tm_mday);
238 break;
239
240 case 'E':
241 sprintf(r, "%u", (uint)t->tm_mday);
242 break;
243
244 case 'p':
245 r = gampm[(t->tm_hour>11)?1:0];
246 break;
247
248 default:
249 format[1] = f[-1];
250 if(strftime(r, 26, format, t) == 0) {
251 buf[0] = '%'; // reconstruct the format
252 buf[1] = f[-1];
253 buf[2] = '\0';
254 if(buf[1] == 0)
255 f--; // back up if at end of string
256 }
257 break;
258 }
259 while(*r) {
260 if(p == q) {
261 *q = '\0';
262 return 0;
263 }
264 *p++ = *r++;
265 }
266 }
267 else {
268 if(p == q) {
269 *q = '\0';
270 return 0;
271 }
272 *p++ = f[-1];
273 }
274 }
275 *p = '\0';
276 return s;
277 }
278
279
280 // ------------------------------------------------------------------
281 // Convert string-month to integer
282
str2mon(const char * ptr)283 int str2mon(const char* ptr) {
284
285 int mon;
286
287 switch(g_toupper(*ptr)) {
288 case 'A':
289 if(g_toupper(ptr[1]) == 'P')
290 mon = 4;
291 else
292 mon = 8;
293 break;
294 case 'D':
295 mon = 12;
296 break;
297 case 'F':
298 mon = 2;
299 break;
300 case 'J':
301 if(g_toupper(ptr[1]) == 'A')
302 mon = 1;
303 else if(g_toupper(ptr[2]) == 'L')
304 mon = 7;
305 else
306 mon = 6;
307 break;
308 case 'M':
309 if(g_toupper(ptr[2]) == 'R')
310 mon = 3;
311 else
312 mon = 5;
313 break;
314 case 'N':
315 mon = 11;
316 break;
317 case 'O':
318 mon = 10;
319 break;
320 case 'S':
321 mon = 9;
322 break;
323 default:
324 mon = 0;
325 }
326
327 return mon;
328 }
329
330
331 // ------------------------------------------------------------------
332
FTimeToTime(FTime * __ftime,struct tm * __tm)333 time32_t FTimeToTime(FTime* __ftime, struct tm* __tm) {
334
335 struct tm _tm;
336 uint32_t _time = 0;
337
338 if(__tm == NULL)
339 __tm = &_tm;
340
341 // Only try to convert a valid date
342 if(__ftime->ft_year >= 5) { // FidoNet standards didn't exist before 1985
343 if((__ftime->ft_day >= 1) and (__ftime->ft_day <= 31)) {
344 if((__ftime->ft_month >= 1) and (__ftime->ft_month <= 12)) {
345
346 __tm->tm_year = __ftime->ft_year + 80;
347 __tm->tm_mon = __ftime->ft_month - 1;
348 __tm->tm_mday = __ftime->ft_day;
349 __tm->tm_hour = __ftime->ft_hour;
350 __tm->tm_min = __ftime->ft_min;
351 __tm->tm_sec = __ftime->ft_tsec * 2;
352 __tm->tm_isdst = -1;
353
354 time32_t a = gmktime(__tm);
355 struct tm tp; ggmtime(&tp, &a);
356 tp.tm_isdst = -1;
357 time32_t b = gmktime(&tp);
358 _time = a + a - b;
359
360 if(_time == (uint32_t)0xFFFFFFFFL)
361 _time = 0;
362 }
363 }
364 }
365
366 return _time;
367 }
368
369
370 // ------------------------------------------------------------------
371
TimeToFTime(time32_t __time)372 FTime TimeToFTime(time32_t __time) {
373
374 FTime _ft;
375 memset(&_ft, 0, sizeof(FTime));
376
377 if (__time)
378 {
379 struct tm _tmp; ggmtime(&_tmp, &__time);
380 _ft.ft_year = (word)(_tmp.tm_year - 80);
381 _ft.ft_month = (word)(_tmp.tm_mon + 1);
382 _ft.ft_day = (word)(_tmp.tm_mday);
383 _ft.ft_hour = (word)(_tmp.tm_hour);
384 _ft.ft_min = (word)(_tmp.tm_min);
385 _ft.ft_tsec = (word)(_tmp.tm_sec / 2);
386 }
387
388 return _ft;
389 }
390
391
392 // ------------------------------------------------------------------
393
FidoTimeToUnix(char * ptr)394 time32_t FidoTimeToUnix(char* ptr) {
395
396 bool date_ok = false;
397 int year=0, month=0, day=0;
398 int hour=0, minute=0, second=0;
399
400 ptr = strskip_wht(ptr);
401 if(not isdigit(*ptr)) {
402 // Skip past weekday string (SEA format)
403 ptr = strskip_wht(strskip_txt(ptr));
404 }
405 if(*ptr) {
406 if(isdigit(*ptr)) {
407 day = atoi(ptr);
408 ptr = strskip_wht(strskip_txt(ptr));
409 if(g_isalpha(*ptr)) {
410 month = str2mon(ptr);
411 if(month) {
412 ptr = strskip_wht(strskip_txt(ptr));
413 if(isdigit(*ptr)) {
414 year = atoi(ptr);
415 ptr = strskip_wht(strskip_txt(ptr));
416 if(isdigit(*ptr)) {
417 hour = atoi(ptr);
418 ptr = strskip_digits(ptr);
419 if(*ptr and isdigit(ptr[1])) {
420 minute = atoi(++ptr);
421 date_ok = true;
422 // The seconds part is only in the FTS-1 format
423 ptr = strskip_digits(ptr);
424 if(*ptr and isdigit(ptr[1])) {
425 second = atoi(++ptr);
426 }
427 }
428 }
429 }
430 }
431 }
432 }
433 }
434
435 // Convert datetime to UNIX timestamp
436 if(date_ok) {
437 struct tm t;
438 t.tm_year = (year < 80) ? (year+100) : year;
439 t.tm_mon = month - 1;
440 t.tm_mday = day;
441 t.tm_hour = hour;
442 t.tm_min = minute;
443 t.tm_sec = second;
444 t.tm_isdst = -1;
445 time32_t a = gmktime(&t);
446 struct tm tp; ggmtime(&tp, &a);
447 tp.tm_isdst = -1;
448 time32_t b = gmktime(&tp);
449 return a + a - b;
450 }
451 return (uint32_t)-1;
452 }
453
454
455 // ------------------------------------------------------------------
456
TimeToStr(char * buf,time32_t t)457 char* TimeToStr(char* buf, time32_t t)
458 {
459 struct tm tm; ggmtime(&tm, &t);
460 return strftimei(buf, 20, "%Y-%m-%d %H:%M:%S", &tm);
461 }
462
463
464 // ------------------------------------------------------------------
465
FTimeToStr(char * buf,FTime & t)466 char* FTimeToStr(char* buf, FTime& t) {
467
468 sprintf(buf, "%04u-%02u-%02u %02u:%02u:%02u",
469 t.ft_year+1980, t.ft_month, t.ft_day,
470 t.ft_hour, t.ft_min, t.ft_tsec*2
471 );
472 return buf;
473 }
474
475
476 // ------------------------------------------------------------------
477
c_str(char * buf)478 const char* gfiletime::c_str(char* buf) {
479
480 sprintf(buf, "%04u-%02u-%02u %02u:%02u:%02u",
481 ft_year+1980, ft_month, ft_day,
482 ft_hour, ft_min, ft_tsec*2
483 );
484 return buf;
485 }
486
487
488 // ------------------------------------------------------------------
489
c_str(char * buf)490 const char* gopustime::c_str(char* buf) {
491
492 sprintf(buf, "%04u-%02u-%02u %02u:%02u:%02u",
493 ft_year+1980, ft_month, ft_day,
494 ft_hour, ft_min, ft_tsec*2
495 );
496 return buf;
497 }
498
499
500 // ------------------------------------------------------------------
501