6fed585ab39f2239c82ff53a9fe2e1bec013729b
[tpg/acess2.git] / Usermode / Libraries / libc.so_src / timeconv.c
1 /*
2  * Acess2 C Library
3  * - By John Hodge (thePowersGang)
4  *
5  * timeconv.c
6  * - Shared User/Kernel time conversion code
7  */
8 #include "timeconv.h"
9 #include <errno.h>
10
11 #define ENABLE_LEAP_SECONDS     1
12
13 static const short DAYS_IN[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
14 static const short DAYS_BEFORE[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
15 #define DEC31   365
16 #define JUN30   181
17
18 #if ENABLE_LEAP_SECONDS
19 // Leap seconds after 2000 (stored as day numbers of application)
20 // - Leap seconds apply after the last second of said day (23:59:60)
21 // TODO: Need to encode possible double leap seconds, and negative leap seconds
22 static int64_t  leap_seconds_after[] = {
23         // ls + (year + leap + day + thisleap)
24         (5*365+2+DEC31+0),      // Dec 31 2005
25         (8*365+2+DEC31+1),      // Dec 31 2008
26         (12*365+3+JUN30+1)      // Jun 30 2012
27 };
28 static int      n_leap_seconds_after = sizeof(leap_seconds_after)/sizeof(leap_seconds_after[0]);
29 #endif
30
31 static int YearIsLeap(int year) {
32         return (year % 4 == 0) - (year % 100 == 0) + (year % 400 == 0);
33 }
34 static int DaysUntilYear(int year) {
35         return year*365 + (year/400) - (year/100) + (year/4);
36 }
37
38 int64_t seconds_since_y2k(int years_since, int mon, int day, int h, int m, int s)
39 {
40         errno = EINVAL;
41         if( !(1 <= mon && mon <= 12) )  return 0;
42         if( !(1 <= day && day <= DAYS_IN[mon-1]) )      return 0;
43         if( !(0 <= h && h <= 23) )      return 0;
44         if( !(0 <= m && m <= 59) )      return 0;
45         if( !(0 <= s && s <= 60) )      return 0;
46
47         // Special case check for leap days
48         if( mon == 2 && day == 29 )
49         {
50                 if( !YearIsLeap(years_since) )
51                         return 0;
52         }
53
54         // Day
55         int64_t days = DaysUntilYear(years_since);
56         days += DAYS_BEFORE[mon-1];
57         if(mon > 2 && YearIsLeap(years_since))
58                 days ++;
59         days += day;
60         
61         // Seconds
62         int64_t seconds = h * 60*60 + m * 60;
63         seconds += s;
64         bool today_has_ls = false;
65         #if ENABLE_LEAP_SECONDS
66         if( days > 0 )
67         {
68                 for( int i = 0; i < n_leap_seconds_after; i ++ )
69                 {
70                         if( days < leap_seconds_after[i] )
71                                 break ;
72                         if(days > leap_seconds_after[i])
73                                 seconds ++;
74                         else
75                                 today_has_ls = true;
76                         // A leap second on this day is handled with s=60
77                 }
78         }
79
80         // Now that we know if this day has a leap second, sanity check leap seconds
81         if( s == 60 )
82         {
83                 if( !today_has_ls || !(h == 23 && m == 59) )
84                         return 0;
85         }
86         #else
87         if( s == 60 )   return 0;
88         #endif
89
90         errno = 0;
91         
92         return days * 24*60*60 + seconds;
93 }
94
95 // returns number of days
96 int64_t get_days_since_y2k(int64_t ts, int *h, int *m, int *s)
97 {
98         #if ENABLE_LEAP_SECONDS
99         // Calculate leap second count
100          int    n_leap = 0;
101         bool    is_ls = false;
102         if( ts > 0 )
103         {
104                 for( int i = 0; i < n_leap_seconds_after; i ++ )
105                 {
106                         // lts = Timestamp of the leap second
107                         int64_t lts = leap_seconds_after[i] * 24*60*60 + 23*60*60 + 59*60 + 60 + i;
108                         if( lts > ts )  break;
109                         if( lts == ts )
110                                 is_ls = true;
111                         n_leap ++;
112                 }
113         }
114         ts -= n_leap;
115         #endif
116         
117         int64_t days = ts / 24*60*60;
118         int64_t seconds = ts % (24*60*60);
119         *s = (is_ls ? 60 : seconds % 60);
120         *m = (seconds/60 % 24);
121         *h = seconds / (60*60);
122         
123         return days;
124 }
125
126 /**
127  * \param days  Days since 1 Jan 2000
128  */
129 int64_t get_years_since_y2k(int64_t days, bool *is_leap, int *doy)
130 {
131         // Calculate Year
132         bool    is_ly = false;
133         // Year (400 yr blocks) - (400/4-3) leap years
134         const int       days_per_400_yrs = 365*400 + (400/4-3);
135          int year = 400 * days / days_per_400_yrs;
136         days = days % days_per_400_yrs;
137         if( days < 366 )        // First year in 400 is a leap
138                 is_ly = true;
139         else
140         {
141                 // 100 yr blocks - 100/4-1 leap years
142                 const int       days_per_100_yrs = 365*100 + (100/4-1);
143                 year += 100 * days / days_per_100_yrs;
144                 days = days % days_per_100_yrs;
145                 if( days < 366 )        // First year in 100 isn't a leap
146                         is_ly = false;
147                 else
148                 {
149                         const int       days_per_4_yrs = 365*4 + 1;
150                         year += 4 * days / days_per_4_yrs;
151                         days = days % days_per_4_yrs;
152                         if( days < 366 )        // First year in 4 is a leap
153                                 is_ly = true;
154                         else {
155                                 year += days / 365;
156                                 days = days % 365;
157                         }
158                 }
159         }
160         *doy = days;
161         *is_leap = is_ly;
162         
163         return year;
164 }
165
166 void get_month_day(int doy, bool is_ly, int *mon, int *day)
167 {
168         if( doy > 365+(is_ly?1:0) ) {
169                 *mon = 0;
170                 *day = 0;
171         }
172         
173         bool mon_day_set = false;
174         if( doy >= DAYS_BEFORE[2] && is_ly )
175         {
176                 if( doy == DAYS_BEFORE[2] ) {
177                         *mon = 2;
178                         *day = 29;
179                         mon_day_set = true;
180                 }
181                 doy --;
182         }
183         for( int i = 1; i < 12+1 && !mon_day_set; i ++ )
184         {
185                 if( doy < DAYS_BEFORE[i] )
186                 {
187                         *mon = i;
188                         *day = doy - DAYS_BEFORE[i];
189                         mon_day_set = true;
190                 }
191         }
192 }
193
194 int expand_from_secs_since_y2k(int64_t ts, int *years_since, int *mon, int *day, int *h, int *m, int *s)
195 {
196         int64_t days = get_days_since_y2k(ts, h, m, s);
197         
198         bool    is_ly;
199          int    doy;
200         *years_since = get_years_since_y2k(days, &is_ly, &doy);
201
202         // Calculate month/day of month
203         get_month_day(doy, is_ly, mon, day);
204         return 0;
205 }
206

UCC git Repository :: git.ucc.asn.au