initial commit of Gregorian/IFC date conversion tools and unit tests

This commit is contained in:
2025-02-16 21:12:54 -08:00
parent 08f09ce109
commit 95985ef032
8 changed files with 339 additions and 0 deletions

5
DAYSOFWEEK.m Normal file
View File

@@ -0,0 +1,5 @@
classdef DAYSOFWEEK < uint8
enumeration
Saturday (0), Sunday (1), Monday (2), Tuesday (3), Wednesday (4), Thursday (5), Friday (6), YearDay (7), LeapDay (8)
end
end

53
IFC.m Normal file
View File

@@ -0,0 +1,53 @@
function fixed = IFC(gregorian)
arguments (Input)
gregorian (:,1) datetime
end
arguments (Output)
fixed (:, 4) table
end
% gregorian datetime 1 arg input?
% y/m/d 3 args input?
% TODO inputs validation
% prepare output struct
fixed = struct('Year', [], 'Month', '', 'Day', [], 'DayOfWeek', '');
fixed = repmat(fixed, length(gregorian), 1);
% eliminate time from input, only date matters
gregorian.Hour = 0; gregorian.Minute = 0; gregorian.Second = 0;
% year remains constant
Year = num2cell(uint16(year(gregorian')));
[fixed.Year] = Year{:};
% find number of days into the current year
Days = days(gregorian - datetime([fixed.Year] - 1, 12, 31)');
% Calculate IFC month, number of days into that month, and if leap day
% and/or year day occured
[nMonths, nDays, leaped, yeared] = IFCmonths(Days, [fixed.Year]');
% write month to output
Month = num2cell(MONTHS(nMonths));
[fixed.Month] = Month{:};
% write number of days in month to output
nDays = num2cell(nDays);
[fixed.Day] = nDays{:};
% write day of week to output
DOW = num2cell(DAYSOFWEEK(mod([fixed.Day]', 7)));
[fixed.DayOfWeek] = DOW{:};
% Write year days as special days of the week
yearDays = num2cell(repmat(DAYSOFWEEK(7), 1, sum(yeared)));
[fixed(yeared).DayOfWeek] = yearDays{:}; % try 2 year/leap days in a vector input
% Write leap days as special days of the week
leapDayFlags = ~yeared & leaped & [fixed.Month]' == MONTHS(14) & [fixed.Day]' == 0;
leapDays = num2cell(repmat(DAYSOFWEEK(8), 1, sum(leapDayFlags)));
[fixed(leapDayFlags).DayOfWeek] = leapDays{:};
% convert output to table
fixed = struct2table(fixed);
end

53
IFCmonths.m Normal file
View File

@@ -0,0 +1,53 @@
function [nMonths, Days, leaped, yeared] = IFCmonths(Days, Year)
arguments (Input)
Days (:, 1) uint16
Year (:, 1) uint16
end
arguments (Output)
nMonths (:, 1) uint8
Days (:, 1) uint8
leaped (:, 1) logical
yeared (:, 1) logical
end
% intialize output to first month
nMonths = ones(size(Days));
% initialize output saying a leap day happened to false
leaped = false(size(Days));
% initialize output saying a year day happened to false
yeared = false(size(Days));
% check if it's a leap year
leap = isLeap(Year);
% while more days than 1 month, add months to nMonths
excess = Days > 28;
while any(excess)
% Subtract 28 days and add one month
Days(excess) = Days(excess) - 28;
nMonths(excess) = nMonths(excess) + 1;
% Find dates which are reaching the leap day (if it exists)
leaping = (leap & nMonths == 7);
% subtract leap day before proceeding to add another month
Days(leaping) = Days(leaping) - 1;
% Set leap day output to true
leaped(leaping) = true;
% Set the month to 14 (no month) if there are no days left
% meaning the last day is the leap day
% (which is not in any month)
noneLeft = ~Days;
nMonths(noneLeft) = 14;
% continue subtracting days and adding months as needed
% find excess days remaining for next iteration
excess = Days > 28;
end
% check for if a year day occured
yearing = (nMonths == 14 & Days == 1);
yeared(yearing) = true; % Year day achieved, set flag
Days(yearing) = 0; % decrement Days since year day was taken
end

5
MONTHS.m Normal file
View File

@@ -0,0 +1,5 @@
classdef MONTHS < uint8
enumeration
January (1), February (2), March (3), April (4), May (5), June (6), Sol (7), July (8), August (9), September (10), October (11), November (12), December (13), None (14)
end
end

30
isLeap.m Normal file
View File

@@ -0,0 +1,30 @@
function flag = isLeap(Year)
arguments (Input)
Year (:, 1) uint16
end
arguments (Output)
flag (:, 1) logical
end
% flag = NaN(size(Year));
isDivisibleBy4 = ~mod(Year, 4);
isNotDivisibleBy100 = logical(mod(Year, 100));
isDivisibleBy400 = ~mod(Year, 400);
flag = isDivisibleBy4 & (isDivisibleBy400 | isNotDivisibleBy100);
% if ~mod(Year, 4)
% if ~mod(Year, 100)
% if ~mod(Year, 400)
% flag = true;
% else
% flag = false;
% end
% else
% flag = true;
% end
% else
% flag = false;
% end
end

63
test_IFC.m Normal file
View File

@@ -0,0 +1,63 @@
classdef test_IFC < matlab.unittest.TestCase
methods(TestClassSetup)
% Shared setup for the entire test class
end
methods(TestMethodSetup)
% Setup for each test
end
methods(Test)
% Test methods
function test_today(testCase)
ifc = IFC(datetime('28-Dec-2024'));
testCase.verifyEqual(ifc.Year, uint16(2024));
testCase.verifyEqual(ifc.Month, MONTHS(13));
testCase.verifyEqual(ifc.Day, uint8(26));
testCase.verifyEqual(ifc.DayOfWeek, DAYSOFWEEK(5));
end
function test_yearday_noleap(testCase)
ifc = IFC(datetime('31-Dec-2023'));
testCase.verifyEqual(ifc.Year, uint16(2023));
testCase.verifyEqual(ifc.Month, MONTHS(14));
testCase.verifyEqual(ifc.Day, uint8(0));
testCase.verifyEqual(ifc.DayOfWeek, DAYSOFWEEK(7));
end
function test_yearday_leap(testCase)
ifc = IFC(datetime('31-Dec-2020'));
testCase.verifyEqual(ifc.Year, uint16(2020));
testCase.verifyEqual(ifc.Month, MONTHS(14));
testCase.verifyEqual(ifc.Day, uint8(0));
testCase.verifyEqual(ifc.DayOfWeek, DAYSOFWEEK(7));
end
function test_leapday_noleap(testCase)
ifc = IFC(datetime('17-Jun-2003'));
testCase.verifyEqual(ifc.Year, uint16(2003));
testCase.verifyEqual(ifc.Month, MONTHS(6));
testCase.verifyEqual(ifc.Day, uint8(28));
testCase.verifyEqual(ifc.DayOfWeek, DAYSOFWEEK(0));
end
function test_leapday_leap(testCase)
ifc = IFC(datetime('17-Jun-2008'));
testCase.verifyEqual(ifc.Year, uint16(2008));
testCase.verifyEqual(ifc.Month, MONTHS(14));
testCase.verifyEqual(ifc.Day, uint8(0));
testCase.verifyEqual(ifc.DayOfWeek, DAYSOFWEEK(8));
end
function test_all_vectorized(testCase)
ifc = IFC(datetime(["28-Dec-2024"; "31-Dec-2023"; "31-Dec-2020"; "17-Jun-2003"; "17-Jun-2008"]));
testCase.verifyEqual([ifc.Year], uint16([2024; 2023; 2020; 2003; 2008]));
testCase.verifyEqual([ifc.Month], MONTHS([13; 14; 14; 6; 14]));
testCase.verifyEqual([ifc.Day], uint8([26; 0; 0; 28; 0]));
testCase.verifyEqual([ifc.DayOfWeek], DAYSOFWEEK([5; 7; 7; 0; 8]));
end
end
end

95
test_IFCmonths.m Normal file
View File

@@ -0,0 +1,95 @@
classdef test_IFCmonths < matlab.unittest.TestCase
methods(TestClassSetup)
% Shared setup for the entire test class
end
methods(TestMethodSetup)
% Setup for each test
end
methods(Test)
% Test methods
function test_jan1(testCase)
[nMonths, nDays, leaped, yeared] = IFCmonths(1, 2000);
testCase.verifyEqual(nMonths, uint8(1));
testCase.verifyEqual(nDays, uint8(1));
testCase.verifyFalse(leaped);
testCase.verifyFalse(yeared);
end
function test_jan31(testCase)
[nMonths, nDays, leaped, yeared] = IFCmonths(31, 2000);
testCase.verifyEqual(nMonths, uint8(2));
testCase.verifyEqual(nDays, uint8(3));
testCase.verifyFalse(leaped);
testCase.verifyFalse(yeared);
end
function test_jun17_leap(testCase)
[nMonths, nDays, leaped, yeared] = IFCmonths(168, 2000);
testCase.verifyEqual(nMonths, uint8(6));
testCase.verifyEqual(nDays, uint8(28));
testCase.verifyFalse(leaped);
testCase.verifyFalse(yeared);
end
function test_jun17_noleap(testCase)
[nMonths, nDays, leaped, yeared] = IFCmonths(168, 2001);
testCase.verifyEqual(nMonths, uint8(6));
testCase.verifyEqual(nDays, uint8(28));
testCase.verifyFalse(leaped);
testCase.verifyFalse(yeared);
end
function test_jun18_leap(testCase)
[nMonths, nDays, leaped, yeared] = IFCmonths(169, 2000);
testCase.verifyEqual(nMonths, uint8(14));
testCase.verifyEqual(nDays, uint8(0));
testCase.verifyTrue(leaped);
testCase.verifyFalse(yeared);
end
function test_jun18_noleap(testCase)
[nMonths, nDays, leaped, yeared] = IFCmonths(169, 2001);
testCase.verifyEqual(nMonths, uint8(7));
testCase.verifyEqual(nDays, uint8(1));
testCase.verifyFalse(leaped);
testCase.verifyFalse(yeared);
end
function test_dec31_noleap(testCase)
[nMonths, nDays, leaped, yeared] = IFCmonths(365, 2001);
testCase.verifyEqual(nMonths, uint8(14));
testCase.verifyEqual(nDays, uint8(0));
testCase.verifyFalse(leaped);
testCase.verifyTrue(yeared);
end
function test_dec31_leap(testCase)
[nMonths, nDays, leaped, yeared] = IFCmonths(366, 2000);
testCase.verifyEqual(nMonths, uint8(14));
testCase.verifyEqual(nDays, uint8(0));
testCase.verifyTrue(leaped);
testCase.verifyTrue(yeared);
end
function test_today(testCase)
[nMonths, nDays, leaped, yeared] = IFCmonths(363, 2024);
testCase.verifyEqual(nMonths, uint8(13));
testCase.verifyEqual(nDays, uint8(26));
testCase.verifyTrue(leaped);
testCase.verifyFalse(yeared);
end
function test_vectorized(testCase)
[nMonths, nDays, leaped, yeared] = IFCmonths([1; 31; 168; 168; 169; 169; 365; 366; 363], [2000; 2000; 2000; 2001; 2000; 2001; 2001; 2000; 2024]);
testCase.verifyEqual(nMonths, uint8([1; 2; 6; 6; 14; 7; 14; 14; 13]));
testCase.verifyEqual(nDays, uint8([1; 3; 28; 28; 0; 1; 0; 0; 26]));
testCase.verifyEqual(leaped, [false; false; false; false; true; false; false; true; true]);
testCase.verifyEqual(yeared, [false; false; false; false; false; false; true; true; false]);
end
end
end

35
test_isLeap.m Normal file
View File

@@ -0,0 +1,35 @@
classdef test_isLeap < matlab.unittest.TestCase
methods(TestClassSetup)
% Shared setup for the entire test class
end
methods(TestMethodSetup)
% Setup for each test
end
methods(Test)
% Test methods
function test_basic_2001(testCase)
testCase.verifyFalse(isLeap(2001));
end
function test_basic_1996(testCase)
testCase.verifyTrue(isLeap(1996));
end
function test_exception1_2100(testCase)
testCase.verifyFalse(isLeap(2100));
end
function test_exception2_2000(testCase)
testCase.verifyTrue(isLeap(2000));
end
function test_vectorized_all(testCase)
testCase.verifyEqual([true; false; false; false; true; false], isLeap([2000; 1999; 1998; 1997; 1996; 2100]));
end
end
end