#
#
# Time Tools
#
# Author: Jim Fowler
# Email: jrf@utexas.edu
# Created: 18 Mar 2015
#
#
'''
Useful (or not) tools for dealing with Het time in its
different formations.
Some definitions:
unix time -- the number of seconds since the epoch. The epoch is
defined as 00:00:00 UTC, January 1, 1970. This number
may be a floating point value with factional seconds.
index time -- The tracker's definition of the time of day. Index time
is reset to 0.0 at 1800 UT hours or 12 noon CST (1300 CDT).
There is no information contained here that might indicate
which day this index time occurred on. This number may be
a floating point value.
0000 hours UT == 00000 secs UT == 0600 hours index == 21600 seconds index
0600 hours UT == 21600 secs UT == 1200 hours index == 43200 seconds index
1200 hours UT == 43200 secs UT == 1800 hours index == 64800 seconds index
1800 hours UT == 64800 secs UT == 2400 hours index == 86400 seconds index (0000)
(0000) 2400 hours UT == 86400 secs UT == 0600 hours index == 21600 seconds index
ISO time -- an ISO-8601 string of the format YYYY-MM-DDThh:mm:ss.mmmmmm, where YYYY is
the four digit year, MM is the two digit month (starts at 01), DD is the
two digit day (starts at 01), hh is the two digit hour, mm is the two
digit minute, ss is the two digit second, and mmmmmm is the fractional
part of the second.
datetime -- a python datetime.datetime() object. This is the intermediate
object through which the time conversion routines work. It is also
a value which Python can work with. See the documentation for
the datetime package within Python.
Note that the timezone information in the datetime.datetime() object is explicitly
set to None and the assumption is that all times are UT. This may change in
the future if we need to compare local time with HET times. In this case the timezone
will be explicitly set to UTC.
Future Plans.
Convert to object hetTime(), subclass of datetime, which takes an index time, unix time,
datetime object or ISO string at instantion. added functions hetTime.indexTime(), hetTime.unixTime(),
hetTime.ISOtime() to return time in different format. ISOtime and unixTime contain year/mon/day information
but index time does not. Use the current year/mon/day unless there is a specified year/mon/day at instantation.
e.g. hetTime(index|unix|iso, year=now.year, mon=year.mon, day=now.day)
The following functions are defined:
'''
from __future__ import print_function
import datetime
import calendar
#
# The functions
#
[docs]def ISO_To_DT(isoStr):
'''Convert an ISO-8601 time string to
a datetime.datetime() object.
input: ISO-8601 time string
output: datetime object'''
# there must be an easier way to parse strings in python
(year, month, remainder) = isoStr.split('-')
try:
remainder.index('T')
(day, remainder) = remainder.split('T')
except:
remainder.index(' ')
(day, remainder) = remainder.split(' ')
(hour, mins, remainder) = remainder.split(':')
try:
(sec, usec) = remainder.split('.')
usec = (float(remainder) - float(sec)) * 1000000.0
usec = int(round(usec))
usec = str(usec)
except:
sec = remainder
usec = '0'
return datetime.datetime( int(year), int(month), int(day), int(hour), int(mins), int(sec), int(usec))
[docs]def DT_To_ISO(dt):
'''Convert a datatime.datetime() object to a
ISO time string.
input: datetime object
output: ISO-8601 format string'''
return dt.isoformat('T')
[docs]def Index_To_DT(ind):
'''Convert an Index time into a datetime.datetime() object.
input: Index time
output: datetime.datetime()
Note that Index time has no date information
'''
# Convert to UT seconds
uttime = ind - 21600.0
if uttime > 86399.999999:
uttime = uttime - 86400.0
elif uttime < 0.000000:
uttime = uttime + 86400.0
# convert UT seconds to hours, mins, secs, msecs
hours = int(uttime / 3600.0)
minsf = uttime - (hours * 3600.0)
mins = int(minsf / 60.0)
secsf = minsf - (mins * 60.0)
secs = int(secsf)
msecs = int((secsf - secs) * 1e6)
#print( "hours", hours, "mins", mins, "secs", secs, "msecs", msecs)
return datetime.datetime(1970, 1, 1, hours, mins, secs, msecs)
[docs]def DT_To_Index(dt):
'''Convert datetime.datetime() to an index time.
input: datetime.datetime()
output: float
Note that we lose the date information from the datetime() object.
'''
itime = (((dt.hour * 60.0) + dt.minute) * 60.0) + dt.second + (dt.microsecond / 1e6) + 21600.0
if itime > 86399.999999:
itime = itime - 86400.0
elif itime < 0.000000:
itime = itime + 86400.0
return itime
[docs]def Unix_To_DT(utime):
'''Convert a Unix time value to a datatime.datetime) object. This
function duplicates datetime.fromtimestamp() and is included only
for consistence with the other time functions.
input: Unix time value
output: datetime.datetime()'''
dt = datetime.datetime.fromtimestamp(utime)
return dt
[docs]def DT_To_Unix(dt):
'''Convert a datetime.datetime() object to a Unix time value.
input: datetime.datetime()
output: Unix time value
'''
# calendar.timegm does not support microseconds
secs = calendar.timegm((dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second))
secs += (dt.microsecond / 1e6)
return secs
#
# And now for all the other combinations
#
[docs]def ISO_To_Unix(isoStr):
'''Convert an ISO time string to a
Unix time value.
input: ISO-8601 format string
output: Unix time value'''
return DT_To_Unix(ISO_To_DT(isoStr))
[docs]def Unix_To_ISO(utime):
'''Convert a Unix time value to an
ISO time string.
input: Unix time value
output: ISO-8601 format string'''
return DT_To_ISO(Unix_To_DT(utime))
[docs]def ISO_To_Index(isoStr):
'''Convert a ISO string to an Index time.
input: ISO-8601 format string
output: Index time
Note that we lose the date information from the ISO string.
'''
return DT_To_Index(ISO_To_DT(isoStr))
[docs]def Index_To_ISO(idxtime):
'''Convert an Index time to a
ISO time string.
input: Index time
output: ISO-8601 format string
Note that Index time has no date information
'''
return DT_To_ISO(Index_To_DT(idxtime))
[docs]def Unix_To_Index(utime):
'''Convert a Unix time value to an
Index time.
input: datetime object
output: ISO-8601 format string
Note that we lose the date information from the Unix time value.
'''
return DT_To_Index(Unix_To_DT(utime))
[docs]def Index_To_Unix(idxtime):
'''Convert an Index time to a
Unix time value
input: Index time
output: Unix time value
Note that Index time has no date information
'''
return DT_To_Unix(Index_To_DT(idxtime))
if __name__ == '__main__':
# Test the defined functions
import pprint
pp = pprint.PrettyPrinter()
#
# Some useful globals
#
# enums (sort of)
#
(Pass, Fail, Pass_Only, Fail_Only, Pass_Fail) = range(5)
TestCount = 0
FailCount = 0
verbose = Fail_Only
#
# define some useful functions
#
def UpdateTestCount(tcount):
'''Increment the test counter and return the counter
value and a two digit string with the test counter value
for use in the function testit().'''
tcount += 1
tstr = '%02d' % (TestCount)
return (tcount, tstr)
def testit( num, name, func, inarg, testarg, verbose=Fail_Only):
'''A generic test function. The inputs arguments are
num -- string giving the test number
name -- string giving the name of the test
func -- the function to be tested
inarg -- the input argument for the function to be tested
testarg -- the expected output of the function
verbose -- print the test results if
Pass_Only -- only if the test passes
Fail_Only -- only if the test fails (default)
Pass_Fail -- if the test passes or fails
Returns: Pass or Fail
'''
# The test
# How should we deal with round off errors
testout = func(inarg)
if testarg == testout:
tp = Pass
else:
tp = Fail
# Should we print out results?
if ((verbose == Fail_Only and tp == Fail) or
(verbose == Pass_Only and tp == Pass) or
(verbose == Pass_Fail)):
print('\nTest %s %s:' % (num, name))
print( 'Input argument: ', end='')
pp.pprint(inarg)
print( 'Output argument: ', end='')
pp.pprint(testout)
print( 'Expected output: ', end='' )
pp.pprint(testarg)
if tp == Pass:
print('Test %s PASS' % (num))
else:
print('Test %s FAIL' % (num))
# Return something to the user
if tp == Pass:
return 0
else:
return 1
#
# Perform the tests
#
# test just before 1800 UT 0000 index
isotime01 = "2015-03-18T17:59:59"
isoindextime01 = '1970-01-01T17:59:59'
indextime01 = 86399.0
unixtime01 = 1426701599.0
unixindextime01 = 64799.0
dt01 = datetime.datetime(2015, 3, 18, 17, 59, 59, 00)
dt01a = datetime.datetime(1970, 1, 1, 17, 59, 59, 00)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'DT_To_ISO', DT_To_ISO, dt01, isotime01, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'DT_To_Unix', DT_To_Unix, dt01, unixtime01, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'DT_To_Index', DT_To_Index, dt01, indextime01, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'ISO_To_DT', ISO_To_DT, isotime01, dt01, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Unix_To_DT', Unix_To_DT, unixtime01, dt01, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Index_To_DT', Index_To_DT, indextime01, dt01a, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'ISO_To_Unix', ISO_To_Unix, isotime01, unixtime01, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Unix_To_ISO', Unix_To_ISO, unixtime01, isotime01, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Index_To_ISO', Index_To_ISO, indextime01, isoindextime01, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'ISO_To_Index', ISO_To_Index, isotime01, indextime01, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Index_To_Unix', Index_To_Unix, indextime01, unixindextime01, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Unix_To_Index', Unix_To_Index, unixtime01, indextime01, verbose=verbose)
# test at 1800 UT 0000 index
isotime02 = '2015-03-18T18:00:00'
isoindextime02 = '1970-01-01T18:00:00'
indextime02 = 0.0
unixtime02 = 1426701600.0
unixindextime02 = 64800.0
dt02 = datetime.datetime(2015, 3, 18, 18, 00, 00, 00)
dt02a = datetime.datetime(1970, 1, 1, 18, 00, 00, 00)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'DT_To_ISO', DT_To_ISO, dt02, isotime02, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'DT_To_Unix', DT_To_Unix, dt02, unixtime02, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'DT_To_Index', DT_To_Index, dt02, indextime02, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'ISO_To_DT', ISO_To_DT, isotime02, dt02, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Unix_To_DT', Unix_To_DT, unixtime02, dt02, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Index_To_DT', Index_To_DT, indextime02, dt02a, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'ISO_To_Unix', ISO_To_Unix, isotime02, unixtime02, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Unix_To_ISO', Unix_To_ISO, unixtime02, isotime02, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Index_To_ISO', Index_To_ISO, indextime02, isoindextime02, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'ISO_To_Index', ISO_To_Index, isotime02, indextime02, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Index_To_Unix', Index_To_Unix, indextime02, unixindextime02, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Unix_To_Index', Unix_To_Index, unixtime02, indextime02, verbose=verbose)
# test just after 1800 UT 0000 index
isotime03 = '2015-03-18T18:00:01'
isoindextime03 = '1970-01-01T18:00:01'
indextime03 = 1.0
unixtime03 = 1426701601.0
unixindextime03 = 64801.0
dt03 = datetime.datetime(2015, 3, 18, 18, 00, 01, 00)
dt03a = datetime.datetime(1970, 1, 1, 18, 00, 01, 00)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'DT_To_ISO', DT_To_ISO, dt03, isotime03, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'DT_To_Unix', DT_To_Unix, dt03, unixtime03, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'DT_To_Index', DT_To_Index, dt03, indextime03, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'ISO_To_DT', ISO_To_DT, isotime03, dt03, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Unix_To_DT', Unix_To_DT, unixtime03, dt03, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Index_To_DT', Index_To_DT, indextime03, dt03a, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'ISO_To_Unix', ISO_To_Unix, isotime03, unixtime03, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Unix_To_ISO', Unix_To_ISO, unixtime03, isotime03, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Index_To_ISO', Index_To_ISO, indextime03, isoindextime03, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'ISO_To_Index', ISO_To_Index, isotime03, indextime03, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Index_To_Unix', Index_To_Unix, indextime03, unixindextime03, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Unix_To_Index', Unix_To_Index, unixtime03, indextime03, verbose=verbose)
# test at 1800 CST 0000 UT 21600 index
isotime04 = '2015-03-18T00:00:00'
isoindextime04 = '1970-01-01T00:00:00'
indextime04 = 21600.0
unixtime04 = 1426636800.0
unixindextime04 = 0.0
dt04 = datetime.datetime(2015, 3, 18, 00, 00, 00, 00)
dt04a = datetime.datetime(1970, 1, 1, 00, 00, 00, 00)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'DT_To_ISO', DT_To_ISO, dt04, isotime04, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'DT_To_Unix', DT_To_Unix, dt04, unixtime04, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'DT_To_Index', DT_To_Index, dt04, indextime04, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'ISO_To_DT', ISO_To_DT, isotime04, dt04, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Unix_To_DT', Unix_To_DT, unixtime04, dt04, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Index_To_DT', Index_To_DT, indextime04, dt04a, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'ISO_To_Unix', ISO_To_Unix, isotime04, unixtime04, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Unix_To_ISO', Unix_To_ISO, unixtime04, isotime04, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Index_To_ISO', Index_To_ISO, indextime04, isoindextime04, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'ISO_To_Index', ISO_To_Index, isotime04, indextime04, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Index_To_Unix', Index_To_Unix, indextime04, unixindextime04, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Unix_To_Index', Unix_To_Index, unixtime04, indextime04, verbose=verbose)
# test with fractional seconds
inisotime05 = '2015-03-18T12:34:56.123'
outisotime05 = '2015-03-18T12:34:56.123000'
isoindextime05 = '1970-01-01T12:34:56.123000'
indextime05 = 66896.123
unixtime05 = 1426682096.123
unixindextime05 = 45296.123
dt05 = datetime.datetime(2015, 3,18, 12, 34, 56, 123000)
dt05a = datetime.datetime(1970, 1,1, 12, 34, 56, 123000)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'DT_To_ISO', DT_To_ISO, dt05, outisotime05, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'DT_To_Unix', DT_To_Unix, dt05, unixtime05, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'DT_To_Index', DT_To_Index, dt05, indextime05, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'ISO_To_DT', ISO_To_DT, inisotime05, dt05, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Unix_To_DT', Unix_To_DT, unixtime05, dt05, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Index_To_DT', Index_To_DT, indextime05, dt05a, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'ISO_To_Unix', ISO_To_Unix, inisotime05, unixtime05, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Unix_To_ISO', Unix_To_ISO, unixtime05, outisotime05, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Index_To_ISO', Index_To_ISO, indextime05, isoindextime05, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'ISO_To_Index', ISO_To_Index, inisotime05, indextime05, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Index_To_Unix', Index_To_Unix, indextime05, unixindextime05, verbose=verbose)
TestCount, testnum = UpdateTestCount(TestCount)
FailCount += testit(testnum, 'Unix_To_Index', Unix_To_Index, unixtime05, indextime05, verbose=verbose)
if FailCount == 1:
plural = ''
else:
plural = 's'
print('\n%d Failure%s out of %d tests' % (FailCount, plural, TestCount))