from typing import Sequence, Union
from datetime import datetime, timezone
import numpy as np
import numbers
import math
from .mjd import mjd_to_datetime64
# ------------------------------------------------------------------------------
# def datetime64_to_timestamp_new( utc ):
# ------------------------------------------------------------------------------
[docs]
def datetime64_to_timestamp(utc):
"""
Converts a scalar or array of numpy.datetime64 to floating point timestamp (seconds since 1970). The conversion includes
the fraction of seconds.
Parameters
----------
utc : numpy.datetime64 or Array[numpy.datetime64]
A time specified with a numpy datetime64 object. Explicit time zones are
not currently supported. Only single scalar values are supported in this function.
Returns
-------
float
The time expressed as a timestamp value. Number of seconds since Jan 1, 1970.
"""
if type(utc) is np.ndarray:
if (utc.dtype.kind == 'M'): # if we have an array of datetime64
tstamp = np.zeros_like(utc, dtype=np.float64) # create the output array
with np.nditer([utc, tstamp],
flags=['refs_ok', 'buffered'], # create the numpy iterator object
op_flags=[['readonly'], ['writeonly']]) as it:
for a, b in it: # and convert each elemnet
t = np.datetime64(a, 'us').astype(datetime).replace(tzinfo=timezone.utc).timestamp() # from datetime64 to a timestamp
b[...] = t
tstamp = it.operands[1]
# Handle an numpy array of MJD values, datetime64, datetime objects or mjd numbers
elif (utc.dtype.kind == 'O') and (len(utc) > 0) and (type(utc[0]) is datetime): # otherwiseif we have a numpy array of datetime objects
tstamp = np.zeros_like(utc, dtype=np.float64) # create the output array
with np.nditer([utc, tstamp],
flags=['refs_ok', 'buffered'], # create the nupy iterator object
op_flags=[['readonly'], ['writeonly']]) as it:
for a, b in it: # and convert each elemnet
t = a.replace(tzinfo=timezone.utc).timestamp() # from datetime to mjd
b[...] = t
tstamp = it.operands[1]
elif (utc.dtype.kind == 'U'): # otherwise if the array is strings
tstamp = np.zeros_like(utc, dtype=np.float64) # then make an array to hold the answer
with np.nditer([utc, tstamp],
flags=['refs_ok', 'buffered'],
op_flags=[['readonly'], ['writeonly']]) as it:
for a, b in it: # For each element in the array
t = datetime.fromisoformat(str(a)) # convert the string to datetime
b[...] = t.replace(tzinfo=timezone.utc).timestamp() # and from datetime to mjd
tstamp = it.operands[1]
else: # otherwise we have an array of something else, I assume its array of numbers
tstamp = np.array(utc, dtype=np.float64) # so try and convert the array of numbers to floating point MJD.
elif type(utc) is str: # if we have a single string
tstamp = datetime.fromisoformat(utc).timestamp() # then convert the string to datetime
elif isinstance(utc, Sequence): # if we have a list or tuple but not numpy array
newutc = np.array(utc) # then convert the array to numpy array
tstamp = datetime64_to_timestamp(newutc) # and convert the numpy array to mjd
else: # Its not a numpy array, its not a sequence, so assume its a scalar
if (type(utc) is datetime): # if its a datetime object
tstamp = utc.replace(tzinfo=timezone.utc).timestamp() # to mjd as a floating point
elif type(utc) is np.datetime64: # if we have a single numpy.datetime64
tstamp = np.datetime64(utc, 'us').astype(datetime).replace(tzinfo=timezone.utc).timestamp() # then convert it
else:
print('utc_to_timestamp, unsupported data type {}. Cannot robustly convert to timestamp. I will try and convert it to a float'.format(str(type(utc))))
tstamp = float(utc) # then assume its already an MJD
return tstamp
# ------------------------------------------------------------------------------
# datetime64_to_datetime
# ------------------------------------------------------------------------------
[docs]
def datetime64_to_datetime(usertime: np.datetime64) -> datetime:
"""
Converts a single numpy.datetime64 to a datetime.datetime
Parameters
----------
usertime : numpy.datetime64
A time specified with a numpy datetime64 object. Explicit time zones are
not currently supported. Only single scalar values are supported in this function.
Returns
-------
datetime.datetime
The time expressed as a regular python datetime.datetime.
"""
t = np.datetime64(usertime, 'us').astype(datetime)
return t
# ------------------------------------------------------------------------------
# datetime_to_datetime64
# ------------------------------------------------------------------------------
def datetime_to_datetime64(usertime: np.datetime64) -> np.datetime64:
"""
Converts a single datetime to a np.datetime64
Parameters
----------
usertime : datetime.datetime
A time specified with a python datetime object. Explicit time zones are
not currently supported. Only single scalar values are supported in this function.
Returns
-------
datetime.datetime
The time expressed as a regular python datetime.datetime.
"""
t = np.datetime64(usertime, 'us')
return t
# ------------------------------------------------------------------------------
# ut_to_datetime64
# ------------------------------------------------------------------------------
[docs]
def ut_to_datetime64(utc):
"""
A convenience function that converts various arrays or scalar representations of UT to datetime64
Scalar input values will return as a scalar datyetime64 while array, list or tuple input values are all returned
as numpy arrays of utc in datetime64.
Parameters
----------
utc : scalar, list, tuple or array
The input time which represents a coordinated universal time. It can be represented by
(i) a string in a supported python datetime.isoformat
(ii) a number. The number is assumed to represent a modified julian date.
(iii) a datetime.datetime object.
(iv) a numpy.datetime64 object.
The *utc* object can be a scalar, list, tuple or numpy array. The same representation format must be used for
all objects in the arrays and sequences.
Returns
-------
scalar or numpy.ndarray of np.datetim64
The time is returned as a scalar if the input was a scalar or as a numpy array for input sequences and arrays
"""
d64 = None
if type(utc) is np.ndarray:
# Handle an numpy array of MJD values, datetime64, datetime objects or mjd numbers
if utc.dtype.kind == 'M': # if we have a numpy array of datetime64 object
d64 = utc # then do nothing
elif (utc.dtype.kind == 'O') and (len(utc) > 0) and (type(utc[0]) is datetime): # otherwiseif we have a numpy array of datetime objects
d64 = np.zeros_like(utc, dtype='datetime64[us]') # create the output array
with np.nditer([utc, d64], flags=['refs_ok'], op_flags=[['readwrite'], ['writeonly']]) as it:
for a, b in it: # and convert each elemnet
t = datetime_to_datetime64(a.tolist()) # The object comes in as a zero shape object of datetime. But the toplist converts it back to a datetime
b[...] = t
d64 = it.operands[1]
elif (utc.dtype.kind == 'U'): # otherwise if the array is strings
d64 = np.zeros_like(utc, dtype='datetime64[us]') # then make an array to hold the answer
with np.nditer([utc, d64], flags=['refs_ok', 'buffered'], op_flags=[['readonly'], ['writeonly']]) as it:
for a, b in it: # For each element in the array
b[...] = np.datetime64(str(a), 'us') # and from datetime to mjd
d64 = it.operands[1]
else: # otherwise we have an array of something else, I assume its array of numbers
d64 = np.zeros_like(utc, dtype='datetime64[us]') # then make an array to hold the answer
with np.nditer([utc, d64], flags=['refs_ok', 'buffered'], op_flags=[['readonly'], ['writeonly']]) as it:
for a, b in it: # For each element in the array
b[...] = mjd_to_datetime64(float(a)) # atry and convert the MJD to datetime64
d64 = it.operands[1]
elif type(utc) is str: # if we have a single string
d64 = np.datetime64(utc, 'us') # then convert the string to datetime
elif isinstance(utc, Sequence): # if we have a list or tuple but not numpy array
newutc = np.array(utc) # then convert the array to numpy array
d64 = ut_to_datetime64(newutc) # and convert the numpy array to mjd
else: # Its not a numpy array, its not a sequence, so assume its a scalar
if (type(utc) is datetime): # if its a datetime object
d64 = datetime_to_datetime64(utc) # to mjd as a floating point
elif type(utc) is np.datetime64: # if we have a single numpy.datetime64
d64 = utc # then convert it
elif isinstance(utc, numbers.Number): # if we have a number
d64 = mjd_to_datetime64(float(utc)) # then assume its already an MJD
else: # otherwise
d64 = math.nan
raise ValueError('unsupported data type {}. Cannot yet convert this to np.datetime64'.format(str(type(utc))))
return d64
# ------------------------------------------------------------------------------
# ut_to_datetime64
# ------------------------------------------------------------------------------
[docs]
def ut_to_datetime(utc: Union[np.ndarray, Sequence, float, str, np.datetime64, datetime]) -> Union[np.ndarray, datetime, None]:
"""
A convenience function that converts various arrays or scalar representations of UT to datetime
Scalar input values will return as a scalar datetime while array, list or tuple input values are all returned
as numpy arrays of datetime objects
Parameters
----------
utc : scalar, list, tuple or array
The input time which represents a coordinated universal time. It can be represented by
(i) a string in a supported python datetime.isoformat
(ii) a number. The number is assumed to represent a modified julian date.
(iii) a datetime.datetime object.
(iv) a numpy.datetime64 object.
The *utc* object can be a scalar, list, tuple or numpy array. The same representation format must be used for
all objects in the arrays and sequences.
Returns
-------
scalar or numpy.ndarray of datetime
The time is returned as a scalar if the input was a scalar or as a numpy array for input sequences and arrays
"""
d64 = None
if type(utc) is np.ndarray:
# Handle an numpy array of MJD values, datetime64, datetime objects or mjd numbers
if (utc.dtype.kind == 'O'): # we have a numpy array of datetime objects
d64 = utc # then do nothing
if (len(utc) > 0) and (type(utc[0]) is not datetime):
raise ValueError('ut_to_datetime does support conversion of arrays of python objects other than datetime')
elif (utc.dtype.kind == 'M'): # otherwiseif we have a numpy array of datetime64 objects
d64 = np.zeros_like(utc, dtype='O') # create the output array
with np.nditer([utc, d64], flags=['refs_ok'], op_flags=[['readwrite'], ['writeonly']]) as it:
for a, b in it: # and convert each elemnet
t = datetime64_to_datetime(a.tolist()) # The object comes in as a zero shape object of datetime. But the toplist converts it back to a datetime
b[...] = t
d64 = it.operands[1]
elif (utc.dtype.kind == 'U'): # otherwise if the array is strings
d64 = np.zeros_like(utc, dtype='O') # then make an array to hold the answer
with np.nditer([utc, d64], flags=['refs_ok', 'buffered'], op_flags=[['readonly'], ['writeonly']]) as it:
for a, b in it: # For each element in the array
b[...] = datetime.fromisoformat(str(a)) # and from datetime to mjd
d64 = it.operands[1]
else: # otherwise we have an array of something else, I assume its array of numbers
d64 = np.zeros_like(utc, dtype='O') # then make an array to hold the answer
with np.nditer([utc, d64], flags=['refs_ok', 'buffered'], op_flags=[['readonly'], ['writeonly']]) as it:
for a, b in it: # For each element in the array
t = mjd_to_datetime64(float(a)) # Convert the mjd to datetime 64
b[...] = datetime64_to_datetime(t) # and then convert to datetime
d64 = it.operands[1]
elif type(utc) is str: # if we have a single string
d64 = datetime.fromisoformat(utc) # then convert the string to datetime
elif isinstance(utc, Sequence): # if we have a list or tuple but not numpy array
newutc = np.array(utc) # then convert the array to numpy array
d64 = ut_to_datetime(newutc) # and convert the numpy array to mjd
else: # Its not a numpy array, its not a sequence, so assume its a scalar
if (type(utc) is datetime): # if its a datetime object
d64 = utc # to mjd as a floating point
elif type(utc) is np.datetime64: # if we have a single numpy.datetime64
d64 = datetime64_to_datetime(utc) # then convert it
elif isinstance(utc, numbers.Number): # if we have a number
t = mjd_to_datetime64(float(utc)) # then assume its already an MJD
d64 = datetime64_to_datetime(t)
else: # otherwise
raise ValueError('unsupported data type {}. Cannot yet convert this to np.datetime64'.format(str(type(utc))))
return d64