xref: /plan9/sys/src/ape/lib/ap/gen/mktime.c (revision 950da38c45231b6a9e69da8a15c59eba74b17dd3)
1 #include <time.h>
2 
3 /*
4  * BUG: Doesn't do leap years in full glory,
5  * or calendar changes. In 2038 the sign bit
6  * will be needed in time_t, but we say it
7  * can't be represented.
8  */
9 static int
dysize(int y)10 dysize(int y)
11 {
12 	y += 1900; /* arg is a tm_year, number of years since 1900 */
13 	if((y%4) == 0 && ((y%100) !=0 || (y%400) == 0))
14 		return 366;
15 	return 365;
16 }
17 
18 static int
dmsize(int m,int y)19 dmsize(int m, int y)
20 {
21 	static	char	sizes[12] =
22 		{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
23 
24 	if(m == 1)
25 		return (dysize(y)==366)? 29 : 28;
26 	else
27 		return sizes[m];
28 }
29 
30 /* Reduce *v to [0, mult), adding 1 to *next for every mult
31  * subtracted from *v, and return 1 if reduction worked (no overflow)
32  */
33 static int
reduce(int * v,int * next,int mult)34 reduce(int *v, int *next, int mult)
35 {
36 	int oldnext;
37 
38 	while(*v < 0){
39 		*v += mult;
40 		oldnext = *next;
41 		--*next;
42 		if(!(*next < oldnext))
43 			return 0;
44 	}
45 	while(*v >= mult){
46 		*v -= mult;
47 		oldnext = *next;
48 		++*next;
49 		if(!(*next > oldnext))
50 			return 0;
51 	}
52 	return 1;
53 }
54 
55 static int
jan1(int yr)56 jan1(int yr)
57 {
58 	int y, d;
59 
60 	y = yr+1900;
61 	d = (4+y+(y+3)/4-(y-1701)/100+(y-1601)/400+3)%7;
62 	return d;
63 }
64 
65 time_t
mktime(struct tm * t)66 mktime(struct tm *t)
67 {
68 	time_t a;
69 	int i, d;
70 	struct tm *ptm;
71 
72 	if(!(reduce(&t->tm_sec, &t->tm_min, 60) &&
73 	     reduce(&t->tm_min, &t->tm_hour, 60) &&
74 	     reduce(&t->tm_hour, &t->tm_mday, 24) &&
75 	     reduce(&t->tm_mon, &t->tm_year, 12)))
76 		return -1;
77 	while(t->tm_mday < 1){
78 		if(--t->tm_mon == -1){
79 			t->tm_mon = 11;
80 			t->tm_year--;
81 		}
82 		t->tm_mday += dmsize(t->tm_mon, t->tm_year);
83 	}
84 	while(t->tm_mday > dmsize(t->tm_mon, t->tm_year)){
85 		t->tm_mday -= dmsize(t->tm_mon, t->tm_year);
86 		if(++t->tm_mon == 12){
87 			t->tm_mon = 0;
88 			t->tm_year++;
89 		}
90 	}
91 	a = t->tm_sec + 60*t->tm_min + 3600*t->tm_hour;
92 	t->tm_yday = t->tm_mday-1;
93 	for(i=0; i<t->tm_mon; i++)
94 		t->tm_yday += dmsize(i, t->tm_year);
95 	a += t->tm_yday*86400L;
96 	if(t->tm_year < 70){
97 		for(i=t->tm_year; i<70; i++)
98 			if((a -= dysize(i)*86400L) < 0)
99 				return -1;
100 	}else if(t->tm_year > 70){
101 		for(i=70; i<t->tm_year; i++)
102 			if((a += dysize(i)*86400L) < 0)
103 				return -1;
104 	}
105 	/*
106 	 * Now a is number of seconds past Jan 1 1970.
107 	 * Convert to GMT.
108 	 */
109 	ptm = gmtime(&a);
110 	d = ptm->tm_hour;
111 	ptm = localtime(&a);
112 	d -= ptm->tm_hour;
113 	if(d < 0)
114 		d += 24;
115 	if(t->tm_isdst == 0 && ptm->tm_isdst)
116 		d--;
117 	if(t->tm_isdst > 0 && !ptm->tm_isdst)
118 		d++;
119 	a += d*3600;
120 	t->tm_wday = (jan1(t->tm_year)+t->tm_yday)%7;
121 	return a;
122 }
123