timeutils.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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. #include "py/obj.h"
  28. #include "lib/timeutils/timeutils.h"
  29. // LEAPOCH corresponds to 2000-03-01, which is a mod-400 year, immediately
  30. // after Feb 29. We calculate seconds as a signed integer relative to that.
  31. //
  32. // Our timebase is relative to 2000-01-01.
  33. #define LEAPOCH ((31 + 29) * 86400)
  34. #define DAYS_PER_400Y (365*400 + 97)
  35. #define DAYS_PER_100Y (365*100 + 24)
  36. #define DAYS_PER_4Y (365*4 + 1)
  37. STATIC const uint16_t days_since_jan1[]= { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
  38. bool timeutils_is_leap_year(mp_uint_t year) {
  39. return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
  40. }
  41. // month is one based
  42. mp_uint_t timeutils_days_in_month(mp_uint_t year, mp_uint_t month) {
  43. mp_uint_t mdays = days_since_jan1[month] - days_since_jan1[month - 1];
  44. if (month == 2 && timeutils_is_leap_year(year)) {
  45. mdays++;
  46. }
  47. return mdays;
  48. }
  49. // compute the day of the year, between 1 and 366
  50. // month should be between 1 and 12, date should start at 1
  51. mp_uint_t timeutils_year_day(mp_uint_t year, mp_uint_t month, mp_uint_t date) {
  52. mp_uint_t yday = days_since_jan1[month - 1] + date;
  53. if (month >= 3 && timeutils_is_leap_year(year)) {
  54. yday += 1;
  55. }
  56. return yday;
  57. }
  58. void timeutils_seconds_since_2000_to_struct_time(mp_uint_t t, timeutils_struct_time_t *tm) {
  59. // The following algorithm was adapted from musl's __secs_to_tm and adapted
  60. // for differences in MicroPython's timebase.
  61. mp_int_t seconds = t - LEAPOCH;
  62. mp_int_t days = seconds / 86400;
  63. seconds %= 86400;
  64. if (seconds < 0) {
  65. seconds += 86400;
  66. days -= 1;
  67. }
  68. tm->tm_hour = seconds / 3600;
  69. tm->tm_min = seconds / 60 % 60;
  70. tm->tm_sec = seconds % 60;
  71. mp_int_t wday = (days + 2) % 7; // Mar 1, 2000 was a Wednesday (2)
  72. if (wday < 0) {
  73. wday += 7;
  74. }
  75. tm->tm_wday = wday;
  76. mp_int_t qc_cycles = days / DAYS_PER_400Y;
  77. days %= DAYS_PER_400Y;
  78. if (days < 0) {
  79. days += DAYS_PER_400Y;
  80. qc_cycles--;
  81. }
  82. mp_int_t c_cycles = days / DAYS_PER_100Y;
  83. if (c_cycles == 4) {
  84. c_cycles--;
  85. }
  86. days -= (c_cycles * DAYS_PER_100Y);
  87. mp_int_t q_cycles = days / DAYS_PER_4Y;
  88. if (q_cycles == 25) {
  89. q_cycles--;
  90. }
  91. days -= q_cycles * DAYS_PER_4Y;
  92. mp_int_t years = days / 365;
  93. if (years == 4) {
  94. years--;
  95. }
  96. days -= (years * 365);
  97. /* We will compute tm_yday at the very end
  98. mp_int_t leap = !years && (q_cycles || !c_cycles);
  99. tm->tm_yday = days + 31 + 28 + leap;
  100. if (tm->tm_yday >= 365 + leap) {
  101. tm->tm_yday -= 365 + leap;
  102. }
  103. tm->tm_yday++; // Make one based
  104. */
  105. tm->tm_year = 2000 + years + 4 * q_cycles + 100 * c_cycles + 400 * qc_cycles;
  106. // Note: days_in_month[0] corresponds to March
  107. STATIC const int8_t days_in_month[] = {31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29};
  108. mp_int_t month;
  109. for (month = 0; days_in_month[month] <= days; month++) {
  110. days -= days_in_month[month];
  111. }
  112. tm->tm_mon = month + 2;
  113. if (tm->tm_mon >= 12) {
  114. tm->tm_mon -= 12;
  115. tm->tm_year++;
  116. }
  117. tm->tm_mday = days + 1; // Make one based
  118. tm->tm_mon++; // Make one based
  119. tm->tm_yday = timeutils_year_day(tm->tm_year, tm->tm_mon, tm->tm_mday);
  120. }
  121. // returns the number of seconds, as an integer, since 2000-01-01
  122. mp_uint_t timeutils_seconds_since_2000(mp_uint_t year, mp_uint_t month,
  123. mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second) {
  124. return
  125. second
  126. + minute * 60
  127. + hour * 3600
  128. + (timeutils_year_day(year, month, date) - 1
  129. + ((year - 2000 + 3) / 4) // add a day each 4 years starting with 2001
  130. - ((year - 2000 + 99) / 100) // subtract a day each 100 years starting with 2001
  131. + ((year - 2000 + 399) / 400) // add a day each 400 years starting with 2001
  132. ) * 86400
  133. + (year - 2000) * 31536000;
  134. }
  135. mp_uint_t timeutils_mktime(mp_uint_t year, mp_int_t month, mp_int_t mday,
  136. mp_int_t hours, mp_int_t minutes, mp_int_t seconds) {
  137. // Normalize the tuple. This allows things like:
  138. //
  139. // tm_tomorrow = list(time.localtime())
  140. // tm_tomorrow[2] += 1 # Adds 1 to mday
  141. // tomorrow = time.mktime(tm_tomorrow)
  142. //
  143. // And not have to worry about all the weird overflows.
  144. //
  145. // You can subtract dates/times this way as well.
  146. minutes += seconds / 60;
  147. if ((seconds = seconds % 60) < 0) {
  148. seconds += 60;
  149. minutes--;
  150. }
  151. hours += minutes / 60;
  152. if ((minutes = minutes % 60) < 0) {
  153. minutes += 60;
  154. hours--;
  155. }
  156. mday += hours / 24;
  157. if ((hours = hours % 24) < 0) {
  158. hours += 24;
  159. mday--;
  160. }
  161. month--; // make month zero based
  162. year += month / 12;
  163. if ((month = month % 12) < 0) {
  164. month += 12;
  165. year--;
  166. }
  167. month++; // back to one based
  168. while (mday < 1) {
  169. if (--month == 0) {
  170. month = 12;
  171. year--;
  172. }
  173. mday += timeutils_days_in_month(year, month);
  174. }
  175. while ((mp_uint_t)mday > timeutils_days_in_month(year, month)) {
  176. mday -= timeutils_days_in_month(year, month);
  177. if (++month == 13) {
  178. month = 1;
  179. year++;
  180. }
  181. }
  182. return timeutils_seconds_since_2000(year, month, mday, hours, minutes, seconds);
  183. }