Jak sprawdzić poprawność formatu ciągu daty w Pythonie?

143

Mam metodę Pythona, która akceptuje dane wejściowe jako ciąg .

Jak dodać walidację, aby upewnić się, że ciąg daty przekazywany do metody znajduje się w pliku ffg. format:

'YYYY-MM-DD'

jeśli tak nie jest, metoda powinna wywołać jakiś błąd

codemickeycode
źródło
2
Może być bardziej Pythonic (poproś o wybaczenie, a nie pozwolenie), aby w ogóle nie sprawdzać i wychwytywać wszelkie wynikające z tego wyjątki, które wystąpią.
Thomas

Odpowiedzi:

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
źródło
8
Czy można to zrobić bez próby / z wyjątkiem? Python ma tendencję do znacznego spowolnienia, gdy wyjątek jest zgłaszany i przechwytywany.
chiffa
1
@chiffa Możesz dopasować wyrażenie regularne w formacie daty, ale nie jest to zalecane, ponieważ jest mniej niezawodne, a wyjątki są wyraźniejsze. Czy na pewno walidacja daty jest Twoim wąskim gardłem?
jamylak
1
Niezupełnie, więc na koniec po prostu zawijam konstrukcję rzutowania z wyjątkiem funkcji. Jestem po prostu zaskoczony, że nie ma funkcji walidującej zwracającej wartość bool, która wyzwoliłaby rzut wyjątku w bibliotece datetime.
chiffa
@chiffa Może celowo nie uwzględnili funkcji bool zwracającej funkcję walidacji, może ona istnieć w bibliotekach zewnętrznych
jamylak
2
Dla tych, którzy chcą dopełnienia zerami w datach, to rozwiązanie nie zadziała, ponieważ strptime nie wymaga ścisłego uzupełniania zer. Zaimplementuj własne wyrażenie regularne lub sprawdź długość powstałego ciągu znaków po usunięciu białych znaków, a następnie skorzystaj z tego rozwiązania.
Suparshva
65

Pythondateutil biblioteka jest przeznaczony do tego (i więcej). Automatycznie przekształci to w datetimeobiekt dla ciebie i podniesie, ValueErrorjeśli nie może.

Jako przykład:

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

Wiąże się ValueErrorto z nieprawidłowym formatowaniem daty:

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

dateutiljest również niezwykle przydatny, jeśli w przyszłości będziesz musiał analizować inne formaty, ponieważ może inteligentnie obsługiwać większość znanych formatów i umożliwia modyfikowanie specyfikacji: dateutilanalizowanie przykładów .

Obsługuje również strefy czasowe, jeśli tego potrzebujesz.

Aktualizacja oparta na komentarzach : parseakceptuje również argument słowa kluczowego, dayfirstktóry kontroluje, czy dzień lub miesiąc ma być pierwszy, jeśli data jest niejednoznaczna. Domyślnie jest to False. Na przykład

>>> 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
źródło
1
może przyjąć za dużo, np. parse('13/12/2001')„13 grudnia”, ale parse('11/12/2001')„12 listopada” (pierwszy wynik sugerowałby tutaj „11 grudnia”).
jfs
2
parsew rzeczywistości przyjmuje dayfirstargument słowa kluczowego, który pozwala ci to kontrolować. parse('11/12/2001', dayfirst=True)zwróci „11 grudnia”. Dateutil domyślnie todayfirst=False
Jacinda
brakuje Ci punktu, który datetutil.parser.parse()akceptuje zbyt wiele formatów czasu (możesz znaleźć inne przykłady z niejednoznacznymi danymi wejściowymi). Jeśli chcesz sprawdzić , czy wprowadzone dane są w formacie RRRR-MM-DD, parse()funkcja jest niewłaściwym narzędziem.
jfs
1
To całkowicie słuszna uwaga - jeśli naprawdę chcesz ograniczyć się tylko do tego konkretnego formatu, to tego nie robi, a zaakceptowana odpowiedź już robi świetną robotę, robiąc właściwą rzecz w tym przypadku. Myślę, że kiedy pisałem odpowiedź, myślałem bardziej o wskazaniu, jak sprawdzić, czy była to ważna data, w przeciwieństwie do konkretnego formatu, o który prosił autor, który, gdy ludzie napotykają to pytanie, jest tym, co często szukam.
Jacinda
Czy istnieje sposób na .parse()zwrócenie ciągu formatu oprócz datetimeobiektu?
citynorman
35

Myślę, że pełna funkcja walidacji powinna wyglądać tak:

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

Wykonywanie tylko

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

nie wystarczy, ponieważ metoda strptime nie sprawdza, czy miesiąc i dzień miesiąca są liczbami dziesiętnymi uzupełnionymi zerami. Na przykład

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

zostanie wykonany bez błędów.

Eduard Stepanov
źródło
3
„Jesteś poprawny technicznie - najlepszy rodzaj poprawności”. Musiałem to zapewnić w moich strunach.
delrocco
Działa to dobrze w przypadku moich testów, jednak dokumentacja wydaje się niepoprawna, ponieważ stwierdza: "% d -> Dzień miesiąca jako liczba dziesiętna z zerami -> 01, 02,…, 31" i to samo dla% m -> Miesiąc jako liczba dziesiętna z zerami. -> 01, 02,…, 12 docs.python.org/2/library/…
thanos.a
Jeśli chcesz sprawdzić, czy miesiąc i dzień są wypełnione zerami, czy nie wystarczyłoby po prostu sprawdzić długość ciągu i datetime.strptime(date_text, "%Y-%m-%d")?
Kyle Barron
17
from datetime import datetime

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

.. wywołuje to, ValueErrorjeśli otrzyma niezgodny format.

..jeśli masz do czynienia z datami i czasami (w sensie obiektów datetime, w przeciwieństwie do unx timestamp float), dobrym pomysłem jest zajrzenie do modułu pytz i przechowywanie / db, przechowywanie wszystkiego w UTC .

Pan B.
źródło
2
Byłeś szybszy, sam bym to opublikował ( ideone.com/vuxDDf ). Głosuj za.
Tadeck
.. po prostu zobaczyłem to zaraz po opublikowaniu i akurat dzisiaj pracowałem z obiektami datetime.
Pan B
-7

To najłatwiejszy sposób:

date = datetime.now()
date = date.strftime('%Y-%m-%d_%H-%M-%S.jpg')
TimorEranAV
źródło
2
Lepiej byłoby mieć wyjaśnienie zamiast samego kodu.
lukas_o