Cách kiểm tra xem một chuỗi có phải là ngày hợp lệ hay không


114

Tôi có một chuỗi: "31-02-2010"và muốn kiểm tra xem đó có phải là ngày hợp lệ hay không. Cách tốt nhất để làm điều đó là gì?

Tôi cần một phương thức trả về true nếu chuỗi là ngày hợp lệ và false nếu không phải.


2
tại sao không chỉ tạo date_time thả xuống thay vì lấy một chuỗi mà bạn phải xác thực?
bị ăn mòn

yêu cầu của khách hàng. tôi đã đề xuất nó nhưng không thể làm điều đó :)
Salil

Theo nguyên tắc chung, bạn không phải thực hiện xác thực đầu vào trên giao diện người dùng của một ứng dụng?
Seanny123

Nhiều câu trả lời tốt hơn ở đây - đây là cách thực hiện. stackoverflow.com/questions/597328/…
n13

3
Luôn xác thực trên phần phụ trợ, cho dù giao diện người dùng của bạn có tốt đến đâu, đừng tin tưởng vào nó!
SparK

Câu trả lời:


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

27
Nó không phải là một tính năng của lớp Ngày, nó là tính năng xử lý ngoại lệ.
Pier-Olivier Thibault

3
Date.parse ('2012-08-13 == 00:00:00') # => Thứ Hai, ngày 13 tháng 8 năm 2012
Bob

13
Sử dụng giải pháp "bắt tất cả" nên được coi là một hình thức phản đối. Nó có thể ẩn đi các lỗi khác mà chúng tôi không mong đợi và làm cho việc gỡ lỗi mã trở nên cực kỳ khó khăn.
yagooar

8
Vậy ... nếu nó thực sự là anti-pattern, bạn sẽ trả lời câu hỏi như thế nào?
Matthew Brown

11
Xin lỗi, đây là một câu trả lời sai. Nó không kiểm tra xem một chuỗi có phải là ngày hợp lệ hay không, nó chỉ kiểm tra Date.parse có thể phân tích cú pháp chuỗi. Date.parse dường như rất dễ bị tha thứ khi nói đến ngày tháng, ví dụ: nó sẽ phân tích cú pháp "FOOBAR_09_2010" là ngày 2012-09-09.
n13

54

Đây là một lớp lót đơn giản:

DateTime.parse date rescue nil

Tôi có lẽ không khuyên bạn nên làm điều này chính xác trong mọi tình huống trong cuộc sống thực vì bạn buộc người gọi kiểm tra nil, ví dụ: đặc biệt là khi định dạng. Nếu bạn trả về lỗi ngày | mặc định thì nó có thể thân thiện hơn.


8
Số giải cứu DateTime.parse "123". Điều này trả lại một ngày thực .. Ngày 3 tháng 5 năm 2017
baash05

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

1
Không rescuekhuyến khích sử dụng nội tuyến .
sương mù

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

Điều này khá đơn giản và tốt để thực thi định dạng xx-xx-YYYY
n13

Nếu bạn muốn thực thi một tùy chọn chuỗi ngày có định dạng đơn nghiêm ngặt, thì đây là tùy chọn tốt nhất vì nó tránh được Ngoại lệ và mang tính xác định. Date.valid_date? là phương pháp để sử dụng ít nhất cho Ruby 2. ruby-doc.org/stdlib-2.0.0/libdoc/date/rdoc/...
Ashley Raiteri

4
Phiên bản nào của Ruby hỗ trợ is_valid?? Đã thử 1.8, 2.0 và 2.1. Không ai trong số họ dường như có cái đó. Tất cả dường như có valid_date?, mặc dù.
Henrik N

29

Phân tích cú pháp ngày có thể gặp phải một số gotcha, đặc biệt khi chúng ở định dạng MM / DD / YYYY hoặc DD / MM / YYYY, chẳng hạn như các ngày ngắn được sử dụng ở Hoa Kỳ hoặc Châu Âu.

Date#parse cố gắng tìm ra cái nào để sử dụng, nhưng có nhiều ngày trong một tháng trong năm khi sự không rõ ràng giữa các định dạng có thể gây ra sự cố phân tích cú pháp.

Tôi khuyên bạn nên tìm hiểu LOCALE của người dùng là gì, sau đó, dựa vào đó, bạn sẽ biết cách phân tích cú pháp thông minh bằng cách sử dụng Date.strptime . Cách tốt nhất để tìm vị trí của người dùng là hỏi họ trong quá trình đăng ký, sau đó cung cấp cài đặt theo sở thích của họ để thay đổi cài đặt đó. Giả sử bạn có thể khai thác nó bằng một số kinh nghiệm thông minh và không làm phiền người dùng về thông tin đó, thì rất dễ bị thất bại, vì vậy hãy hỏi.

Đây là một thử nghiệm sử dụng Date.parse. Tôi đang ở Mỹ:

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

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

Đầu tiên là định dạng chính xác cho Hoa Kỳ: mm / dd / yyyy, nhưng Date không thích nó. Điều thứ hai đúng đối với Châu Âu, nhưng nếu khách hàng của bạn chủ yếu sống ở Hoa Kỳ, bạn sẽ nhận được rất nhiều ngày được phân tích cú pháp sai.

Ruby Date.strptimeđược sử dụng như:

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

LOCALE của bạn có vẻ đã tắt. Tôi nhận được điều này >> Date.parse ('01 / 31/2001 ') => Thứ 4, ngày 31 tháng 1 năm 2001 >>?> Date.parse ('31 / 01/2001') ArgumentError: ngày không hợp lệ
hoyhoy

Không chắc liệu tôi có đang bị nhầm lẫn ở đây hay không, nhưng điều này dường như chỉ giải thích cách phân tích cú pháp một ngày chứ không phải cách xác thực nó. Câu trả lời được chấp nhận sử dụng ngoại lệ ArgumentError, nhưng điều đó có vẻ không phải là một ý tưởng hay, vì những lý do đã được nêu trong một nhận xét ở đó.
cesoid

Có một tình huống đã biết mà bạn không thể xác thực ngày tháng. Đó là nơi giá trị ngày và tháng không rõ ràng và bạn phải biết định dạng của chuỗi ngày đến. Và, đó là những gì câu trả lời đang nói. Nếu nó giống như thế nào 01/01/2014, đó là tháng và ngày nào? Ở Mỹ, tháng sẽ đứng đầu, các nước còn lại trên thế giới sẽ đứng thứ hai.
the Tin Man

Sự mơ hồ ngăn bạn xác thực ngày chỉ làm như vậy trong phạm vi nó cũng ngăn bạn phân tích cú pháp ngày đó, nhưng bạn đã rất cố gắng phân tích cú pháp nó với thông tin đã biết. Sẽ rất tốt nếu bạn sử dụng một số trong những thứ bạn phải cố gắng xác nhận tốt nhất có thể. Bạn có thể nghĩ cách làm điều đó mà không sử dụng các ngoại lệ mà Date.parse và Date.strptime tạo ra không?
cesoid

Phân tích cú pháp ngày ở định dạng "mm / dd / yyyy" hoặc "dd / mm / yyyy" CẦN biết trước về định dạng của ngày. Nếu không có điều đó, chúng tôi không thể xác định đó là ngoại lệ trừ khi chúng tôi lấy mẫu từ một nguồn / người dùng, sau đó phân tích cú pháp bằng cả hai biểu mẫu và sau đó xem biểu mẫu nào tạo ra nhiều ngoại lệ nhất và sử dụng biểu mẫu kia. Điều này bị phá vỡ khi phân tích cú pháp ngày từ nhiều nguồn, đặc biệt khi chúng toàn cầu hoặc chúng được nhập bằng tay. Cách chính xác duy nhất để xử lý ngày tháng là không cho phép nhập bằng tay, buộc người dùng sử dụng công cụ chọn ngày hoặc chỉ cho phép các định dạng ngày ISO rõ ràng.
the Tin Man

26

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


Cảm ơn vô cùng. Cái này dường như có sẵn ít nhất trong 1.8, 2.0 và 2.1. Lưu ý rằng bạn cần phải làm require "date"trước nếu không bạn sẽ nhận được "phương thức không xác định".
Henrik N

2
Phương thức này có thể đưa ra một ngoại lệ 'ArgumentError: sai số đối số' thay vì trả về false. Ví dụ: nếu chuỗi của bạn chứa dấu gạch chéo thay vì dấu gạch ngang
vincentp

2
Có lẽ chúng ta có thể làm một cái gì đó như thế này, để xử lý xấu ngày định dạng:Date.valid_date? *Array.new(3).zip(date_string.split('-')).transpose.last.reverse.map(&:to_i)
vincentp

9

Tôi muốn mở rộng Datelớp học.

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

thí dụ

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

Một cách khác để xác thực ngày:

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

Hãy thử regex cho tất cả các ngày:

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

Chỉ dành cho định dạng của bạn với các số 0 ở đầu, năm trước và dấu gạch ngang:

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

[- /] có nghĩa là - hoặc /, dấu gạch chéo phía trước phải được thoát ra. Bạn có thể kiểm tra điều này trên http://gskinner.com/RegExr/

thêm các dòng sau, tất cả chúng sẽ được đánh dấu nếu bạn sử dụng regex đầu tiên, không có đầu tiên và cuối cùng / (chúng được sử dụng trong mã ruby).

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

2
Cụm từ thông dụng này chỉ phù hợp với một số định dạng cố định mà không cần quan tâm đến ngữ nghĩa của ngày tháng. Đối sánh của bạn cho phép các ngày như 32-13-2010sai.
yagooar

2
Đủ tốt nếu bạn muốn có một ước tính sơ bộ nếu bất kỳ chuỗi tùy ý nào có thể được phân tích cú pháp thành ngày.
Neil Goodman,

Trong đó "ước tính thô" có nghĩa là các giá trị như '00/00/0000''99-99/9999'có thể là các ứng cử viên.
the Tin Man

1
Một ví dụ tuyệt vời về khi regex tùy chỉnh là một Ý TƯỞNG XẤU!
Tom Lord

3

Một giải pháp nghiêm ngặt hơn

Sẽ dễ dàng hơn để xác minh tính đúng đắn của một ngày nếu bạn chỉ định định dạng ngày mà bạn mong đợi. Tuy nhiên, ngay cả khi đó, Ruby vẫn hơi quá dung tục đối với trường hợp sử dụng của tôi:

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

Rõ ràng, ít nhất một trong những chuỗi này chỉ định sai ngày trong tuần, nhưng Ruby vui vẻ bỏ qua điều đó.

Đây là một phương pháp không; nó xác thực sẽ date.strftime(format)chuyển đổi trở lại cùng một chuỗi đầu vào mà nó đã phân tích cú pháp Date.strptimetheo đó format.

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

Đó chính xác là những gì tôi đang tìm kiếm. Cảm ơn bạn!
Lucas Caton

điều này không thành công đối với các trường hợp như "2019-1-1" là ngày hợp lệ nhưng strftime làm cho nó là 2019-01-01
saGii

@saGii không phù hợp với định dạng được chỉ định (iso8601 - xem Date.today().iso8601); %mkhông có đệm. Nếu bạn muốn điều gì đó khác biệt, hãy xem ruby-doc.org/stdlib-2.4.0/libdoc/date/rdoc/…
Nathan Long

2

Đăng cái này vì nó có thể được sử dụng cho ai đó sau này. Không có manh mối nào nếu đây là một cách "tốt" để làm điều đó hay không, nhưng nó hiệu quả với tôi và có thể mở rộng.

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

Tiện ích bổ sung này cho lớp String cho phép bạn chỉ định danh sách các dấu phân cách ở dòng 4 và sau đó là danh sách các định dạng hợp lệ của bạn ở dòng 5. Không phải là khoa học tên lửa, nhưng làm cho nó thực sự dễ dàng mở rộng và cho phép bạn chỉ cần kiểm tra một chuỗi như vậy:

"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

Bạn có thể thử cách sau, đó là cách đơn giản:

"31-02-2010".try(:to_date)

Nhưng bạn cần phải xử lý ngoại lệ.


0

Date.parse không nêu ra ngoại lệ cho các ví dụ này:

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

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

Tôi thích giải pháp này hơn:

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

Phương pháp:

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

Sử dụng:

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

Điều này trả về true hoặc false nếu config [: date] = "12/10 / 2012,05 / 09/1520"

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.