MATLAB Answers

0

Loss of precision with netcdf time when using datenum

Asked by mashtine on 1 Feb 2016
Latest activity Commented on by mashtine on 16 Feb 2016
Hi there,
I am trying to read the time variable in from an netcdf file and convert it to datenum values but the end time always seems to fall short (26-11-2005 instead of 01-01-2006).
The time variable from the netcdf file is in 'days since 1860-01-01 00:00:00' and in '365_day' format but I am having trouble getting the proper datenum for the data. The time format is also 'double' precision so I am not sure why I am going wrong when I use the following for example:
time1980_05 = timestamp1980_05' + datenum('1860-01-01 00:00:00');
Any ideas?

  1 Comment

Hi Mashtine,
It is a little difficult to know what's going on without some sample input and codes from your attempt. In general though, the datetime type (available for R2014b and up) is preferred to datenum - for one it has much better precision. You may want to give it a try.

Sign in to comment.

2 Answers

Answer by Kelly Kearney
on 4 Feb 2016
Edited by Kelly Kearney
on 4 Feb 2016
 Accepted Answer

I wrote a function, daynoleap2datenum, that does the conversion that Peter suggested (in a slightly simpler manner... I just figure out which years need a leap day and bump the numbers up one at those locations). It converts the output to either datenumbers or datetime objects, so it can work regardless of Matlab version.
As you can see in this example, the missing leap days are what lead to your simple addition method coming up a bit short:
t = [0 58:60 364]';
t1 = datetime(2000,1,1) + t
t2 = daynoleap2datenum(t, 2000, 'dt')
t1 =
01-Jan-2000 00:00:00
28-Feb-2000 00:00:00
29-Feb-2000 00:00:00
01-Mar-2000 00:00:00
30-Dec-2000 00:00:00
t2 =
01-Jan-2000 00:00:00
28-Feb-2000 00:00:00
01-Mar-2000 00:00:00
02-Mar-2000 00:00:00
31-Dec-2000 00:00:00

  0 Comments

Sign in to comment.


Answer by Peter Perkins
on 4 Feb 2016

Mashtine, I'm not super familiar with netCDF, but "365 day format" and this
>> dt = between(datetime(2005,11,26),datetime(2006,1,1),'days')
dt =
36d
>> (2006-1860)/4
ans =
36.5
suggests that this is a leap day issue. I imagine you could solve this using datetime, if you have R2014b or later. This might be something like what you are looking for:
>> y = [datetime(1860,1,0) datetime(1860,2,28) + calyears(0:5)]
y =
Columns 1 through 6
31-Dec-1859 28-Feb-1860 28-Feb-1861 28-Feb-1862 28-Feb-1863 28-Feb-1864
Column 7
28-Feb-1865
>> % how many days from Feb 28 in year(i) to Feb 28 in year(i+1)
>> d = caldiff(y,'days')
d =
59d 366d 365d 365d 365d 366d
>> d = caldays(d)
d =
59 366 365 365 365 366
>> % which years have leap days?
>> hasLeapDay = (d == 366)
hasLeapDay =
0 1 0 0 0 1
>> adjust = cumsum(hasLeapDay)
adjust =
0 1 1 1 1 2
>> breakPoints = [1 cumsum(d)+1]
breakPoints =
1 60 426 791 1156 1521 1887
% try it out on some "365 day" dates
>> netcdfDay = [(58:61) (58:61)+365 (58:61)+2*365 (58:61)+3*365]
netcdfDay =
Columns 1 through 7
58 59 60 61 423 424 425
Columns 8 through 14
426 788 789 790 791 1153 1154
Columns 15 through 16
1155 1156
>> realDay = netcdfDay + discretize(netcdfDay,breakPoints,adjust)
>> realDay =
Columns 1 through 7
58 59 61 62 424 425 426
Columns 8 through 14
427 789 790 791 792 1154 1155
Columns 15 through 16
1156 1157
>> datestr(realDay + datenum([1860,1,0]))
ans =
27-Feb-1860
28-Feb-1860
01-Mar-1860
02-Mar-1860
27-Feb-1861
28-Feb-1861
01-Mar-1861
02-Mar-1861
27-Feb-1862
28-Feb-1862
01-Mar-1862
02-Mar-1862
27-Feb-1863
28-Feb-1863
01-Mar-1863
02-Mar-1863

  5 Comments

Thank you so much Peter and Walter for your help on this. Sorry for the delay as I was away. The datetime function is not working for me so I have attached some data here. The sample data is in the format described before, 'days since 1860-01-01 00:00:00' and in '365_day' format. I then want to get that into the correct time but I have no idea what the issue is (probably a rookie one!).
I think it has to do with the data format itself. Here is what ncdisp gives me from the netcdf file for the time variable:
time
Size: 163520x1
Dimensions: time
Datatype: double
Attributes:
standard_name = 'time'
units = 'days since 1860-01-01 00:00:00'
calendar = '365_day'
Any chance you can help me further with this? Thanks again.
The datetime object and associated functions were introduced in R2014b, so if you're running an older version, you won't be able to use Peter's code. But I really suggest giving my daynoleap2datenum function a try... it's pretty simple, version independent, and accounts for the 365-day calendar:
S = load('sampledata.mat');
dn = daynoleap2datenum(S.timestamp1980_05, 1860);
datestr(dn([1 end]))
ans =
01-Jan-1980 03:00:00
01-Jan-2006 00:00:00
Works! Thanks Kelly. Very useful code for me as I deal a lot with netcdf files.

Sign in to comment.