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