mirror of
https://gitee.com/Lamdonn/varch.git
synced 2025-12-06 16:56:42 +08:00
235 lines
7.6 KiB
C
235 lines
7.6 KiB
C
/*********************************************************************************************************
|
|
* ------------------------------------------------------------------------------------------------------
|
|
* file description
|
|
* ------------------------------------------------------------------------------------------------------
|
|
* \file date.c
|
|
* \unit date
|
|
* \brief This is a simple date calculate module for C language
|
|
* \author Lamdonn
|
|
* \version v1.0.0
|
|
* \license GPL-2.0
|
|
* \copyright Copyright (C) 2023 Lamdonn.
|
|
********************************************************************************************************/
|
|
#include "date.h"
|
|
|
|
// TODO: 1582.10.04 - 1582.10.15
|
|
|
|
/**
|
|
* \brief Check if a year is a leap year.
|
|
* \param[in] year: The year to check.
|
|
* \return 1 if the year is a leap year, 0 otherwise.
|
|
*/
|
|
uint8_t date_isleap(uint16_t year)
|
|
{
|
|
return (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0));
|
|
}
|
|
|
|
/**
|
|
* \brief Get the number of days in a century.
|
|
* \param[in] century: The century to evaluate.
|
|
* \return Number of days in the given century.
|
|
*/
|
|
uint32_t date_century_days(uint16_t century)
|
|
{
|
|
if (century == 0) return 0u; // No days in century 0
|
|
return (century % 4 == 0) ? 36525u : 36524u; // 25 leap years in 400 years
|
|
}
|
|
|
|
/**
|
|
* \brief Get the number of days in a year.
|
|
* \param[in] year: The year to evaluate.
|
|
* \return Number of days in the given year.
|
|
*/
|
|
uint32_t date_year_days(uint16_t year)
|
|
{
|
|
if (year == 0) return 0u; // No days in year 0
|
|
return date_isleap(year) ? 366u : 365u;
|
|
}
|
|
|
|
/**
|
|
* \brief Get the number of days in a month of a given year.
|
|
* \param[in] year: The year of the month.
|
|
* \param[in] month: The month to evaluate (1-12).
|
|
* \return Number of days in the specified month of the specified year.
|
|
*/
|
|
uint32_t date_month_days(uint16_t year, uint8_t month)
|
|
{
|
|
if (year == 0) return 0; // No valid month days for year 0
|
|
switch (month)
|
|
{
|
|
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
|
|
return 31; // Months with 31 days
|
|
case 4: case 6: case 9: case 11:
|
|
return 30; // Months with 30 days
|
|
case 2:
|
|
return date_isleap(year) ? 29 : 28; // February days depending on leap year
|
|
default:
|
|
return 0; // Invalid month
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Validate a date.
|
|
* \param[in] date: The date structure to validate.
|
|
* \return 1 if the date is valid, 0 otherwise.
|
|
*/
|
|
uint8_t date_isvalid(DATE date)
|
|
{
|
|
if (date.year == 0 || date.month == 0 || date.day == 0) return 0; // Year, month or day 0 is not valid
|
|
return (date_month_days(date.year, date.month) >= date.day);
|
|
}
|
|
|
|
/**
|
|
* \brief Calculate the total number of days from a given date to a base date.
|
|
* \param[in] date: The date to calculate the total days for.
|
|
* \return Total number of days from a base date to the given date, 0 if invalid.
|
|
*/
|
|
uint32_t date_current_days(DATE date)
|
|
{
|
|
uint32_t days = 0;
|
|
uint16_t year = 1;
|
|
uint8_t month = 1;
|
|
|
|
// Validate the input date; return 0 if invalid
|
|
if (!date_isvalid(date)) return 0;
|
|
|
|
// Count days for complete centuries
|
|
for ( ; year + 2000 <= date.year; year += 2000) days += 730485u;
|
|
// Count days for complete centuries within the current century
|
|
for ( ; year + 100 <= date.year; year += 100) days += date_century_days((year / 100) + 1);
|
|
// Count days for complete years within the current century
|
|
for ( ; year + 1 <= date.year; year++) days += date_year_days(year);
|
|
// Count days for completed months in the current year
|
|
for ( ; month < date.month; month++) days += date_month_days(date.year, month);
|
|
// Add the days of the current month
|
|
days += date.day;
|
|
|
|
return days;
|
|
}
|
|
|
|
/**
|
|
* \brief Get the week number of a given date based on the total days.
|
|
* \param[in] date: The date to evaluate.
|
|
* \return The week number [1, 7] for the specified date (Mon | Tue | Wed | Thu | Fri | Sat | Sun), 0 if invalid.
|
|
*/
|
|
uint32_t date_get_week(DATE date)
|
|
{
|
|
#define bias 0 // Adjust to week start
|
|
uint32_t days = date_current_days(date);
|
|
uint32_t week = 0;
|
|
|
|
// If days is 0, return 0 indicating invalid date
|
|
if (days == 0) return 0;
|
|
|
|
week = (days + bias) % 7;
|
|
if (week == 0) week = 7;
|
|
|
|
return week;
|
|
}
|
|
|
|
/**
|
|
* \brief Calculate the difference in days between two dates.
|
|
* \param[in] date1: The first date.
|
|
* \param[in] date2: The second date.
|
|
* \return The difference in days between the two dates. Returns 0 if either date is invalid.
|
|
*/
|
|
int32_t date_diff_days(DATE date1, DATE date2)
|
|
{
|
|
uint32_t days1 = date_current_days(date1);
|
|
uint32_t days2 = date_current_days(date2); // Corrected to use date2
|
|
|
|
// Return 0 if either date is invalid
|
|
if (days1 == 0 || days2 == 0) return 0;
|
|
|
|
return (int32_t)(days1 - days2);
|
|
}
|
|
|
|
/**
|
|
* \brief Convert a number of days to a DATE structure.
|
|
* \param[in] days: The number of days to convert.
|
|
* \return A DATE structure representing the converted days.
|
|
*/
|
|
DATE date_from_days(uint32_t days)
|
|
{
|
|
DATE date = {1, 1, 1}; // Initialize to base date (1/1/1)
|
|
uint32_t tdays = 0;
|
|
|
|
// Return zero date if days are 0
|
|
if (days == 0) return DATE(0,0,0);
|
|
|
|
// Convert days to years
|
|
for ( ; days > 730485; days -= 730485) date.year += 2000;
|
|
for ( ; days > (tdays = date_century_days((date.year / 100) + 1)); days -= tdays) date.year += 100;
|
|
for ( ; days > (tdays = date_year_days(date.year)); days -= tdays) date.year++;
|
|
for ( ; days > (tdays = date_month_days(date.year, date.month)); days -= tdays) date.month++;
|
|
date.day = days;
|
|
|
|
return date;
|
|
}
|
|
|
|
/**
|
|
* \brief Offset a date by a certain number of days.
|
|
* \param[in] date: The date to offset.
|
|
* \param[in] days: The number of days to offset (can be negative).
|
|
* \return A new DATE structure after applying the offset, or a zero date if invalid.
|
|
*/
|
|
DATE date_offset(DATE date, int32_t days)
|
|
{
|
|
if (!date_isvalid(date)) return DATE(0,0,0); // Return zero date if invalid
|
|
return date_from_days(date_current_days(date) + days);
|
|
}
|
|
|
|
/**
|
|
* \brief Show the calendar for a specific month and year.
|
|
* \param[in] year: The year of the calendar to show.
|
|
* \param[in] month: The month of the calendar to show.
|
|
*/
|
|
void date_show(uint16_t year, uint8_t month)
|
|
{
|
|
DATE date = {year, month, 1};
|
|
uint32_t days;
|
|
uint32_t tday = 1;
|
|
uint32_t week = 0;
|
|
int len = 0;
|
|
|
|
days = date_month_days(year, month);
|
|
if (days == 0)
|
|
{
|
|
printf("[ERROR] Invalid date!\r\n");
|
|
return; // Exit if the date is invalid
|
|
}
|
|
|
|
week = date_get_week(date) % 7; // Get the starting day of the week
|
|
|
|
// Print the calendar header
|
|
printf("+-----------------------------------------+\r\n");
|
|
len = printf("| %04d/%02d", year, month); while (len++ < 42) printf(" "); printf("|\r\n");
|
|
printf("+-----------------------------------------+\r\n");
|
|
printf("| Sun | Mon | Tue | Wed | Thu | Fri | Sat |\r\n");
|
|
printf("+-----------------------------------------+\r\n");
|
|
|
|
// Print the days of the month
|
|
while (tday <= days)
|
|
{
|
|
for (int i = 0; i < 7; i++)
|
|
{
|
|
if (i == 0) printf("|"); // Start of the week
|
|
|
|
if (i == week && tday <= days)
|
|
{
|
|
printf(" %3u |", tday); // Print the current day
|
|
tday++;
|
|
week = (week + 1) % 7; // Move to the next week day
|
|
}
|
|
else
|
|
{
|
|
printf(" |"); // Empty space for days not in the current month
|
|
}
|
|
|
|
if (i == 6) printf("\r\n"); // End of the week
|
|
}
|
|
}
|
|
|
|
printf("+-----------------------------------------+\r\n"); // End of the calendar
|
|
}
|