Bagaimana cara memvalidasi format string tanggal dengan python?

143

Saya memiliki metode python yang menerima input tanggal sebagai string .

Bagaimana cara menambahkan validasi untuk memastikan string tanggal yang diteruskan ke metode ada di ffg. format:

'YYYY-MM-DD'

jika tidak, metode harus memunculkan semacam kesalahan

codemickeycode
sumber
2
Mungkin lebih Pythonic (meminta pengampunan, bukan izin) untuk tidak memeriksa sama sekali, dan menangkap pengecualian yang terjadi.
Thomas

Jawaban:

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
sumber
8
Apakah ada cara untuk melakukan itu tanpa mencoba / kecuali? Python cenderung melambat secara signifikan ketika pengecualian dimunculkan dan ditangkap.
chiffa
1
@ chiffa Anda dapat mencocokkan regex format tanggal tetapi tidak disarankan karena kurang kuat dan pengecualiannya lebih jelas. Apakah Anda yakin validasi tanggal adalah hambatan Anda?
jamylak
1
Tidak juga, jadi pada akhirnya saya hanya akan membungkus throw-kecuali membangun sebuah fungsi. Saya hanya terkejut bahwa tidak ada fungsi validasi bool-return yang akan memicu lemparan Exception di pustaka datetime.
chiffa
@ chiffa Mungkin mereka tidak menyertakan bool mengembalikan fungsi validasi dengan sengaja, mungkin ada di perpustakaan eksternal
jamylak
2
Bagi mereka yang menginginkan zero padding pada tanggal, solusi ini tidak akan berfungsi karena strptime tidak ketat tentang zero padding. Terapkan regex Anda sendiri atau periksa panjang string yang dihasilkan setelah menghapus spasi dan kemudian gunakan solusi ini.
Suparshva
65

The Pythondateutil perpustakaan ini dirancang untuk ini (dan lebih). Ini akan secara otomatis mengonversikan ini menjadi datetimeobjek untuk Anda dan menaikkan ValueErrorjika tidak.

Sebagai contoh:

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

Ini memunculkan a ValueErrorjika tanggal tidak diformat dengan benar:

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

dateutiljuga sangat berguna jika Anda mulai perlu mem-parsing format lain di masa depan, karena dapat menangani sebagian besar format yang dikenal secara cerdas dan memungkinkan Anda untuk memodifikasi spesifikasi Anda: dateutilcontoh parsing .

Ini juga menangani zona waktu jika Anda membutuhkannya.

Pembaruan berdasarkan komentar : parsejuga menerima argumen kata kunci dayfirstyang mengontrol apakah hari atau bulan diharapkan didahulukan jika suatu tanggal ambigu. Ini default ke Salah. Misalnya

>>> 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
sumber
1
mungkin menerima terlalu banyak misalnya, parse('13/12/2001')"13 Des" tetapi parse('11/12/2001')"12 Nov" (hasil pertama akan menyarankan "11 Des" di sini).
jfs
2
parsesebenarnya mengambil dayfirstargumen kata kunci yang memungkinkan Anda untuk mengontrol ini. parse('11/12/2001', dayfirst=True)akan kembali "11 Des." default dateutil adalahdayfirst=False
Jacinda
Anda kehilangan titik yang datetutil.parser.parse()menerima terlalu banyak format waktu (Anda bisa menemukan contoh lain dengan input yang ambigu). Jika Anda ingin memvalidasi bahwa input Anda dalam format YYYY-MM-DD maka parse()fungsinya adalah alat yang salah.
jfs
1
Itu poin yang benar-benar valid - jika Anda benar-benar ingin membatasi hanya ke format tertentu ini tidak melakukan itu, dan jawaban yang diterima sudah melakukan pekerjaan yang baik dalam melakukan hal yang benar dalam kasus itu. Saya pikir ketika saya menulis jawaban, saya berpikir lebih dalam untuk menunjukkan bagaimana memvalidasi apakah itu tanggal yang valid dibandingkan dengan format khusus yang diminta penulis, yang ketika orang menemukan pertanyaan ini adalah apa yang sering mereka temukan. mencari.
Jacinda
Apakah ada cara untuk .parse()mengembalikan string format selain datetimeobjek?
citynorman
35

Saya pikir fungsi validasi penuh akan terlihat seperti ini:

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

Eksekusi secara adil

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

tidak cukup karena metode strptime tidak memeriksa bahwa bulan dan hari dalam bulan adalah angka desimal nol-empuk. Sebagai contoh

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

akan dieksekusi tanpa kesalahan.

Eduard Stepanov
sumber
3
"Secara teknis Anda benar - jenis yang terbaik benar." Saya perlu memastikan ini dalam string saya.
delrocco
Ini berfungsi baik terhadap pengujian saya, namun saya dokumentasi tampaknya salah karena menyatakan: "% d -> Hari dalam sebulan sebagai angka desimal nol-empuk -> 01, 02,…, 31" dan sama untuk% m -> Bulan sebagai angka desimal nol-empuk. -> 01, 02,…, 12 docs.python.org/2/library/…
thanos.a
Jika Anda perlu memeriksa bahwa bulan dan hari itu berlapis nol, bukankah cukup dengan memeriksa panjang tali dan datetime.strptime(date_text, "%Y-%m-%d")?
Kyle Barron
17
from datetime import datetime

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

..ini memunculkan ValueErrorjika menerima format yang tidak kompatibel.

..jika Anda berurusan dengan tanggal dan waktu banyak (dalam arti objek waktu, sebagai lawan dari unix timestamp mengapung), itu ide yang baik untuk melihat ke modul pytz, dan untuk penyimpanan / db, simpan segala sesuatu di UTC .

Tuan B
sumber
2
Anda lebih cepat, saya akan mempostingnya sendiri ( ideone.com/vuxDDf ). Suara positif.
Tadeck
..hanya melihatnya tepat setelah diposting, dan kebetulan telah bekerja dengan objek datetime hari ini.
Tn. B
-7

Ini cara termudah:

date = datetime.now()
date = date.strftime('%Y-%m-%d_%H-%M-%S.jpg')
TimorEranAV
sumber
2
Akan lebih baik untuk memiliki penjelasan, daripada hanya kode.
lukas_o