HOME

Code ported to C from the excelent article:

http://howardhinnant.github.io/date_algorithms.html

Added datet type that represents the number of days since 1 jan 1970 and conversions with timet.

#include "pch.h"
#include <time.h>
#include <stdio.h>
#include <assert.h>
#include <stdbool.h>
#include <assert.h>
#include <limits.h>
#include <Windows.h>

//Ported from C++ version:
//http://howardhinnant.github.io/date_algorithms.html

bool is_leap(int y)
{
return  y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
}

//number of days since civil 1970-01-01.  Negative values indicate
//days prior to 1970-01-01.
typedef int date_t;

// Returns number of days since civil 1970-01-01.  Negative values indicate
//    days prior to 1970-01-01.
// Preconditions:  y-m-d represents a date in the civil (Gregorian) calendar
//                 m is in [1, 12]
//                 d is in [1, last_day_of_month(y, m)]
//                 y is "approximately" in
//                   [numeric_limits<Int>::min()/366, numeric_limits<Int>::max()/366]
//                 Exact range of validity is:
//                 [civil_from_days(numeric_limits<Int>::min()),
//                  civil_from_days(numeric_limits<Int>::max()-719468)]

int days_from_civil(int y, unsigned m, unsigned d)
{
y -= m <= 2;
const int era = (y >= 0 ? y : y - 399) / 400;
const unsigned yoe = (unsigned)(y - era * 400);      // [0, 399]
const unsigned doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;  // [0, 365]
const unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;         // [0, 146096]
return era * 146097 + (int)(doe) - 719468;
}

// Returns year/month/day triple in civil calendar
// Preconditions:  z is number of days since 1970-01-01 and is in the range:
//                   [numeric_limits<Int>::min(), numeric_limits<Int>::max()-719468].

void civil_from_days(int z, int *year, int *month, int *day)
{
z += 719468;
const int era = (z >= 0 ? z : z - 146096) / 146097;
const unsigned doe = (unsigned)(z - era * 146097);          // [0, 146096]
const unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;  // [0, 399]
const int y = (int)(yoe) + era * 400;
const unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100);                // [0, 365]
const unsigned mp = (5 * doy + 2) / 153;                                   // [0, 11]
const unsigned d = doy - (153 * mp + 2) / 5 + 1;                             // [1, 31]
const unsigned m = mp + (mp < 10 ? 3 : -9);                            // [1, 12]

*year = y + (m <= 2);
*month = m;
*day = d;
}

// Preconditions: m is in [1, 12]
// Returns: The number of days in the month m of leap year
// The result is always in the range [29, 31].

unsigned last_day_of_month_leap_year(unsigned m)
{
unsigned char a[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
return a[m - 1];
}

unsigned last_day_of_month_common_year(unsigned m)
{
unsigned char a[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
return a[m - 1];
}

unsigned last_day_of_month(int y, unsigned m)
{
return m != 2 || !is_leap(y) ? last_day_of_month_common_year(m) : 29u;
}

unsigned weekday_from_days(int z)
{
return (unsigned)(z >= -4 ? (z + 4) % 7 : (z + 5) % 7 + 6);
}

time_t date_to_time(date_t d)
{
return d * (60 * 60 * 24);
}

date_t time_to_date(time_t t)
{
return (date_t) (t / (60 * 60 * 24));
}

unsigned long long FileTimeToTicks(FILETIME ft)
{
return ((ULONGLONG)(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
}

time_t FileTimeToTime(FILETIME ft)
{
return FileTimeToTicks(ft) / 10000000ULL - 11644473600ULL;
}

date_t GetFileTimeAsDate(FILETIME ft)
{
return (date_t) ((FileTimeToTicks(ft) / 10000000ULL - 11644473600ULL) / (60 * 60 * 24));
}

Test

int main()
{
FILETIME    ft;
SYSTEMTIME st = {0};
st.wYear = 1970;
st.wDay = 1;
st.wMonth = 1;
SystemTimeToFileTime(&st, &ft);
unsigned long long ll = FileTimeToTicks(ft);
ll = 0;
time_t t = time(NULL);
int year0, month0, day0;
civil_from_days(time_to_date(t), &year0, &month0, &day0);

//1970 - 01 - 01
int d = 1;
int m = 1;
int y = 1970;
int w = 4;//[0, 6] -> [Sun, Sat]
for (int z = 0; z < (INT_MAX - 719468); z++)
{
if (days_from_civil(y, m, d) != z)
{
printf("days_from_civil(%d, %d, %d) != %d\n", y, m, d, z);
}

int year, month, day;
civil_from_days(z, &year, &month, &day);
if (year != y || month != m || day != d)
{
printf("%d  %d != %d || %d != %d || %d == %d\n", z, year, y, month, m, day, d);
}

if (weekday_from_days(z) != w)
{
printf("weekday_from_days(z) != w\n");
}

if (last_day_of_month(y, m) == d)
{
d = 1;
if (m == 12)
{
m = 1;
y++;
}
else
{
m++;
}
}
else
{
d++;
}

if (w == 6)
w = 0;
else
w++;
}
printf("ok");
}