Branch data Line data Source code
1 : : /*
2 : : * This file is part of the MicroPython project, http://micropython.org/
3 : : *
4 : : * The MIT License (MIT)
5 : : *
6 : : * Copyright (c) 2013, 2014 Damien P. George
7 : : * Copyright (c) 2015 Daniel Campora
8 : : *
9 : : * Permission is hereby granted, free of charge, to any person obtaining a copy
10 : : * of this software and associated documentation files (the "Software"), to deal
11 : : * in the Software without restriction, including without limitation the rights
12 : : * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 : : * copies of the Software, and to permit persons to whom the Software is
14 : : * furnished to do so, subject to the following conditions:
15 : : *
16 : : * The above copyright notice and this permission notice shall be included in
17 : : * all copies or substantial portions of the Software.
18 : : *
19 : : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 : : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 : : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 : : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 : : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 : : * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 : : * THE SOFTWARE.
26 : : */
27 : :
28 : : #include "py/obj.h"
29 : :
30 : : #include "shared/timeutils/timeutils.h"
31 : :
32 : : // LEAPOCH corresponds to 2000-03-01, which is a mod-400 year, immediately
33 : : // after Feb 29. We calculate seconds as a signed integer relative to that.
34 : : //
35 : : // Our timebase is relative to 2000-01-01.
36 : :
37 : : #define LEAPOCH ((31 + 29) * 86400)
38 : :
39 : : #define DAYS_PER_400Y (365 * 400 + 97)
40 : : #define DAYS_PER_100Y (365 * 100 + 24)
41 : : #define DAYS_PER_4Y (365 * 4 + 1)
42 : :
43 : : static const uint16_t days_since_jan1[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
44 : :
45 : 12 : bool timeutils_is_leap_year(mp_uint_t year) {
46 [ + - - + : 12 : return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
- - ]
47 : : }
48 : :
49 : : // month is one based
50 : 0 : mp_uint_t timeutils_days_in_month(mp_uint_t year, mp_uint_t month) {
51 : 0 : mp_uint_t mdays = days_since_jan1[month] - days_since_jan1[month - 1];
52 [ # # # # ]: 0 : if (month == 2 && timeutils_is_leap_year(year)) {
53 : 0 : mdays++;
54 : : }
55 : 0 : return mdays;
56 : : }
57 : :
58 : : // compute the day of the year, between 1 and 366
59 : : // month should be between 1 and 12, date should start at 1
60 : 14 : mp_uint_t timeutils_year_day(mp_uint_t year, mp_uint_t month, mp_uint_t date) {
61 : 14 : mp_uint_t yday = days_since_jan1[month - 1] + date;
62 [ + + + - ]: 14 : if (month >= 3 && timeutils_is_leap_year(year)) {
63 : 12 : yday += 1;
64 : : }
65 : 14 : return yday;
66 : : }
67 : :
68 : 0 : void timeutils_seconds_since_2000_to_struct_time(mp_uint_t t, timeutils_struct_time_t *tm) {
69 : : // The following algorithm was adapted from musl's __secs_to_tm and adapted
70 : : // for differences in MicroPython's timebase.
71 : :
72 : 0 : mp_int_t seconds = t - LEAPOCH;
73 : :
74 : 0 : mp_int_t days = seconds / 86400;
75 : 0 : seconds %= 86400;
76 [ # # ]: 0 : if (seconds < 0) {
77 : 0 : seconds += 86400;
78 : 0 : days -= 1;
79 : : }
80 : 0 : tm->tm_hour = seconds / 3600;
81 : 0 : tm->tm_min = seconds / 60 % 60;
82 : 0 : tm->tm_sec = seconds % 60;
83 : :
84 : 0 : mp_int_t wday = (days + 2) % 7; // Mar 1, 2000 was a Wednesday (2)
85 [ # # ]: 0 : if (wday < 0) {
86 : 0 : wday += 7;
87 : : }
88 : 0 : tm->tm_wday = wday;
89 : :
90 : 0 : mp_int_t qc_cycles = days / DAYS_PER_400Y;
91 : 0 : days %= DAYS_PER_400Y;
92 [ # # ]: 0 : if (days < 0) {
93 : 0 : days += DAYS_PER_400Y;
94 : 0 : qc_cycles--;
95 : : }
96 : 0 : mp_int_t c_cycles = days / DAYS_PER_100Y;
97 [ # # ]: 0 : if (c_cycles == 4) {
98 : 0 : c_cycles--;
99 : : }
100 : 0 : days -= (c_cycles * DAYS_PER_100Y);
101 : :
102 : 0 : mp_int_t q_cycles = days / DAYS_PER_4Y;
103 [ # # ]: 0 : if (q_cycles == 25) {
104 : 0 : q_cycles--;
105 : : }
106 : 0 : days -= q_cycles * DAYS_PER_4Y;
107 : :
108 : 0 : mp_int_t years = days / 365;
109 [ # # ]: 0 : if (years == 4) {
110 : 0 : years--;
111 : : }
112 : 0 : days -= (years * 365);
113 : :
114 : : /* We will compute tm_yday at the very end
115 : : mp_int_t leap = !years && (q_cycles || !c_cycles);
116 : :
117 : : tm->tm_yday = days + 31 + 28 + leap;
118 : : if (tm->tm_yday >= 365 + leap) {
119 : : tm->tm_yday -= 365 + leap;
120 : : }
121 : :
122 : : tm->tm_yday++; // Make one based
123 : : */
124 : :
125 : 0 : tm->tm_year = 2000 + years + 4 * q_cycles + 100 * c_cycles + 400 * qc_cycles;
126 : :
127 : : // Note: days_in_month[0] corresponds to March
128 : 0 : static const int8_t days_in_month[] = {31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29};
129 : :
130 : 0 : mp_int_t month;
131 [ # # ]: 0 : for (month = 0; days_in_month[month] <= days; month++) {
132 : 0 : days -= days_in_month[month];
133 : : }
134 : :
135 : 0 : tm->tm_mon = month + 2;
136 [ # # ]: 0 : if (tm->tm_mon >= 12) {
137 : 0 : tm->tm_mon -= 12;
138 : 0 : tm->tm_year++;
139 : : }
140 : 0 : tm->tm_mday = days + 1; // Make one based
141 : 0 : tm->tm_mon++; // Make one based
142 : :
143 : 0 : tm->tm_yday = timeutils_year_day(tm->tm_year, tm->tm_mon, tm->tm_mday);
144 : 0 : }
145 : :
146 : : // returns the number of seconds, as an integer, since 2000-01-01
147 : 14 : mp_uint_t timeutils_seconds_since_2000(mp_uint_t year, mp_uint_t month,
148 : : mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second) {
149 : 14 : return
150 : : second
151 : 14 : + minute * 60
152 : 14 : + hour * 3600
153 : 14 : + (timeutils_year_day(year, month, date) - 1
154 : 14 : + ((year - 2000 + 3) / 4) // add a day each 4 years starting with 2001
155 : 14 : - ((year - 2000 + 99) / 100) // subtract a day each 100 years starting with 2001
156 : 14 : + ((year - 2000 + 399) / 400) // add a day each 400 years starting with 2001
157 : : ) * 86400
158 : 14 : + (year - 2000) * 31536000;
159 : : }
160 : :
161 : 0 : mp_uint_t timeutils_mktime_2000(mp_uint_t year, mp_int_t month, mp_int_t mday,
162 : : mp_int_t hours, mp_int_t minutes, mp_int_t seconds) {
163 : :
164 : : // Normalize the tuple. This allows things like:
165 : : //
166 : : // tm_tomorrow = list(time.localtime())
167 : : // tm_tomorrow[2] += 1 # Adds 1 to mday
168 : : // tomorrow = time.mktime(tm_tomorrow)
169 : : //
170 : : // And not have to worry about all the weird overflows.
171 : : //
172 : : // You can subtract dates/times this way as well.
173 : :
174 : 0 : minutes += seconds / 60;
175 [ # # ]: 0 : if ((seconds = seconds % 60) < 0) {
176 : 0 : seconds += 60;
177 : 0 : minutes--;
178 : : }
179 : :
180 : 0 : hours += minutes / 60;
181 [ # # ]: 0 : if ((minutes = minutes % 60) < 0) {
182 : 0 : minutes += 60;
183 : 0 : hours--;
184 : : }
185 : :
186 : 0 : mday += hours / 24;
187 [ # # ]: 0 : if ((hours = hours % 24) < 0) {
188 : 0 : hours += 24;
189 : 0 : mday--;
190 : : }
191 : :
192 : 0 : month--; // make month zero based
193 : 0 : year += month / 12;
194 [ # # ]: 0 : if ((month = month % 12) < 0) {
195 : 0 : month += 12;
196 : 0 : year--;
197 : : }
198 : 0 : month++; // back to one based
199 : :
200 [ # # ]: 0 : while (mday < 1) {
201 [ # # ]: 0 : if (--month == 0) {
202 : 0 : month = 12;
203 : 0 : year--;
204 : : }
205 : 0 : mday += timeutils_days_in_month(year, month);
206 : : }
207 [ # # ]: 0 : while ((mp_uint_t)mday > timeutils_days_in_month(year, month)) {
208 : 0 : mday -= timeutils_days_in_month(year, month);
209 [ # # ]: 0 : if (++month == 13) {
210 : 0 : month = 1;
211 : 0 : year++;
212 : : }
213 : : }
214 : 0 : return timeutils_seconds_since_2000(year, month, mday, hours, minutes, seconds);
215 : : }
216 : :
217 : : // Calculate the weekday from the date.
218 : : // The result is zero based with 0 = Monday.
219 : : // by Michael Keith and Tom Craver, 1990.
220 : 0 : int timeutils_calc_weekday(int y, int m, int d) {
221 [ # # ]: 0 : return ((d += m < 3 ? y-- : y - 2, 23 * m / 9 + d + 4 + y / 4 - y / 100 + y / 400) + 6) % 7;
222 : : }
|