backported more fixes from 256boss
[bootcensus] / src / libc / time.c
1 /*
2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018-2019  John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY, without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include "time.h"
20 #include "rtc.h"
21 #include "timer.h"
22 #include "config.h"
23
24 #define MINSEC          60
25 #define HOURSEC         (60 * MINSEC)
26 #define DAYSEC          (24 * HOURSEC)
27 #define YEARDAYS(x)     (is_leap_year(x) ? 366 : 365)
28
29 /* 1-1-1970 was a thursday */
30 #define EPOCH_WDAY      4
31
32 static int is_leap_year(int yr);
33
34 static int mdays[2][12] = {
35         {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
36         {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
37 };
38
39 static char *wday[] = {
40         "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
41 };
42 static char *mon[] = {
43         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
44         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
45 };
46
47
48 time_t time(time_t *tp)
49 {
50         time_t res = start_time + nticks / TICK_FREQ_HZ;
51
52         if(tp) *tp = res;
53         return res;
54 }
55
56 char *asctime(struct tm *tm)
57 {
58         static char buf[64];
59         return asctime_r(tm, buf);
60 }
61
62 char *asctime_r(struct tm *tm, char *buf)
63 {
64         sprintf(buf, "%s %s %d %02d:%02d:%02d %d\n", wday[tm->tm_wday],
65                         mon[tm->tm_mon], tm->tm_mday, tm->tm_hour, tm->tm_min,
66                         tm->tm_sec, tm->tm_year + 1900);
67         return buf;
68 }
69
70 time_t mktime(struct tm *tm)
71 {
72         int i, num_years = tm->tm_year - 70;
73         int year = 1970;
74         int days = day_of_year(tm->tm_year + 1900, tm->tm_mon, tm->tm_mday - 1);
75
76         /* set correct yearday */
77         tm->tm_yday = days;
78
79         for(i=0; i<num_years; i++) {
80                 days += YEARDAYS(year++);
81         }
82
83         /* set wday correctly */
84         tm->tm_wday = (days + EPOCH_WDAY) % 7;
85
86         return (time_t)days * DAYSEC + tm->tm_hour * HOURSEC +
87                 tm->tm_min * MINSEC + tm->tm_sec;
88 }
89
90 struct tm *gmtime(time_t *tp)
91 {
92         static struct tm tm;
93         return gmtime_r(tp, &tm);
94 }
95
96 struct tm *gmtime_r(time_t *tp, struct tm *tm)
97 {
98         int year, days, leap, yrdays;
99         time_t t;
100
101         year = 1970;
102         days = *tp / DAYSEC;
103         t = *tp % DAYSEC;
104
105         tm->tm_wday = (days + EPOCH_WDAY) % 7;
106
107         while(days >= (yrdays = YEARDAYS(year))) {
108                 days -= yrdays;
109                 year++;
110         }
111         tm->tm_year = year - 1900;
112         tm->tm_yday = days;
113
114         leap = is_leap_year(year);
115         tm->tm_mon = 0;
116         while(days >= mdays[leap][tm->tm_mon]) {
117                 days -= mdays[leap][tm->tm_mon++];
118         }
119
120         tm->tm_mday = days + 1;
121
122         tm->tm_hour = t / HOURSEC;
123         t %= HOURSEC;
124         tm->tm_min = t / MINSEC;
125         tm->tm_sec = t % MINSEC;
126         return tm;
127 }
128
129 struct tm *localtime(time_t *tp)
130 {
131         static struct tm tm;
132         return localtime_r(tp, &tm);
133 }
134
135 struct tm *localtime_r(time_t *tp, struct tm *tm)
136 {
137         time_t t = *tp + timezone;
138         return gmtime_r(&t, tm);
139 }
140
141 int day_of_year(int year, int mon, int day)
142 {
143         int i, yday, leap;
144
145         leap = is_leap_year(year) ? 1 : 0;
146         yday = day;
147
148         for(i=0; i<mon; i++) {
149                 yday += mdays[leap][i];
150         }
151         return yday;
152 }
153
154 static int is_leap_year(int yr)
155 {
156         /* exceptions first */
157         if(yr % 400 == 0) {
158                 return 1;
159         }
160         if(yr % 100 == 0) {
161                 return 0;
162         }
163         /* standard case */
164         return yr % 4 == 0;
165 }