Luego de de esta prolongada compilación de información dimos con la respuesta esta duda que tienen ciertos de nuestros lectores. Te regalamos la solución y esperamos servirte de mucha apoyo.
Solución:
La hora del GPS comenzó en sincronía con UTC: 1980-01-06 (UTC) == 1980-01-06 (GPS)
. Ambos marcan en segundos SI. La diferencia entre la hora del GPS y la hora UTC aumenta con cada segundo intercalar (intercalario).
Para encontrar la hora UTC correcta, necesita saber la cantidad de segundos intercalares ocurridos antes de la hora GPS dada:
#!/usr/bin/env python
from datetime import datetime, timedelta
# utc = 1980-01-06UTC + (gps - (leap_count(2014) - leap_count(1980)))
utc = datetime(1980, 1, 6) + timedelta(seconds=1092121243.0 - (35 - 19))
print(utc)
Producción
2014-08-15 07:00:27 # (UTC)
donde leap_count(date)
es el número de segundos bisiestos introducidos antes de la fecha indicada. De la tabla TAI-UTC (nota: el sitio es la fuente autorizada en segundos bisiestos. Publica el Boletín C anunciando nuevos segundos bisiestos):
1980..: 19s
2012..: 35s
y por lo tanto:
(leap_count(2014) - leap_count(1980)) == (35 - 19)
Si está en Unix, entonces podría usar "right"
zona horaria para obtener la hora UTC de la hora TAI (y es fácil obtener la hora TAI de la hora GPS: TAI = GPS + 19 segundos (desplazamiento constante)):
#!/usr/bin/env python
import os
import time
os.environ['TZ'] = 'right/UTC' # TAI scale with 1970-01-01 00:00:10 (TAI) epoch
time.tzset() # Unix
from datetime import datetime, timedelta
gps_timestamp = 1092121243.0 # input
gps_epoch_as_gps = datetime(1980, 1, 6)
# by definition
gps_time_as_gps = gps_epoch_as_gps + timedelta(seconds=gps_timestamp)
gps_time_as_tai = gps_time_as_gps + timedelta(seconds=19) # constant offset
tai_epoch_as_tai = datetime(1970, 1, 1, 0, 0, 10)
# by definition
tai_timestamp = (gps_time_as_tai - tai_epoch_as_tai).total_seconds()
print(datetime.utcfromtimestamp(tai_timestamp)) # "right" timezone is in effect!
Producción
2014-08-15 07:00:27 # (UTC)
Puede evitar cambiar la zona horaria si extrae la lista de segundos intercalares del correspondiente tzfile(5)
. Es una combinación de los dos primeros métodos donde el cálculo del recuento de saltos del primer método está automatizado y la actualización automática tzdata
(paquete de sistema para la base de datos tz) del segundo método:
>>> from datetime import datetime, timedelta
>>> import leapseconds
>>> leapseconds.gps_to_utc(datetime(1980,1,6) + timedelta(seconds=1092121243.0))
datetime.datetime(2014, 8, 15, 7, 0, 27)
donde leapseconds.py
puede extraer segundos intercalares de /usr/share/zoneinfo/right/UTC
archivo (parte de tzdata
paquete).
Los tres métodos producen el mismo resultado.
Puede usar el paquete astropy.time para hacer esto:
Tiempo GPS para TAI
from astropy.time import Time
mytime = 1092121243.0
t = Time(mytime, format='gps')
t = Time(t, format='iso') # same as scale='tai'
print(t)
que regresa 2014-08-15 07:01:02.000
Hora GPS a UTC
from astropy.time import Time
sec = 1092121243.0
t_in = Time(sec, format='gps')
t_out = Time(t_in, format='iso', scale='utc')
print(t_out)
que salidas 2014-08-15 07:00:27.000
Utilizo la siguiente función que cuenta los segundos intercalares:
import bisect
from datetime import datetime, timedelta
_LEAP_DATES = ((1981, 6, 30), (1982, 6, 30), (1983, 6, 30),
(1985, 6, 30), (1987, 12, 31), (1989, 12, 31),
(1990, 12, 31), (1992, 6, 30), (1993, 6, 30),
(1994, 6, 30), (1995, 12, 31), (1997, 6, 30),
(1998, 12, 31), (2005, 12, 31), (2008, 12, 31),
(2012, 6, 30), (2015, 6, 30), (2016, 12, 31))
LEAP_DATES = tuple(datetime(i[0], i[1], i[2], 23, 59, 59) for i in _LEAP_DATES)
def leap(date):
"""
Return the number of leap seconds since 1980-01-01
:param date: datetime instance
:return: leap seconds for the date (int)
"""
# bisect.bisect returns the index `date` would have to be
# inserted to keep `LEAP_DATES` sorted, so is the number of
# values in `LEAP_DATES` that are less than `date`, or the
# number of leap seconds.
return bisect.bisect(LEAP_DATES, date)
Por supuesto, necesitas actualizar _LEAP_DATES
ocasionalmente, pero estas actualizaciones son bastante raras.
En general, la hora del GPS consta de dos números: Semana GPS y el numero de segundos desde el comienzo de la semana GPS actual. Por lo tanto, puede utilizar lo siguiente:
def gps2utc(week, secs):
"""
:param week: GPS week number, i.e. 1866
:param secs: number of seconds since the beginning of `week`
:return: datetime instance with UTC time
"""
secs_in_week = 604800
gps_epoch = datetime(1980, 1, 6, 0, 0, 0)
date_before_leaps = gps_epoch + timedelta(seconds=week * secs_in_week + secs)
return date_before_leaps - timedelta(seconds=leap(date_before_leaps))
En tu caso week = 0
, entonces:
In [1]: gps2utc(0, 1092121243.0)
Out[1]: datetime.datetime(2014, 8, 15, 7, 0, 27)
Si crees que ha resultado de ayuda nuestro post, sería de mucha ayuda si lo compartieras con el resto entusiastas de la programación de este modo nos ayudas a extender este contenido.