mirror of
				https://github.com/theoludwig/libcproject.git
				synced 2025-05-21 23:21:15 +02:00 
			
		
		
		
	feat: add date module
				
					
				
			This commit is contained in:
		
							
								
								
									
										287
									
								
								lib/date.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										287
									
								
								lib/date.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,287 @@ | ||||
| #include "date.h" | ||||
|  | ||||
| struct date *date_copy(struct date *date_to_copy) { | ||||
|   struct date *date = malloc(sizeof(struct date)); | ||||
|   if (date == NULL) { | ||||
|     perror("Error (date_copy)"); | ||||
|     exit(EXIT_FAILURE); | ||||
|   } | ||||
|   date->year = date_to_copy->year; | ||||
|   date->month = date_to_copy->month; | ||||
|   date->day = date_to_copy->day; | ||||
|   date->hours = date_to_copy->hours; | ||||
|   date->minutes = date_to_copy->minutes; | ||||
|   date->seconds = date_to_copy->seconds; | ||||
|   date->milliseconds = date_to_copy->milliseconds; | ||||
|   date->timezone_utc_offset = date_to_copy->timezone_utc_offset; | ||||
|   return date; | ||||
| } | ||||
|  | ||||
| bool date_get_is_valid_year(uint16_t year) { | ||||
|   return year <= 9999; | ||||
| } | ||||
|  | ||||
| bool date_get_is_valid_month(uint8_t month) { | ||||
|   return month >= 1 && month <= 12; | ||||
| } | ||||
|  | ||||
| bool date_get_is_valid_day(uint8_t day) { | ||||
|   return day >= 1 && day <= 31; | ||||
| } | ||||
|  | ||||
| bool date_get_is_valid_hours(uint8_t hours) { | ||||
|   return hours <= 23; | ||||
| } | ||||
|  | ||||
| bool date_get_is_valid_minutes(uint8_t minutes) { | ||||
|   return minutes <= 59; | ||||
| } | ||||
|  | ||||
| bool date_get_is_valid_seconds(uint8_t seconds) { | ||||
|   return seconds <= 59; | ||||
| } | ||||
|  | ||||
| bool date_get_is_valid_milliseconds(uint16_t milliseconds) { | ||||
|   return milliseconds <= 999; | ||||
| } | ||||
|  | ||||
| bool date_get_is_valid_timezone_utc_offset(int8_t timezone_utc_offset) { | ||||
|   return timezone_utc_offset >= -12 && timezone_utc_offset <= 14; | ||||
| } | ||||
|  | ||||
| bool date_get_is_valid(struct date *date) { | ||||
|   size_t date_days_of_month = date_get_days_of_month(date->month, date->year); | ||||
|   return (date_get_is_valid_month(date->month) && | ||||
|           date_get_is_valid_day(date->day) && | ||||
|           date->day <= date_days_of_month && | ||||
|           date_get_is_valid_hours(date->hours) && | ||||
|           date_get_is_valid_minutes(date->minutes) && | ||||
|           date_get_is_valid_seconds(date->seconds) && | ||||
|           date_get_is_valid_milliseconds(date->milliseconds) && | ||||
|           date_get_is_valid_timezone_utc_offset(date->timezone_utc_offset)); | ||||
| } | ||||
|  | ||||
| string_t date_to_iso_string(struct date *date_original) { | ||||
|   struct date *date = date_copy(date_original); | ||||
|   date_to_utc(date); | ||||
|  | ||||
|   size_t iso_string_length = 24; | ||||
|   string_t result = malloc(sizeof(char) * (iso_string_length + 1)); | ||||
|  | ||||
|   string_t year_string = string_zero_pad(date->year, 4); | ||||
|   string_t month_string = string_zero_pad(date->month, 2); | ||||
|   string_t day_string = string_zero_pad(date->day, 2); | ||||
|   string_t hours_string = string_zero_pad(date->hours, 2); | ||||
|   string_t minutes_string = string_zero_pad(date->minutes, 2); | ||||
|   string_t seconds_string = string_zero_pad(date->seconds, 2); | ||||
|   string_t milliseconds_string = string_zero_pad(date->milliseconds, 3); | ||||
|   sprintf(result, "%s-%s-%sT%s:%s:%s.%sZ", year_string, month_string, day_string, hours_string, minutes_string, seconds_string, milliseconds_string); | ||||
|   free(year_string); | ||||
|   free(month_string); | ||||
|   free(day_string); | ||||
|   free(hours_string); | ||||
|   free(minutes_string); | ||||
|   free(seconds_string); | ||||
|   free(milliseconds_string); | ||||
|  | ||||
|   free(date); | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| string_t date_to_iso_string_without_time(struct date *date) { | ||||
|   size_t iso_string_length = 10; | ||||
|   string_t result = malloc(sizeof(char) * (iso_string_length + 1)); | ||||
|   if (result == NULL) { | ||||
|     perror("Error (date_to_iso_string_without_time)"); | ||||
|     exit(EXIT_FAILURE); | ||||
|   } | ||||
|  | ||||
|   string_t year_string = string_zero_pad(date->year, 4); | ||||
|   string_t month_string = string_zero_pad(date->month, 2); | ||||
|   string_t day_string = string_zero_pad(date->day, 2); | ||||
|   sprintf(result, "%s-%s-%s", year_string, month_string, day_string); | ||||
|   free(year_string); | ||||
|   free(month_string); | ||||
|   free(day_string); | ||||
|  | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| struct date *date_from_iso_string(string_t iso_string) { | ||||
|   struct date *date = malloc(sizeof(struct date)); | ||||
|   if (date == NULL) { | ||||
|     perror("Error (date_from_iso_string)"); | ||||
|     exit(EXIT_FAILURE); | ||||
|   } | ||||
|  | ||||
|   string_t year_string = string_substring(iso_string, 0, 3); | ||||
|   date->year = (uint16_t)convert_string_to_number(year_string); | ||||
|   free(year_string); | ||||
|  | ||||
|   string_t month_string = string_substring(iso_string, 5, 6); | ||||
|   date->month = (uint8_t)convert_string_to_number(month_string); | ||||
|   free(month_string); | ||||
|  | ||||
|   string_t day_string = string_substring(iso_string, 8, 9); | ||||
|   date->day = (uint8_t)convert_string_to_number(day_string); | ||||
|   free(day_string); | ||||
|  | ||||
|   string_t hours_string = string_substring(iso_string, 11, 12); | ||||
|   date->hours = (uint8_t)convert_string_to_number(hours_string); | ||||
|   free(hours_string); | ||||
|  | ||||
|   string_t minutes_string = string_substring(iso_string, 14, 15); | ||||
|   date->minutes = (uint8_t)convert_string_to_number(minutes_string); | ||||
|   free(minutes_string); | ||||
|  | ||||
|   string_t seconds_string = string_substring(iso_string, 17, 18); | ||||
|   date->seconds = (uint8_t)convert_string_to_number(seconds_string); | ||||
|   free(seconds_string); | ||||
|  | ||||
|   string_t milliseconds_string = string_substring(iso_string, 20, 22); | ||||
|   date->milliseconds = (uint16_t)convert_string_to_number(milliseconds_string); | ||||
|   free(milliseconds_string); | ||||
|  | ||||
|   date->timezone_utc_offset = 0; | ||||
|  | ||||
|   return date; | ||||
| } | ||||
|  | ||||
| uint8_t date_get_days_of_month(uint8_t month, uint16_t year) { | ||||
|   switch (month) { | ||||
|     case 1: | ||||
|       return 31; | ||||
|     case 2: | ||||
|       return date_get_is_leap_year(year) ? 29 : 28; | ||||
|     case 3: | ||||
|       return 31; | ||||
|     case 4: | ||||
|       return 30; | ||||
|     case 5: | ||||
|       return 31; | ||||
|     case 6: | ||||
|       return 30; | ||||
|     case 7: | ||||
|       return 31; | ||||
|     case 8: | ||||
|       return 31; | ||||
|     case 9: | ||||
|       return 30; | ||||
|     case 10: | ||||
|       return 31; | ||||
|     case 11: | ||||
|       return 30; | ||||
|     case 12: | ||||
|       return 31; | ||||
|     default: | ||||
|       return 0; | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool date_get_is_leap_year(uint16_t year) { | ||||
|   return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; | ||||
| } | ||||
|  | ||||
| uint64_t date_convert_milliseconds_to_seconds(uint16_t milliseconds) { | ||||
|   return milliseconds / MILLISECONDS_PER_SECOND; | ||||
| } | ||||
|  | ||||
| uint64_t date_convert_seconds_to_milliseconds(uint64_t seconds) { | ||||
|   return seconds * MILLISECONDS_PER_SECOND; | ||||
| } | ||||
|  | ||||
| uint64_t date_convert_days_to_seconds(uint64_t days) { | ||||
|   return days * SECONDS_PER_DAY; | ||||
| } | ||||
|  | ||||
| uint64_t date_convert_hms_to_seconds(uint8_t hours, uint8_t minutes, uint8_t seconds) { | ||||
|   return (hours * SECONDS_PER_HOUR) + (minutes * SECONDS_PER_MINUTE) + seconds; | ||||
| } | ||||
|  | ||||
| uint64_t date_to_total_seconds(struct date *date) { | ||||
|   uint64_t total_seconds = 0; | ||||
|  | ||||
|   for (uint16_t year = 0; year < date->year; year++) { | ||||
|     total_seconds += 365 * SECONDS_PER_DAY; | ||||
|     if (date_get_is_leap_year(year)) { | ||||
|       total_seconds += SECONDS_PER_DAY; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   for (uint8_t month = 1; month < date->month; month++) { | ||||
|     total_seconds += date_convert_days_to_seconds(date_get_days_of_month(month, date->year)); | ||||
|   } | ||||
|  | ||||
|   total_seconds += date_convert_days_to_seconds(date->day - 1); | ||||
|   total_seconds += date_convert_hms_to_seconds(date->hours, date->minutes, date->seconds); | ||||
|  | ||||
|   return total_seconds; | ||||
| } | ||||
|  | ||||
| uint64_t date_duration_seconds_between_2_dates(struct date *date1, struct date *date2) { | ||||
|   struct date *utc_date1 = date_copy(date1); | ||||
|   struct date *utc_date2 = date_copy(date2); | ||||
|   date_to_utc(utc_date1); | ||||
|   date_to_utc(utc_date2); | ||||
|  | ||||
|   uint64_t total_seconds_date1 = date_to_total_seconds(utc_date1); | ||||
|   uint64_t total_seconds_date2 = date_to_total_seconds(utc_date2); | ||||
|  | ||||
|   free(utc_date1); | ||||
|   free(utc_date2); | ||||
|  | ||||
|   return total_seconds_date1 > total_seconds_date2 ? total_seconds_date1 - total_seconds_date2 : total_seconds_date2 - total_seconds_date1; | ||||
| } | ||||
|  | ||||
| void date_add_hours(struct date *date, int64_t hours) { | ||||
|   if (hours == 0) { | ||||
|     return; | ||||
|   } | ||||
|   int64_t total_hours = date->hours + hours; | ||||
|   int64_t additional_days = total_hours / 24; | ||||
|   int64_t new_hours = total_hours % 24; | ||||
|  | ||||
|   if (new_hours < 0) { | ||||
|     new_hours += 24; | ||||
|     additional_days -= 1; | ||||
|   } | ||||
|  | ||||
|   date->hours = (uint8_t)new_hours; | ||||
|  | ||||
|   if (additional_days != 0) { | ||||
|     date->day += additional_days; | ||||
|  | ||||
|     while (date->day > date_get_days_of_month(date->month, date->year)) { | ||||
|       date->day -= date_get_days_of_month(date->month, date->year); | ||||
|       date->month++; | ||||
|  | ||||
|       if (date->month > 12) { | ||||
|         date->month = 1; | ||||
|         date->year++; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     while (date->day < 1) { | ||||
|       date->month--; | ||||
|       if (date->month < 1) { | ||||
|         date->month = 12; | ||||
|         date->year--; | ||||
|       } | ||||
|  | ||||
|       date->day += date_get_days_of_month(date->month, date->year); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| void date_add_days(struct date *date, int64_t days) { | ||||
|   date_add_hours(date, days * 24); | ||||
| } | ||||
|  | ||||
| void date_to_utc(struct date *date) { | ||||
|   if (date->timezone_utc_offset == 0) { | ||||
|     return; | ||||
|   } | ||||
|   int8_t timezone_utc_offset = date->timezone_utc_offset; | ||||
|   date->timezone_utc_offset = 0; | ||||
|   date_add_hours(date, mathematics_opposite(timezone_utc_offset)); | ||||
| } | ||||
							
								
								
									
										324
									
								
								lib/date.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										324
									
								
								lib/date.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,324 @@ | ||||
| #ifndef __LIBCPROJECT_DATE__ | ||||
| #define __LIBCPROJECT_DATE__ | ||||
|  | ||||
| #include <errno.h> | ||||
| #include <stdbool.h> | ||||
| #include <stdint.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| #include "convert.h" | ||||
| #include "mathematics.h" | ||||
| #include "string.h" | ||||
| #include "types.h" | ||||
|  | ||||
| #define SECONDS_PER_MINUTE 60 | ||||
| #define SECONDS_PER_HOUR (60 * SECONDS_PER_MINUTE) | ||||
| #define SECONDS_PER_DAY (24 * SECONDS_PER_HOUR) | ||||
| #define MILLISECONDS_PER_SECOND 1000 | ||||
|  | ||||
| /** | ||||
|  * @brief Date object representing a single moment in time. | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| struct date { | ||||
|   /** | ||||
|    * Year. | ||||
|    * Between [0, 9999] (inclusive). | ||||
|    * As per ISO 8601, a four-digit year [YYYY] and represents years from 0000 to 9999, year 0000 being equal to 1 BC and all others AD. | ||||
|    */ | ||||
|   uint16_t year; | ||||
|  | ||||
|   /** | ||||
|    * Month. | ||||
|    * Between [1, 12] (inclusive). | ||||
|    */ | ||||
|   uint8_t month; | ||||
|  | ||||
|   /** | ||||
|    * Day. | ||||
|    * Between [1, 31] (inclusive). | ||||
|    */ | ||||
|   uint8_t day; | ||||
|  | ||||
|   /** | ||||
|    * Hours. | ||||
|    * Between [0, 23] (inclusive). | ||||
|    */ | ||||
|   uint8_t hours; | ||||
|  | ||||
|   /** | ||||
|    * Minutes. | ||||
|    * Between [0, 59] (inclusive). | ||||
|    */ | ||||
|   uint8_t minutes; | ||||
|  | ||||
|   /** | ||||
|    * Seconds. | ||||
|    * Between [0, 59] (inclusive). | ||||
|    */ | ||||
|   uint8_t seconds; | ||||
|  | ||||
|   /** | ||||
|    * Milliseconds. | ||||
|    * Between [0, 999] (inclusive). | ||||
|    */ | ||||
|   uint16_t milliseconds; | ||||
|  | ||||
|   /** | ||||
|    * Timezone UTC offset. | ||||
|    * Between [-12, 14] | ||||
|    */ | ||||
|   int8_t timezone_utc_offset; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Return the copy of a date. | ||||
|  * | ||||
|  * @param date | ||||
|  * @return struct date* | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| struct date *date_copy(struct date *date_to_copy); | ||||
|  | ||||
| /** | ||||
|  * @brief Check if a year is valid, between [0, 9999] (inclusive). | ||||
|  * | ||||
|  * @param year | ||||
|  * @return bool | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| bool date_get_is_valid_year(uint16_t year); | ||||
|  | ||||
| /** | ||||
|  * @brief Check if a month is valid, between [1, 12] (inclusive). | ||||
|  * | ||||
|  * @param month | ||||
|  * @return bool | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| bool date_get_is_valid_month(uint8_t month); | ||||
|  | ||||
| /** | ||||
|  * @brief Check if a day is valid, between [1, 31] (inclusive). | ||||
|  * | ||||
|  * @param day | ||||
|  * @return bool | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| bool date_get_is_valid_day(uint8_t day); | ||||
|  | ||||
| /** | ||||
|  * @brief Check if hours are valid, between [0, 23] (inclusive). | ||||
|  * | ||||
|  * @param hours | ||||
|  * @return bool | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| bool date_get_is_valid_hours(uint8_t hours); | ||||
|  | ||||
| /** | ||||
|  * @brief Check if minutes are valid, between [0, 59] (inclusive). | ||||
|  * | ||||
|  * @param minutes | ||||
|  * @return bool | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| bool date_get_is_valid_minutes(uint8_t minutes); | ||||
|  | ||||
| /** | ||||
|  * @brief Check if seconds are valid, between [0, 59] (inclusive). | ||||
|  * | ||||
|  * @param seconds | ||||
|  * @return bool | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| bool date_get_is_valid_seconds(uint8_t seconds); | ||||
|  | ||||
| /** | ||||
|  * @brief Check if milliseconds are valid, between [0, 999] (inclusive). | ||||
|  * | ||||
|  * @param milliseconds | ||||
|  * @return bool | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| bool date_get_is_valid_milliseconds(uint16_t milliseconds); | ||||
|  | ||||
| /** | ||||
|  * @brief Check if the timezone UTC offset is valid, between [-12, 14] (inclusive). | ||||
|  * | ||||
|  * @param timezone_utc_offset | ||||
|  * @return bool | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| bool date_get_is_valid_timezone_utc_offset(int8_t timezone_utc_offset); | ||||
|  | ||||
| /** | ||||
|  * @brief Check if the date is valid (all fields are possible). | ||||
|  * | ||||
|  * @param date | ||||
|  * @return bool | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| bool date_get_is_valid(struct date *date); | ||||
|  | ||||
| /** | ||||
|  * @brief String representing the date in the date time string format, a simplified format based on ISO 8601, which is always 24 characters long (`YYYY-MM-DDTHH:mm:ss.sssZ`). The timezone is always UTC, as denoted by the suffix `Z`. | ||||
|  * | ||||
|  * @param date | ||||
|  * @return string_t | ||||
|  * | ||||
|  * @code | ||||
|  * date_to_iso_string() // "2024-09-11T09:39:18.203Z" | ||||
|  * @endcode | ||||
|  * | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| string_t date_to_iso_string(struct date *date); | ||||
|  | ||||
| /** | ||||
|  * @brief String representing the date in the ISO 8601 format, without time information (`YYYY-MM-DD`). | ||||
|  * | ||||
|  * @param date | ||||
|  * @return string_t | ||||
|  * | ||||
|  * @code | ||||
|  * date_to_iso_string_without_time() // "2024-09-11" | ||||
|  * @endcode | ||||
|  * | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| string_t date_to_iso_string_without_time(struct date *date); | ||||
|  | ||||
| /** | ||||
|  * @brief Create a date from an ISO 8601 string, with the format `YYYY-MM-DDTHH:mm:ss.sssZ`. | ||||
|  * | ||||
|  * The timezone is always UTC, as denoted by the suffix `Z`. | ||||
|  * | ||||
|  * @param iso_string | ||||
|  * @return struct date* | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| struct date *date_from_iso_string(string_t iso_string); | ||||
|  | ||||
| /** | ||||
|  * @brief Get number of days in one month [1, 12]. | ||||
|  * | ||||
|  * @param month | ||||
|  * @return uint8_t | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| uint8_t date_get_days_of_month(uint8_t month, uint16_t year); | ||||
|  | ||||
| /** | ||||
|  * @brief Determine if a year is a leap year. | ||||
|  * | ||||
|  * @param year | ||||
|  * @return bool | ||||
|  * | ||||
|  * @code | ||||
|  * date_is_leap_year(2020) // true | ||||
|  * | ||||
|  * date_is_leap_year(2021) // false | ||||
|  * | ||||
|  * date_is_leap_year(2022) // false | ||||
|  * | ||||
|  * date_is_leap_year(2023) // false | ||||
|  * | ||||
|  * date_is_leap_year(2024) // true | ||||
|  * @endcode | ||||
|  * | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| bool date_get_is_leap_year(uint16_t year); | ||||
|  | ||||
| /** | ||||
|  * @brief Convert milliseconds to seconds. | ||||
|  * | ||||
|  * @param milliseconds | ||||
|  * @return uint64_t | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| uint64_t date_convert_milliseconds_to_seconds(uint16_t milliseconds); | ||||
|  | ||||
| /** | ||||
|  * @brief Convert seconds to milliseconds. | ||||
|  * | ||||
|  * @param seconds | ||||
|  * @return uint64_t | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| uint64_t date_convert_seconds_to_milliseconds(uint64_t seconds); | ||||
|  | ||||
| /** | ||||
|  * @brief Convert days to seconds. | ||||
|  * | ||||
|  * @param days | ||||
|  * @return uint64_t | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| uint64_t date_convert_days_to_seconds(uint64_t days); | ||||
|  | ||||
| /** | ||||
|  * @brief Convert hours, minutes, and seconds to seconds. | ||||
|  * | ||||
|  * @param hours | ||||
|  * @param minutes | ||||
|  * @param seconds | ||||
|  * @return uint64_t | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| uint64_t date_convert_hms_to_seconds(uint8_t hours, uint8_t minutes, uint8_t seconds); | ||||
|  | ||||
| /** | ||||
|  * @brief Convert a date to total seconds. | ||||
|  * | ||||
|  * @param date | ||||
|  * @return uint64_t | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| uint64_t date_to_total_seconds(struct date *date); | ||||
|  | ||||
| /** | ||||
|  * @brief Calculate the duration in seconds between 2 dates. | ||||
|  * | ||||
|  * @param date1 | ||||
|  * @param date2 | ||||
|  * @return uint64_t | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| uint64_t date_duration_seconds_between_2_dates(struct date *date1, struct date *date2); | ||||
|  | ||||
| /** | ||||
|  * @brief Add hours to the date, managing the day, month, year changes if necessary. | ||||
|  * | ||||
|  * NOTE: Mutates the date. | ||||
|  * | ||||
|  * @param date | ||||
|  * @param hours | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| void date_add_hours(struct date *date, int64_t hours); | ||||
|  | ||||
| /** | ||||
|  * @brief Adds days to the date, managing month and year changes as needed. | ||||
|  * | ||||
|  * NOTE: Mutates the date. | ||||
|  * | ||||
|  * @param date The date to which days are being added. | ||||
|  * @param days The number of days to add. | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| void date_add_days(struct date *date, int64_t days); | ||||
|  | ||||
| /** | ||||
|  * @brief Transform the date with a Timezone UTC Offset to UTC (timezone_utc_offset = 0). | ||||
|  * | ||||
|  * NOTE: Mutates the date. | ||||
|  * | ||||
|  * @param date | ||||
|  * @since v4.3.0 | ||||
|  */ | ||||
| void date_to_utc(struct date *date); | ||||
|  | ||||
| #endif | ||||
| @@ -4,6 +4,7 @@ | ||||
| #include "lib/array_list.h" | ||||
| #include "lib/character.h" | ||||
| #include "lib/convert.h" | ||||
| #include "lib/date.h" | ||||
| #include "lib/filesystem.h" | ||||
| #include "lib/hash_map.h" | ||||
| #include "lib/linked_list.h" | ||||
|   | ||||
							
								
								
									
										164
									
								
								test/date_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								test/date_test.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,164 @@ | ||||
| #include "date_test.h" | ||||
|  | ||||
| void date_test() { | ||||
|   date_copy_test(); | ||||
|   date_to_iso_string_test(); | ||||
|   date_to_iso_string_without_time_test(); | ||||
|   date_from_iso_string_test(); | ||||
|   date_get_is_leap_year_test(); | ||||
|   date_duration_seconds_between_2_dates_test(); | ||||
|   date_to_utc_test(); | ||||
| } | ||||
|  | ||||
| void date_copy_test() { | ||||
|   struct date *date = malloc(sizeof(struct date)); | ||||
|   date->year = 2024; | ||||
|   date->month = 9; | ||||
|   date->day = 10; | ||||
|   date->hours = 20; | ||||
|   date->minutes = 34; | ||||
|   date->seconds = 25; | ||||
|   date->milliseconds = 76; | ||||
|   date->timezone_utc_offset = 0; | ||||
|  | ||||
|   struct date *date2 = date_copy(date); | ||||
|   assert(date != date2); | ||||
|   assert(date->year == date2->year); | ||||
|   assert(date->month == date2->month); | ||||
|   assert(date->day == date2->day); | ||||
|   assert(date->hours == date2->hours); | ||||
|   assert(date->minutes == date2->minutes); | ||||
|   assert(date->seconds == date2->seconds); | ||||
|   assert(date->milliseconds == date2->milliseconds); | ||||
|   assert(date->timezone_utc_offset == date2->timezone_utc_offset); | ||||
|  | ||||
|   date->year = 2025; | ||||
|   assert(date->year == 2025); | ||||
|   assert(date2->year == 2024); | ||||
|  | ||||
|   free(date); | ||||
|   free(date2); | ||||
| } | ||||
|  | ||||
| void date_to_iso_string_test() { | ||||
|   struct date *date = malloc(sizeof(struct date)); | ||||
|   date->year = 2024; | ||||
|   date->month = 9; | ||||
|   date->day = 10; | ||||
|   date->hours = 20; | ||||
|   date->minutes = 34; | ||||
|   date->seconds = 25; | ||||
|   date->milliseconds = 76; | ||||
|   date->timezone_utc_offset = 0; | ||||
|  | ||||
|   string_t iso_string = date_to_iso_string(date); | ||||
|   assert(assert_string_equal(iso_string, "2024-09-10T20:34:25.076Z")); | ||||
|   free(iso_string); | ||||
|  | ||||
|   free(date); | ||||
| } | ||||
|  | ||||
| void date_to_iso_string_without_time_test() { | ||||
|   struct date *date = malloc(sizeof(struct date)); | ||||
|   date->year = 2024; | ||||
|   date->month = 9; | ||||
|   date->day = 10; | ||||
|   date->hours = 20; | ||||
|   date->minutes = 34; | ||||
|   date->seconds = 25; | ||||
|   date->milliseconds = 76; | ||||
|   date->timezone_utc_offset = 0; | ||||
|  | ||||
|   string_t iso_string = date_to_iso_string_without_time(date); | ||||
|   assert(assert_string_equal(iso_string, "2024-09-10")); | ||||
|   free(iso_string); | ||||
|  | ||||
|   free(date); | ||||
| } | ||||
|  | ||||
| void date_from_iso_string_test() { | ||||
|   string_t iso_string = "2024-09-10T20:34:25.076Z"; | ||||
|   struct date *date = date_from_iso_string(iso_string); | ||||
|   assert(date->year == 2024); | ||||
|   assert(date->month == 9); | ||||
|   assert(date->day == 10); | ||||
|   assert(date->hours == 20); | ||||
|   assert(date->minutes == 34); | ||||
|   assert(date->seconds == 25); | ||||
|   assert(date->milliseconds == 76); | ||||
|   assert(date->timezone_utc_offset == 0); | ||||
|  | ||||
|   free(date); | ||||
| } | ||||
|  | ||||
| void date_get_is_leap_year_test() { | ||||
|   assert(date_get_is_leap_year(2020)); | ||||
|   assert(!date_get_is_leap_year(2021)); | ||||
|   assert(!date_get_is_leap_year(2022)); | ||||
|   assert(!date_get_is_leap_year(2023)); | ||||
|   assert(date_get_is_leap_year(2024)); | ||||
| } | ||||
|  | ||||
| void date_duration_seconds_between_2_dates_test() { | ||||
|   struct date *date1 = date_from_iso_string("2024-09-10T20:34:25.076Z"); | ||||
|   struct date *date2 = date_from_iso_string("2024-09-10T20:34:25.076Z"); | ||||
|   assert(date_duration_seconds_between_2_dates(date1, date2) == 0); | ||||
|   free(date1); | ||||
|   free(date2); | ||||
|  | ||||
|   date1 = date_from_iso_string("2024-09-10T20:34:25.076Z"); | ||||
|   date2 = date_from_iso_string("2024-09-10T23:34:26.076Z"); | ||||
|   assert(date_duration_seconds_between_2_dates(date1, date2) == 10801); | ||||
|   free(date1); | ||||
|   free(date2); | ||||
|  | ||||
|   date1 = date_from_iso_string("2024-09-10T20:34:25.076Z"); | ||||
|   date2 = date_from_iso_string("2024-09-10T20:48:25.076Z"); | ||||
|   assert(date_duration_seconds_between_2_dates(date1, date2) == 840); | ||||
|   free(date1); | ||||
|   free(date2); | ||||
|  | ||||
|   date1 = date_from_iso_string("2024-09-10T20:34:25.076Z"); | ||||
|   date2 = date_from_iso_string("2024-09-10T20:34:38.076Z"); | ||||
|   assert(date_duration_seconds_between_2_dates(date1, date2) == 13); | ||||
|   free(date1); | ||||
|   free(date2); | ||||
| } | ||||
|  | ||||
| void date_to_utc_test() { | ||||
|   struct date *date = date_from_iso_string("2024-09-10T20:34:25.076Z"); | ||||
|   date->timezone_utc_offset = 3; | ||||
|   date_to_utc(date); | ||||
|   assert(date->timezone_utc_offset == 0); | ||||
|   string_t iso_string = date_to_iso_string(date); | ||||
|   assert(assert_string_equal(iso_string, "2024-09-10T17:34:25.076Z")); | ||||
|   free(iso_string); | ||||
|   free(date); | ||||
|  | ||||
|   date = date_from_iso_string("2024-09-10T20:34:25.076Z"); | ||||
|   date->timezone_utc_offset = -3; | ||||
|   date_to_utc(date); | ||||
|   assert(date->timezone_utc_offset == 0); | ||||
|   iso_string = date_to_iso_string(date); | ||||
|   assert(assert_string_equal(iso_string, "2024-09-10T23:34:25.076Z")); | ||||
|   free(iso_string); | ||||
|   free(date); | ||||
|  | ||||
|   date = date_from_iso_string("2024-01-01T00:00:00.000Z"); | ||||
|   date->timezone_utc_offset = 3; | ||||
|   date_to_utc(date); | ||||
|   assert(date->timezone_utc_offset == 0); | ||||
|   iso_string = date_to_iso_string(date); | ||||
|   assert(assert_string_equal(iso_string, "2023-12-31T21:00:00.000Z")); | ||||
|   free(iso_string); | ||||
|   free(date); | ||||
|  | ||||
|   date = date_from_iso_string("2023-12-31T21:00:00.000Z"); | ||||
|   date->timezone_utc_offset = -4; | ||||
|   date_to_utc(date); | ||||
|   assert(date->timezone_utc_offset == 0); | ||||
|   iso_string = date_to_iso_string(date); | ||||
|   assert(assert_string_equal(iso_string, "2024-01-01T01:00:00.000Z")); | ||||
|   free(iso_string); | ||||
|   free(date); | ||||
| } | ||||
							
								
								
									
										25
									
								
								test/date_test.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								test/date_test.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| #ifndef __DATE_TEST__ | ||||
| #define __DATE_TEST__ | ||||
|  | ||||
| #include <assert.h> | ||||
|  | ||||
| #include "libcproject.h" | ||||
| #include "test.h" | ||||
|  | ||||
| void date_test(); | ||||
|  | ||||
| void date_copy_test(); | ||||
|  | ||||
| void date_to_iso_string_test(); | ||||
|  | ||||
| void date_to_iso_string_without_time_test(); | ||||
|  | ||||
| void date_from_iso_string_test(); | ||||
|  | ||||
| void date_get_is_leap_year_test(); | ||||
|  | ||||
| void date_duration_seconds_between_2_dates_test(); | ||||
|  | ||||
| void date_to_utc_test(); | ||||
|  | ||||
| #endif | ||||
| @@ -4,6 +4,7 @@ | ||||
| #include "array_list_test.h" | ||||
| #include "character_test.h" | ||||
| #include "convert_test.h" | ||||
| #include "date_test.h" | ||||
| #include "hash_map_test.h" | ||||
| #include "linked_list_test.h" | ||||
| #include "mathematics_test.h" | ||||
| @@ -15,6 +16,7 @@ int main() { | ||||
|   array_list_test(); | ||||
|   character_test(); | ||||
|   convert_test(); | ||||
|   date_test(); | ||||
|   hash_map_test(); | ||||
|   linked_list_test(); | ||||
|   mathematics_test(); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user