3 * Copyright (C) 2007 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
4 * Copyright (C) 2008 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
5 * Copyright (C) 2009 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 /* use BCD mode, since binary mode seems to be buggy */
29 static unsigned read_register (unsigned reg
)
31 unsigned high_digit
, low_digit
;
35 high_digit
= low_digit
= inb (0x71);
37 /* convert from BCD to binary */
42 return 10 * high_digit
+ low_digit
;
45 /*****************************************************************************
46 Finds the number of days between two dates in the Gregorian calendar.
47 - it's a leap year if the year is divisible by 4,
48 - UNLESS the year is also divisible by 100,
49 - UNLESS the year is also divisible by 400
51 To compute Julian Day Number (JDN;
52 days since Nov 24, 4714 BC/BCE in Gregorian calendar):
53 days_between_dates(-4713, 327, curr_day_in_year, curr_year);
55 To compute days since Jan 1, 1970 (UNIX epoch):
56 days_between_dates(1970, 0, curr_day_in_year, curr_year);
58 days_between_dates(-4713, 327, curr_day_in_year, curr_year) + 2440588L;
60 This code divides the time between start_day/start_year and end_day/end_year
61 into "slices": fourcent (400-year) slices in the middle, bracketed on
62 either end by century slices, fouryear (4-year) slices, and year slices.
64 When used to compute JDN, this code produces the same results as the
66 http://serendipity.magnet.ch/hermetic/cal_stud/jdn.htm
68 IMHO, it's easier to see how the algorithm for this code works,
69 versus the code at the URL above.
70 *****************************************************************************/
71 static long days_between_dates (unsigned start_year
, unsigned start_day
,
72 int end_year
, unsigned end_day
)
74 int fourcents
, centuries
, fouryears
, years
;
77 fourcents
= end_year
/ 400 - start_year
/ 400;
78 centuries
= end_year
/ 100 - start_year
/ 100 -
79 /* subtract from 'centuries' the centuries already accounted for by
82 fouryears
= end_year
/ 4 - start_year
/ 4 -
83 /* subtract from 'fouryears' the fouryears already accounted for by
84 'fourcents' and 'centuries' */
85 fourcents
* 100 - centuries
* 25;
86 years
= end_year
- start_year
-
87 /* subtract from 'years' the years already accounted for by
88 'fourcents', 'centuries', and 'fouryears' */
89 400 * fourcents
- 100 * centuries
- 4 * fouryears
;
90 /* add it up: 97 leap days every fourcent */
91 days
= (365L * 400 + 97) * fourcents
;
92 /* 24 leap days every residual century */
93 days
+= (365L * 100 + 24) * centuries
;
94 /* 1 leap day every residual fouryear */
95 days
+= (365L * 4 + 1) * fouryears
;
96 /* 0 leap days for residual years */
97 days
+= (365L * 1) * years
;
98 /* residual days (need the cast!) */
99 days
+= ((long) end_day
- start_day
);
101 /* account for terminal leap year */
102 if(end_year
% 4 == 0 && end_day
>= 60) {
105 if (end_year
% 100 == 0)
107 if (end_year
% 400 == 0)
111 /* xxx - what have I wrought? I don't know what's going on here,
112 but the code won't work properly without it */
116 if (end_year
% 4 == 0)
118 if (end_year
% 100 == 0)
120 if (end_year
% 400 == 0)
130 /*****************************************************************************
131 month and date start with 1, not with 0
132 *****************************************************************************/
133 #define EPOCH_YEAR 1970
134 #define EPOCH_DAY 0 /* Jan 1 */
136 unsigned long date_time_to_time_t(unsigned year
, unsigned month
,
137 unsigned date
, unsigned hour
, unsigned min
,
140 static const unsigned days_to_date
[12] =
142 /* jan feb mar apr may jun jul aug sep oct nov dec */
148 31 + 28 + 31 + 30 + 31,
149 31 + 28 + 31 + 30 + 31 + 30,
150 31 + 28 + 31 + 30 + 31 + 30 + 31,
151 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
152 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
153 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
154 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30
157 unsigned long ret_val
;
160 /* convert month and year to day-in-year */
161 if (month
< 1 || month
> 12 || date
< 1 || date
> 31)
167 day
= date
+ days_to_date
[month
];
169 /* convert to Unix JDN (UJDN) */
170 ret_val
= days_between_dates (EPOCH_YEAR
, EPOCH_DAY
, year
, day
);
172 /* convert from days to seconds, adding time as you go */
183 /*****************************************************************************
184 NOTE: this function works only with local time, stored in the CMOS clock.
185 It knows nothing of GMT or timezones. This is a feature, not a bug :)
186 *****************************************************************************/
187 tm
*rtc_getcurrtime ()
190 unsigned date
, month
, hour
, minute
, second
;
194 /* b2=0 BCD mode, vs. binary (binary mode seems to be buggy)
195 b1=1 24-hour mode, vs. 12-hour mode */
197 outb (0x71, (inb (0x71) & ~6) | 2);
201 /* wait for stored time value to stop changing */
204 while (inb (0x71) & 128);
206 /* get year/month/date
207 year = read_register (9) + 1900; xxx - OH NO, Y2K!
208 year = read_register (9) + 2000;
209 use the Microsoft method -- this should be good from 1970-2069
210 signed 32-bit time_t will overflow before then, in 2038 */
212 year
= read_register (9); /* 0-99 */
219 month
= read_register (8); /* 1-12 */
220 date
= read_register (7); /* 1-31 */
221 hour
= read_register (4); /* 0-23 */
222 minute
= read_register (2); /* 0-59 */
223 second
= read_register (0); /* 0-59 */
225 rtime
.tm_sec
= second
;
226 rtime
.tm_min
= minute
;
227 rtime
.tm_hour
= hour
;
228 rtime
.tm_mday
= date
;
229 rtime
.tm_mon
= month
;
230 rtime
.tm_year
= year
;
232 rtime
.__tm_gmtoff
= date_time_to_time_t (year
, month
, date
, hour
, minute
, second
);