¿Cómo valido un formato de cadena de fecha en Python?

143

Tengo un método python que acepta una entrada de fecha como una cadena .

¿Cómo agrego una validación para asegurarme de que la cadena de fecha que se pasa al método está en el ffg? formato:

'YYYY-MM-DD'

si no es así, el método debería generar algún tipo de error

codigo clave codemico
fuente
2
Puede ser más Pythonic (pedir perdón, no permiso) no verificar en absoluto, y capturar cualquier excepción resultante que ocurra.
Thomas

Respuestas:

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
fuente
8
¿Hay alguna manera de hacerlo sin probar / excepto? Python tiende a disminuir significativamente cuando se genera una excepción y se detecta.
chiffa
1
@chiffa Podría coincidir con una expresión regular de formato de fecha, pero no se recomienda porque es menos robusta y las excepciones son más claras. ¿Estás seguro de que la validación de la fecha es tu cuello de botella?
jamylak
1
En realidad no, así que al final simplemente envolveré throw-except construct en una función. Me sorprende que no haya una función de validación de retorno de bool que desencadene el lanzamiento de excepción en la biblioteca de fecha y hora.
chiffa
Tal vez aún no ha @chiffa incluyen bool devolver la función de validación a propósito, que podría existir en las bibliotecas externas
jamylak
2
Para aquellos que desean cero relleno en las fechas, esta solución no funcionará ya que strptime no es estricto con respecto al cero relleno. Implemente una expresión regular propia o compruebe la longitud de la cadena resultante después de eliminar el espacio en blanco y luego utilice esta solución.
Suparshva
65

La biblioteca Pythondateutil está diseñada para esto (y más). Automáticamente convertirá esto en un datetimeobjeto para usted y elevará un ValueErrorsi no puede.

Como ejemplo:

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

Esto aumenta un ValueErrorsi la fecha no está formateada correctamente:

>>> 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

dateutilTambién es extremadamente útil si comienza a necesitar analizar otros formatos en el futuro, ya que puede manejar los formatos más conocidos de manera inteligente y le permite modificar su especificación: dateutilanálisis de ejemplos .

También maneja zonas horarias si lo necesita.

Actualización basada en comentarios : parsetambién acepta el argumento de la palabra clave dayfirstque controla si se espera que el día o el mes sean los primeros si una fecha es ambigua. Esto predeterminado es False. P.ej

>>> 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
fuente
1
puede aceptar demasiado, por ejemplo, parse('13/12/2001')es "13 dic" pero parse('11/12/2001')es "12 nov" (el primer resultado sugeriría "11 dic" aquí).
jfs
2
parseen realidad toma un dayfirstargumento de palabra clave que le permite controlar esto. parse('11/12/2001', dayfirst=True)volverá el "11 de diciembre" el valor predeterminado de dateutil esdayfirst=False
Jacinda el
se está perdiendo el punto que datetutil.parser.parse()acepta demasiados formatos de tiempo (podría encontrar otros ejemplos con entradas ambiguas). Si desea validar que su entrada está en formato AAAA-MM-DD, entonces la parse()función es la herramienta incorrecta.
jfs
1
Ese es un punto completamente válido: si realmente desea restringir solo a ese formato específico, esto no lo hace, y la respuesta aceptada ya hace un gran trabajo al hacer lo correcto en ese caso. Creo que cuando escribí la respuesta estaba pensando más en la línea de señalar cómo validar si era una fecha válida en lugar del formato particular que solicitó el autor, que cuando las personas se encuentran con esta pregunta es lo que a menudo son buscando.
Jacinda
¿Hay alguna manera de .parse()devolver la cadena de formato además del datetimeobjeto?
citynorman
35

Creo que la función de validación completa debería verse así:

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

Ejecutando solo

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

no es suficiente porque el método strptime no verifica que el mes y el día del mes sean números decimales rellenados con ceros. Por ejemplo

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

Se ejecutará sin errores.

Eduard Stepanov
fuente
3
"Eres técnicamente correcto, el mejor tipo de corrección". Necesitaba asegurar esto en mis cadenas.
delrocco
Esto funciona bien en mis pruebas, sin embargo, la documentación parece incorrecta ya que dice: "% d -> Día del mes como un número decimal con relleno de cero -> 01, 02, ..., 31" y lo mismo para el% m -> Mes como un número decimal rellenado con ceros. -> 01, 02,…, 12 docs.python.org/2/library/…
thanos.a
Si necesita verificar que el mes y el día están rellenados con ceros, ¿no sería suficiente verificar la longitud de la cadena y datetime.strptime(date_text, "%Y-%m-%d")?
Kyle Barron
17
from datetime import datetime

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

... esto genera un ValueErrorsi recibe un formato incompatible.

..si se trata mucho de fechas y horas (en el sentido de los objetos de fecha y hora, a diferencia de los flotantes de marca de tiempo de Unix), es una buena idea buscar en el módulo pytz y, para almacenamiento / db, almacenar todo en UTC .

Señor b
fuente
2
Fuiste más rápido, lo habría publicado yo mismo ( ideone.com/vuxDDf ). Voto a favor.
Tadeck
.. acabo de verlo justo después de que fue publicado, y ha estado trabajando con objetos de fecha y hora hoy.
Sr. B
-7

Esta es la manera más fácil:

date = datetime.now()
date = date.strftime('%Y-%m-%d_%H-%M-%S.jpg')
TimorEranAV
fuente
2
Sería mejor tener una explicación, en lugar de solo código.
lukas_o