datevec with no leap year timeseries

9 views (last 30 days)
Hello,
I am having issues using the function datevec on a time series with a no-leap calendar. I have a 4000 year time series with a time series resembling:
[31, 59, 90, 120, 151, 181, 212.... 1460000]
I would like to create a matrix in the form [year, month, day] for the time series.
When I run the code,
datevalue = time + datenum(1,1,1)-2;
DateVec = datevec(datevalue);
everything runs fine, but the values are wrong for the year and month. I was wondering if there was a way to use the datevec function with a no-leap calendar.
Thank you for any comments or suggestions.
  1 Comment
Jan
Jan on 8 Jul 2013
What an interesting and bewildering question. +1

Sign in to comment.

Accepted Answer

Kelly Kearney
Kelly Kearney on 8 Jul 2013
Edited: Kelly Kearney on 8 Jul 2013
Here's a function I wrote for the task:
And in response to Jan's query as to why one would ever want to do this, I often need it when I want to compare model simulations (which often use a 365-day calendar) with observations. I'm going to guess that the OP has a similar need.
function dn = daynoleap2datenum(day, pivotyr)
%DAYNOLEAP2DATENUM Convert from days since, no leap to serial date number
%
% dn = daynoleap2datenum(day, pivotyr)
%
% A lot of model output is saved with a time scale of "days since
% YYYY-01-01 00:00:00", where every year has 365 day. This function
% converts those dates into a serial date number.
%
% Input variables:
%
% day: number of days since pivot year
%
% pivotyr: pivot year, i.e. year that day count begins (on Jan 1)
% Determine which years in range are leap years
nyr = max(day./365);
yrs = pivotyr:(pivotyr + nyr);
isleap = @(x) (mod(x,4)==0 & mod(x,100)~=0) | mod(x,400) == 0;
leapyrs = yrs(isleap(yrs));
% Calculate date numbers
dayofyear = rem(day, 365);
yr = floor(day/365) + pivotyr;
dn = datenum(pivotyr, 1, 1) + day;
for ileap = 1:length(leapyrs)
needsbump = yr > leapyrs(ileap) | (yr == leapyrs(ileap) & dayofyear > 59);
dn(needsbump) = dn(needsbump) + 1;
end
  4 Comments
Szabó-Takács Beáta
Szabó-Takács Beáta on 23 Mar 2016
I also have same problem with model date from 1971-1975 where the reference date is: 1949,12,01. I tried Kelly Kearney's solution with a small change in:
dn = datenum(pivotyr, 12, 1) + day
but the received results also contains leap year in 1972 and the 1973, 1, 28 is missing. Do someone an idea how I can fix this matter?
Kelly Kearney
Kelly Kearney on 23 Mar 2016
With a pivot date that isn't the first of the year, the dayofyear calculation will also need to be modified. I think the easiest way to deal with a non-first-of-the-year pivot date without adding a bunch of new math to my function is to just add the extra days to your input data:
t = 365*22 + 0:364; % A year's worth of days starting Dec 1, 1971
tshifted = t + 334; % Add the 334 days between Jan 1, 1949 and Dec 1, 1949
dn = daynoleap2datenum(tshifted, 1949)
You can get an up to date version of the daynoleap2datenum function (with the bug Ian found above now corrected) here.

Sign in to comment.

More Answers (1)

Jan
Jan on 8 Jul 2013
Edited: Jan on 8 Jul 2013
No, there isn't. datevec considers leap years and it would need are very complicated and indirect method to remove this afterwards. It will be much easier to rewrite datevec and omit the leap year stuff.
I guess, that the show vector is the number of days (please mention such details explicitly in the future). Then:
function v = noLeapDateVec(day)
% Cumsum of the number of days, no leap years:
cum = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
% Get year:
day = reshape(day, 1, []); % Row-shape
y = floor(day / 365);
t = day - y * 365;
% Adjust year for the last day of the year:
idx = (t <= 0);
y(idx) = y(idx) - 1;
t(idx) = day(idx) - y(idx) * 365;
% Get month roughly and refine using accumulated number of days:
m = ceil(t / 29) - 1; % 30 or 31 would work also!
idx = t > cum(m + 1);
m(idx) = m(idx) + 1;
% Get day:
d = t - cum(m);
% Join the date:
v = [y(:), m(:), d(:)];
And a test, because the t/29 formula was developed for leap years also:
cum = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
in = 1:1460000;
v = noLeapDateVec(in);
d = v(:, 1)' * 365 + cum(v(:, 2)) + v(:,3)';
isequal(d, in)
Yep, it works. But now let me ask, why on earth should anybody calculate the datevector for a non-leap-year calendar?!
Note 1: You find an equivalent algorithm in datevecmx.c (of course with leap years), which has been shipped with older Matlab releases. Unfortunately modern releases lost a lot of these important examples, e.g. cellfun.c also.
Note 2: The shown M-code is only 6% slower than the compiled Mex function datevecmx for the shown test vector. This is caused by the expensive consideration of the leap year, which requires several operations with floating point numbers. I'm convinced a pure integer arithmetic method will faster.

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!