Comment valider un format de chaîne de date en python?

143

J'ai une méthode python qui accepte une entrée de date sous forme de chaîne .

Comment ajouter une validation pour m'assurer que la chaîne de date transmise à la méthode est dans le fichier ffg. format:

'YYYY-MM-DD'

si ce n'est pas le cas, la méthode devrait générer une sorte d'erreur

codemickeycode
la source
2
Il pourrait être plus pythonique (demander pardon, pas la permission) de ne pas vérifier du tout et d'attraper toutes les exceptions qui en résultent.
Thomas

Réponses:

230
>>> import datetime
>>> def validate(date_text):
    try:
        datetime.datetime.strptime(date_text, '%Y-%m-%d')
    except ValueError:
        raise ValueError("Incorrect data format, should be YYYY-MM-DD")


>>> validate('2003-12-23')
>>> validate('2003-12-32')

Traceback (most recent call last):
  File "<pyshell#20>", line 1, in <module>
    validate('2003-12-32')
  File "<pyshell#18>", line 5, in validate
    raise ValueError("Incorrect data format, should be YYYY-MM-DD")
ValueError: Incorrect data format, should be YYYY-MM-DD
Jamylak
la source
8
Y a-t-il un moyen de faire cela sans essayer / sauf? Python a tendance à ralentir considérablement lorsqu'une exception est déclenchée et interceptée.
chiffa
1
@chiffa Vous pouvez correspondre à une expression régulière de format de date, mais ce n'est pas recommandé car elle est moins robuste et les exceptions sont plus claires. Êtes-vous sûr que la validation de la date est votre goulot d'étranglement?
jamylak
1
Pas vraiment, donc à la fin je vais juste envelopper la construction throw-except dans une fonction. Je suis juste surpris qu'il n'y ait pas de fonction de validation de retour booléen qui déclencherait le lancer d'exception dans la bibliothèque datetime.
chiffa
@chiffa Peut-être qu'ils n'ont pas inclus la fonction de validation de retour booléen exprès, cela pourrait exister dans des bibliothèques externes
jamylak
2
Pour ceux qui veulent un remplissage nul dans les dates, cette solution ne fonctionnera pas car strptime n'est pas strict sur le remplissage zéro. Implémentez votre propre expression régulière ou vérifiez la longueur de la chaîne résultante après avoir supprimé les espaces, puis utilisez cette solution.
Suparshva
66

La bibliothèque Pythondateutil est conçue pour cela (et plus encore). Il le convertira automatiquement en datetimeobjet pour vous et lèvera un ValueErrors'il ne le peut pas.

Par exemple:

>>> from dateutil.parser import parse
>>> parse("2003-09-25")
datetime.datetime(2003, 9, 25, 0, 0)

Cela déclenche un ValueErrorsi la date n'est pas formatée correctement:

>>> parse("2003-09-251")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/jacinda/envs/dod-backend-dev/lib/python2.7/site-packages/dateutil/parser.py", line 720, in parse
    return DEFAULTPARSER.parse(timestr, **kwargs)
  File "/Users/jacinda/envs/dod-backend-dev/lib/python2.7/site-packages/dateutil/parser.py", line 317, in parse
    ret = default.replace(**repl)
ValueError: day is out of range for month

dateutilest également extrêmement utile si vous commencez à avoir besoin d'analyser d'autres formats à l'avenir, car il peut gérer intelligemment les formats les plus connus et vous permet de modifier vos spécifications: des dateutilexemples d'analyse .

Il gère également les fuseaux horaires si vous en avez besoin.

Mise à jour basée sur les commentaires : parseaccepte également l'argument mot-clé dayfirstqui contrôle si le jour ou le mois doit venir en premier si une date est ambiguë. La valeur par défaut est False. Par exemple

>>> parse('11/12/2001')
>>> datetime.datetime(2001, 11, 12, 0, 0) # Nov 12
>>> parse('11/12/2001', dayfirst=True)
>>> datetime.datetime(2001, 12, 11, 0, 0) # Dec 11
Jacinda
la source
1
il peut en accepter trop, par exemple parse('13/12/2001')"13 décembre" mais parse('11/12/2001')"12 novembre" (le premier résultat suggérerait ici "11 décembre").
jfs
2
parseprend en fait un dayfirstargument de mot - clé qui vous permet de contrôler cela. parse('11/12/2001', dayfirst=True)retournera "11 décembre" La valeur par défaut de dateutil estdayfirst=False
Jacinda
vous manquez le point qui datetutil.parser.parse()accepte trop de formats d'heure (vous pourriez trouver d'autres exemples avec une entrée ambiguë). Si vous souhaitez valider que votre entrée est au format AAAA-MM-JJ, la parse()fonction n'est pas le bon outil.
jfs
1
C'est un point tout à fait valable - si vous voulez vraiment vous limiter à ce format spécifique, cela ne le fait pas, et la réponse acceptée fait déjà un excellent travail pour faire la bonne chose dans ce cas. Je pense que lorsque j'ai écrit la réponse, je pensais davantage à indiquer comment valider s'il s'agissait d'une date valide par opposition au format particulier demandé par l'auteur, qui lorsque les gens rencontrent cette question est ce qu'ils sont souvent. à la recherche de.
Jacinda
Existe-t-il un moyen de .parse()retourner la chaîne de format en plus de l' datetimeobjet?
citynorman
35

Je pense que la fonction de validation complète devrait ressembler à ceci:

from datetime import datetime

def validate(date_text):
    try:
        if date_text != datetime.strptime(date_text, "%Y-%m-%d").strftime('%Y-%m-%d'):
            raise ValueError
        return True
    except ValueError:
        return False

Exécuter juste

datetime.strptime(date_text, "%Y-%m-%d") 

n'est pas suffisant car la méthode strptime ne vérifie pas que le mois et le jour du mois sont des nombres décimaux remplis de zéro. Par exemple

datetime.strptime("2016-5-3", '%Y-%m-%d')

sera exécuté sans erreur.

Eduard Stepanov
la source
3
"Vous êtes techniquement correct - le meilleur type de correct." J'avais besoin de m'en assurer dans mes cordes.
delrocco
Cela fonctionne bien contre mes tests, mais la documentation semble incorrecte car elle indique: "% d -> Jour du mois sous forme de nombre décimal complété par des zéros -> 01, 02,…, 31" et la même chose pour le% m -> Mois sous forme de nombre décimal complété par des zéros. -> 01, 02,…, 12 docs.python.org/2/library/…
thanos.a
Si vous avez besoin de vérifier que le mois et le jour sont remplis de zéro, ne suffirait-il pas simplement de vérifier la longueur de la chaîne et datetime.strptime(date_text, "%Y-%m-%d")?
Kyle Barron le
17
from datetime import datetime

datetime.strptime(date_string, "%Y-%m-%d")

.. cela lève un ValueErrors'il reçoit un format incompatible.

..si vous traitez beaucoup de dates et d'heures (dans le sens des objets datetime, par opposition aux flottants d'horodatage unix), c'est une bonne idée de regarder dans le module pytz, et pour le stockage / db, de tout stocker en UTC .

Monsieur B
la source
2
Vous étiez plus rapide, je l'aurais posté moi-même ( ideone.com/vuxDDf ). Vote positif.
Tadeck
.. vient de le voir juste après sa publication, et il se trouve qu'il a travaillé avec des objets datetime aujourd'hui.
M. B
-7

C'est le moyen le plus simple:

date = datetime.now()
date = date.strftime('%Y-%m-%d_%H-%M-%S.jpg')
TimorEranAV
la source
2
Il vaudrait mieux avoir une explication, plutôt que du code uniquement.
lukas_o