MATLAB Answers

Loss of precision with netcdf time when using datenum

14 views (last 30 days)
mashtine
mashtine on 1 Feb 2016
Commented: 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

Campion Loong
Campion Loong on 4 Feb 2016
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.

Accepted Answer

Kelly Kearney
Kelly Kearney on 4 Feb 2016
Edited: Kelly Kearney on 4 Feb 2016
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.

More Answers (1)

Peter Perkins
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

Show 2 older comments
mashtine
mashtine on 15 Feb 2016
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.
Kelly Kearney
Kelly Kearney on 16 Feb 2016
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
mashtine
mashtine on 16 Feb 2016
Works! Thanks Kelly. Very useful code for me as I deal a lot with netcdf files.

Sign in to comment.

Community Treasure Hunt

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

Start Hunting!