วิธีตรวจสอบว่าสตริงเป็นวันที่ที่ถูกต้องหรือไม่


114

ฉันมีสตริง: "31-02-2010"และต้องการตรวจสอบว่าเป็นวันที่ที่ถูกต้องหรือไม่ วิธีที่ดีที่สุดคืออะไร?

ฉันต้องการเมธอดที่คืนค่าจริงหากสตริงเป็นวันที่ที่ถูกต้องและเป็นเท็จหากไม่ใช่


2
ทำไมไม่ทำแบบเลื่อนลง date_time แทนที่จะใช้สตริงที่คุณต้องตรวจสอบความถูกต้อง
สึกกร่อน

ความต้องการของลูกค้า ฉันแนะนำไปแล้ว แต่ทำไม่ได้ :)
สลิล

ตามกฎทั่วไปคุณไม่ควรทำการตรวจสอบอินพุตที่ส่วนหน้าของแอปพลิเคชันใช่หรือไม่?
Seanny123

คำตอบที่ดีกว่าที่นี่ - นี่คือวิธีการทำ stackoverflow.com/questions/597328/…
13

3
ตรวจสอบความถูกต้องในแบ็กเอนด์เสมอไม่ว่าส่วนหน้าของคุณจะดีแค่ไหนอย่าไว้ใจ!
SparK

คำตอบ:


112
require 'date'
begin
   Date.parse("31-02-2010")
rescue ArgumentError
   # handle invalid date
end

27
ไม่ใช่คุณสมบัติคลาสวันที่ แต่เป็นการจัดการข้อยกเว้น
Pier-Olivier Thibault

3
Date.parse ('2012-08-13 == 00:00:00') # => จันทร์, 13 ส.ค. 2555
Bob

13
การใช้การช่วยเหลือแบบ "จับมันทั้งหมด" ควรถือเป็นการต่อต้านรูปแบบ สามารถซ่อนข้อผิดพลาดอื่น ๆ ที่เราไม่คาดคิดและทำให้การดีบักโค้ดทำได้ยากมาก
yagooar

8
ดังนั้น ... ถ้ามันเป็นรูปแบบการต่อต้านคุณจะตอบคำถามอย่างไร?
Matthew Brown

11
ขออภัยนี่เป็นคำตอบที่ผิด ไม่ได้ตรวจสอบว่าสตริงเป็นวันที่ที่ถูกต้องหรือไม่เพียงแค่ตรวจสอบ Date.parse เท่านั้นที่สามารถแยกวิเคราะห์สตริงได้ Date.parse ดูเหมือนจะไม่น่าให้อภัยอย่างยิ่งเมื่อพูดถึงวันที่เช่นจะแยกวิเคราะห์ "FOOBAR_09_2010" เป็นวันที่ 2012-09-09
13

54

นี่คือซับง่ายๆ:

DateTime.parse date rescue nil

ฉันคงไม่แนะนำให้ทำแบบนี้ในทุกสถานการณ์ในชีวิตจริงในขณะที่คุณบังคับให้ผู้โทรตรวจหาศูนย์เช่น โดยเฉพาะอย่างยิ่งเมื่อจัดรูปแบบ หากคุณส่งคืนวันที่เริ่มต้น | ข้อผิดพลาดอาจเป็นมิตรกว่า


8
DateTime.parse ศูนย์ช่วยเหลือ "123" ส่งคืนวันที่จริง .. 3 พ.ค. 2017
baash05

3
Date.strptime(date, '%d/%m/%Y') rescue nil
bonafernando

1
การใช้แบบอินไลน์rescueเป็นกำลังใจ
smoyth

52
d, m, y = date_string.split '-'
Date.valid_date? y.to_i, m.to_i, d.to_i

นี่ค่อนข้างง่ายและดีสำหรับการบังคับใช้รูปแบบ xx-xx-YYYY
n13

หากคุณต้องการบังคับใช้อ็อพชันสตริงวันที่รูปแบบเดียวที่เข้มงวดนี่เป็นตัวเลือกที่ดีที่สุดเนื่องจากหลีกเลี่ยงข้อยกเว้นและถูกกำหนด Date.valid_date? เป็นวิธีที่ใช้อย่างน้อยสำหรับ Ruby 2. ruby-doc.org/stdlib-2.0.0/libdoc/date/rdoc/…
Ashley Raiteri

4
Ruby รุ่นใดที่รองรับis_valid?? พยายาม 1.8, 2.0 และ 2.1 ดูเหมือนจะไม่มีเลย ทั้งหมดดูเหมือนจะมีvalid_date?แต่
Henrik N

29

การแยกวิเคราะห์วันที่สามารถใช้งานได้ใน gotcha โดยเฉพาะอย่างยิ่งเมื่ออยู่ในรูปแบบ MM / DD / YYYY หรือ DD / MM / YYYY เช่นวันที่สั้น ๆ ที่ใช้ในสหรัฐอเมริกาหรือยุโรป

Date#parse พยายามคิดว่าจะใช้อะไร แต่มีหลายวันในหนึ่งเดือนตลอดทั้งปีเมื่อความคลุมเครือระหว่างรูปแบบอาจทำให้เกิดปัญหาในการแยกวิเคราะห์

ฉันขอแนะนำให้ค้นหาว่า LOCALE ของผู้ใช้คืออะไรจากนั้นคุณจะรู้วิธีแยกวิเคราะห์อย่างชาญฉลาดโดยใช้ Date.strptimeของผู้ใช้เป็นแล้วขึ้นอยู่กับว่าคุณจะรู้วิธีการแยกอย่างชาญฉลาดใช้ วิธีที่ดีที่สุดในการค้นหาว่าผู้ใช้อยู่ที่ใดคือถามพวกเขาในระหว่างการลงชื่อสมัครใช้จากนั้นระบุการตั้งค่าในการตั้งค่าเพื่อเปลี่ยนแปลง สมมติว่าคุณสามารถขุดมันออกมาได้ด้วยการวิเคราะห์หาเหตุผลที่ชาญฉลาดและไม่รบกวนผู้ใช้สำหรับข้อมูลนั้นมีแนวโน้มที่จะล้มเหลวดังนั้นเพียงแค่ถาม

นี่คือการทดสอบโดยใช้Date.parse. ฉันอยู่ในสหรัฐอเมริกา:

>> Date.parse('01/31/2001')
ArgumentError: invalid date

>> Date.parse('31/01/2001') #=> #<Date: 2001-01-31 (4903881/2,0,2299161)>

รูปแบบแรกคือรูปแบบที่ถูกต้องสำหรับสหรัฐอเมริกา: mm / dd / yyyy แต่ Date ไม่ชอบ ประการที่สองถูกต้องสำหรับยุโรป แต่ถ้าลูกค้าของคุณส่วนใหญ่อยู่ในสหรัฐอเมริกาคุณจะได้รับวันที่ที่แยกวิเคราะห์ไม่ดีจำนวนมาก

ทับทิมDate.strptimeใช้เช่น:

>> Date.strptime('12/31/2001', '%m/%d/%Y') #=> #<Date: 2001-12-31 (4904549/2,0,2299161)>

LOCALE ของคุณปิดอยู่ ฉันได้รับสิ่งนี้ >> Date.parse ('01 / 31/2001 ') => พุธ, 31 มกราคม 2544 >>?> Date.parse ('31 / 01/2001') ArgumentError: วันที่ไม่ถูกต้อง
hoyhoy

ไม่แน่ใจว่าฉันกำลังบ้าอยู่ที่นี่หรือเปล่า แต่ดูเหมือนว่าจะอธิบายเฉพาะวิธีแยกวิเคราะห์วันที่ไม่ใช่วิธีตรวจสอบความถูกต้อง คำตอบที่ยอมรับใช้ข้อยกเว้น ArgumentError แต่ดูเหมือนจะไม่ใช่ความคิดที่ดีด้วยเหตุผลที่ระบุไว้ในความคิดเห็นที่นั่น
cesoid

มีสถานการณ์ที่ทราบแล้วซึ่งคุณไม่สามารถตรวจสอบวันที่ได้ เป็นที่ที่ค่าวันและเดือนไม่ชัดเจนและคุณต้องรู้รูปแบบของสตริงวันที่เข้ามา และนั่นคือสิ่งที่คำตอบกำลังพูด ถ้าเป็นเช่น01/01/2014นี้คือเดือนไหนและวันไหน? ในเดือนของสหรัฐอเมริกาจะเป็นเดือนแรกส่วนที่เหลือของโลกจะเป็นอันดับสอง
The Tin Man

ความคลุมเครือที่ทำให้คุณไม่สามารถตรวจสอบวันที่ได้จะทำเช่นนั้นในขอบเขตที่ป้องกันไม่ให้คุณแยกวิเคราะห์วันที่ แต่คุณได้พยายามอย่างดีในการแยกวิเคราะห์ด้วยข้อมูลที่ทราบ จะเป็นการดีที่จะใช้สิ่งที่คุณต้องพยายามตรวจสอบให้ดีที่สุดเท่าที่จะทำได้ คุณคิดวิธีทำโดยไม่ใช้ข้อยกเว้นที่ Date.parse และ Date.strptime สร้างได้หรือไม่?
cesoid

วันที่ที่แยกวิเคราะห์ในรูปแบบ "mm / dd / yyyy" หรือ "dd / mm / yyyy" จำเป็นต้องทราบล่วงหน้าเกี่ยวกับรูปแบบของวันที่ หากไม่มีเราก็ไม่สามารถระบุได้ว่าเป็นแบบใดเว้นแต่เราจะมีการสุ่มตัวอย่างจากแหล่งที่มา / ผู้ใช้รายหนึ่งจากนั้นแยกวิเคราะห์โดยใช้ทั้งสองรูปแบบจากนั้นดูว่าแบบฟอร์มใดสร้างข้อยกเว้นมากที่สุดและใช้แบบอื่น สิ่งนี้จะแบ่งออกเมื่อแยกวิเคราะห์วันที่จากแหล่งที่มาหลายแห่งโดยเฉพาะอย่างยิ่งเมื่อเป็นวันที่ทั่วโลกหรือป้อนด้วยมือ วิธีเดียวที่ถูกต้องในการจัดการกับวันที่คือไม่อนุญาตให้ป้อนด้วยมือบังคับให้ผู้ใช้ใช้ตัวเลือกวันที่หรืออนุญาตเฉพาะรูปแบบวันที่ ISO ที่ไม่คลุมเครือ
คนดีบุก

26

Date.valid_date? *date_string.split('-').reverse.map(&:to_i)


ยอดเยี่ยมขอบคุณ อันนี้ดูเหมือนจะมีอย่างน้อยใน 1.8, 2.0 และ 2.1 โปรดทราบว่าคุณต้องทำrequire "date"ก่อนมิฉะนั้นคุณจะได้รับ "วิธีการที่ไม่ได้กำหนด"
Henrik N

2
วิธีนี้สามารถเพิ่มข้อยกเว้น 'ArgumentError: จำนวนอาร์กิวเมนต์ไม่ถูกต้อง' แทนที่จะส่งคืนเท็จ ตัวอย่างเช่นหากสตริงของคุณมีเครื่องหมายทับแทนเครื่องหมายขีดกลาง
vincentp

2
บางทีเราอาจทำบางอย่างเช่นนี้เพื่อจัดการวันที่ที่มีรูปแบบไม่ถูกต้อง:Date.valid_date? *Array.new(3).zip(date_string.split('-')).transpose.last.reverse.map(&:to_i)
vincentp

9

ฉันต้องการขยายDateชั้นเรียน

class Date
  def self.parsable?(string)
    begin
      parse(string)
      true
    rescue ArgumentError
      false
    end
  end
end

ตัวอย่าง

Date.parsable?("10-10-2010")
# => true
Date.parse("10-10-2010")
# => Sun, 10 Oct 2010
Date.parsable?("1")
# => false
Date.parse("1")
# ArgumentError: invalid date from (pry):106:in `parse'

5

อีกวิธีในการตรวจสอบวันที่:

date_hash = Date._parse(date.to_s)
Date.valid_date?(date_hash[:year].to_i,
                 date_hash[:mon].to_i,
                 date_hash[:mday].to_i)

3

ลอง regex สำหรับวันที่ทั้งหมด:

/(\d{1,2}[-\/]\d{1,2}[-\/]\d{4})|(\d{4}[-\/]\d{1,2}[-\/]\d{1,2})/.match("31-02-2010")

สำหรับเฉพาะรูปแบบของคุณที่มีเลขศูนย์นำหน้าปีที่แล้วและขีดกลาง:

/(\d{2}-\d{2}-\d{4})/.match("31-02-2010")

[- /] หมายถึงอย่างใดอย่างหนึ่ง - หรือ /, เครื่องหมายทับไปข้างหน้าจะต้องหลีกหนี คุณสามารถทดสอบได้ที่http://gskinner.com/RegExr/

เพิ่มบรรทัดต่อไปนี้ซึ่งทั้งหมดจะถูกไฮไลต์หากคุณใช้ regex แรกโดยไม่ต้องใช้ / บรรทัดแรกและตัวสุดท้าย (ใช้สำหรับรหัสทับทิม)

2004-02-01
2004/02/01
01-02-2004
1-2-2004
2004-2-1

2
นิพจน์ทั่วไปนี้จะจับคู่เฉพาะรูปแบบคงที่บางรูปแบบโดยไม่สนใจความหมายของวันที่ ผู้จับคู่ของคุณอนุญาตให้วันที่32-13-2010ที่ผิดพลาด
yagooar

2
ดีพอหากคุณต้องการค่าประมาณคร่าวๆว่าสตริงที่กำหนดเองใด ๆ สามารถแยกวิเคราะห์เป็นวันที่ได้
Neil Goodman

โดยที่ "ค่าประมาณคร่าวๆ" หมายถึงค่าที่เหมือน'00/00/0000'และ'99-99/9999'เป็นตัวเลือกที่เป็นไปได้
The Tin Man

1
ตัวอย่างที่ยอดเยี่ยมเมื่อ regex ที่กำหนดเองเป็น BAD IDEA!
ทอมลอร์ด

3

วิธีแก้ปัญหาที่เข้มงวดขึ้น

ตรวจสอบความถูกต้องของวันที่ได้ง่ายขึ้นหากคุณระบุรูปแบบวันที่ที่คุณต้องการ อย่างไรก็ตามถึงอย่างนั้น Ruby ก็ทนได้สำหรับกรณีการใช้งานของฉัน:

Date.parse("Tue, 2017-01-17", "%a, %Y-%m-%d") # works
Date.parse("Wed, 2017-01-17", "%a, %Y-%m-%d") # works - !?

เห็นได้ชัดว่าอย่างน้อยหนึ่งในสตริงเหล่านี้ระบุวันทำงานผิดพลาด แต่ Ruby กลับเพิกเฉยอย่างมีความสุข

นี่เป็นวิธีการที่ไม่ มันจะตรวจสอบว่าdate.strftime(format)แปลงกลับไปที่สายป้อนเดียวกันว่ามันแยกวิเคราะห์ด้วยตามDate.strptimeformat

module StrictDateParsing
  # If given "Tue, 2017-01-17" and "%a, %Y-%m-%d", will return the parsed date.
  # If given "Wed, 2017-01-17" and "%a, %Y-%m-%d", will error because that's not
  # a Wednesday.
  def self.parse(input_string, format)
    date = Date.strptime(input_string, format)
    confirmation = date.strftime(format)
    if confirmation == input_string
      date
    else
      fail InvalidDate.new(
        "'#{input_string}' parsed as '#{format}' is inconsistent (eg, weekday doesn't match date)"
      )
    end
  end

  InvalidDate = Class.new(RuntimeError)
end

นั่นคือสิ่งที่ฉันกำลังมองหา ขอบคุณ!
Lucas Caton

สิ่งนี้ล้มเหลวสำหรับกรณีเช่น "2019-1-1" ซึ่งเป็นวันที่ที่ถูกต้อง แต่ strftime ทำให้ 2019-01-01
saGii

@saGii ที่ไม่ตรงกับรูปแบบที่ระบุ (iso8601 - ดูDate.today().iso8601); %mไม่มีเบาะเป็นศูนย์ หากคุณต้องการบางสิ่งที่แตกต่างไปที่ruby-doc.org/stdlib-2.4.0/libdoc/date/rdoc/…
Nathan Long

2

โพสต์สิ่งนี้เนื่องจากอาจมีคนนำไปใช้ในภายหลัง ไม่มีเงื่อนงำว่านี่เป็นวิธีที่ "ดี" ที่จะทำหรือไม่ แต่ได้ผลสำหรับฉันและสามารถขยายได้

class String

  def is_date?
  temp = self.gsub(/[-.\/]/, '')
  ['%m%d%Y','%m%d%y','%M%D%Y','%M%D%y'].each do |f|
  begin
    return true if Date.strptime(temp, f)
      rescue
       #do nothing
    end
  end

  return false
 end
end

Add-on สำหรับคลาส String ช่วยให้คุณระบุรายการตัวคั่นในบรรทัดที่ 4 จากนั้นรายการรูปแบบที่ถูกต้องในบรรทัดที่ 5 ไม่ใช่วิทยาศาสตร์จรวด แต่ช่วยให้ขยายได้ง่ายมากและให้คุณตรวจสอบสตริงได้ดังนี้:

"test".is_date?
"10-12-2010".is_date?
params[:some_field].is_date?
etc.

0
require 'date'
#new_date and old_date should be String
# Note we need a ()
def time_between(new_date, old_date)
  new_date = (Date.parse new_date rescue nil)
  old_date = (Date.parse old_date rescue nil)
  return nil if new_date.nil? || old_date.nil?
  (new_date - old_date).to_i
end

puts time_between(1,2).nil?
#=> true
puts time_between(Time.now.to_s,Time.now.to_s).nil?
#=> false


0

Date.parse ไม่ได้ยกข้อยกเว้นสำหรับตัวอย่างนี้:

Date.parse("12!12*2012")
=> Thu, 12 Apr 2018

Date.parse("12!12&2012")
=> Thu, 12 Apr 2018

ฉันชอบวิธีนี้:

Date.parse("12!12*2012".gsub(/[^\d,\.,\-]/, ''))
=> ArgumentError: invalid date

Date.parse("12-12-2012".gsub(/[^\d,\.,\-]/, ''))
=> Wed, 12 Dec 2012

Date.parse("12.12.2012".gsub(/[^\d,\.,\-]/, ''))
=> Wed, 12 Dec 2012

-1

วิธี:

require 'date'
def is_date_valid?(d)
  Date.valid_date? *"#{Date.strptime(d,"%m/%d/%Y")}".split('-').map(&:to_i) rescue nil
end

การใช้งาน:

config[:dates].split(",").all? { |x| is_date_valid?(x)}

สิ่งนี้จะส่งคืนจริงหรือเท็จถ้า config [: วันที่] = "12/10 / 2012,05 / 09/1520"

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.