1 /*
2  * (C) Copyright 2001
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23 
24 /*
25  * Date & Time support for Philips PCF8563 RTC
26  */
27 
28 #include <common.h>
29 #include <command.h>
30 #include <rtc.h>
31 
32 #if defined(CONFIG_CMD_DATE) || defined(CONFIG_TIMESTAMP)
33 
34 #define FEBRUARY		2
35 #define	STARTOFTIME		1970
36 #define SECDAY			86400L
37 #define SECYR			(SECDAY * 365)
38 #define	leapyear(year)		((year) % 4 == 0)
39 #define	days_in_year(a)		(leapyear(a) ? 366 : 365)
40 #define	days_in_month(a)	(month_days[(a) - 1])
41 
42 static int month_days[12] = {
43 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
44 };
45 
46 /*
47  * This only works for the Gregorian calendar - i.e. after 1752 (in the UK)
48  */
GregorianDay(struct rtc_time * tm)49 void GregorianDay(struct rtc_time * tm)
50 {
51 	int leapsToDate;
52 	int lastYear;
53 	int day;
54 	int MonthOffset[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
55 
56 	lastYear=tm->tm_year-1;
57 
58 	/*
59 	 * Number of leap corrections to apply up to end of last year
60 	 */
61 	leapsToDate = lastYear/4 - lastYear/100 + lastYear/400;
62 
63 	/*
64 	 * This year is a leap year if it is divisible by 4 except when it is
65 	 * divisible by 100 unless it is divisible by 400
66 	 *
67 	 * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be
68 	 */
69 	if((tm->tm_year%4==0) &&
70 	   ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) &&
71 	   (tm->tm_mon>2)) {
72 		/*
73 		 * We are past Feb. 29 in a leap year
74 		 */
75 		day=1;
76 	} else {
77 		day=0;
78 	}
79 
80 	day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + tm->tm_mday;
81 
82 	tm->tm_wday=day%7;
83 }
84 
to_tm(int tim,struct rtc_time * tm)85 void to_tm(int tim, struct rtc_time * tm)
86 {
87 	register int    i;
88 	register long   hms, day;
89 
90 	day = tim / SECDAY;
91 	hms = tim % SECDAY;
92 
93 	/* Hours, minutes, seconds are easy */
94 	tm->tm_hour = hms / 3600;
95 	tm->tm_min = (hms % 3600) / 60;
96 	tm->tm_sec = (hms % 3600) % 60;
97 
98 	/* Number of years in days */
99 	for (i = STARTOFTIME; day >= days_in_year(i); i++) {
100 		day -= days_in_year(i);
101 	}
102 	tm->tm_year = i;
103 
104 	/* Number of months in days left */
105 	if (leapyear(tm->tm_year)) {
106 		days_in_month(FEBRUARY) = 29;
107 	}
108 	for (i = 1; day >= days_in_month(i); i++) {
109 		day -= days_in_month(i);
110 	}
111 	days_in_month(FEBRUARY) = 28;
112 	tm->tm_mon = i;
113 
114 	/* Days are what is left over (+1) from all that. */
115 	tm->tm_mday = day + 1;
116 
117 	/*
118 	 * Determine the day of week
119 	 */
120 	GregorianDay(tm);
121 }
122 
123 /* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
124  * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
125  * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
126  *
127  * [For the Julian calendar (which was used in Russia before 1917,
128  * Britain & colonies before 1752, anywhere else before 1582,
129  * and is still in use by some communities) leave out the
130  * -year/100+year/400 terms, and add 10.]
131  *
132  * This algorithm was first published by Gauss (I think).
133  *
134  * WARNING: this function will overflow on 2106-02-07 06:28:16 on
135  * machines were long is 32-bit! (However, as time_t is signed, we
136  * will already get problems at other places on 2038-01-19 03:14:08)
137  */
138 unsigned long
mktime(unsigned int year,unsigned int mon,unsigned int day,unsigned int hour,unsigned int min,unsigned int sec)139 mktime (unsigned int year, unsigned int mon,
140 	unsigned int day, unsigned int hour,
141 	unsigned int min, unsigned int sec)
142 {
143 	if (0 >= (int) (mon -= 2)) {	/* 1..12 -> 11,12,1..10 */
144 		mon += 12;		/* Puts Feb last since it has leap day */
145 		year -= 1;
146 	}
147 
148 	return (((
149 		(unsigned long) (year/4 - year/100 + year/400 + 367*mon/12 + day) +
150 			year*365 - 719499
151 	    )*24 + hour /* now have hours */
152 	  )*60 + min /* now have minutes */
153 	)*60 + sec; /* finally seconds */
154 }
155 
156 #endif
157