Source code for hetTime

#
#
#  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))