1 //
2 // strftime.c
3 //
4 // Date to string conversion
5 //
6 // Copyright (C) 2002 Michael Ringgaard. All rights reserved.
7 //
8 // Redistribution and use in source and binary forms, with or without
9 // modification, are permitted provided that the following conditions
10 // are met:
11 //
12 // 1. Redistributions of source code must retain the above copyright
13 // notice, this list of conditions and the following disclaimer.
14 // 2. Redistributions in binary form must reproduce the above copyright
15 // notice, this list of conditions and the following disclaimer in the
16 // documentation and/or other materials provided with the distribution.
17 // 3. Neither the name of the project nor the names of its contributors
18 // may be used to endorse or promote products derived from this software
19 // without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
25 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 // SUCH DAMAGE.
32 //
33
34 /////////////////////////////////////////////////////////////////////////////////////////////
35 // //
36 // time() //
37 // //
38 /////////////////////////////////////////////////////////////////////////////////////////////
39
40
41 #include <windows.h>
42 #include <time.h>
43 #include "ce_time.h"
44
45 time_t
time(time_t * timer)46 time(time_t* timer)
47 {
48 SYSTEMTIME systime;
49 struct tm tmtime;
50 time_t tt;
51
52 GetLocalTime(&systime);
53
54 tmtime.tm_year = systime.wYear-1900;
55 tmtime.tm_mon = systime.wMonth-1;
56 tmtime.tm_mday = systime.wDay;
57 tmtime.tm_wday = systime.wDayOfWeek;
58 tmtime.tm_hour = systime.wHour;
59 tmtime.tm_min = systime.wMinute;
60 tmtime.tm_sec = systime.wSecond;
61
62 tt = mktime(&tmtime);
63
64 if(timer)
65 *timer = tt;
66
67 return tt;
68 }
69
70
71 /////////////////////////////////////////////////////////////////////////////////////////////
72 // //
73 // mktime() //
74 // //
75 /////////////////////////////////////////////////////////////////////////////////////////////
76
77 static int month_to_day[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
78
mktime(struct tm * t)79 time_t mktime(struct tm *t)
80 {
81 short month, year;
82 time_t result;
83
84 month = t->tm_mon;
85 year = t->tm_year + month / 12 + 1900;
86 month %= 12;
87 if (month < 0)
88 {
89 year -= 1;
90 month += 12;
91 }
92 result = (year - 1970) * 365 + (year - 1969) / 4 + month_to_day[month];
93 result = (year - 1970) * 365 + month_to_day[month];
94 if (month <= 1)
95 year -= 1;
96 result += (year - 1968) / 4;
97 result -= (year - 1900) / 100;
98 result += (year - 1600) / 400;
99 result += t->tm_mday;
100 result -= 1;
101 result *= 24;
102 result += t->tm_hour;
103 result *= 60;
104 result += t->tm_min;
105 result *= 60;
106 result += t->tm_sec;
107 return(result);
108 }
109
110
111 /////////////////////////////////////////////////////////////////////////////////////////////
112 // //
113 // strftime() - taken from OpenBSD //
114 // //
115 /////////////////////////////////////////////////////////////////////////////////////////////
116
117 #define IN_NONE 0
118 #define IN_SOME 1
119 #define IN_THIS 2
120 #define IN_ALL 3
121 #define CHAR_BIT 8
122
123 #define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
124 #define TYPE_SIGNED(type) (((type) -1) < 0)
125
126 #define INT_STRLEN_MAXIMUM(type) \
127 ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + 1 + TYPE_SIGNED(type))
128
129 #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
130
131 #define MONSPERYEAR 12
132 #define DAYSPERWEEK 7
133 #define TM_YEAR_BASE 1900
134 #define HOURSPERDAY 24
135 #define DAYSPERNYEAR 365
136 #define DAYSPERLYEAR 366
137
138 static char wildabbr[] = "WILDABBR";
139
140 static char * tzname[2] = {
141 wildabbr,
142 wildabbr
143 };
144
145
146 #define Locale (&C_time_locale)
147
148 struct lc_time_T {
149 const char * mon[MONSPERYEAR];
150 const char * month[MONSPERYEAR];
151 const char * wday[DAYSPERWEEK];
152 const char * weekday[DAYSPERWEEK];
153 const char * X_fmt;
154 const char * x_fmt;
155 const char * c_fmt;
156 const char * am;
157 const char * pm;
158 const char * date_fmt;
159 };
160
161 static const struct lc_time_T C_time_locale = {
162 {
163 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
164 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
165 }, {
166 "January", "February", "March", "April", "May", "June",
167 "July", "August", "September", "October", "November", "December"
168 }, {
169 "Sun", "Mon", "Tue", "Wed",
170 "Thu", "Fri", "Sat"
171 }, {
172 "Sunday", "Monday", "Tuesday", "Wednesday",
173 "Thursday", "Friday", "Saturday"
174 },
175
176 /* X_fmt */
177 "%H:%M:%S",
178
179 /*
180 ** x_fmt
181 ** C99 requires this format.
182 ** Using just numbers (as here) makes Quakers happier;
183 ** it's also compatible with SVR4.
184 */
185 "%m/%d/%y",
186
187 /*
188 ** c_fmt
189 ** C99 requires this format.
190 ** Previously this code used "%D %X", but we now conform to C99.
191 ** Note that
192 ** "%a %b %d %H:%M:%S %Y"
193 ** is used by Solaris 2.3.
194 */
195 "%a %b %e %T %Y",
196
197 /* am */
198 "AM",
199
200 /* pm */
201 "PM",
202
203 /* date_fmt */
204 "%a %b %e %H:%M:%S %Z %Y"
205 };
206
207
208 static char *
_add(const char * str,char * pt,const char * const ptlim)209 _add(const char * str, char * pt, const char * const ptlim)
210 {
211 while (pt < ptlim && (*pt = *str++) != '\0')
212 ++pt;
213 return pt;
214 }
215
216
217 static char *
_conv(const int n,const char * const format,char * const pt,const char * const ptlim)218 _conv(const int n, const char * const format, char * const pt, const char * const ptlim)
219 {
220 char buf[INT_STRLEN_MAXIMUM(int) + 1];
221
222 (void) _snprintf(buf, sizeof buf, format, n);
223 return _add(buf, pt, ptlim);
224 }
225
226
227 static char *
_fmt(const char * format,const struct tm * const t,char * pt,const char * const ptlim,int * warnp)228 _fmt(const char * format, const struct tm * const t, char * pt, const char * const ptlim, int * warnp)
229 {
230 for ( ; *format; ++format) {
231 if (*format == '%') {
232 label:
233 switch (*++format) {
234 case '\0':
235 --format;
236 break;
237 case 'A':
238 pt = _add((t->tm_wday < 0 ||
239 t->tm_wday >= DAYSPERWEEK) ?
240 "?" : Locale->weekday[t->tm_wday],
241 pt, ptlim);
242 continue;
243 case 'a':
244 pt = _add((t->tm_wday < 0 ||
245 t->tm_wday >= DAYSPERWEEK) ?
246 "?" : Locale->wday[t->tm_wday],
247 pt, ptlim);
248 continue;
249 case 'B':
250 pt = _add((t->tm_mon < 0 ||
251 t->tm_mon >= MONSPERYEAR) ?
252 "?" : Locale->month[t->tm_mon],
253 pt, ptlim);
254 continue;
255 case 'b':
256 case 'h':
257 pt = _add((t->tm_mon < 0 ||
258 t->tm_mon >= MONSPERYEAR) ?
259 "?" : Locale->mon[t->tm_mon],
260 pt, ptlim);
261 continue;
262 case 'C':
263 /*
264 ** %C used to do a...
265 ** _fmt("%a %b %e %X %Y", t);
266 ** ...whereas now POSIX 1003.2 calls for
267 ** something completely different.
268 ** (ado, 1993-05-24)
269 */
270 pt = _conv((t->tm_year + TM_YEAR_BASE) / 100,
271 "%02d", pt, ptlim);
272 continue;
273 case 'c':
274 {
275 int warn2 = IN_SOME;
276
277 pt = _fmt(Locale->c_fmt, t, pt, ptlim, warnp);
278 if (warn2 == IN_ALL)
279 warn2 = IN_THIS;
280 if (warn2 > *warnp)
281 *warnp = warn2;
282 }
283 continue;
284 case 'D':
285 pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
286 continue;
287 case 'd':
288 pt = _conv(t->tm_mday, "%02d", pt, ptlim);
289 continue;
290 case 'E':
291 case 'O':
292 /*
293 ** C99 locale modifiers.
294 ** The sequences
295 ** %Ec %EC %Ex %EX %Ey %EY
296 ** %Od %oe %OH %OI %Om %OM
297 ** %OS %Ou %OU %OV %Ow %OW %Oy
298 ** are supposed to provide alternate
299 ** representations.
300 */
301 goto label;
302 case 'e':
303 pt = _conv(t->tm_mday, "%2d", pt, ptlim);
304 continue;
305 case 'F':
306 pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
307 continue;
308 case 'H':
309 pt = _conv(t->tm_hour, "%02d", pt, ptlim);
310 continue;
311 case 'I':
312 pt = _conv((t->tm_hour % 12) ?
313 (t->tm_hour % 12) : 12,
314 "%02d", pt, ptlim);
315 continue;
316 case 'j':
317 pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
318 continue;
319 case 'k':
320 /*
321 ** This used to be...
322 ** _conv(t->tm_hour % 12 ?
323 ** t->tm_hour % 12 : 12, 2, ' ');
324 ** ...and has been changed to the below to
325 ** match SunOS 4.1.1 and Arnold Robbins'
326 ** strftime version 3.0. That is, "%k" and
327 ** "%l" have been swapped.
328 ** (ado, 1993-05-24)
329 */
330 pt = _conv(t->tm_hour, "%2d", pt, ptlim);
331 continue;
332 #ifdef KITCHEN_SINK
333 case 'K':
334 /*
335 ** After all this time, still unclaimed!
336 */
337 pt = _add("kitchen sink", pt, ptlim);
338 continue;
339 #endif /* defined KITCHEN_SINK */
340 case 'l':
341 /*
342 ** This used to be...
343 ** _conv(t->tm_hour, 2, ' ');
344 ** ...and has been changed to the below to
345 ** match SunOS 4.1.1 and Arnold Robbin's
346 ** strftime version 3.0. That is, "%k" and
347 ** "%l" have been swapped.
348 ** (ado, 1993-05-24)
349 */
350 pt = _conv((t->tm_hour % 12) ?
351 (t->tm_hour % 12) : 12,
352 "%2d", pt, ptlim);
353 continue;
354 case 'M':
355 pt = _conv(t->tm_min, "%02d", pt, ptlim);
356 continue;
357 case 'm':
358 pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
359 continue;
360 case 'n':
361 pt = _add("\n", pt, ptlim);
362 continue;
363 case 'p':
364 pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
365 Locale->pm :
366 Locale->am,
367 pt, ptlim);
368 continue;
369 case 'R':
370 pt = _fmt("%H:%M", t, pt, ptlim, warnp);
371 continue;
372 case 'r':
373 pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
374 continue;
375 case 'S':
376 pt = _conv(t->tm_sec, "%02d", pt, ptlim);
377 continue;
378 case 's':
379 {
380 struct tm tm;
381 char buf[INT_STRLEN_MAXIMUM(
382 time_t) + 1];
383 time_t mkt;
384
385 tm = *t;
386 mkt = mktime(&tm);
387 if (TYPE_SIGNED(time_t))
388 (void) _snprintf(buf, sizeof buf,
389 "%ld", (long) mkt);
390 else (void) _snprintf(buf, sizeof buf,
391 "%lu", (unsigned long) mkt);
392 pt = _add(buf, pt, ptlim);
393 }
394 continue;
395 case 'T':
396 pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
397 continue;
398 case 't':
399 pt = _add("\t", pt, ptlim);
400 continue;
401 case 'U':
402 pt = _conv((t->tm_yday + DAYSPERWEEK -
403 t->tm_wday) / DAYSPERWEEK,
404 "%02d", pt, ptlim);
405 continue;
406 case 'u':
407 /*
408 ** From Arnold Robbins' strftime version 3.0:
409 ** "ISO 8601: Weekday as a decimal number
410 ** [1 (Monday) - 7]"
411 ** (ado, 1993-05-24)
412 */
413 pt = _conv((t->tm_wday == 0) ?
414 DAYSPERWEEK : t->tm_wday,
415 "%d", pt, ptlim);
416 continue;
417 case 'V': /* ISO 8601 week number */
418 case 'G': /* ISO 8601 year (four digits) */
419 case 'g': /* ISO 8601 year (two digits) */
420 {
421 int year;
422 int yday;
423 int wday;
424 int w;
425
426 year = t->tm_year + TM_YEAR_BASE;
427 yday = t->tm_yday;
428 wday = t->tm_wday;
429 for ( ; ; ) {
430 int len;
431 int bot;
432 int top;
433
434 len = isleap(year) ?
435 DAYSPERLYEAR :
436 DAYSPERNYEAR;
437 /*
438 ** What yday (-3 ... 3) does
439 ** the ISO year begin on?
440 */
441 bot = ((yday + 11 - wday) %
442 DAYSPERWEEK) - 3;
443 /*
444 ** What yday does the NEXT
445 ** ISO year begin on?
446 */
447 top = bot -
448 (len % DAYSPERWEEK);
449 if (top < -3)
450 top += DAYSPERWEEK;
451 top += len;
452 if (yday >= top) {
453 ++year;
454 w = 1;
455 break;
456 }
457 if (yday >= bot) {
458 w = 1 + ((yday - bot) /
459 DAYSPERWEEK);
460 break;
461 }
462 --year;
463 yday += isleap(year) ?
464 DAYSPERLYEAR :
465 DAYSPERNYEAR;
466 }
467 if (*format == 'V')
468 pt = _conv(w, "%02d",
469 pt, ptlim);
470 else if (*format == 'g') {
471 *warnp = IN_ALL;
472 pt = _conv(year % 100, "%02d",
473 pt, ptlim);
474 } else pt = _conv(year, "%04d",
475 pt, ptlim);
476 }
477 continue;
478 case 'v':
479 pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
480 continue;
481 case 'W':
482 pt = _conv((t->tm_yday + DAYSPERWEEK -
483 (t->tm_wday ?
484 (t->tm_wday - 1) :
485 (DAYSPERWEEK - 1))) / DAYSPERWEEK,
486 "%02d", pt, ptlim);
487 continue;
488 case 'w':
489 pt = _conv(t->tm_wday, "%d", pt, ptlim);
490 continue;
491 case 'X':
492 pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
493 continue;
494 case 'x':
495 {
496 int warn2 = IN_SOME;
497
498 pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
499 if (warn2 == IN_ALL)
500 warn2 = IN_THIS;
501 if (warn2 > *warnp)
502 *warnp = warn2;
503 }
504 continue;
505 case 'y':
506 *warnp = IN_ALL;
507 pt = _conv((t->tm_year + TM_YEAR_BASE) % 100,
508 "%02d", pt, ptlim);
509 continue;
510 case 'Y':
511 pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d",
512 pt, ptlim);
513 continue;
514 case 'Z':
515 if (t->tm_isdst >= 0)
516 pt = _add(tzname[t->tm_isdst != 0],
517 pt, ptlim);
518 /*
519 ** C99 says that %Z must be replaced by the
520 ** empty string if the time zone is not
521 ** determinable.
522 */
523 continue;
524 case 'z':
525 {
526 int diff;
527 char const * sign;
528
529 if (t->tm_isdst < 0)
530 continue;
531 continue;
532 if (diff < 0) {
533 sign = "-";
534 diff = -diff;
535 } else sign = "+";
536 pt = _add(sign, pt, ptlim);
537 diff /= 60;
538 pt = _conv((diff/60)*100 + diff%60,
539 "%04d", pt, ptlim);
540 }
541 continue;
542 case '+':
543 pt = _fmt(Locale->date_fmt, t, pt, ptlim,
544 warnp);
545 continue;
546 case '%':
547 default:
548 break;
549 }
550 }
551 if (pt == ptlim)
552 break;
553 *pt++ = *format;
554 }
555 return pt;
556 }
557
558
559 size_t
strftime(char * const s,const size_t maxsize,const char * const format,const struct tm * const t)560 strftime(char * const s, const size_t maxsize, const char * const format, const struct tm * const t)
561 {
562 char * p;
563 int warn;
564
565 //tzset();
566
567 warn = IN_NONE;
568 p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn);
569
570 if (p == s + maxsize) {
571 if (maxsize > 0)
572 s[maxsize - 1] = '\0';
573 return 0;
574 }
575 *p = '\0';
576 return p - s;
577 }
578
579
580
581 /////////////////////////////////////////////////////////////////////////////////////////////
582 // //
583 // gmtime() //
584 // //
585 /////////////////////////////////////////////////////////////////////////////////////////////
586
587
588
589 static struct tm mytm;
590
591 static int DMonth[13] = { 0,31,59,90,120,151,181,212,243,273,304,334,365 };
592 static int monthCodes[12] = { 6, 2, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
593
594
595 static int
calcDayOfWeek(const struct tm * nTM)596 calcDayOfWeek(const struct tm* nTM)
597 {
598 int day;
599
600 day = (nTM->tm_year%100);
601 day += day/4;
602 day += monthCodes[nTM->tm_mon];
603 day += nTM->tm_mday;
604 while(day>=7)
605 day -= 7;
606
607 return day;
608 }
609
610
611 static struct tm *
gmtime(const time_t * timer)612 gmtime(const time_t *timer)
613 {
614 unsigned long x = *timer;
615 int imin, ihrs, iday, iyrs;
616 int sec, min, hrs, day, mon, yrs;
617 int lday, qday, jday, mday;
618
619
620 imin = x / 60; // whole minutes since 1/1/70
621 sec = x - (60 * imin); // leftover seconds
622 ihrs = imin / 60; // whole hours since 1/1/70
623 min = imin - 60 * ihrs; // leftover minutes
624 iday = ihrs / 24; // whole days since 1/1/70
625 hrs = ihrs - 24 * iday; // leftover hours
626 iday = iday + 365 + 366; // whole days since 1/1/68
627 lday = iday / (( 4* 365) + 1); // quadyr = 4 yr period = 1461 days
628 qday = iday % (( 4 * 365) + 1); // days since current quadyr began
629 if(qday >= (31 + 29)) // if past feb 29 then
630 lday = lday + 1; // add this quadyr�s leap day to the
631 // # of quadyrs (leap days) since 68
632 iyrs = (iday - lday) / 365; // whole years since 1968
633 jday = iday - (iyrs * 365) - lday; // days since 1 /1 of current year.
634 if(qday <= 365 && qday >= 60) // if past 2/29 and a leap year then
635 jday = jday + 1; // add a leap day to the # of whole
636 // days since 1/1 of current year
637 yrs = iyrs + 1968; // compute year
638 mon = 13; // estimate month ( +1)
639 mday = 366; // max days since 1/1 is 365
640 while(jday < mday) // mday = # of days passed from 1/1
641 { // until first day of current month
642 mon = mon - 1; // mon = month (estimated)
643 mday = DMonth[mon]; // # elapsed days at first of �mon�
644 if((mon > 2) && (yrs % 4) == 0) // if past 2/29 and leap year then
645 mday = mday + 1; // add leap day
646 // compute month by decrementing
647 } // month until found
648
649 day = jday - mday + 1; // compute day of month
650
651 mytm.tm_sec = sec;
652 mytm.tm_min = min;
653 mytm.tm_hour = hrs;
654 mytm.tm_mday = day;
655 mytm.tm_mon = mon;
656 mytm.tm_year = yrs - 1900;
657
658 mytm.tm_wday = calcDayOfWeek(&mytm);
659 mytm.tm_yday = jday;
660 mytm.tm_isdst = 0;
661
662 return &mytm;
663 }
664
665
666 /////////////////////////////////////////////////////////////////////////////////////////////
667 // //
668 // localtime() - simply using gmtime() //
669 // //
670 /////////////////////////////////////////////////////////////////////////////////////////////
671
672
673 struct tm *
localtime(const time_t * timer)674 localtime(const time_t *timer)
675 {
676 return gmtime(timer);
677 }
678