# GPS time conversion

# This is kind of awful in that leapSeconds
# are hard coded and needs to be kept up to date.
# Also, this probably is/will be/should be in glue.
# Also, there are almost certainly failing edge cases at leap second adjustment times.
# Oh yea, and I don't think this works exactly right for some periods 2006-2008, say.

import pytz
import datetime

import calendar
from time import mktime

gpsEpoch = calendar.timegm((1980, 1, 6, 0,  0,  0,  0,  0,  0))

leapSeconds = map(calendar.timegm, [
    (1981, 7, 0, 0, 0, 0, 0, 0, 0),
    (1982, 7, 0, 0, 0, 0, 0, 0, 0),
    (1983, 7, 0, 0, 0, 0, 0, 0, 0),
    (1985, 7, 0, 0, 0, 0, 0, 0, 0),
    (1988, 1, 0, 0, 0, 0, 0, 0, 0),
    (1990, 1, 0, 0, 0, 0, 0, 0, 0),
    (1991, 1, 0, 0, 0, 0, 0, 0, 0),
    (1992, 7, 0, 0, 0, 0, 0, 0, 0),
    (1993, 7, 0, 0, 0, 0, 0, 0, 0),
    (1994, 7, 0, 0, 0, 0, 0, 0, 0),
    (1996, 1, 0, 0, 0, 0, 0, 0, 0),
    (1997, 7, 0, 0, 0, 0, 0, 0, 0),
    (1999, 1, 0, 0, 0, 0, 0, 0, 0),
    (2006, 1, 0, 0, 0, 0, 0, 0, 0),
    (2009, 1, 0, 0, 0, 0, 0, 0, 0),
    (2012, 7, 0, 0, 0, 0, 0, 0, 0),
])

def gpsToPosixTime(gpsTime):
    t = gpsEpoch + gpsTime
    for leap in leapSeconds:
        if t >= leap:
            t = t - 1
    return t

def posixToGpsTime(posixTime):
    change = 0
    for leap in leapSeconds:
        if posixTime > leap:
            change += 1
    return posixTime + change - gpsEpoch

def gpsToUtc(gpsTime):
    t = gpsToPosixTime(gpsTime)
    return datetime.datetime.fromtimestamp(t, pytz.utc)

def isoToGps(t):
    # The input is a string in ISO time format: 2012-10-28T05:04:31.91
    # First strip out whitespace, then split off the factional 
    # second.  We'll add that back later.
    t=t.strip()
    ISOTime = t.split('.')[0]
    ISOTime = datetime.datetime.strptime(ISOTime,"%Y-%m-%dT%H:%M:%S")
    # Need to set UTC time zone or this is interpreted as local time.
    ISOTime = ISOTime.replace(tzinfo=pytz.utc)
    sec_substr = t.split('.')[1]
    if sec_substr:
        fracSec = float('0.' + sec_substr)
    else:
        fracSec = 0
    posixTime = calendar.timegm(ISOTime.utctimetuple()) + fracSec 
    return int(round(posixToGpsTime(posixTime)))