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