Mam ciąg: "31-02-2010"
i chcę sprawdzić, czy jest to ważna data. Jak najlepiej to zrobić?
Potrzebuję metody, która zwraca prawdę, jeśli ciąg jest prawidłową datą i fałsz, jeśli nie jest.
Mam ciąg: "31-02-2010"
i chcę sprawdzić, czy jest to ważna data. Jak najlepiej to zrobić?
Potrzebuję metody, która zwraca prawdę, jeśli ciąg jest prawidłową datą i fałsz, jeśli nie jest.
Odpowiedzi:
require 'date'
begin
Date.parse("31-02-2010")
rescue ArgumentError
# handle invalid date
end
Oto prosta jedna wkładka:
DateTime.parse date rescue nil
Prawdopodobnie nie polecałbym robienia tego dokładnie w każdej sytuacji w prawdziwym życiu, ponieważ zmuszasz dzwoniącego do sprawdzenia, czy nie ma zera, np. szczególnie podczas formatowania. Jeśli zwrócisz domyślną datę | błąd, może to być bardziej przyjazne.
Date.strptime(date, '%d/%m/%Y') rescue nil
rescue
jest odradzane.
d, m, y = date_string.split '-'
Date.valid_date? y.to_i, m.to_i, d.to_i
is_valid?
? Wypróbowano 1.8, 2.0 i 2.1. Żaden z nich nie wydaje się mieć tego. Wydaje się jednak valid_date?
, że wszyscy mają .
Parsowanie dat może napotkać pewne problemy, zwłaszcza gdy są one w formacie MM / DD / RRRR lub DD / MM / RRRR, na przykład krótkie daty używane w USA lub Europie.
Date#parse
próbuje dowiedzieć się, którego użyć, ale jest wiele dni w miesiącu w ciągu roku, kiedy niejednoznaczność między formatami może powodować problemy z analizą.
Radziłbym dowiedzieć się, jakie jest LOCALE użytkownika, a następnie na tej podstawie będziesz wiedział, jak inteligentnie analizować za pomocą Date.strptime
. Najlepszym sposobem, aby dowiedzieć się, gdzie znajduje się użytkownik, jest zapytanie go podczas rejestracji, a następnie podanie ustawienia w jego preferencjach, aby to zmienić. Zakładając, że możesz to wykopać za pomocą sprytnej heurystyki i nie zawracać głowy użytkownikowi tymi informacjami, jest podatne na niepowodzenie, więc po prostu zapytaj.
To jest test przy użyciu Date.parse
. Jestem w USA:
>> Date.parse('01/31/2001')
ArgumentError: invalid date
>> Date.parse('31/01/2001') #=> #<Date: 2001-01-31 (4903881/2,0,2299161)>
Pierwszy był prawidłowym formatem dla Stanów Zjednoczonych: mm / dd / rrrr, ale data nie podobała się. Drugi był poprawny dla Europy, ale jeśli Twoi klienci pochodzą głównie z USA, otrzymasz wiele źle przeanalizowanych dat.
Ruby Date.strptime
jest używany w następujący sposób:
>> Date.strptime('12/31/2001', '%m/%d/%Y') #=> #<Date: 2001-12-31 (4904549/2,0,2299161)>
01/01/2014
, to który miesiąc, a który dzień? W Stanach Zjednoczonych miesiąc byłby pierwszy, reszta świata byłby drugi.
Date.valid_date? *date_string.split('-').reverse.map(&:to_i)
require "date"
najpierw, albo otrzymasz „niezdefiniowaną metodę”.
Date.valid_date? *Array.new(3).zip(date_string.split('-')).transpose.last.reverse.map(&:to_i)
Chciałbym przedłużyć Date
klasę.
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'
Inny sposób weryfikacji daty:
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)
Wypróbuj wyrażenie regularne dla wszystkich dat:
/(\d{1,2}[-\/]\d{1,2}[-\/]\d{4})|(\d{4}[-\/]\d{1,2}[-\/]\d{1,2})/.match("31-02-2010")
Tylko dla twojego formatu z wiodącymi zerami, ostatnim rokiem i myślnikami:
/(\d{2}-\d{2}-\d{4})/.match("31-02-2010")
[- /] oznacza albo - albo /, ukośnik musi zostać zmieniony. Możesz to przetestować na http://gskinner.com/RegExr/
dodaj następujące wiersze, wszystkie zostaną podświetlone, jeśli użyjesz pierwszego wyrażenia regularnego, bez pierwszego i ostatniego / (są one używane w kodzie ruby).
2004-02-01
2004/02/01
01-02-2004
1-2-2004
2004-2-1
32-13-2010
błędne.
'00/00/0000'
i '99-99/9999'
są potencjalnymi kandydatami.
Łatwiej jest zweryfikować poprawność daty, jeśli określisz oczekiwany format daty. Jednak nawet wtedy Ruby jest trochę zbyt tolerancyjny dla mojego przypadku użycia:
Date.parse("Tue, 2017-01-17", "%a, %Y-%m-%d") # works
Date.parse("Wed, 2017-01-17", "%a, %Y-%m-%d") # works - !?
Oczywiście co najmniej jeden z tych ciągów określa zły dzień tygodnia, ale Ruby szczęśliwie to ignoruje.
Oto metoda, która tego nie robi; sprawdza, czy date.strftime(format)
konwertuje z powrotem na ten sam ciąg wejściowy, z którym był analizowany Date.strptime
zgodnie z 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
Date.today().iso8601
); %m
jest wypełniony zerami. Jeśli chcesz czegoś innego, zobacz ruby-doc.org/stdlib-2.4.0/libdoc/date/rdoc/ ...
Publikowanie tego, ponieważ może to być przydatne dla kogoś później. Nie mam pojęcia, czy jest to „dobry” sposób, aby to zrobić, czy nie, ale działa dla mnie i można go rozszerzyć.
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
Ten dodatek do klasy String pozwala określić listę ograniczników w linii 4, a następnie listę prawidłowych formatów w linii 5. Nie jest to fizyka jądrowa, ale bardzo ułatwia jej rozszerzenie i pozwala po prostu sprawdzić ciąg w następujący sposób:
"test".is_date?
"10-12-2010".is_date?
params[:some_field].is_date?
etc.
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
Możesz spróbować wykonać następujące czynności, co jest prostym sposobem:
"31-02-2010".try(:to_date)
Ale musisz obsłużyć wyjątek.
Date.parse nie zgłoszono wyjątku dla tych przykładów:
Date.parse("12!12*2012")
=> Thu, 12 Apr 2018
Date.parse("12!12&2012")
=> Thu, 12 Apr 2018
Wolę takie rozwiązanie:
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
Metoda:
require 'date'
def is_date_valid?(d)
Date.valid_date? *"#{Date.strptime(d,"%m/%d/%Y")}".split('-').map(&:to_i) rescue nil
end
Stosowanie:
config[:dates].split(",").all? { |x| is_date_valid?(x)}
Zwraca wartość true lub false, jeśli config [: dates] = "12/10 / 2012,05 / 09/1520"