ArgumentError: invalid strptime format - `%m/%d/%y' work around - ruby

I am in the process of working with an sftp import bug in which I'm trying to flag any dates that are imported that are incorrect. There are two types of dates that could be off. The first is when the year is in the future, or way in the past; the second is when the actual months and days are too high. (Example, 13/20/1995, or 11/35/2000)
I'm using strptime and for dates that are off, flagging them and displaying them as a specific message. The problem I'm running into is that with the strptime format that I'm using, the errors happen right before I sub in the error message.
table_birth_dates = self.class.connection.execute("SELECT birth_date FROM #{temp_table_name}").values.flatten
table_birth_dates.map! do |date|
birth_date = Date.strptime(date, '%m/%d/%Y')
if birth_date.nil?
month_day_error_message = 'Invalid Month/Day'
elsif birth_date > Time.zone.today
future_error_message = 'Year in Future'
elsif birth_date.year < 1900
past_error_message = 'Year too old'
else
birth_date
end
end
The error is happening at the birth_date = Date.strptime(date, '%m/%d/%Y')
For a date such as 10/3/1891, it displays them as Sat, 03 Oct 1891.
However, for the messed up dates such as 33/33/2000 it shows me an error (which makes sense) however I was hoping to fix this error in my conditional.
Would anyone know what I could do?

If you want to use strptime your only option really is to rescue the error:
begin
birth_date = Date.strptime(date, '%m/%d/%Y')
rescue ArgumentError => ex
raise ex unless ex.message == 'invalid date'
# handle invalid date
end
You could set date to, e.g., :invalid there and then have date == :invalid in your conditional if you want to keep all the logic there instead of in the rescue itself.

Related

Date validation for invalid years

I am trying to do what feels like something quite basic, but struggling. I want to be able to determine if a date is valid, if it is, parse it and output in a formatted string, else return 'bad date'. This is where I have got so far;
require 'date'
date_last_printed = current_item.getProperties['Last Printed']
begin
d = DateTime.parse(date_last_printed.to_s)
if Date.valid_date?(d.year, d.month, d.day)
d.strftime("%d/%m/%Y %H:%M:%S")
else
'bad date'
end
rescue Exception => ex
ex.message
end
However 01/01/1601 is returning as a valid date.
How can I adjust my snippet to return only valid dates (on or after the start of Unix epoch time).
you could perform epoch conversion inside the begin statement.
begin
raise if (t=Time.parse(date_last_printed)).to_i < 0
t.strftime("%d/%m/%Y %H:%M:%S")
rescue
'bad date'
end

How to check whether correct date format has been provided

I'm currently working through the Well-Grounded Rubyist and have a question about an exercise that is asking to check whether a date provided is in the format 'yyyy-mm-dd' as opposed to 'yy-mm-dd'.
We have a class Ticket and should create a date= method that checks whether the date provided is in the above mentioned format.
Is .strftime correct to use here?
In the end the method should return the date in the correct format and provide an error message for dates in the wrong format, like so:
ticket = Ticket.new
ticket.date = "2013-11-12"
=> "2013-11-12"
ticket.date = "13-11-12"
=> "Please submit the date in the format 'yyyy-mm-dd'."
Could someone indicate how I could perform these checks on dates?
Date::xmlschema is strict about this specific format (try this in IRB):
require 'date'
Date.xmlschema("2013-11-12") #<Date: 2013-11-12 ((2456609j,0s,0n),+0s,2299161j)>
#invalid month number:
Date.xmlschema("2013-13-12") #<ArgumentError: invalid date>
# 2 digit year:
Date.xmlschema("13-11-12") #<ArgumentError: invalid date>
# no leap year:
Date.xmlschema("2013-02-29") #<ArgumentError: invalid date>
You can throw the error to the user by using begin..rescue
require 'date'
begin
Date.parse("31-02-2010")
rescue => e
p "#{e}" #print your own custom messages and return accordingly
end
Also write
rescue ArgumentError
It will throw By default error
ArgumentError (invalid date)

Creating an error message for an invalid input

I have the following code that returns the # of days in any given month, which works fine unless someone types in something that isn't a date, or they format the date wrong. To remedy this I want to send out an error message for an invalid input, but I don't know how. So how do I create an error message for this small app?
#type in the month and year you want like so ---> "Feb 2034"
require 'date'
input = gets.chomp
inputArray = input.split(" ").to_a
textMonth = inputArray[0]
textYear = inputArray[1]
startOfMonth = Date.strptime(input, "%b %Y")
nextMonth = startOfMonth.next_month
endOfMonth = nextMonth - 1
daysInMonth = (endOfMonth - startOfMonth + 1).to_i
puts "#{textMonth} of year #{textYear} has #{daysInMonth} days!"
Probably the best way to do this is putting your input in a while loop, prompting for a new answer every time the input isn't what you expected it to be.
To check the input you should use a Regexp. Here's an explanation
how to write a regexp to match a date.
For Creating a Custom Error refer below code:
Here I create and raise InvalidDateError for the wrong date input.
#type in the month and year you want like so ---> "Feb 2034"
class InvalidDateError < StandardError
end
require 'date'
require 'pry-byebug'
input = gets.chomp
inputArray = input.split(" ").to_a
textMonth = inputArray[0]
textYear = inputArray[1]
begin
startOfMonth = Date.strptime(input, "%b %Y")
nextMonth = startOfMonth.next_month
endOfMonth = nextMonth - 1
daysInMonth = (endOfMonth - startOfMonth + 1).to_i
puts "#{textMonth} of year #{textYear} has #{daysInMonth} days!"
rescue StandardError=> e
raise InvalidDateError.new("Invalid Date : #{input}")
end
If you don't want to raise an error and only want to show error message then replace raise InvalidDateError.new("Invalid Date : #{input}")
with puts "Invalid Date : #{input}"
As suggested by Viktor, and stolen :) from crantok
require 'date'
date_valid = false
while !date_valid
puts 'Insert date as yyyy-mm-dd:'
input_date = gets.chomp
begin
parsed_date = Date.parse(input_date)
date_valid = true
rescue ArgumentError
puts 'format error'
end
end
month = parsed_date.month
year = parsed_date.year
days_in_month = Date.new(year, month, -1).day
puts "In #{year} month #{month} has #{days_in_month} days"

How to check that date is correct when using date_select

I search a solution for validate user birth date, for example if user try to submit only day and month but he doesn't select year, date will be invalid, but rails don't show any exception or error if year is not set for example but he skip date, and date will not be updated
I found some old answers about the subject, and it seem not to be clear for me. (note i use date_select helper in my view)
I did already a search about date validation but I'm looking for an effective solution that is up to date
i will be thankful for any suggestion. thank you
DateTime.parse(..) will raise an error if the date is not valid:
[1] pry(main)> DateTime.parse("Feb 31, 2013")
ArgumentError: invalid date
from (pry):1:in `parse'
[2] pry(main)> e = begin; DateTime.parse("Feb 31, 2013"); rescue => e; e; end
=> #<ArgumentError: invalid date>
So just capture the error to check for a valid date:
def valid_date?(string)
begin
DateTime.parse(string)
return true
rescue => e
return false
end
end

Ruby Date range and matching

I'm writing a small log-sniffer program for my work environment that searches for a few key words in the logs and let's the user know they have some things to look at. That part is pretty simple, but one of the features I'm trying to implement is the option for the user to choose how far back form today they would like to go, so they're not getting errors from months ago that no longer matter.
I've got the date, and I have the gap from now and a user-specified range. I'm just not sure how to get the range and matching working. Here is my code:
require 'date'
### Get the logfile path from the user
p "What is the path to your log file?"
logfile = gets.chomp
### Get number of days user would like to go back
p "How many days back from today would you like to look?"
num_days = gets.chomp
############################################################################################
### Define Log class. Accept argumnet for path of the file. Called by foo = Log.new
############################################################################################
class Log
def scan_log(file_name, days)
error_exists = false
verify = false
apn = false
strict = false
days = days.to_i
# time = Time.now.strftime("%Y-%m-%d")
now = Date.today
days_ago = (now - days)
p now
p days_ago
File.open(file_name, "r") do |file_handle|
file_handle.each_line do |line|
if line =~ /Unable to verify signature/
verify = true
error_exists = true
end
if line =~ /gateway.push.apple.com/
apn = true
error_exists = true
end
if line =~ /Data truncation/
strict = true
error_exists = true
end
end
end
p "Verify signature error found" if verify
p "You have an APNS error" if apn
p "You have strict mode enabled" if strict
end
end
l = Log.new
l.scan_log(logfile, num_days)
My thought is that a loop under file_handle.each_line do... would work. I would include the existing if statements in the loop, and the loop would match the date range set by the user to the dates in the logs. The format in the log is:
2013-04-17 15:10:42, 767
I don't care about the timestamp, just the datestamp.
Thanks if you can help.
one robust way to parse a free-form date would be:
> Time.parse(" 2013-04-17 13:15:24 -0700 ").strftime("%Y-%m-%d")
=> "2013-04-17"
> Time.parse("Wed Apr 17 13:15:09 PDT 2013 ").strftime("%Y-%m-%d")
=> "2013-04-17"
assuming the user specifies the start_date in "%Y-%m-%d" format and the time range, you could do this:
start_date = Time.parse( user_provided_date ) # e.g. "2013-04-10"
log_date = Time.parse( line )
do_something if (start_date - log_date) < number_of_seconds # or whatever time range
If you just want to show all logs which are newer than a given time-range, then you could do this:
log_date = Time.parse( line )
do_something if (Time.now - log_date) < number_of_seconds # or whatever time range
To get pretty time ranges in Rails style, like 1.day or 12.hours , you can include ActiveSupport::Duration
You can construct a Range from dates (like the question title suggests) and use it's include? method.
require 'date'
puts "How many days back from today would you like to look?"
days_back = gets.to_i #input: 3
logline_date = "2013-04-17 15:10:42, 767"
end_date = Date.today
start_date = end_date - days_back
selected_range = start_date..end_date
p selected_range.include?( Date.parse( logline_date )) #true (today for me, that is)

Resources