¿Formato de cadena de fecha en Python para fechas anteriores a 1900?

¿Puede alguien explicar la mejor manera de formatear una cadena de fecha y hora en Python donde el valor de la fecha es anterior al año 1900? strftime requiere fechas posteriores a 1900.

Es un poco engorroso, pero funciona (al menos en versiones estables de python):

 >>> ts = datetime.datetime(1895, 10, 6, 16, 4, 5) >>> '{0.year}-{0.month:{1}}-{0.day:{1}} {0.hour:{1}}:{0.minute:{1}}'.format(ts, '02') '1895-10-06 16:04' 

tenga en cuenta que str todavía produciría una cadena legible:

 >>> str(ts) '1895-10-06 16:04:05' 

editar
La forma más cercana posible de emular el comportamiento predeterminado es codificar el diccionario como:

 >>> d = {'%Y': '{0.year}', '%m': '{0.month:02}'} # need to include all the formats >>> '{%Y}-{%m}'.format(**d).format(ts) '1895-10' 

Deberá incluir todos los especificadores de formato en las llaves con la expresión regular simple:

 >>> re.sub('(%\w)', r'{\1}', '%Y-%m-%d %H sdf') '{%Y}-{%m}-{%d} {%H} sdf' 

Y al final llegamos al código simple:

 def ancient_fmt(ts, fmt): fmt = fmt.replace('%%', '%') fmt = re.sub('(%\w)', r'{\1}', fmt) return fmt.format(**d).format(ts) def main(ts, format): if ts.year < 1900: return ancient_format(ts, fmt) else: return ts.strftime(fmt) 

donde d es un diccionario global con claves correspondientes a algunos especificadores en la tabla de tiempo de strftime .

editar 2
Para aclarar: este enfoque solo funcionará para los siguientes especificadores: %Y, %m, %d, %H, %M, %S, %f , es decir, aquellos que son numéricos, si necesita información textual, Mejor con babel o cualquier otra solución.

La biblioteca de internacionalización de babel parece no tener problemas con ella. Ver los documentos para babel.dates

El calendario es exactamente el mismo cada 400 años. Por lo tanto, es suficiente cambiar el año por un múltiplo de 400 como el year >= 1900 antes de llamar a datetime.strftime() .

El código muestra qué problemas tiene este enfoque:

 #/usr/bin/env python2.6 import re import warnings from datetime import datetime def strftime(datetime_, format, force=False): """`strftime()` that works for year < 1900. Disregard calendars shifts. >>> def f(fmt, force=False): ... return strftime(datetime(1895, 10, 6, 11, 1, 2), fmt, force) >>> f('abc %Y %m %D') 'abc 1895 10 10/06/95' >>> f('%X') '11:01:02' >>> f('%c') #doctest:+NORMALIZE_WHITESPACE Traceback (most recent call last): ValueError: '%c', '%x' produce unreliable results for year < 1900 use force=True to override >>> f('%c', force=True) 'Sun Oct 6 11:01:02 1895' >>> f('%x') #doctest:+NORMALIZE_WHITESPACE Traceback (most recent call last): ValueError: '%c', '%x' produce unreliable results for year < 1900 use force=True to override >>> f('%x', force=True) '10/06/95' >>> f('%%x %%Y %Y') '%x %Y 1895' """ year = datetime_.year if year >= 1900: return datetime_.strftime(format) # make year larger then 1900 using 400 increment assert year < 1900 factor = (1900 - year - 1) // 400 + 1 future_year = year + factor * 400 assert future_year > 1900 format = Specifier('%Y').replace_in(format, year) result = datetime_.replace(year=future_year).strftime(format) if any(f.ispresent_in(format) for f in map(Specifier, ['%c', '%x'])): msg = "'%c', '%x' produce unreliable results for year < 1900" if not force: raise ValueError(msg + " use force=True to override") warnings.warn(msg) result = result.replace(str(future_year), str(year)) assert (future_year % 100) == (year % 100) # last two digits are the same return result class Specifier(str): """Model %Y and such in `strftime`'s format string.""" def __new__(cls, *args): self = super(Specifier, cls).__new__(cls, *args) assert self.startswith('%') assert len(self) == 2 self._regex = re.compile(r'(%*{0})'.format(str(self))) return self def ispresent_in(self, format): m = self._regex.search(format) return m and m.group(1).count('%') & 1 # odd number of '%' def replace_in(self, format, by): def repl(m): n = m.group(1).count('%') if n & 1: # odd number of '%' prefix = '%'*(n-1) if n > 0 else '' return prefix + str(by) # replace format else: return m.group(0) # leave unchanged return self._regex.sub(repl, format) if __name__=="__main__": import doctest; doctest.testmod() 

Sólo por diversión, acabo con esta solución.

 a = datetime.datetime(1322, 10, 10) # %Y%m%d ''.join(map(lambda x: '{:02}'.format(getattr(a, x)), ('year', 'month', 'day'))) # %Y-%m-%d '-'.join(map(lambda x: '{:02}'.format(getattr(a, x)), ('year', 'month', 'day'))) # %Y%m%d%H%M%S ''.join(map(lambda x: '{:02}'.format(getattr(a, x)), ('year', 'month', 'day', 'hour', 'minute', 'second')))