LCOV - code coverage report
Current view: top level - shared/timeutils - timeutils.c (source / functions) Hit Total Coverage
Test: unix_coverage_v1.24.0-7-g548babf8a.info Lines: 16 97 16.5 %
Date: 2024-10-30 09:06:48 Functions: 3 7 42.9 %
Branches: 5 48 10.4 %

           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                 :            : }

Generated by: LCOV version 1.15-5-g462f71d