LCOV - code coverage report
Current view: top level - timelib - interval.c (source / functions) Coverage Total Hit
Test: Coverage Report Lines: 8.6 % 220 19
Test Date: 2025-09-24 08:46:11 Functions: - 0 0

            Line data    Source code
       1              : /*
       2              :  * The MIT License (MIT)
       3              :  *
       4              :  * Copyright (c) 2015-2019 Derick Rethans
       5              :  *
       6              :  * Permission is hereby granted, free of charge, to any person obtaining a copy
       7              :  * of this software and associated documentation files (the "Software"), to deal
       8              :  * in the Software without restriction, including without limitation the rights
       9              :  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      10              :  * copies of the Software, and to permit persons to whom the Software is
      11              :  * furnished to do so, subject to the following conditions:
      12              :  *
      13              :  * The above copyright notice and this permission notice shall be included in
      14              :  * all copies or substantial portions of the Software.
      15              :  *
      16              :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      17              :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      18              :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      19              :  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      20              :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      21              :  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      22              :  * THE SOFTWARE.
      23              :  */
      24              : 
      25              : #include "timelib.h"
      26              : #include "timelib_private.h"
      27              : #include <math.h>
      28              : 
      29            0 : static void swap_times(timelib_time **one, timelib_time **two, timelib_rel_time *rt)
      30              : {
      31              :         timelib_time *swp;
      32              : 
      33            0 :         swp = *two;
      34            0 :         *two = *one;
      35            0 :         *one = swp;
      36            0 :         rt->invert = 1;
      37            0 : }
      38              : 
      39            0 : static void sort_old_to_new(timelib_time **one, timelib_time **two, timelib_rel_time *rt)
      40              : {
      41              :         /* Check whether date/times need to be inverted. If both times are
      42              :          * TIMELIB_ZONETYPE_ID times with the same TZID, we use the y-s + us fields. */
      43            0 :         if (
      44            0 :                 (*one)->zone_type == TIMELIB_ZONETYPE_ID &&
      45            0 :                 (*two)->zone_type == TIMELIB_ZONETYPE_ID &&
      46            0 :                 (strcmp((*one)->tz_info->name, (*two)->tz_info->name) == 0)
      47              :         ) {
      48            0 :                 if (
      49            0 :                         ((*one)->y > (*two)->y) ||
      50            0 :                         ((*one)->y == (*two)->y && (*one)->m > (*two)->m) ||
      51            0 :                         ((*one)->y == (*two)->y && (*one)->m == (*two)->m && (*one)->d > (*two)->d) ||
      52            0 :                         ((*one)->y == (*two)->y && (*one)->m == (*two)->m && (*one)->d == (*two)->d && (*one)->h > (*two)->h) ||
      53            0 :                         ((*one)->y == (*two)->y && (*one)->m == (*two)->m && (*one)->d == (*two)->d && (*one)->h == (*two)->h && (*one)->i > (*two)->i) ||
      54            0 :                         ((*one)->y == (*two)->y && (*one)->m == (*two)->m && (*one)->d == (*two)->d && (*one)->h == (*two)->h && (*one)->i == (*two)->i && (*one)->s > (*two)->s) ||
      55            0 :                         ((*one)->y == (*two)->y && (*one)->m == (*two)->m && (*one)->d == (*two)->d && (*one)->h == (*two)->h && (*one)->i == (*two)->i && (*one)->s == (*two)->s && (*one)->us > (*two)->us)
      56              :                 ) {
      57            0 :                         swap_times(one, two, rt);
      58              :                 }
      59            0 :                 return;
      60              :         }
      61              : 
      62              :         /* Fall back to using the SSE instead to rearrange */
      63            0 :         if (
      64            0 :                 ((*one)->sse > (*two)->sse) ||
      65            0 :                 ((*one)->sse == (*two)->sse && (*one)->us > (*two)->us)
      66              :         ) {
      67            0 :                 swap_times(one, two, rt);
      68              :         }
      69              : }
      70              : 
      71            0 : static timelib_rel_time *timelib_diff_with_tzid(timelib_time *one, timelib_time *two)
      72              : {
      73              :         timelib_rel_time *rt;
      74            0 :         timelib_sll       dst_corr = 0, dst_h_corr = 0, dst_m_corr = 0;
      75              :         int32_t           trans_offset;
      76              :         timelib_sll       trans_transition_time;
      77              : 
      78            0 :         rt = timelib_rel_time_ctor();
      79            0 :         rt->invert = 0;
      80              : 
      81            0 :         sort_old_to_new(&one, &two, rt);
      82              : 
      83              :         /* Calculate correction for UTC offset changes between first and second SSE */
      84            0 :         dst_corr = two->z - one->z;
      85            0 :         dst_h_corr = dst_corr / 3600;
      86            0 :         dst_m_corr = (dst_corr % 3600) / 60;
      87              : 
      88            0 :         rt->y = two->y - one->y;
      89            0 :         rt->m = two->m - one->m;
      90            0 :         rt->d = two->d - one->d;
      91            0 :         rt->h = two->h - one->h;
      92            0 :         rt->i = two->i - one->i;
      93            0 :         rt->s = two->s - one->s;
      94            0 :         rt->us = two->us - one->us;
      95              : 
      96            0 :         rt->days = timelib_diff_days(one, two);
      97              : 
      98              :         /* Fall Back: Cater for transition period, where rt->invert is 0, but there are negative numbers */
      99            0 :         if (two->sse < one->sse) {
     100            0 :                 timelib_sll flipped = llabs((rt->i * 60) + (rt->s) - dst_corr);
     101            0 :                 rt->h = flipped / SECS_PER_HOUR;
     102            0 :                 rt->i = (flipped - rt->h * SECS_PER_HOUR) / 60;
     103            0 :                 rt->s = flipped % 60;
     104              : 
     105            0 :                 rt->invert = 1 - rt->invert;
     106              :         }
     107              : 
     108            0 :         timelib_do_rel_normalize(rt->invert ? one : two, rt);
     109              : 
     110            0 :         if (one->dst == 1 && two->dst == 0) { /* Fall Back */
     111            0 :                 if (two->tz_info) {
     112            0 :                         if ((two->sse - one->sse + dst_corr) < SECS_PER_DAY) {
     113            0 :                                 rt->h -= dst_h_corr;
     114            0 :                                 rt->i -= dst_m_corr;
     115              :                         }
     116              :                 }
     117            0 :         } else if (one->dst == 0 && two->dst == 1) { /* Spring Forward */
     118            0 :                 if (two->tz_info) {
     119            0 :                         int success = timelib_get_time_zone_offset_info(two->sse, two->tz_info, &trans_offset, &trans_transition_time, NULL);
     120              : 
     121            0 :                         if (
     122            0 :                                 success &&
     123            0 :                                 !((one->sse + SECS_PER_DAY > trans_transition_time) && (one->sse + SECS_PER_DAY <= (trans_transition_time + dst_corr))) &&
     124            0 :                                 two->sse >= trans_transition_time &&
     125            0 :                                 ((two->sse - one->sse + dst_corr) % SECS_PER_DAY) > (two->sse - trans_transition_time)
     126              :                         ) {
     127            0 :                                 rt->h -= dst_h_corr;
     128            0 :                                 rt->i -= dst_m_corr;
     129              :                         }
     130              :                 }
     131            0 :         } else if (two->sse - one->sse >= SECS_PER_DAY) {
     132              :                 /* Check whether we're in the period to the next transition time */
     133            0 :                 if (timelib_get_time_zone_offset_info(two->sse - two->z, two->tz_info, &trans_offset, &trans_transition_time, NULL)) {
     134            0 :                         dst_corr = one->z - trans_offset;
     135              : 
     136            0 :                         if (two->sse >= trans_transition_time - dst_corr && two->sse < trans_transition_time) {
     137            0 :                                 rt->d--;
     138            0 :                                 rt->h = 24;
     139              :                         }
     140              :                 }
     141              :         }
     142              : 
     143            0 :         return rt;
     144              : }
     145              : 
     146            0 : timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two)
     147              : {
     148              :         timelib_rel_time *rt;
     149              : 
     150            0 :         if (one->zone_type == TIMELIB_ZONETYPE_ID && two->zone_type == TIMELIB_ZONETYPE_ID && strcmp(one->tz_info->name, two->tz_info->name) == 0) {
     151            0 :                 return timelib_diff_with_tzid(one, two);
     152              :         }
     153              : 
     154            0 :         rt = timelib_rel_time_ctor();
     155            0 :         rt->invert = 0;
     156              : 
     157            0 :         sort_old_to_new(&one, &two, rt);
     158              : 
     159            0 :         rt->y = two->y - one->y;
     160            0 :         rt->m = two->m - one->m;
     161            0 :         rt->d = two->d - one->d;
     162            0 :         rt->h = two->h - one->h;
     163            0 :         if (one->zone_type != TIMELIB_ZONETYPE_ID) {
     164            0 :                 rt->h = rt->h + one->dst;
     165              :         }
     166            0 :         if (two->zone_type != TIMELIB_ZONETYPE_ID) {
     167            0 :                 rt->h = rt->h - two->dst;
     168              :         }
     169            0 :         rt->i = two->i - one->i;
     170            0 :         rt->s = two->s - one->s - two->z + one->z;
     171            0 :         rt->us = two->us - one->us;
     172              : 
     173            0 :         rt->days = timelib_diff_days(one, two);
     174              : 
     175            0 :         timelib_do_rel_normalize(rt->invert ? one : two, rt);
     176              : 
     177            0 :         return rt;
     178              : }
     179              : 
     180              : 
     181            0 : int timelib_diff_days(timelib_time *one, timelib_time *two)
     182              : {
     183            0 :         int days = 0;
     184              : 
     185            0 :         if (timelib_same_timezone(one, two)) {
     186              :                 timelib_time *earliest, *latest;
     187              :                 double earliest_time, latest_time;
     188              : 
     189            0 :                 if (timelib_time_compare(one, two) < 0) {
     190            0 :                         earliest = one;
     191            0 :                         latest = two;
     192              :                 } else {
     193            0 :                         earliest = two;
     194            0 :                         latest = one;
     195              :                 }
     196            0 :                 timelib_hmsf_to_decimal_hour(earliest->h, earliest->i, earliest->s, earliest->us, &earliest_time);
     197            0 :                 timelib_hmsf_to_decimal_hour(latest->h, latest->i, latest->s, latest->us, &latest_time);
     198              : 
     199            0 :                 days = llabs(timelib_epoch_days_from_time(one) - timelib_epoch_days_from_time(two));
     200            0 :                 if (latest_time < earliest_time && days > 0) {
     201            0 :                         days--;
     202              :                 }
     203              :         } else {
     204              :                 /* FIXME: This truncates the range of days to INT_MAX to avoid an
     205              :                  * overflow later on. Ideally, all APIs have better error handling. */
     206            0 :                 double ddays = fabs(floor(one->sse - two->sse) / 86400);
     207            0 :                 days = ddays <= INT_MAX ? ddays : INT_MAX;
     208              :         }
     209              : 
     210            0 :         return days;
     211              : }
     212              : 
     213              : 
     214            0 : timelib_time *timelib_add(timelib_time *old_time, timelib_rel_time *interval)
     215              : {
     216            0 :         int bias = 1;
     217            0 :         timelib_time *t = timelib_time_clone(old_time);
     218              : 
     219            0 :         if (interval->have_weekday_relative || interval->have_special_relative) {
     220            0 :                 memcpy(&t->relative, interval, sizeof(timelib_rel_time));
     221              :         } else {
     222            0 :                 if (interval->invert) {
     223            0 :                         bias = -1;
     224              :                 }
     225            0 :                 memset(&t->relative, 0, sizeof(timelib_rel_time));
     226            0 :                 t->relative.y = interval->y * bias;
     227            0 :                 t->relative.m = interval->m * bias;
     228            0 :                 t->relative.d = interval->d * bias;
     229            0 :                 t->relative.h = interval->h * bias;
     230            0 :                 t->relative.i = interval->i * bias;
     231            0 :                 t->relative.s = interval->s * bias;
     232            0 :                 t->relative.us = interval->us * bias;
     233              :         }
     234            0 :         t->have_relative = 1;
     235            0 :         t->sse_uptodate = 0;
     236              : 
     237            0 :         timelib_update_ts(t, NULL);
     238              : 
     239            0 :         timelib_update_from_sse(t);
     240            0 :         t->have_relative = 0;
     241              : 
     242            0 :         return t;
     243              : }
     244              : 
     245          368 : timelib_time *timelib_sub(timelib_time *old_time, timelib_rel_time *interval)
     246              : {
     247          368 :         int bias = 1;
     248          368 :         timelib_time *t = timelib_time_clone(old_time);
     249              : 
     250          368 :         if (interval->invert) {
     251           16 :                 bias = -1;
     252              :         }
     253              : 
     254          368 :         memset(&t->relative, 0, sizeof(timelib_rel_time));
     255          368 :         t->relative.y = 0 - (interval->y * bias);
     256          368 :         t->relative.m = 0 - (interval->m * bias);
     257          368 :         t->relative.d = 0 - (interval->d * bias);
     258          368 :         t->relative.h = 0 - (interval->h * bias);
     259          368 :         t->relative.i = 0 - (interval->i * bias);
     260          368 :         t->relative.s = 0 - (interval->s * bias);
     261          368 :         t->relative.us = 0 - (interval->us * bias);
     262          368 :         t->have_relative = 1;
     263          368 :         t->sse_uptodate = 0;
     264              : 
     265          368 :         timelib_update_ts(t, NULL);
     266              : 
     267          368 :         timelib_update_from_sse(t);
     268              : 
     269          368 :         t->have_relative = 0;
     270              : 
     271          368 :         return t;
     272              : }
     273              : 
     274            0 : static void do_range_limit(timelib_sll start, timelib_sll end, timelib_sll adj, timelib_sll *a, timelib_sll *b)
     275              : {
     276            0 :         if (*a < start) {
     277            0 :                 *b -= (start - *a - 1) / adj + 1;
     278            0 :                 *a += adj * ((start - *a - 1) / adj + 1);
     279              :         }
     280            0 :         if (*a >= end) {
     281            0 :                 *b += *a / adj;
     282            0 :                 *a -= adj * (*a / adj);
     283              :         }
     284            0 : }
     285              : 
     286              : 
     287            0 : timelib_time *timelib_add_wall(timelib_time *old_time, timelib_rel_time *interval)
     288              : {
     289            0 :         int bias = 1;
     290            0 :         timelib_time *t = timelib_time_clone(old_time);
     291              : 
     292            0 :         t->have_relative = 1;
     293            0 :         t->sse_uptodate = 0;
     294              : 
     295            0 :         if (interval->have_weekday_relative || interval->have_special_relative) {
     296            0 :                 memcpy(&t->relative, interval, sizeof(timelib_rel_time));
     297              : 
     298            0 :                 timelib_update_ts(t, NULL);
     299              : 
     300            0 :                 timelib_update_from_sse(t);
     301              :         } else {
     302            0 :                 if (interval->invert) {
     303            0 :                         bias = -1;
     304              :                 }
     305            0 :                 memset(&t->relative, 0, sizeof(timelib_rel_time));
     306            0 :                 t->relative.y = interval->y * bias;
     307            0 :                 t->relative.m = interval->m * bias;
     308            0 :                 t->relative.d = interval->d * bias;
     309              : 
     310            0 :                 if (t->relative.y || t->relative.m || t->relative.d) {
     311            0 :                         timelib_update_ts(t, NULL);
     312              :                 }
     313              : 
     314            0 :                 if (interval->us == 0) {
     315            0 :                         t->sse += bias * timelib_hms_to_seconds(interval->h, interval->i, interval->s);
     316            0 :                         timelib_update_from_sse(t);
     317              :                 } else {
     318            0 :                         timelib_rel_time *temp_interval = timelib_rel_time_clone(interval);
     319              : 
     320            0 :                         do_range_limit(0, 1000000, 1000000, &temp_interval->us, &temp_interval->s);
     321            0 :                         t->sse += bias * timelib_hms_to_seconds(temp_interval->h, temp_interval->i, temp_interval->s);
     322            0 :                         timelib_update_from_sse(t);
     323            0 :                         t->us += temp_interval->us * bias;
     324              : 
     325            0 :                         timelib_do_normalize(t);
     326            0 :                         timelib_update_ts(t, NULL);
     327              : 
     328            0 :                         timelib_rel_time_dtor(temp_interval);
     329              :                 }
     330            0 :                 timelib_do_normalize(t);
     331              :         }
     332              : 
     333            0 :         if (t->zone_type == TIMELIB_ZONETYPE_ID) {
     334            0 :                 timelib_set_timezone(t, t->tz_info);
     335              :         }
     336            0 :         t->have_relative = 0;
     337              : 
     338            0 :         return t;
     339              : }
     340              : 
     341            0 : timelib_time *timelib_sub_wall(timelib_time *old_time, timelib_rel_time *interval)
     342              : {
     343            0 :         int bias = 1;
     344            0 :         timelib_time *t = timelib_time_clone(old_time);
     345              : 
     346            0 :         t->have_relative = 1;
     347            0 :         t->sse_uptodate = 0;
     348              : 
     349            0 :         if (interval->have_weekday_relative || interval->have_special_relative) {
     350            0 :                 memcpy(&t->relative, interval, sizeof(timelib_rel_time));
     351              : 
     352            0 :                 timelib_update_ts(t, NULL);
     353            0 :                 timelib_update_from_sse(t);
     354              :         } else {
     355            0 :                 if (interval->invert) {
     356            0 :                         bias = -1;
     357              :                 }
     358            0 :                 memset(&t->relative, 0, sizeof(timelib_rel_time));
     359            0 :                 t->relative.y = 0 - (interval->y * bias);
     360            0 :                 t->relative.m = 0 - (interval->m * bias);
     361            0 :                 t->relative.d = 0 - (interval->d * bias);
     362              : 
     363            0 :                 if (t->relative.y || t->relative.m || t->relative.d) {
     364            0 :                         timelib_update_ts(t, NULL);
     365              :                 }
     366              : 
     367            0 :                 if (interval->us == 0) {
     368            0 :                         t->sse -= bias * timelib_hms_to_seconds(interval->h, interval->i, interval->s);
     369            0 :                         timelib_update_from_sse(t);
     370              :                 } else {
     371            0 :                         timelib_rel_time *temp_interval = timelib_rel_time_clone(interval);
     372              : 
     373            0 :                         do_range_limit(0, 1000000, 1000000, &temp_interval->us, &temp_interval->s);
     374            0 :                         t->sse -= bias * timelib_hms_to_seconds(temp_interval->h, temp_interval->i, temp_interval->s);
     375            0 :                         timelib_update_from_sse(t);
     376            0 :                         t->us -= temp_interval->us * bias;
     377              : 
     378            0 :                         timelib_do_normalize(t);
     379            0 :                         timelib_update_ts(t, NULL);
     380              : 
     381            0 :                         timelib_rel_time_dtor(temp_interval);
     382              :                 }
     383            0 :                 timelib_do_normalize(t);
     384              :         }
     385              : 
     386            0 :         if (t->zone_type == TIMELIB_ZONETYPE_ID) {
     387            0 :                 timelib_set_timezone(t, t->tz_info);
     388              :         }
     389            0 :         t->have_relative = 0;
     390              : 
     391            0 :         return t;
     392              : }
        

Generated by: LCOV version 2.0-1