Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Ruby 1.9 compatibility, expand tests

  • Loading branch information...
commit 335a548f6c497d61e6e11b7a6f92224c96a5d22f 1 parent 2510c3f
@djsun authored
Showing with 277 additions and 79 deletions.
  1. +51 −0 FORMATS.md
  2. +55 −0 NOTES.md
  3. +97 −30 lib/kronos.rb
  4. +74 −49 spec/parse_spec.rb
View
51 FORMATS.md
@@ -0,0 +1,51 @@
+# Options for Time.strftime / Time.strptime
+
+ %a - abbreviated weekday name according to the current locale
+ %A - full weekday name according to the current locale
+ %b - abbreviated month name according to the current locale
+ %B - full month name according to the current locale
+ %c - preferred date and time representation for the current locale
+ %C - century number (the year divided by 100 and truncated to an integer,
+ range 00 to 99)
+ %d - day of the month as a decimal number (range 01 to 31)
+ %D - same as %m/%d/%y
+ %e - day of the month as a decimal number, a single digit is preceded by
+ a space (range ' 1' to '31')
+ %g - like %G, but without the century.
+ %G - The 4-digit year corresponding to the ISO week number (see %V).
+ This has the same format and value as %Y, except that if the ISO week
+ number to the previous or next year, that year is used instead.
+ %h - same as %b
+ %H - hour as a decimal number using a 24-hour clock (range 00 to 23)
+ %I - hour as a decimal number using a 12-hour clock (range 01 to 12)
+ %j - day of the year as a decimal number (range 001 to 366)
+ %m - month as a decimal number (range 01 to 12)
+ %M - minute as a decimal number
+ %n - newline character
+ %p - either `am' or `pm' according to the given time value, or the
+ corresponding strings for the current locale
+ %r - time in a.m. and p.m. notation
+ %R - time in 24 hour notation
+ %S - second as a decimal number
+ %t - tab character
+ %T - current time, equal to %H:%M:%S
+ %u - weekday as a decimal number [1,7], with 1 representing Monday
+ %U - week number of the current year as a decimal number, starting with
+ the first Sunday as the first day of the first week
+ %V - The ISO 8601:1988 week number of the current year as a decimal
+ number, range 01 to 53, where week 1 is the first week that has at
+ least 4 days in the current year, and with Monday as the first day
+ of the week. (Use %G or %g for the year component that corresponds
+ to the week number for the specified timestamp.)
+ %W - week number of the current year as a decimal number, starting with
+ the first Monday as the first day of the first week
+ %w - day of the week as a decimal, Sunday being 0
+ %x - preferred date representation for the current locale
+ without the time
+ %X - preferred time representation for the current locale
+ without the date
+ %y - year as a decimal number without a century (range 00 to 99)
+ %Y - year as a decimal number including the century
+ %Z or %z - time zone or name or abbreviation
+ %% - a literal `%' character
+
View
55 NOTES.md
@@ -0,0 +1,55 @@
+# 2010
+
+Date.parse('2010')
+ArgumentError: invalid date
+
+Date.strptime('2010', '%m/%d/%Y')
+ArgumentError: invalid date
+
+Chronic.parse('2010')
+TypeError: can't convert Chronic::RepeaterTime::Tick into an exact number
+
+Timeliness.parse('2010')
+nil
+
+# January
+
+Date.parse('January')
+=> #<Date: 2010-01-01 (4910395/2,0,2299161)>
+
+Date.strptime('January', '%m/%d/%Y')
+ArgumentError: invalid date
+
+Chronic.parse('January')
+=> 2011-01-16 12:00:00 -0500
+
+Timeliness.parse('January')
+nil
+
+# February 2010
+
+Date.parse('February 2010')
+=> #<Date: 2010-02-01 (4910457/2,0,2299161)>
+
+Date.strptime('February 2010', '%m/%d/%Y')
+ArgumentError: invalid date
+
+Chronic.parse('February 2010')
+=> 2010-02-15 00:00:00 -0500
+
+Timeliness.parse('February 2010')
+nil
+
+# February 9, 2010
+
+Date.parse('February 9, 2010')
+=> #<Date: 2010-02-09 (4910473/2,0,2299161)>
+
+Date.strptime('February 9, 2010', '%m/%d/%Y')
+ArgumentError: invalid date
+
+Chronic.parse('February 9, 2010')
+nil
+
+Timeliness.parse('February 9, 2010')
+nil
View
127 lib/kronos.rb
@@ -1,5 +1,5 @@
-require File.expand_path("../version", __FILE__)
-require 'parsedate'
+require File.expand_path('../version', __FILE__)
+require 'date'
class Kronos
@@ -8,49 +8,116 @@ class Invalid < Exception; end
attr_accessor :year, :month, :day
- IGNORE_PATTERN = Regexp.compile("^prior")
+ IGNORE_PATTERN = Regexp.compile('^prior')
def parse(string)
if string.nil? || string =~ IGNORE_PATTERN
nil # do nothing
- elsif string =~ /^\d{4}$/
- self.year = string.to_i
- elsif string =~ /^[']?(\d{2})$/
+ elsif\
+ p = parse_date(string, '%Y-%m-%d') || # 1988-09-23
+ p = parse_date(string, '%Y-%b-%d') || # 1988-Sep-23
+ p = parse_date(string, '%Y-%B-%d') || # 1988-September-23
+ p = parse_date(string, '%y-%m-%d') || # 88-9-23
+ p = parse_date(string, '%y-%b-%d') || # 88-Sep-23
+ p = parse_date(string, '%y-%B-%d') || # 88-September-23
+ p = parse_date(string, '%m/%d/%Y') || # 9/23/1988
+ p = parse_date(string, '%b/%d/%Y') || # Sep/23/1988
+ p = parse_date(string, '%B/%d/%Y') || # September/23/1988
+ p = parse_date(string, '%m/%d/%y') || # 11/23/88
+ p = parse_date(string, '%b/%d/%y') || # Sep/23/88
+ p = parse_date(string, '%B/%d/%y') || # September/23/88
+ p = parse_date(string, '%m-%d-%Y') || # 11-23-1988
+ p = parse_date(string, '%b-%d-%Y') || # Sep-23-1988
+ p = parse_date(string, '%B-%d-%Y') || # September-23-1988
+ p = parse_date(string, '%m %d %Y') || # 11 23 1988
+ p = parse_date(string, '%b %d %Y') || # Sep 23 1988
+ p = parse_date(string, '%B %d %Y') || # September 23 1988
+ p = parse_date(string, '%m %d %y') || # 11 23 88
+ p = parse_date(string, '%b %d %y') || # Sep 23 88
+ p = parse_date(string, '%B %d %y') || # September 23 88
+ p = parse_date(string, '%d-%b-%Y') || # 23-Sep-1988
+ p = parse_date(string, '%d-%B-%Y') || # 23-September-1988
+ p = parse_date(string, '%d-%b-%y') || # 23-Sep-88
+ p = parse_date(string, '%d-%B-%y') || # 23-September-88
+ p = parse_date(string, '%d %b %Y') || # 23 Sep 1988
+ p = parse_date(string, '%d %B %Y') || # 23 September 1988
+ p = parse_date(string, '%d %b %y') || # 23 Sep 88
+ p = parse_date(string, '%d %B %y') || # 23 September 88
+ false
+ self.year = to_full_year(p.year)
+ self.month = p.month
+ self.day = p.day
+ elsif\
+ p = parse_date(string, '%Y-%m') || # 1976-4
+ p = parse_date(string, '%Y-%b') || # 1976-Apr
+ p = parse_date(string, '%Y-%B') || # 1976-April
+ p = parse_date(string, '%m/%Y') || # 4/1976
+ p = parse_date(string, '%b/%Y') || # Apr/1976
+ p = parse_date(string, '%B/%Y') || # April/1976
+ p = parse_date(string, '%m-%Y') || # 4-1976
+ p = parse_date(string, '%b-%Y') || # Apr-1976
+ p = parse_date(string, '%B-%Y') || # April-1976
+ p = parse_date(string, '%m %Y') || # 4 1976
+ p = parse_date(string, '%b %Y') || # Apr 1976
+ p = parse_date(string, '%B %Y') || # April 1976
+ p = parse_date(string, '%m/%y') || # 4/76
+ p = parse_date(string, '%b/%y') || # Apr/76
+ p = parse_date(string, '%B/%y') || # April/76
+ p = parse_date(string, '%m-%y') || # 4-76
+ p = parse_date(string, '%b-%y') || # Apr-76
+ p = parse_date(string, '%B-%y') || # April-76
+ p = parse_date(string, '%m %y') || # 4 76
+ p = parse_date(string, '%b %y') || # Apr 76
+ p = parse_date(string, '%B %y') || # April 76
+ false
+ self.year = to_full_year(p.year)
+ self.month = p.month
+ elsif\
+ p = parse_date(string, '%Y') || # 1991
+ p = parse_date(string, '%y') || # 91
+ false
+ self.year = to_full_year(p.year)
+ elsif\
+ string =~ /^[']?(\d{2})$/ # '91
self.year = to_full_year($1.to_i)
- else
- values = ParseDate.parsedate(string)
- if values[0]
- # ParseDate parsed a year
- self.year = to_full_year(values[0])
- self.month = values[1]
- self.day = values[2]
- elsif values[2]
- # ParseDate parsed a day but not a year
- self.year = to_full_year(values[2])
- self.month = values[1]
- self.day = nil
- end
end
self
end
+ # This method:
+ # 1. Swallows ArgumentError from Date.strptime
+ # 2. Does not trust a match from Date.strptime unless it matches the
+ # inverse operation, namely Date#strftime.
+ # 3. However, the inverse operation is complicated by the way that
+ # #strftime handles leading zeros. For now, I have a hack that
+ # removes leading zeros.
+ def parse_date(original, format)
+ parsed = Date.strptime(original, format)
+ t1 = parsed.strftime(format)
+ t2 = t1.gsub('/0', '/').gsub('-0', '-').gsub(/^0/, '')
+ return unless original == t1 || original == t2
+ parsed
+ rescue ArgumentError
+ nil
+ end
+
def to_s
- s = ""
+ s = ''
raise Invalid, errors unless valid?
- s << "%04i" % year if year
- s << "-%02i" % month if month
- s << "-%02i" % day if day
+ s << '%04i' % year if year
+ s << '-%02i' % month if month
+ s << '-%02i' % day if day
s
end
def errors
errors = []
if day && (!month || !year)
- errors << "if day is given, then month and year must also be given"
+ errors << 'if day is given, then month and year must also be given'
elsif month && !year
- errors << "if month is given, then year must also be given"
+ errors << 'if month is given, then year must also be given'
elsif !year
- errors << "year must be given"
+ errors << 'year must be given'
else
if day && !((1 .. 31) === day)
errors << "day (#{day}) must be between 1 and 31"
@@ -100,11 +167,11 @@ def <(other)
elsif self.day == other.day
false
else
- raise Exception, "unexpected error"
+ raise Exception, 'unexpected error'
end
end
else
- raise Exception, "unexpected error"
+ raise Exception, 'unexpected error'
end
end
@@ -146,11 +213,11 @@ def >(other)
elsif self.day == other.day
false
else
- raise Exception, "unexpected error"
+ raise Exception, 'unexpected error'
end
end
else
- raise Exception, "unexpected error"
+ raise Exception, 'unexpected error'
end
end
View
123 spec/parse_spec.rb
@@ -2,109 +2,134 @@
describe "Kronos" do
+ it "1/17/2007" do
+ c = Kronos.parse("1/17/2007")
+ [c.year, c.month, c.day].should == [2007, 1, 17]
+ end
+
+ it "01/17/2007" do
+ c = Kronos.parse("01/17/2007")
+ [c.year, c.month, c.day].should == [2007, 1, 17]
+ end
+
+ it "05/09/2007" do
+ c = Kronos.parse("05/09/2007")
+ [c.year, c.month, c.day].should == [2007, 5, 9]
+ end
+
+ it "05-09-2007" do
+ c = Kronos.parse("05-09-2007")
+ [c.year, c.month, c.day].should == [2007, 5, 9]
+ end
+
it "15-Mar-2001" do
c = Kronos.parse("15-Mar-2001")
- c.year.should == 2001
- c.month.should == 3
- c.day.should == 15
+ [c.year, c.month, c.day].should == [2001, 3, 15]
+ end
+
+ it "30-March-2001" do
+ c = Kronos.parse("30-March-2001")
+ [c.year, c.month, c.day].should == [2001, 3, 30]
+ end
+
+ it "6 Dec 2001" do
+ c = Kronos.parse("6 Dec 2001")
+ [c.year, c.month, c.day].should == [2001, 12, 6]
+ end
+
+ it "23 Jan 23" do
+ c = Kronos.parse("23 Jan 23")
+ [c.year, c.month, c.day].should == [2023, 1, 23]
+ end
+
+ it "23 Jan 1923" do
+ c = Kronos.parse("23 Jan 1923")
+ [c.year, c.month, c.day].should == [1923, 1, 23]
+ end
+
+ it "12 Nov 77" do
+ c = Kronos.parse("12 Nov 77")
+ [c.year, c.month, c.day].should == [1977, 11, 12]
+ end
+
+ it "Nov 12 1977" do
+ c = Kronos.parse("Nov 12 1977")
+ [c.year, c.month, c.day].should == [1977, 11, 12]
end
it "January 1976" do
c = Kronos.parse("January 1976")
- c.year.should == 1976
- c.month.should == 1
- c.day.should == nil
+ [c.year, c.month, c.day].should == [1976, 1, nil]
+ end
+
+ it "Jun 1976" do
+ c = Kronos.parse("Jun 1976")
+ [c.year, c.month, c.day].should == [1976, 6, nil]
+ end
+
+ it "Jul-71" do
+ c = Kronos.parse("Jul-71")
+ [c.year, c.month, c.day].should == [1971, 7, nil]
end
it "1991" do
c = Kronos.parse("1991")
- c.year.should == 1991
- c.month.should == nil
- c.day.should == nil
+ [c.year, c.month, c.day].should == [1991, nil, nil]
end
it "91" do
c = Kronos.parse("91")
- c.year.should == 1991
- c.month.should == nil
- c.day.should == nil
+ [c.year, c.month, c.day].should == [1991, nil, nil]
end
it "'91" do
c = Kronos.parse("'91")
- c.year.should == 1991
- c.month.should == nil
- c.day.should == nil
+ [c.year, c.month, c.day].should == [1991, nil, nil]
end
it "2019" do
c = Kronos.parse("2019")
- c.year.should == 2019
- c.month.should == nil
- c.day.should == nil
+ [c.year, c.month, c.day].should == [2019, nil, nil]
end
it "19" do
c = Kronos.parse("19")
- c.year.should == 2019
- c.month.should == nil
- c.day.should == nil
+ [c.year, c.month, c.day].should == [2019, nil, nil]
end
it "'19" do
c = Kronos.parse("'19")
- c.year.should == 2019
- c.month.should == nil
- c.day.should == nil
- end
-
- it "1/17/2007" do
- c = Kronos.parse("1/17/2007")
- c.year.should == 2007
- c.month.should == 1
- c.day.should == 17
+ [c.year, c.month, c.day].should == [2019, nil, nil]
end
it "Aug-96" do
c = Kronos.parse("Aug-96")
- c.year.should == 1996
- c.month.should == 8
- c.day.should == nil
+ [c.year, c.month, c.day].should == [1996, 8, nil]
end
it "15-Mar-96" do
c = Kronos.parse("15-Mar-96")
- c.year.should == 1996
- c.month.should == 3
- c.day.should == 15
+ [c.year, c.month, c.day].should == [1996, 3, 15]
end
it "nil" do
c = Kronos.parse(nil)
- c.year.should == nil
- c.month.should == nil
- c.day.should == nil
+ [c.year, c.month, c.day].should == [nil, nil, nil]
end
it "empty string" do
c = Kronos.parse("")
- c.year.should == nil
- c.month.should == nil
- c.day.should == nil
+ [c.year, c.month, c.day].should == [nil, nil, nil]
end
it "unknown" do
c = Kronos.parse("unknown")
- c.year.should == nil
- c.month.should == nil
- c.day.should == nil
+ [c.year, c.month, c.day].should == [nil, nil, nil]
end
it "prior to 1998" do
c = Kronos.parse("prior to 1998")
- c.year.should == nil
- c.month.should == nil
- c.day.should == nil
+ [c.year, c.month, c.day].should == [nil, nil, nil]
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.