4

I have a question very similar to this one and this one but I'm stuck on some rounding issue.

I have a time series from a netCDF file and I'm trying to convert them to a datetime format. The format of the time series is in 'days since 1990-01-01 00:00:00'. Eventually I want output in the format .strftime('%Y%m%d.%H%M'). So for example I read my netCDF file as follows

import netCDF4
nc = netCDF4.Dataset(file_name)
time = np.array(nc['time'][:])

I then have

In [180]: time[0]
Out[180]: 365
In [181]: time[1]
Out[181]: 365.04166666651145

I then did

In [182]: start = datetime.datetime(1990,1,1)
In [183]: delta = datetime.timedelta(time[1])
In [184]: new_time = start + delta
In [185]: print(new_time.strftime('%Y%m%d.%H%M'))
19910101.0059

Is there a a way to "round" to the nearest hour so I get 19910101.0100?

Hawky
  • 85
  • 1
  • 6
  • What does `365.04166666651145` represents? – Sohaib Farooqi Jan 05 '18 at 05:37
  • Days since 1990-01-01 00:00:00 @GarbageCollector – Hawky Jan 05 '18 at 05:42
  • Since then to today? No it isn't... There are much better ways to get "days since" – OneCricketeer Jan 05 '18 at 05:43
  • And how do you define "nearest hour"? 29 mins rounds down? 30 minutes rounds up? – OneCricketeer Jan 05 '18 at 05:44
  • I got the time (in days) from a netCDF file as initially stated. This is a quite common dateformat in netCDF datasets. Output in the netCDF file is hourly (e.g. `365, 365.04166666651145, 365.08333333348855` etc.) but when loading the file there is some rounding error somewhere. So I shouldn't have to deal with rounding minutes - it'll just be a matter of rounding the milliseconds up/down. I edited the original question to clarify where `time` comes from. – Hawky Jan 05 '18 at 05:55
  • If one is using `pandas`, see [this answer](https://stackoverflow.com/a/40392278/6131611) – Pavel Dec 10 '18 at 00:38

2 Answers2

10

You can round down with datetime.replace(), and round up by adding an hour to the rounded down value using datetime.timedelta(hours=1).

import datetime

def round_to_hour(dt):
    dt_start_of_hour = dt.replace(minute=0, second=0, microsecond=0)
    dt_half_hour = dt.replace(minute=30, second=0, microsecond=0)

    if dt >= dt_half_hour:
        # round up
        dt = dt_start_of_hour + datetime.timedelta(hours=1)
    else:
        # round down
        dt = dt_start_of_hour

    return dt

Note that since we're using replace the values we're not replacing (like the timezone - tzinfo) will be preserved.

John Carter
  • 53,924
  • 26
  • 111
  • 144
0

I don't think datetime provides a way to round times, you'll have to provide the code to do that yourself. Something like this should work:

def round_to_hour(dt):
    round_delta = 60 * 30
    round_timestamp = datetime.datetime.fromtimestamp(dt.timestamp() + round_delta)
    round_dt = datetime.datetime.fromtimestamp(round_timestamp)

    return round_dt.replace(microsecond=0, second=0, minute=0)
Turn
  • 6,656
  • 32
  • 41