LCOV - code coverage report
Current view: top level - src - date.c (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 190 190 100.0 %
Date: 2017-04-30 Functions: 32 32 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Present - Date/Time Library
       3             :  *
       4             :  * Implementation of the Date methods
       5             :  *
       6             :  * Licensed under the MIT License.
       7             :  * For details, see LICENSE.
       8             :  */
       9             : 
      10             : #include <assert.h>
      11             : #include <stddef.h>
      12             : #include <string.h>
      13             : 
      14             : #include "present.h"
      15             : 
      16             : #include "utils/constants.h"
      17             : #include "utils/impl-utils.h"
      18             : #include "utils/time-utils.h"
      19             : 
      20             : /**
      21             :  * Get the week number of the last week of a given year (either 52 or 53).
      22             :  */
      23             : static int_week_of_year
      24          46 : last_week_of_year(int_year year)
      25             : {
      26             :     int_week_of_year week;
      27             :     time_t time;
      28             :     struct tm tm;
      29             :     int_day_of_week day_of_week;
      30             : 
      31             :     /* https://en.wikipedia.org/wiki/ISO_week_date#Weeks_per_year */
      32          46 :     week = 52;
      33             : 
      34             :     /* Get some information on Jan. 1 of this year */
      35          46 :     time = unix_timestamp_to_time_t(to_unix_timestamp(year, 1, 1, 0, 0, 0));
      36          46 :     time_t_to_struct_tm(&time, &tm);
      37             : 
      38          46 :     day_of_week = tm.tm_wday;
      39          46 :     if (day_of_week == DAY_OF_WEEK_SUNDAY_COMPAT) {
      40           1 :         day_of_week = DAY_OF_WEEK_SUNDAY;
      41             :     }
      42             : 
      43             :     /* If the year starts on a Thursday, it has 53 weeks */
      44          46 :     if (day_of_week == DAY_OF_WEEK_THURSDAY) {
      45          10 :         week = 53;
      46             :     }
      47             : 
      48             :     /* If this year is a leap year, and it starts on a Wednesday,
      49             :        it has 53 weeks */
      50          46 :     if (IS_LEAP_YEAR(year) && day_of_week == DAY_OF_WEEK_WEDNESDAY) {
      51           5 :         week = 53;
      52             :     }
      53             : 
      54          46 :     return week;
      55             : }
      56             : 
      57             : /**
      58             :  * Make sure that year, month, and day are valid, and set day_of_year and
      59             :  * day_of_week to their correct values.
      60             :  */
      61             : static void
      62        3792 : check_date_data(struct PresentDateData * const data)
      63             : {
      64             :     time_t time;
      65             :     struct tm tm;
      66             : 
      67        3792 :     time = unix_timestamp_to_time_t(to_unix_timestamp(
      68             :             data->year, data->month, data->day, 0, 0, 0));
      69        3792 :     time_t_to_struct_tm(&time, &tm);
      70             : 
      71        3792 :     data->year = (int_year)tm.tm_year + STRUCT_TM_YEAR_OFFSET;
      72        3792 :     data->month = (int_month)tm.tm_mon + STRUCT_TM_MONTH_OFFSET;
      73        3792 :     data->day = (int_day)tm.tm_mday;
      74        3792 :     data->day_of_year = (int_day_of_year)tm.tm_yday +
      75             :         STRUCT_TM_DAY_OF_YEAR_OFFSET;
      76        3792 :     data->day_of_week = (int_day_of_week)tm.tm_wday;
      77        3792 :     if (data->day_of_week == DAY_OF_WEEK_SUNDAY_COMPAT) {
      78         540 :         data->day_of_week = DAY_OF_WEEK_SUNDAY;
      79             :     }
      80        3792 : }
      81             : 
      82             : /**
      83             :  * Initialize a new Date instance based on its data parameters.
      84             :  */
      85             : static void
      86        3769 : init_date(
      87             :         struct Date * const result,
      88             :         int_year year,
      89             :         int_month month,
      90             :         int_day day)
      91             : {
      92             :     static const int_day DAYS_PER_MONTH[13] = {
      93             :         0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
      94             :     };
      95             : 
      96             :     int_day days_in_month;
      97             : 
      98        3769 :     assert(result != NULL);
      99        3769 :     CLEAR(result);
     100             : 
     101        3769 :     if (month < 1 || month > 12) {
     102           5 :         result->has_error = 1;
     103           5 :         result->errors.month_out_of_range = 1;
     104             :     }
     105             : 
     106        3769 :     days_in_month = (IS_LEAP_YEAR(year) && month == 2) ? 29 :
     107             :         DAYS_PER_MONTH[month];
     108        3769 :     if (day < 1 || day > days_in_month) {
     109          10 :         result->has_error = 1;
     110          10 :         result->errors.day_out_of_range = 1;
     111             :     }
     112             : 
     113        3769 :     if (!result->has_error) {
     114        3757 :         result->data_.year = year;
     115        3757 :         result->data_.month = month;
     116        3757 :         result->data_.day = day;
     117        3757 :         check_date_data(&result->data_);
     118             :     }
     119        3769 : }
     120             : 
     121             : /**
     122             :  * Initialize a new Date instance based on its data parameters, without
     123             :  * checking any bounds.
     124             :  */
     125             : static void
     126          20 : init_date_no_bounds_check(
     127             :         struct Date * const result,
     128             :         int_year year,
     129             :         int_month month,
     130             :         int_day day)
     131             : {
     132          20 :     assert(result != NULL);
     133          20 :     CLEAR(result);
     134             : 
     135          20 :     result->data_.year = year;
     136          20 :     result->data_.month = month;
     137          20 :     result->data_.day = day;
     138          20 :     check_date_data(&result->data_);
     139          20 : }
     140             : 
     141             : 
     142             : struct Date
     143           1 : Date_from_year(int_year year)
     144             : {
     145             :     struct Date result;
     146           1 :     init_date(&result, year, 1, 1);
     147           1 :     return result;
     148             : }
     149             : 
     150             : void
     151           6 : Date_ptr_from_year(struct Date * const result, int_year year)
     152             : {
     153           6 :     init_date(result, year, 1, 1);
     154           6 : }
     155             : 
     156             : struct Date
     157           2 : Date_from_year_month(int_year year, int_month month)
     158             : {
     159             :     struct Date result;
     160           2 :     init_date(&result, year, month, 1);
     161           2 :     return result;
     162             : }
     163             : 
     164             : void
     165           7 : Date_ptr_from_year_month(
     166             :         struct Date * const result,
     167             :         int_year year,
     168             :         int_month month)
     169             : {
     170           7 :     init_date(result, year, month, 1);
     171           7 : }
     172             : 
     173             : struct Date
     174           2 : Date_from_year_month_day(int_year year, int_month month, int_day day)
     175             : {
     176             :     struct Date result;
     177           2 :     init_date(&result, year, month, day);
     178           2 :     return result;
     179             : }
     180             : 
     181             : void
     182        3751 : Date_ptr_from_year_month_day(
     183             :         struct Date * const result,
     184             :         int_year year,
     185             :         int_month month,
     186             :         int_day day)
     187             : {
     188        3751 :     init_date(result, year, month, day);
     189        3751 : }
     190             : 
     191             : struct Date
     192           1 : Date_from_year_day(int_year year, int_day_of_year day_of_year)
     193             : {
     194             :     struct Date result;
     195           1 :     init_date_no_bounds_check(&result, year, 1, (int_day)(day_of_year));
     196           1 :     return result;
     197             : }
     198             : 
     199             : void
     200          19 : Date_ptr_from_year_day(
     201             :         struct Date * const result,
     202             :         int_year year,
     203             :         int_day_of_year day_of_year)
     204             : {
     205          19 :     init_date_no_bounds_check(result, year, 1, (int_day)(day_of_year));
     206          19 : }
     207             : 
     208             : struct Date
     209           3 : Date_from_year_week_day(
     210             :         int_year year,
     211             :         int_week_of_year week_of_year,
     212             :         int_day_of_week day_of_week)
     213             : {
     214             :     struct Date result;
     215           3 :     Date_ptr_from_year_week_day(&result, year, week_of_year, day_of_week);
     216           3 :     return result;
     217             : }
     218             : 
     219             : void
     220          22 : Date_ptr_from_year_week_day(
     221             :         struct Date * const result,
     222             :         int_year year,
     223             :         int_week_of_year week_of_year,
     224             :         int_day_of_week day_of_week)
     225             : {
     226             :     time_t time;
     227             :     struct tm tm;
     228             :     int_day_of_week jan_4_day_of_week;
     229             :     int_day_of_year ordinal_date;
     230             : 
     231          22 :     assert(result != NULL);
     232          22 :     CLEAR(result);
     233             : 
     234          22 :     if (week_of_year < 1 || week_of_year > last_week_of_year(year)) {
     235           7 :         result->has_error = 1;
     236           7 :         result->errors.week_of_year_out_of_range = 1;
     237             :     }
     238             : 
     239          22 :     if (day_of_week == DAY_OF_WEEK_SUNDAY_COMPAT) {
     240           1 :         day_of_week = DAY_OF_WEEK_SUNDAY;
     241             :     }
     242          22 :     if (day_of_week < 1 || day_of_week > 7) {
     243           4 :         result->has_error = 1;
     244           4 :         result->errors.day_of_week_out_of_range = 1;
     245             :     }
     246             : 
     247          22 :     if (!result->has_error) {
     248             :         /* Get the weekday of Jan. 4 of this year */
     249          11 :         time = unix_timestamp_to_time_t(to_unix_timestamp(
     250             :                 year, 1, 4, 0, 0, 0));
     251          11 :         time_t_to_struct_tm(&time, &tm);
     252             : 
     253          11 :         jan_4_day_of_week = tm.tm_wday;
     254          11 :         if (jan_4_day_of_week == DAY_OF_WEEK_SUNDAY_COMPAT) {
     255           3 :             jan_4_day_of_week = DAY_OF_WEEK_SUNDAY;
     256             :         }
     257          11 :         assert(jan_4_day_of_week >= 1 && jan_4_day_of_week <= 7);
     258             : 
     259             :         /* Calculate the date using voodoo magic
     260             :            https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
     261             :          */
     262          11 :         ordinal_date = week_of_year * 7 + day_of_week - (jan_4_day_of_week + 3);
     263          11 :         if (ordinal_date < 1) {
     264           1 :             year -= 1;
     265           1 :             ordinal_date += DAYS_IN_YEAR(year);
     266             :         }
     267          11 :         if (ordinal_date > DAYS_IN_YEAR(year)) {
     268           4 :             ordinal_date -= DAYS_IN_YEAR(year);
     269           4 :             year += 1;
     270             :         }
     271             : 
     272          11 :         Date_ptr_from_year_day(result, year, ordinal_date);
     273             :     }
     274          22 : }
     275             : 
     276             : int_year
     277        3788 : Date_year(const struct Date * const self)
     278             : {
     279        3788 :     assert(self != NULL);
     280        3788 :     assert(self->has_error == 0);
     281             : 
     282        3788 :     return self->data_.year;
     283             : }
     284             : 
     285             : int_month
     286        3788 : Date_month(const struct Date * const self)
     287             : {
     288        3788 :     assert(self != NULL);
     289        3788 :     assert(self->has_error == 0);
     290             : 
     291        3788 :     return self->data_.month;
     292             : }
     293             : 
     294             : int_day
     295        3788 : Date_day(const struct Date * const self)
     296             : {
     297        3788 :     assert(self != NULL);
     298        3788 :     assert(self->has_error == 0);
     299             : 
     300        3788 :     return self->data_.day;
     301             : }
     302             : 
     303             : int_day_of_year
     304           5 : Date_day_of_year(const struct Date * const self)
     305             : {
     306           5 :     assert(self != NULL);
     307           5 :     assert(self->has_error == 0);
     308             : 
     309           5 :     return self->data_.day_of_year;
     310             : }
     311             : 
     312             : struct PresentWeekYear
     313          21 : Date_week_of_year(const struct Date * const self)
     314             : {
     315             :     int_year year;
     316             :     int_week_of_year week;
     317             :     struct PresentWeekYear p;
     318             : 
     319          21 :     assert(self != NULL);
     320          21 :     assert(self->has_error == 0);
     321             : 
     322             :     /* Caculate the week number using pure voodoo magic
     323             :        https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_of_a_given_date
     324             :        */
     325          21 :     year = self->data_.year;
     326          21 :     week = (self->data_.day_of_year - self->data_.day_of_week + 10) / 7;
     327             : 
     328          21 :     if (week == 0) {
     329             :         /* It's the last week of the previous year */
     330           7 :         year -= 1;
     331           7 :         week = last_week_of_year(year);
     332             :     }
     333          21 :     if (week > last_week_of_year(year)) {
     334             :         /* It's the first week of the next year */
     335           2 :         year += 1;
     336           2 :         week = 1;
     337             :     }
     338             : 
     339          21 :     p.week = week;
     340          21 :     p.year = year;
     341          21 :     return p;
     342             : }
     343             : 
     344             : int_day_of_week
     345          13 : Date_day_of_week(const struct Date * const self)
     346             : {
     347          13 :     assert(self != NULL);
     348          13 :     assert(self->has_error == 0);
     349             : 
     350          13 :     return self->data_.day_of_week;
     351             : }
     352             : 
     353             : struct DayDelta
     354           4 : Date_difference(
     355             :         const struct Date * const self,
     356             :         const struct Date * const other)
     357             : {
     358             :     struct ClockTime noon;
     359             :     struct Timestamp self_timestamp, other_timestamp;
     360             :     struct TimeDelta time_delta;
     361             : 
     362           4 :     assert(self != NULL);
     363           4 :     assert(self->has_error == 0);
     364           4 :     assert(other != NULL);
     365           4 :     assert(other->has_error == 0);
     366             : 
     367           4 :     noon = ClockTime_noon();
     368           4 :     self_timestamp = Timestamp_create_utc(self, &noon);
     369           4 :     other_timestamp = Timestamp_create_utc(other, &noon);
     370           4 :     time_delta = Timestamp_difference(&self_timestamp, &other_timestamp);
     371           4 :     return TimeDelta_to_DayDelta_truncated(&time_delta);
     372             : }
     373             : 
     374             : struct DayDelta
     375           2 : Date_absolute_difference(
     376             :         const struct Date * const self,
     377             :         const struct Date * const other)
     378             : {
     379             :     struct DayDelta delta;
     380             : 
     381           2 :     assert(self != NULL);
     382           2 :     assert(self->has_error == 0);
     383           2 :     assert(other != NULL);
     384           2 :     assert(other->has_error == 0);
     385             : 
     386           2 :     delta = Date_difference(self, other);
     387           2 :     if (DayDelta_is_negative(&delta)) {
     388           1 :         DayDelta_negate(&delta);
     389             :     }
     390           2 :     return delta;
     391             : }
     392             : 
     393             : void
     394           4 : Date_add_DayDelta(
     395             :         struct Date * const self,
     396             :         const struct DayDelta * const delta)
     397             : {
     398           4 :     assert(self != NULL);
     399           4 :     assert(self->has_error == 0);
     400           4 :     assert(delta != NULL);
     401             : 
     402           4 :     self->data_.day += delta->data_.delta_days;
     403           4 :     check_date_data(&self->data_);
     404           4 : }
     405             : 
     406             : void
     407           5 : Date_add_MonthDelta(
     408             :         struct Date * const self,
     409             :         const struct MonthDelta * const delta)
     410             : {
     411           5 :     assert(self != NULL);
     412           5 :     assert(self->has_error == 0);
     413           5 :     assert(delta != NULL);
     414             : 
     415           5 :     self->data_.month += delta->data_.delta_months;
     416           5 :     check_date_data(&self->data_);
     417           5 : }
     418             : 
     419             : void
     420           3 : Date_subtract_DayDelta(
     421             :         struct Date * const self,
     422             :         const struct DayDelta * const delta)
     423             : {
     424           3 :     assert(self != NULL);
     425           3 :     assert(self->has_error == 0);
     426           3 :     assert(delta != NULL);
     427             : 
     428           3 :     self->data_.day -= delta->data_.delta_days;
     429           3 :     check_date_data(&self->data_);
     430           3 : }
     431             : 
     432             : void
     433           3 : Date_subtract_MonthDelta(
     434             :         struct Date * const self,
     435             :         const struct MonthDelta * const delta)
     436             : {
     437           3 :     assert(self != NULL);
     438           3 :     assert(self->has_error == 0);
     439           3 :     assert(delta != NULL);
     440             : 
     441           3 :     self->data_.month -= delta->data_.delta_months;
     442           3 :     check_date_data(&self->data_);
     443           3 : }
     444             : 
     445             : short
     446        1276 : Date_compare(
     447             :         const struct Date * const lhs,
     448             :         const struct Date * const rhs)
     449             : {
     450        1276 :     assert(lhs != NULL);
     451        1276 :     assert(lhs->has_error == 0);
     452        1276 :     assert(rhs != NULL);
     453        1276 :     assert(rhs->has_error == 0);
     454             : 
     455        7581 :     return
     456        7581 :         STRUCT_COMPARE(year,
     457             :             STRUCT_COMPARE(month,
     458             :                 STRUCT_COMPARE(day, 0)));
     459             : }
     460             : 
     461        1272 : STRUCT_COMPARISON_OPERATORS(Date)
     462             : 

Generated by: LCOV version 1.10