xref: /freebsd/contrib/unbound/compat/gmtime_r.c (revision 5f4c09dd85bff675e0ca63c55ea3c517e0fddfcc)
1 /*
2  * Taken from FreeBSD src / lib / libc / stdtime / localtime.c 1.43 revision.
3  * localtime.c 7.78.
4  * tzfile.h 1.8
5  * adapted to be replacement gmtime_r.
6  */
7 #include "config.h"
8 
9 #ifdef HAVE_TIME_H
10 #include <time.h>
11 #endif
12 
13 #define MONSPERYEAR 12
14 #define DAYSPERNYEAR 365
15 #define DAYSPERLYEAR 366
16 #define SECSPERMIN 60
17 #define SECSPERHOUR (60*60)
18 #define SECSPERDAY (24*60*60)
19 #define DAYSPERWEEK 7
20 #define TM_SUNDAY	0
21 #define TM_MONDAY	1
22 #define TM_TUESDAY	2
23 #define TM_WEDNESDAY	3
24 #define TM_THURSDAY	4
25 #define TM_FRIDAY	5
26 #define TM_SATURDAY	6
27 
28 #define TM_YEAR_BASE	1900
29 
30 #define EPOCH_YEAR	1970
31 #define EPOCH_WDAY	TM_THURSDAY
32 
33 #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
34 
35 static const int	mon_lengths[2][MONSPERYEAR] = {
36 	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
37 	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
38 };
39 
40 static const int	year_lengths[2] = {
41 	DAYSPERNYEAR, DAYSPERLYEAR
42 };
43 
44 static void
45 timesub(timep, offset, tmp)
46 const time_t * const			timep;
47 const long				offset;
48 struct tm * const		tmp;
49 {
50 	long			days;
51 	long			rem;
52 	long			y;
53 	int			yleap;
54 	const int *		ip;
55 
56 	days = *timep / SECSPERDAY;
57 	rem = *timep % SECSPERDAY;
58 	rem += (offset);
59 	while (rem < 0) {
60 		rem += SECSPERDAY;
61 		--days;
62 	}
63 	while (rem >= SECSPERDAY) {
64 		rem -= SECSPERDAY;
65 		++days;
66 	}
67 	tmp->tm_hour = (int) (rem / SECSPERHOUR);
68 	rem = rem % SECSPERHOUR;
69 	tmp->tm_min = (int) (rem / SECSPERMIN);
70 	/*
71 	** A positive leap second requires a special
72 	** representation.  This uses "... ??:59:60" et seq.
73 	*/
74 	tmp->tm_sec = (int) (rem % SECSPERMIN) ;
75 	tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
76 	if (tmp->tm_wday < 0)
77 		tmp->tm_wday += DAYSPERWEEK;
78 	y = EPOCH_YEAR;
79 #define LEAPS_THRU_END_OF(y)	((y) / 4 - (y) / 100 + (y) / 400)
80 	while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) {
81 		long	newy;
82 
83 		newy = y + days / DAYSPERNYEAR;
84 		if (days < 0)
85 			--newy;
86 		days -= (newy - y) * DAYSPERNYEAR +
87 			LEAPS_THRU_END_OF(newy - 1) -
88 			LEAPS_THRU_END_OF(y - 1);
89 		y = newy;
90 	}
91 	tmp->tm_year = y - TM_YEAR_BASE;
92 	tmp->tm_yday = (int) days;
93 	ip = mon_lengths[yleap];
94 	for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
95 		days = days - (long) ip[tmp->tm_mon];
96 	tmp->tm_mday = (int) (days + 1);
97 	tmp->tm_isdst = 0;
98 }
99 
100 /*
101 * Re-entrant version of gmtime.
102 */
103 struct tm * gmtime_r(const time_t* timep, struct tm *tm)
104 {
105 	timesub(timep, 0L, tm);
106 	return tm;
107 }
108