added stdc time support
[fw/sdcc] / device / lib / time.c
1 /*-------------------------------------------------------------------------
2   time.c - stdlib time conversion routines
3   
4    Written By - Johan Knol, johan.knol@iduna.nl
5     
6    This program is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 2, or (at your option) any
9    later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19    
20    In other words, you are welcome to use, share and improve this program.
21    You are forbidden to forbid anyone else to use, share and improve
22    what you give them.   Help stamp out software-hoarding!  
23 -------------------------------------------------------------------------*/
24
25 #include <stdio.h>
26 #include <time.h>
27
28 #define FIXDS390BUG (long)
29
30 // please note that the tm structure has the years since 1900,
31 // but time returns the seconds since 1970
32
33 /* You need some kind of real time clock for the time() function.
34    Either a rtc-chip or some kind of DCF device will do. For TINI, the 
35    HAVE_RTC is defined in tinibios.h
36    If not, the conversion routines still work.
37 */
38
39 #ifndef HAVE_RTC
40 unsigned char RtcRead(struct tm *timeptr) {
41   // no real time hardware 
42   return 0;
43 }
44 #endif
45
46 // return the calendar time, seconds since the Epoch (Jan 1 1970 00:00:00)
47 time_t time(time_t *timeptr) {
48   struct tm now;
49   time_t t=-1;
50
51   if (RtcRead(&now)) {
52     t=mktime(&now);
53   }
54   if (timeptr) {
55     *timeptr=t;
56   }
57   return t;
58 }
59
60 static code char monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31};
61
62 static code char *month[]={"Jan","Feb","Mar","Apr","May","Jun",
63                  "Jul","Aug","Sep","Oct","Nov","Dec"};
64
65 static code char *day[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
66
67 static char ascTimeBuffer[32];
68
69 // validate the tm structure
70 static void CheckTime(struct tm *timeptr) {
71     // we could do some normalization here, e.g.
72     // change 40 october to 9 november
73     if (timeptr->tm_sec<0) timeptr->tm_sec=0;
74     else if (timeptr->tm_sec>59) timeptr->tm_sec=59;
75     if (timeptr->tm_min<0) timeptr->tm_min=0;
76     else if (timeptr->tm_min>59) timeptr->tm_min=59;
77     if (timeptr->tm_hour<0) timeptr->tm_hour=0;
78     else if (timeptr->tm_hour>23) timeptr->tm_hour=23;
79     if (timeptr->tm_wday<0) timeptr->tm_wday=0;
80     else if (timeptr->tm_wday>6) timeptr->tm_wday=6;
81     if (timeptr->tm_mday<1) timeptr->tm_mday=1;
82     else if (timeptr->tm_mday>31) timeptr->tm_mday=31;
83     if (timeptr->tm_mon<0) timeptr->tm_mon=0;
84     else if (timeptr->tm_mon>11) timeptr->tm_mon=11;
85     if (timeptr->tm_year<0) timeptr->tm_year=0;
86 }
87
88 // format the time into "Sat Feb 17 17:45:23 2001\n"
89 char *asctime(struct tm *timeptr) {
90   CheckTime(timeptr);
91   sprintf (ascTimeBuffer, "%s %s %2d %02d:%02d:%02d %04d\n",
92            day[timeptr->tm_wday], month[timeptr->tm_mon], timeptr->tm_mday,
93            timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec, 
94            timeptr->tm_year+1900);
95   return ascTimeBuffer;
96 }
97
98 char *ctime(time_t *timep) {
99   return asctime(localtime(timep));
100 }
101
102 static struct tm lastTime;
103
104 /* convert calendar time (seconds since 1970) to broken-time
105    This only works for dates between 01-01-1970 00:00:00 and 
106    19-01-2038 03:14:07
107
108    A leap year is ((((year%4)==0) && ((year%100)!=0)) || ((year%400)==0)) 
109    but since we have no fancy years between 1970 and 2038 we can do:
110 */
111
112 #define LEAP_YEAR(year) ((year%4)==0)
113
114 // forget about timezones for now
115 struct tm *localtime(time_t *timep) {
116     return gmtime(timep);
117 }
118
119 struct tm *gmtime(time_t *timep) {
120   unsigned long epoch=*timep;
121   unsigned int year;
122   unsigned char month, monthLength;
123   unsigned long days;
124   
125   lastTime.tm_sec=epoch%60;
126   epoch/=60; // now it is minutes
127   lastTime.tm_min=epoch%60;
128   epoch/=60; // now it is hours
129   lastTime.tm_hour=epoch%24;
130   epoch/=24; // now it is days
131   lastTime.tm_wday=(epoch+4)%7;
132   
133   year=1970;
134   days=0;
135   while((days += (LEAP_YEAR(year) ? 366 : 365)) <= epoch) {
136     year++;
137   }
138   lastTime.tm_year=year-1900;
139   
140   days -= LEAP_YEAR(year) ? 366 : 365;
141   epoch -= days; // now it is days in this year, starting at 0
142   lastTime.tm_yday=epoch;
143   
144   days=0;
145   month=0;
146   monthLength=0;
147   for (month=0; month<12; month++) {
148     if (month==1) { // februari
149       if (LEAP_YEAR(year)) {
150         monthLength=29;
151       } else {
152         monthLength=28;
153       }
154     } else {
155       monthLength = monthDays[month];
156     }
157     
158     if (epoch>=monthLength) {
159       epoch-=monthLength;
160     } else {
161         break;
162     }
163   }
164   lastTime.tm_mon=month;
165   lastTime.tm_mday=epoch+1;
166   
167   lastTime.tm_isdst=0;
168   
169   return &lastTime;
170 }
171
172 // convert broken time to calendar time (seconds since 1970)
173 time_t mktime(struct tm *timeptr) {
174     int year=timeptr->tm_year+1900, month=timeptr->tm_mon, i;
175     long seconds;
176     
177     CheckTime(timeptr);
178
179     // seconds from 1970 till 1 jan 00:00:00 this year
180     seconds= FIXDS390BUG (year-1970)*60*60*24*365;
181
182     // add extra days for leap years
183     for (i=1970; i<year; i++) {
184         if (LEAP_YEAR(i)) {
185             seconds+= FIXDS390BUG 60*60*24;
186         }
187     }
188
189     // add days for this year
190     for (i=0; i<month; i++) {
191       if (i==1 && LEAP_YEAR(year)) { 
192         seconds+= FIXDS390BUG 60*60*24*29;
193       } else {
194         seconds+= FIXDS390BUG 60*60*24*monthDays[i];
195       }
196     }
197
198     seconds+= FIXDS390BUG (timeptr->tm_mday-1)*60*60*24;
199     seconds+= FIXDS390BUG timeptr->tm_hour*60*60;
200     seconds+= FIXDS390BUG timeptr->tm_min*60;
201     seconds+= FIXDS390BUG timeptr->tm_sec;
202     return seconds;
203 }
204