initial commit of Gregorian/IFC date conversion tools and unit tests
This commit is contained in:
5
DAYSOFWEEK.m
Normal file
5
DAYSOFWEEK.m
Normal 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
53
IFC.m
Normal 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
53
IFCmonths.m
Normal 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
5
MONTHS.m
Normal 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
30
isLeap.m
Normal 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
63
test_IFC.m
Normal 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
95
test_IFCmonths.m
Normal 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
35
test_isLeap.m
Normal 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
|
||||||
Reference in New Issue
Block a user