¿Hay alguna forma de usar una función similar a strftime para fechas anteriores a 1900 en Python?
-
10-07-2019 - |
Pregunta
No me di cuenta de esto, pero aparentemente la función strftime
de Python no admite fechas anteriores a 1900:
>>> from datetime import datetime
>>> d = datetime(1899, 1, 1)
>>> d.strftime('%Y-%m-%d')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: year=1899 is before 1900; the datetime strftime() methods require year >= 1900
Estoy seguro de que podría hackear algo para hacer esto, pero creo que la función strftime
está ahí por una razón (y también hay una razón por la que no puede admitir 1900 fechas). Necesito poder admitir fechas anteriores a 1900. Solo usaría str
, pero hay demasiada variación. En otras palabras, puede o no tener microsegundos o puede o no tener una zona horaria. ¿Hay alguna solución para esto?
Si hace la diferencia, estoy haciendo esto para poder escribir los datos en un archivo de texto y cargarlos en una base de datos usando Oracle SQL * Loader.
Básicamente terminé haciendo la respuesta de Alex Martelli. Aquí hay una implementación más completa:
>>> from datetime import datetime
>>> d = datetime.now()
>>> d = d.replace(microsecond=0, tzinfo=None)
>>> str(d)
'2009-10-29 11:27:27'
La única diferencia es que str (d)
es equivalente a d.isoformat ('')
.
Solución
isoformat funciona en instancias de fecha y hora
sin limitación de rango:
>>> import datetime
>>> x=datetime.datetime(1865, 7, 2, 9, 30, 21)
>>> x.isoformat()
'1865-07-02T09:30:21'
Si necesita una cadena de formato diferente, no es demasiado difícil de cortar, cortar en dados y mezclar piezas de la cadena que obtiene de isoformat
, que es muy consistente ( AAAA-MM-DDTHH : MM: SS.mmmmmm
, con el punto y los siguientes microsegundos omitidos si los microsegundos son cero).
Otros consejos
La la documentación parece bastante clara al respecto:
El rango exacto de años para el que
strftime ()
también varía entre plataformas. Independientemente de la plataforma, años antes de 1900 no se pueden usar.
Entonces no habrá una solución que use strftime ()
. Afortunadamente, es bastante sencillo hacer esto "a mano":
>>> "%02d-%02d-%02d %02d:%02d" % (d.year,d.month,d.day,d.hour,d.minute)
'1899-01-01 00:00'
mxDateTime
puede manejar fechas arbitrarias . Los módulos time
y datetime
de Python usan marcas de tiempo UNIX internamente, por eso tienen un rango limitado.
In [5]: mx.DateTime.DateTime(1899)
Out[5]: <mx.DateTime.DateTime object for '1899-01-01 00:00:00.00' at 154a960>
In [6]: DateTime.DateTime(1899).Format('%Y-%m-%d')
Out[6]: 1899-01-01
Esto es de fuente de matplotlib . Podría proporcionar un buen punto de partida para rodar el suyo.
def strftime(self, dt, fmt):
fmt = self.illegal_s.sub(r"\1", fmt)
fmt = fmt.replace("%s", "s")
if dt.year > 1900:
return cbook.unicode_safe(dt.strftime(fmt))
year = dt.year
# For every non-leap year century, advance by
# 6 years to get into the 28-year repeat cycle
delta = 2000 - year
off = 6*(delta // 100 + delta // 400)
year = year + off
# Move to around the year 2000
year = year + ((2000 - year)//28)*28
timetuple = dt.timetuple()
s1 = time.strftime(fmt, (year,) + timetuple[1:])
sites1 = self._findall(s1, str(year))
s2 = time.strftime(fmt, (year+28,) + timetuple[1:])
sites2 = self._findall(s2, str(year+28))
sites = []
for site in sites1:
if site in sites2:
sites.append(site)
s = s1
syear = "%4d" % (dt.year,)
for site in sites:
s = s[:site] + syear + s[site+4:]
return cbook.unicode_safe(s)
Esta es la " característica " de la biblioteca ctime (UTF). También es posible que tenga un problema por encima de 2038.