Source code for ocmw.core.timeFuncs

# -*- coding: utf-8 -*-

"""
Basic time variable manipulation functions. 
"""

#Functions for manipulating time variables associated with *in situ* measurment 
#and modelled data.
#
#Chris Old
#IES, School of Engineering, University of Edinburgh
#Jan  2020

# ----------------------------------------------------------------------------
#   IMPORTS
# ----------------------------------------------------------------------------
# Standard Python Dependencies
from datetime import datetime, timedelta
import numpy as np
# Non-Standard Python Dependencies
# Local Module Dependencies
# Other Dependencies


# ----------------------------------------------------------------------------
#   GLOBAL VARIABLES
# ----------------------------------------------------------------------------
stdTimeUnits = 'days since 0001-01-01 00:00:00'
stdTimeFmt = "%Y-%m-%d %H:%M:%S"
decTimeFmt = "%Y-%m-%d %H:%M:%S.%f" # Include fractions of a second
stdCalendar = 'gregorian'
stdEpochStr = "0001-01-01 00:00:00"
julianDay_Offset = 2400000.500

# ----------------------------------------------------------------------------
#   CLASS DEFINITIONS
# ----------------------------------------------------------------------------
[docs] class temporalCoverage: """Object used to determine if any part of a time record lies within a time bound defined by timeStart and timeEnd """ def __init__(self, timeStart, timeEnd): self.timeRangeStr = [timeStart, timeEnd] self.timeRangeNum = [dateNumFromDateStr(timeStart), dateNumFromDateStr(timeEnd)] self.units = stdTimeUnits self.format = stdTimeFmt self.calendar = stdCalendar def timeOverlap(self, tRec): if len(tRec) > 0: t0 = tRec[0] t1 = tRec[-1] case01 = (t0 >= min(self.timeRangeNum)) & \ (t1 <= max(self.timeRangeNum)) case02 = (t0 < min(self.timeRangeNum)) & \ (t1 >= min(self.timeRangeNum)) case03 = (t0 < max(self.timeRangeNum)) & \ (t1 >= max(self.timeRangeNum)) case04 = (t0 < min(self.timeRangeNum)) & \ (t1 > max(self.timeRangeNum)) overlaps = case01 or case02 or case03 or case04 if overlaps: tindx0 = np.where(tRec >= self.timeRangeNum[0])[0][0] tindx1 = np.where(tRec <= self.timeRangeNum[1])[0][-1] indices = [tindx0, tindx1] else: indices = [None, None] else: overlaps = False indices = [None, None] return overlaps, indices
class dateTime: nparray = type(np.asarray([])) def __init__(self,dt,tfmt=stdTimeFmt): self.dt = dt self.dt_type = type(dt) self.timeFmt = tfmt def dateStr(self): if self.dt_type in [list,tuple,self.nparray]: ds = self.dt.copy() for i,d in enumerate(self.dt): ds[i] = self.__dt2ds(d) else: ds = self.__dt2ds(self.dt) return ds def dateNum(self): if self.dt_type in [list,tuple,self.nparray]: dn = self.dt.copy() for i,d in enumerate(self.dt): dn[i] = self.__dt2dn(d) else: dn = self.__dt2dn(self.dt) return dn def __dt2ds(self,dt): ds = dt.strftime(self.timeFmt) return ds def __dt2dn(self,dt): dn = dt.toordinal() + (dt-datetime.fromordinal(dt.toordinal())).total_seconds()/(24*60*60) return dn class dateString: nparray = type(np.asarray([])) def __init__(self,ds,tfmt=stdTimeFmt): self.ds = ds self.ds_type = type(ds) self.timeFmt = tfmt def dateTime(self): if self.ds_type in [list,tuple,self.nparray]: dt = self.ds.copy() for i,d in enumerate(self.ds): dt[i] = self.__ds2dt(d) else: dt = self.__ds2dt(self.ds) return dt def dateNum(self): if self.ds_type in [list,tuple,self.nparray]: dn = self.ds.copy() for i,d in enumerate(self.ds): dt = self.__ds2dt(d) dn[i] = self.__dt2dn(dt) else: dt = self.__ds2dt(self.ds) dn = self.__dt2dn(dt) return dn def __ds2dt(self,ds): dt = datetime.strptime(ds,self.timeFmt) return dt def __dt2dn(self,dt): dn = dt.toordinal() + (dt-datetime.fromordinal(dt.toordinal())).total_seconds()/(24*60*60) return dn class dateNumber: nparray = type(np.asarray([])) def __init__(self,dn,tfmt=stdTimeFmt): self.dn = dn self.dn_type = type(dn) self.timeFmt = tfmt def dateTime(self): if self.dn_type in [list,tuple,self.nparray]: dt = self.dn.copy() for i,d in enumerate(self.dn): dt[i] = self.__dn2dt(d) else: dt = self.__dn2dt(self.dn) return dt def dateStr(self): if self.dn_type in [list,tuple,self.nparray]: ds = self.dn.copy() for i,d in enumerate(self.dn): dt = self.__dn2dt(d) ds[i] = self.__dt2ds(dt) else: dt = self.__dn2dt(self.dn) ds = self.__dt2ds(dt) return ds def __dn2dt(self,dn): dt = datetime.fromordinal(int(dn)) + timedelta(days=(dn%1)) return dt def __dt2ds(self,dt): ds = dt.strftime(self.timeFmt) return ds # ---------------------------------------------------------------------------- # FUNCTION DEFINITIONS # ----------------------------------------------------------------------------
[docs] def datetimeFromStr(dateStr, timeFmt=stdTimeFmt): """Generates a python datetime object from a date string with a format defined by timeFmt. """ dt = datetime.strptime(dateStr, timeFmt) return dt
[docs] def datetimeFromNum(dateNum): """Generates a python datetime object from a date number assuming the python default epoch 0001-01-01 00:00:00. """ dt = datetime.fromordinal(int(dateNum)) + timedelta(days=(dateNum%1)) return dt
def datetimeFromJulianDay(julian): dt = datetime(1858, 11, 17) + timedelta(julian-julianDay_Offset) return dt
[docs] def dateStrFromDatetime(dtime, timeFmt=stdTimeFmt): """Generates a date string from a python datetime object with the format defined by timeFmt. """ ds = dtime.strftime(timeFmt) return ds
[docs] def dateNumFromDatetime(dtime): """Generates a date number from a python datetime object assuming the python default epoch of 0001-01-01 00:00:00. """ dn = dtime.toordinal() + (dtime-datetime.fromordinal(dtime.toordinal())).total_seconds()/(24*60*60) return dn
def dateNumFromJulianDay(julian): dt = datetimeFromJulianDay(julian) dn = dateNumFromDatetime(dt) return dn
[docs] def dateStrFromDateNum(dateNum, timeFmt=stdTimeFmt): """Generates a date string from a date number assuming the python default epoch of 0001-01-01 00:00:00, using the fromat defined by timeFmt. """ dt = datetimeFromNum(dateNum) ds = dateStrFromDatetime(dt, timeFmt) return ds
def dateStrFromJulianDay(julian, timeFmt=stdTimeFmt): dt = datetimeFromJulianDay(julian) ds = dateStrFromDatetime(dt, timeFmt=timeFmt) return ds
[docs] def dateNumFromDateStr(dateStr, timeFmt=stdTimeFmt): """Generates a date number from a date string using the format defined by timeFmt, and python default epoch 0001-01-01 00:00:00. """ dt = datetimeFromStr(dateStr, timeFmt) dn = dateNumFromDatetime(dt) return dn
[docs] def dateNumFromEpoch(dateNum, epochStr=stdEpochStr): """Converts a date number from the default python epoch 0001-01-01 00:00:00 to days from the epoch define in epochStr. """ nepoch = dateNumFromDateStr(epochStr) dn = dateNum - nepoch return dn
def matlab2std(dateNum, isLeapYear=False): dn = dateNum - 366.0 return dn def std2matlab(dateNum, isLeapYear=False): dn = dateNum + 366.0 return dn
[docs] def matlab2python(dateNum): """Converts a date number based on the matlab default epoch 0000-01-01 00:00:00 to the python default epoch 0001-01-01 00:00:00. """ dn = dateNum - 366.0 return dn
[docs] def python2matlab(dateNum): """Converts a date number based on the python default epoch 0001-01-01 00:00:00 to the matlab default epoch 0000-01-01 00:00:00. """ dn = dateNum + 366.0 return dn
def isLeapYear(year): if year % 400 == 0: return True if year % 100 == 0: return False if year % 4 == 0: return True else: return False