from datetime import datetime, timedelta, timezone
import numpy as np
from .tools.misc import fillgaps
def _fullyear(year):
if year > 100:
return year
year += 1900 + 100 * (year < 90)
return year
[docs]
def epoch2dt64(ep_time):
"""Convert from epoch time (seconds since 1/1/1970 00:00:00)
to numpy.datetime64 array
Parameters
----------
ep_time : xarray.DataArray
Time coordinate data-array or single time element
Returns
-------
time : numpy.datetime64
The converted datetime64 array
"""
# assumes t0=1970-01-01 00:00:00
out = np.array(ep_time.astype('int')).astype('datetime64[s]')
out = out + ((ep_time % 1) * 1e9).astype('timedelta64[ns]')
return out
[docs]
def dt642epoch(dt64):
"""Convert numpy.datetime64 array to epoch time
(seconds since 1/1/1970 00:00:00)
Parameters
----------
dt64 : numpy.datetime64
Single or array of datetime64 object(s)
Returns
-------
time : float
Epoch time (seconds since 1/1/1970 00:00:00)
"""
return dt64.astype('datetime64[ns]').astype('float') / 1e9
[docs]
def date2dt64(dt):
"""Convert numpy.datetime64 array to list of datetime objects
Parameters
----------
time : datetime.datetime
The converted datetime object
Returns
-------
dt64 : numpy.datetime64
Single or array of datetime64 object(s)
"""
return np.array(dt).astype('datetime64[ns]')
[docs]
def dt642date(dt64):
"""Convert numpy.datetime64 array to list of datetime objects
Parameters
----------
dt64 : numpy.datetime64
Single or array of datetime64 object(s)
Returns
-------
time : datetime.datetime
The converted datetime object
"""
return epoch2date(dt642epoch(dt64))
[docs]
def epoch2date(ep_time, offset_hr=0, to_str=False):
"""Convert from epoch time (seconds since 1/1/1970 00:00:00) to a list
of datetime objects
Parameters
----------
ep_time : xarray.DataArray
Time coordinate data-array or single time element
offset_hr : int
Number of hours to offset time by (e.g. UTC -7 hours = PDT)
to_str : logical
Converts datetime object to a readable string
Returns
-------
time : datetime.datetime
The converted datetime object or list(strings)
Notes
-----
The specific time instance is set during deployment, usually sync'd to the
deployment computer. The time seen by |dlfn| is in the timezone of the
deployment computer, which is unknown to |dlfn|.
"""
try:
ep_time = ep_time.values
except AttributeError:
pass
if isinstance(ep_time, (np.ndarray)) and ep_time.ndim == 0:
ep_time = [ep_time.item()]
elif not isinstance(ep_time, (np.ndarray, list)):
ep_time = [ep_time]
######### IMPORTANT #########
# Note the use of `utcfromtimestamp` here, rather than `fromtimestamp`
# This is CRITICAL! See the difference between those functions here:
# https://docs.python.org/3/library/datetime.html#datetime.datetime.fromtimestamp
# Long story short: `fromtimestamp` used system-specific timezone
# info to calculate the datetime object, but returns a
# timezone-agnostic object.
if offset_hr != 0:
delta = timedelta(hours=offset_hr)
time = [datetime.utcfromtimestamp(t) + delta for t in ep_time]
else:
time = [datetime.utcfromtimestamp(t) for t in ep_time]
if to_str:
time = date2str(time)
return time
[docs]
def date2str(dt, format_str=None):
"""Convert list of datetime objects to legible strings
Parameters
----------
dt : datetime.datetime
Single or list of datetime object(s)
format_str : string
Timestamp string formatting, default: '%Y-%m-%d %H:%M:%S.%f'.
See datetime.strftime documentation for timestamp string formatting
Returns
-------
time : string
Converted timestamps
"""
if format_str is None:
format_str = '%Y-%m-%d %H:%M:%S.%f'
if not isinstance(dt, list):
dt = [dt]
return [t.strftime(format_str) for t in dt]
[docs]
def date2epoch(dt):
"""Convert list of datetime objects to epoch time
Parameters
----------
dt : datetime.datetime
Single or list of datetime object(s)
Returns
-------
time : float
Datetime converted to epoch time (seconds since 1/1/1970 00:00:00)
"""
if not isinstance(dt, list):
dt = [dt]
return [t.replace(tzinfo=timezone.utc).timestamp() for t in dt]
[docs]
def date2matlab(dt):
"""Convert list of datetime objects to MATLAB datenum
Parameters
----------
dt : datetime.datetime
List of datetime objects
Returns
-------
time : float
List of timestamps in MATLAB datnum format
"""
time = list()
for i in range(len(dt)):
mdn = dt[i] + timedelta(days=366)
frac_seconds = (dt[i]-datetime(dt[i].year, dt[i].month,
dt[i].day, 0, 0, 0)).seconds / (24*60*60)
frac_microseconds = dt[i].microsecond / (24*60*60*1000000)
time.append(mdn.toordinal() + frac_seconds + frac_microseconds)
return time
[docs]
def matlab2date(matlab_dn):
"""Convert MATLAB datenum to list of datetime objects
Parameters
----------
matlab_dn : float
List of timestamps in MATLAB datnum format
Returns
-------
dt : datetime.datetime
List of datetime objects
"""
time = list()
for i in range(len(matlab_dn)):
day = datetime.fromordinal(int(matlab_dn[i]))
dayfrac = timedelta(days=matlab_dn[i] % 1) - timedelta(days=366)
time.append(day + dayfrac)
# Datenum is precise down to 100 microseconds - add difference to round
us = int(round(time[i].microsecond/100, 0))*100
time[i] = time[i].replace(microsecond=time[i].microsecond) + \
timedelta(microseconds=us-time[i].microsecond)
return time
def _fill_time_gaps(epoch, sample_rate_hz):
"""Fill gaps (NaN values) in the timeseries by simple linear
interpolation. The ends are extrapolated by stepping
forward/backward by 1/sample_rate_hz.
"""
# epoch is seconds since 1970
dt = 1. / sample_rate_hz
epoch = fillgaps(epoch)
if np.isnan(epoch[0]):
i0 = np.nonzero(~np.isnan(epoch))[0][0]
delta = np.arange(-i0, 0, 1) * dt
epoch[:i0] = epoch[i0] + delta
if np.isnan(epoch[-1]):
# Search backward through the array to get the 'negative index'
ie = -np.nonzero(~np.isnan(epoch[::-1]))[0][0] - 1
delta = np.arange(1, -ie, 1) * dt
epoch[(ie + 1):] = epoch[ie] + delta
return epoch