Skip to content

Instantly share code, notes, and snippets.

@madrobby
Created February 15, 2013 17:37
Show Gist options
  • Save madrobby/4961970 to your computer and use it in GitHub Desktop.
Save madrobby/4961970 to your computer and use it in GitHub Desktop.
`Date.parse` with some extra leeway to handle user input errors. Handles things like `2013-02-31` (parses it as `2013-02-29`) and handles things in general like users would expect.
module Freckle
module Date
class << self
def parse(string)
raw = string.to_s.strip
return nil if raw.empty?
begin
# reverse order if we encounter "European" formatting
raw = raw.split('.').reverse.join('-') if raw =~ /\./
# parse date components
year, month, day = raw.split('-')
# Integer() throws an exception if it can't parse something
# (#to_i doesn't do that). Need to strip leading zeros.
year = Integer(year.sub!(/^0*(.)/){$1})
# we live in the future
year += 2000 if year < 100
# assume day/month == 1 if not given
day = day.nil?? 1 : Integer(day.sub!(/^0*(.)/){$1})
month = month.nil?? 1 : Integer(month.sub!(/^0*(.)/){$1})
# cap to valid month
month = 1 if month<1
month = 12 if month>12
# cap to valid day for the given year/month combination
day = 1 if day < 1
if month == 2 and ::Date.leap?(year)
day = 29 if day > 29
elsif month == 2
day = 28 if day > 28
end
day = 30 if day > 30 and [4,6,9,11].include?(month)
day = 31 if day > 31 and [1,3,5,7,8,10,12].include?(month)
::Date.civil(year, month, day)
rescue ArgumentError
# for when we can't parse a date component with Integer()
return nil
end
end
end
end
end
require File.expand_path('../../test_helper', __FILE__)
describe Freckle::Date do
it "should parse YYYY-MM-DD" do
assert_equal Time.utc(2011,1,1).to_date, Freckle::Date.parse('2011-01-01')
assert_equal Time.utc(2012,7,15).to_date, Freckle::Date.parse('2012-07-15')
assert_equal Time.utc(2012,12,31).to_date, Freckle::Date.parse('2012-12-31')
end
it "should parse YY-MM-DD" do
assert_equal Time.utc(2011,1,15).to_date, Freckle::Date.parse('11-01-15')
end
it "should parse YY-M-D" do
assert_equal Time.utc(2011,1,15).to_date, Freckle::Date.parse('11-1-15')
end
it "should parse DD.MM.YYYY" do
assert_equal Time.utc(2010,12,31).to_date, Freckle::Date.parse('31.12.2010')
end
it "should parse DD.MM.YYYY" do
assert_equal Time.utc(2010,12,31).to_date, Freckle::Date.parse('31.12.10')
end
it "should assume YYYY-MM means the first day of the month" do
assert_equal Time.utc(2011,5,1).to_date, Freckle::Date.parse('2011-05')
end
it "should assume YYYY means the first day of the year" do
assert_equal Time.utc(2011,1,1).to_date, Freckle::Date.parse('2011')
end
it "should return nil for empty or invalid dates" do
assert_equal nil, Freckle::Date.parse(nil)
assert_equal nil, Freckle::Date.parse('')
assert_equal nil, Freckle::Date.parse(' xxxx ')
end
it "should bracket months" do
assert_equal Time.utc(2011,1,1).to_date, Freckle::Date.parse('2011-00-01')
assert_equal Time.utc(2011,12,1).to_date, Freckle::Date.parse('2011-99-01')
end
it "should bracket days to the last possible date of the given month" do
assert_equal Time.utc(2011,1,1).to_date, Freckle::Date.parse('2011-00-00')
assert_equal Time.utc(2011,1,31).to_date, Freckle::Date.parse('2011-00-99')
assert_equal Time.utc(2011,1,31).to_date, Freckle::Date.parse('2011-01-32')
assert_equal Time.utc(2011,2,28).to_date, Freckle::Date.parse('2011-02-31')
assert_equal Time.utc(2011,3,31).to_date, Freckle::Date.parse('2011-03-32')
assert_equal Time.utc(2011,4,30).to_date, Freckle::Date.parse('2011-04-31')
assert_equal Time.utc(2011,5,31).to_date, Freckle::Date.parse('2011-05-55')
assert_equal Time.utc(2011,6,30).to_date, Freckle::Date.parse('2011-06-31')
assert_equal Time.utc(2011,7,31).to_date, Freckle::Date.parse('2011-07-44')
assert_equal Time.utc(2011,8,31).to_date, Freckle::Date.parse('2011-08-31')
assert_equal Time.utc(2011,9,30).to_date, Freckle::Date.parse('2011-09-32')
assert_equal Time.utc(2011,10,31).to_date, Freckle::Date.parse('2011-10-31')
assert_equal Time.utc(2011,11,30).to_date, Freckle::Date.parse('2011-11-32')
assert_equal Time.utc(2011,12,31).to_date, Freckle::Date.parse('2011-12-99')
# leap year february
assert_equal Time.utc(2012,2,29).to_date, Freckle::Date.parse('2012-02-30')
assert_equal Time.utc(2012,2,29).to_date, Freckle::Date.parse('2012-02-31')
# check that we do leap years correctly, 1900 is not a leap year, but 2000 is
assert_equal Time.utc(1900,2,28).to_date, Freckle::Date.parse('1900-02-29')
assert_equal Time.utc(2000,2,29).to_date, Freckle::Date.parse('2000-02-29')
end
it "should strip whitespace" do
assert_equal Time.utc(2011,1,1).to_date, Freckle::Date.parse("\n 2011-01-01 ")
end
end
@rafbm
Copy link

rafbm commented Feb 15, 2013

I like how you test. I do it exactly the same: describe + it + assert_*. Nice little lib!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment