diff options
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/libc/gmtime.c | 154 |
1 files changed, 86 insertions, 68 deletions
diff --git a/firmware/libc/gmtime.c b/firmware/libc/gmtime.c index c5deb593cd..b01050904d 100644 --- a/firmware/libc/gmtime.c +++ b/firmware/libc/gmtime.c | |||
@@ -7,6 +7,8 @@ | |||
7 | * \/ \/ \/ \/ \/ | 7 | * \/ \/ \/ \/ \/ |
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (C) 2017 by Michael Sevakis | ||
11 | * | ||
10 | * Copyright (C) 2012 by Bertrik Sikken | 12 | * Copyright (C) 2012 by Bertrik Sikken |
11 | * | 13 | * |
12 | * Based on code from: rtc_as3514.c | 14 | * Based on code from: rtc_as3514.c |
@@ -21,96 +23,112 @@ | |||
21 | * KIND, either express or implied. | 23 | * KIND, either express or implied. |
22 | * | 24 | * |
23 | ****************************************************************************/ | 25 | ****************************************************************************/ |
24 | #include <stdbool.h> | ||
25 | #include <stdint.h> | ||
26 | #include "time.h" | 26 | #include "time.h" |
27 | 27 | ||
28 | #define MINUTE_SECONDS 60 | 28 | /* Constants that mark Thursday, 1 January 1970 */ |
29 | #define HOUR_SECONDS 3600 | 29 | #define UNIX_EPOCH_DAY_NUM 134774 |
30 | #define DAY_SECONDS 86400 | 30 | #define UNIX_EPOCH_YEAR (1601 - 1900) |
31 | #define WEEK_SECONDS 604800 | ||
32 | #define YEAR_SECONDS 31536000 | ||
33 | #define LEAP_YEAR_SECONDS 31622400 | ||
34 | 31 | ||
35 | /* Days in each month */ | 32 | /* Last day number it can do */ |
36 | static uint8_t days_in_month[] = | 33 | #define MAX_DAY_NUM 551879 |
37 | {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; | ||
38 | 34 | ||
39 | static inline bool is_leapyear(int year) | 35 | /* d is days since epoch start, Monday, 1 January 1601 (d = 0) |
36 | * | ||
37 | * That date is the beginning of a full 400 year cycle and so simplifies the | ||
38 | * calculations a bit, not requiring offsets before divisions to shift the | ||
39 | * leap year cycle. | ||
40 | * | ||
41 | * It can handle dates up through Sunday, 31 December 3111 (d = 551879). | ||
42 | * | ||
43 | * time_t can't get near the limits anyway for now but algorithm can be | ||
44 | * altered slightly to increase range if even needed. | ||
45 | */ | ||
46 | static void get_tmdate(unsigned int d, struct tm *tm) | ||
40 | { | 47 | { |
41 | return (((year%4)==0) && (((year%100)!=0) || ((year%400)==0))); | 48 | static const unsigned short mon_yday[13] = |
42 | } | 49 | { |
50 | /* year day of 1st of month (non-leap) | ||
51 | +31 +28 +31 +30 +31 +30 +31 +31 +30 +31 +30 +31 +31 */ | ||
52 | 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 | ||
53 | }; | ||
43 | 54 | ||
44 | struct tm *gmtime(const time_t *timep) | 55 | unsigned int x0, x1, x2, x3; /* scratch variables */ |
45 | { | ||
46 | static struct tm time; | ||
47 | return gmtime_r(timep, &time); | ||
48 | } | ||
49 | 56 | ||
50 | struct tm *gmtime_r(const time_t *timep, struct tm *tm) | 57 | /* calculate year from day number */ |
51 | { | 58 | x0 = d / 1460; |
52 | time_t seconds = *timep; | 59 | x1 = x0 / 25; |
53 | int year, i, mday, hour, min; | 60 | x2 = x1 / 4; |
54 | 61 | ||
55 | /* weekday */ | 62 | unsigned int y = (d - x0 + x1 - x2) / 365; |
56 | tm->tm_wday = (seconds / DAY_SECONDS + 4) % 7; | ||
57 | 63 | ||
58 | /* Year */ | 64 | tm->tm_year = y + UNIX_EPOCH_YEAR; /* year - 1900 */ |
59 | year = 1970; | ||
60 | while (seconds >= LEAP_YEAR_SECONDS) | ||
61 | { | ||
62 | if (is_leapyear(year)){ | ||
63 | seconds -= LEAP_YEAR_SECONDS; | ||
64 | } else { | ||
65 | seconds -= YEAR_SECONDS; | ||
66 | } | ||
67 | 65 | ||
68 | year++; | 66 | /* calculate year day from year number and day number */ |
69 | } | 67 | x0 = y / 4; |
68 | x1 = x0 / 25; | ||
69 | x2 = x1 / 4; | ||
70 | |||
71 | unsigned int yday = d - x0 + x1 - x2 - y * 365; | ||
70 | 72 | ||
71 | if (is_leapyear(year)) { | 73 | tm->tm_yday = x3 = yday; /* 0..364/365 */ |
72 | days_in_month[1] = 29; | 74 | |
73 | } else { | 75 | /* check if leap year; adjust February->March transition if so rather |
74 | days_in_month[1] = 28; | 76 | than keeping a leap year version of mon_yday[] */ |
75 | if(seconds>YEAR_SECONDS){ | 77 | if (y - x0 * 4 == 3 && (x0 - x1 * 25 != 24 || x1 - x2 * 4 == 3)) { |
76 | year++; | 78 | /* retard month lookup to make year day 59 into 29 Feb, both to make |
77 | seconds -= YEAR_SECONDS; | 79 | year day 60 into 01 March, lagging one day for remainder of year */ |
80 | if (x3 >= mon_yday[2] && --x3 >= mon_yday[2]) { | ||
81 | yday--; | ||
78 | } | 82 | } |
79 | } | 83 | } |
80 | tm->tm_year = year - 1900; | ||
81 | 84 | ||
82 | /* Month */ | 85 | /* stab approximately at current month based on year day; advance if |
83 | for (i = 0; i < 12; i++) | 86 | it fell short (never initially more than 1 short). */ |
84 | { | 87 | x0 = x3 / 32; |
85 | if (seconds < days_in_month[i]*DAY_SECONDS){ | 88 | if (mon_yday[x0 + 1] <= x3) { |
86 | tm->tm_mon = i; | 89 | x0++; |
87 | break; | 90 | } |
88 | } | 91 | |
92 | tm->tm_mon = x0; /* 0..11 */ | ||
93 | tm->tm_mday = yday - mon_yday[x0] + 1; /* 1..31 */ | ||
94 | tm->tm_wday = (d + 1) % 7; /* 0..6 */ | ||
95 | } | ||
96 | |||
97 | struct tm * gmtime_r(const time_t *timep, struct tm *tm) | ||
98 | { | ||
99 | time_t t = *timep; | ||
100 | |||
101 | int d = t / 86400; /* day number (-24856..24855) */ | ||
102 | int s = t - (time_t)d * 86400; /* second # of day (0..86399) */ | ||
89 | 103 | ||
90 | seconds -= days_in_month[i]*DAY_SECONDS; | 104 | if (s < 0) { |
105 | /* round towards 0 -> floored division */ | ||
106 | d--; | ||
107 | s += 86400; | ||
91 | } | 108 | } |
92 | 109 | ||
93 | /* Month Day */ | 110 | unsigned int x; |
94 | mday = seconds/DAY_SECONDS; | ||
95 | seconds -= mday*DAY_SECONDS; | ||
96 | tm->tm_mday = mday + 1; /* 1 ... 31 */ | ||
97 | 111 | ||
98 | /* Hour */ | 112 | x = s / 3600; |
99 | hour = seconds/HOUR_SECONDS; | 113 | tm->tm_hour = x; /* 0..23 */ |
100 | seconds -= hour*HOUR_SECONDS; | ||
101 | tm->tm_hour = hour; | ||
102 | 114 | ||
103 | /* Minute */ | 115 | s -= x * 3600; |
104 | min = seconds/MINUTE_SECONDS; | 116 | x = s / 60; |
105 | seconds -= min*MINUTE_SECONDS; | 117 | tm->tm_min = x; /* 0..59 */ |
106 | tm->tm_min = min; | ||
107 | 118 | ||
108 | /* Second */ | 119 | s -= x * 60; |
109 | tm->tm_sec = seconds; | 120 | tm->tm_sec = s; /* 0..59 */ |
110 | 121 | ||
111 | tm->tm_yday = 0; /* Not implemented for now */ | 122 | /* not implemented right now */ |
112 | tm->tm_isdst = -1; /* Not implemented for now */ | 123 | tm->tm_isdst = -1; |
124 | |||
125 | get_tmdate(d + UNIX_EPOCH_DAY_NUM, tm); | ||
113 | 126 | ||
114 | return tm; | 127 | return tm; |
115 | } | 128 | } |
116 | 129 | ||
130 | struct tm * gmtime(const time_t *timep) | ||
131 | { | ||
132 | static struct tm time; | ||
133 | return gmtime_r(timep, &time); | ||
134 | } | ||