Created
February 15, 2013 17:37
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I like how you test. I do it exactly the same:
describe
+it
+assert_*
. Nice little lib!