Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Gérer des dates pré 1900 voire négatives

+6 votes

J'ai une application historique(sous django/postgred) qui doit gérer des dates avant 1900 voie négatives. D'après mes recherches il y a des moyens de gérer jusqu'à l'an 1 mais pas au delà.

A votre avis quel est le meilleur design pattern pour gérer cette problématique sachant que je souhaite au maximum conserver un tapage date au niveau de la bdd pour des raisons de performance pour des recherches.

demandé 9-Jul-2015 par iopsthecloud (160 points)
edité 14-Jul-2015 par foxmask

3 Réponses

+3 votes

Python2

>>> mDt = datetime(1900,01,01) # annee 1900, mois 01, jour 01
>>> dt = datetime.strptime('20-02-1899', "%d-%m-%Y") # jour 20, mois 02, annee 1899
>>> # feinte: passer 1900 en argument, et remplacer juste pr l'affichage
>>> # le +1 devrait normalement etre remplacé par + delta entre mDt et dt
>>> datetime(dt.year + 1, dt.month, dt.day).strftime('%B %d, %Y').replace('1900', str(dt.year))
u'February 20, 1899'
>>> 

Python3: la fonction datetime accepte bien une année inferieure a 1900

>>> mDt = datetime(1900,1,1) # annee 1900, mois 1, jour 1 (01 genere une SyntaxError)
>>> dt = datetime.strptime('20-02-1899', "%d-%m-%Y") # jour 20, mois 02, annee 1899
>>> datetime(dt.year, dt.month, dt.day).strftime('%B %d, %Y') # no problem
'February 20, 1899'
>>> 

Passer donc a python3 semble etre a premiere vue la solution. Autrement, jeter un oeil sur le paquet DateTime, j'ai pas fait de tres grosses manips, mais en python3 ça donne:

(datetime) >>> from DateTime import DateTime
(datetime) >>> x = DateTime('1750/3/9 1:45pm')
(datetime) >>> x.year()
1750
(datetime) >>> 

Peut etre qu'en donnant plus de details, une solution plus precise te sera apportée.

[EDIT: ]

@jc fait bien de nous preciser qu'en python2, seule la fonction strftime ne sait pas gérer les dates avant 1900, il est parfaitement possible de faire:

In [7]: print(datetime.datetime(1899, 2,20))
1899-02-20 00:00:00

In [10]: print( datetime.datetime.min )
0001-01-01 00:00:00

Du coup, si un formattage est necessaire, cela se fera eventuellement a coup de isoformat(), de split() et de replace():

>>> datetime(1865, 7, 2, 9, 30, 21).isoformat()
'1865-07-02T09:30:21'
>>> 

Comment gerer les dates negatives? Travailler en valeur absolue?

répondu 9-Jul-2015 par Nsukami_ (1,998 points)
edité 10-Jul-2015 par Nsukami_

Dans ton premier exemple, ce n'est "que" la fonction strftime qui ne sait pas gérer les dates avant 1900, mais python 2 est bien capable de le faire ;

In [7]: print(datetime.datetime(1899, 2,20))
1899-02-20 00:00:00

Ça peut probleme lors de la conversion pour affichage mais ca devrait passer en interne entre python et postgres.

La ou ça va être compliqué, c'est pour gérer des dates négatives : la date minimum que python sait gérer est le 0001-01-01 :

In [10]: print( datetime.datetime.min )
0001-01-01 00:00:00

Si on essaie de construire une date inferieure a celle-ci, on se mange une ValueError

In [12]: print datetime.datetime(-1,1,1)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-12-9b86d0a362f4> in <module>()
----> 1 print datetime.datetime(-1,1,1)
ValueError: year is out of range
+3 votes

Pour les dates négatives, je vois deux solutions "triviales":
Utiliser numpy.datetime64. C'est un peu overkill d'utiliser numpy juste pour ça mais bon. La fonction n'accepte que les dates formatées en ISO 8601. Un exemple:

import numpy as np
date = np.datetime64('-4-12-25T19:42:16')
print(date.astype('datetime64[Y]').astype(int) + 1970) # année
print(date.astype('datetime64[M]').astype(int) % 12 + 1) # mois
print((date.astype('datetime64[D]') - date.astype('datetime64[M]') + 1).astype(int)) # jour
print(date.astype('datetime64[h]').astype(int) % 24) # heure
print(date.astype('datetime64[m]').astype(int) % 60) # minutes
print(date.astype('datetime64[s]').astype(int) % 60) # secondes

Bon après je ne sais pas comment marche postgres au niveau des dates BC, mais je suppose qu'il supporte la notation ISO 8601 donc ça ne devrait pas poser de problèmes.

Une autre solution serait de stocker les dates en base en format Julien et d'utiliser par exemple le module jdcal:

import jdcal
print(sum(jdcal.gcal2jd(-4, 12, 25))) # 1719957.5
print(jdcal.jd2gcal(1719957.5, 0)) # (-4, 12, 25, 0.0)

Il y a aussi le module flexidate, mais je ne l'ai jamais utilisé.

répondu 11-Jul-2015 par Arza (726 points)

Quelques pistes pour gérer les dates BC avec postgres et sqlalchemy : http://stackoverflow.com/questions/24307420/sqlalchemy-bc-dates-with-postgresql

+1 vote

J'arrive au premier janvier de l'an Un avec le module arrow.

Tu pourrais tester cette condition , et utiliser une solution maison pour gérer les cromagnons.

répondu 11-Jul-2015 par buffalo974 (2,886 points)
...