kronic 0.2.1 → 0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/HISTORY +6 -0
- data/README.rdoc +1 -1
- data/lib/kronic.rb +48 -25
- data/spec/kronic_spec.rb +41 -6
- data/spec/spec_helper.rb +17 -0
- metadata +30 -5
data/HISTORY
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
0.3.0
|
2
|
+
* Support month before day ("June 14")
|
3
|
+
* Support day ordinals ("14th June")
|
4
|
+
* Use Time.zone if available
|
5
|
+
* Fix Kronic.format for ruby 1.8
|
6
|
+
|
1
7
|
0.2.1
|
2
8
|
* Removed dependency on active_support, no external dependencies!
|
3
9
|
* Dates in the future with a year specified are parsed correctly
|
data/README.rdoc
CHANGED
@@ -8,7 +8,7 @@ Chronic and Tickle both parse a *heap* of formats. That's not useful to me when
|
|
8
8
|
|
9
9
|
In addition, Kronic can take a date and give you a human readable form, neither of which Chronic nor Tickle does.
|
10
10
|
|
11
|
-
Oh yeah, Kronic is
|
11
|
+
Oh yeah, Kronic is about 100 lines of code.
|
12
12
|
|
13
13
|
== Usage
|
14
14
|
|
data/lib/kronic.rb
CHANGED
@@ -2,25 +2,28 @@ require 'date'
|
|
2
2
|
|
3
3
|
class Kronic
|
4
4
|
# Public: Converts a human readable day (Today, yesterday) to a Date.
|
5
|
+
# If Time.zone is available and set (added by active_support and rails),
|
6
|
+
# Time.zone.today will be used as a reference point, otherwise Date.today
|
7
|
+
# will be used.
|
5
8
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# yesterday, tomorrow, last thursday, this thursday, 14 Sep,
|
11
|
-
# 14 June 2010. Parsing is case-insensitive.
|
9
|
+
# string - The String to convert to a Date. #to_s is called on the parameter.
|
10
|
+
# Supported formats are: Today, yesterday, tomorrow, last thursday,
|
11
|
+
# this thursday, 14 Sep, Sep 14, 14 June 2010. Parsing is
|
12
|
+
# case-insensitive.
|
12
13
|
#
|
13
14
|
# Returns the Date, or nil if the input could not be parsed.
|
14
15
|
def self.parse(string)
|
15
16
|
string = string.to_s.downcase.strip
|
16
|
-
|
17
|
+
now = today
|
17
18
|
|
18
|
-
parse_nearby_days(string,
|
19
|
-
parse_last_or_this_day(string,
|
20
|
-
parse_exact_date(string,
|
19
|
+
parse_nearby_days(string, now) ||
|
20
|
+
parse_last_or_this_day(string, now) ||
|
21
|
+
parse_exact_date(string, now)
|
21
22
|
end
|
22
23
|
|
23
|
-
# Public: Converts a date to a human readable string.
|
24
|
+
# Public: Converts a date to a human readable string. If Time.zone is
|
25
|
+
# available and set (added by active_support and rails), Time.zone.today
|
26
|
+
# will be used as a reference point, otherwise Date.today will be used.
|
24
27
|
#
|
25
28
|
# date - The Date to be converted
|
26
29
|
# opts - The Hash options used to customize formatting
|
@@ -29,7 +32,7 @@ class Kronic
|
|
29
32
|
# Returns a relative string ("Today", "This Monday") if available, otherwise
|
30
33
|
# the full representation of the date ("19 September 2010").
|
31
34
|
def self.format(date, opts = {})
|
32
|
-
case (date
|
35
|
+
case (date - (opts[:today] || today)).to_i
|
33
36
|
when (2..7) then "This " + date.strftime("%A")
|
34
37
|
when 1 then "Tomorrow"
|
35
38
|
when 0 then "Today"
|
@@ -42,6 +45,17 @@ class Kronic
|
|
42
45
|
class << self
|
43
46
|
private
|
44
47
|
|
48
|
+
NUMBER = /^[0-9]+$/
|
49
|
+
NUMBER_WITH_ORDINAL = /^[0-9]+(st|nd|rd|th)?$/
|
50
|
+
|
51
|
+
def today
|
52
|
+
if Time.respond_to?(:zone) && Time.zone
|
53
|
+
Time.zone.today
|
54
|
+
else
|
55
|
+
Date.today
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
45
59
|
# Examples
|
46
60
|
#
|
47
61
|
# month_from_name("january") # => 1
|
@@ -75,25 +89,34 @@ class Kronic
|
|
75
89
|
end
|
76
90
|
end
|
77
91
|
|
78
|
-
# Parse "14 Sep, 14 September, 14 September 2010"
|
92
|
+
# Parse "14 Sep", "14 September", "14 September 2010", "Sept 14 2010"
|
79
93
|
def parse_exact_date(string, today)
|
80
94
|
tokens = string.split(/\s+/)
|
81
95
|
|
82
|
-
if tokens
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
tokens[
|
87
|
-
else
|
88
|
-
today.year
|
96
|
+
if tokens.length >= 2
|
97
|
+
if tokens[0] =~ NUMBER_WITH_ORDINAL
|
98
|
+
parse_exact_date_parts(tokens[0], tokens[1], tokens[2], today)
|
99
|
+
elsif tokens[1] =~ NUMBER_WITH_ORDINAL
|
100
|
+
parse_exact_date_parts(tokens[1], tokens[0], tokens[2], today)
|
89
101
|
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Parses day, month and year parts
|
106
|
+
def parse_exact_date_parts(raw_day, raw_month, raw_year, today)
|
107
|
+
day = raw_day.to_i
|
108
|
+
month = month_from_name(raw_month)
|
109
|
+
year = if raw_year
|
110
|
+
raw_year =~ NUMBER ? raw_year.to_i : nil
|
111
|
+
else
|
112
|
+
today.year
|
113
|
+
end
|
90
114
|
|
91
|
-
|
115
|
+
return nil unless day && month && year
|
92
116
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
end
|
117
|
+
result = Date.new(year, month, day)
|
118
|
+
result = result << 12 if result > today && !raw_year
|
119
|
+
result
|
97
120
|
end
|
98
121
|
end
|
99
122
|
end
|
data/spec/kronic_spec.rb
CHANGED
@@ -18,14 +18,18 @@ describe Kronic do
|
|
18
18
|
:today => Date.new(2010, 9, 18),
|
19
19
|
:last_monday => Date.new(2010, 9, 13),
|
20
20
|
:next_monday => Date.new(2010, 9, 20),
|
21
|
-
:sep_4
|
22
|
-
:sep_20
|
23
|
-
:sep_28
|
21
|
+
:sep_4 => Date.new(2010, 9, 4),
|
22
|
+
:sep_20 => Date.new(2009, 9, 20),
|
23
|
+
:sep_28 => Date.new(2010, 9, 28)
|
24
24
|
}.fetch(key)
|
25
25
|
end
|
26
|
+
def date(key); self.class.date(key); end;
|
26
27
|
|
27
|
-
before :
|
28
|
-
|
28
|
+
before :each do
|
29
|
+
Time.zone = nil
|
30
|
+
ENV['TZ'] = "Australia/Melbourne"
|
31
|
+
d = date(:today)
|
32
|
+
Timecop.freeze(Time.utc(d.year, d.month, d.day))
|
29
33
|
end
|
30
34
|
|
31
35
|
should_parse('Today', date(:today))
|
@@ -41,7 +45,12 @@ describe Kronic do
|
|
41
45
|
should_parse('4 September', date(:sep_4))
|
42
46
|
should_parse('20 Sep', date(:sep_20))
|
43
47
|
should_parse('28 Sep 2010', date(:sep_28))
|
44
|
-
should_parse('14 Sep 2008',
|
48
|
+
should_parse('14 Sep 2008', Date.new(2008, 9, 14))
|
49
|
+
should_parse('14th Sep 2008', Date.new(2008, 9, 14))
|
50
|
+
should_parse('23rd Sep 2008', Date.new(2008, 9, 23))
|
51
|
+
should_parse('September 14 2008', Date.new(2008, 9, 14))
|
52
|
+
should_parse('Sep 4th', date(:sep_4))
|
53
|
+
should_parse('September 4', date(:sep_4))
|
45
54
|
should_parse('bogus', nil)
|
46
55
|
should_parse('14', nil)
|
47
56
|
should_parse('14 bogus in', nil)
|
@@ -53,4 +62,30 @@ describe Kronic do
|
|
53
62
|
should_format('Last Monday', date(:last_monday))
|
54
63
|
should_format('This Monday', date(:next_monday))
|
55
64
|
should_format('14 September 2008', Date.new(2008, 9, 14))
|
65
|
+
|
66
|
+
describe 'timezone support' do
|
67
|
+
before :all do
|
68
|
+
Time.extend(MethodVisibility)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should be timezone aware if activesupport Time.zone is set' do
|
72
|
+
Time.zone = "US/Eastern"
|
73
|
+
Kronic.parse("today").should == date(:today) - 1
|
74
|
+
Kronic.format(date(:today) - 1).should == "Today"
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should fallback to Date.today if Time.zone is not available' do
|
78
|
+
Time.hide_class_method(:zone) do
|
79
|
+
Kronic.parse("today").should == date(:today)
|
80
|
+
Kronic.format(date(:today)).should == "Today"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should fallback to Date.today if Time.zone is not set' do
|
85
|
+
Time.zone = nil
|
86
|
+
Kronic.parse("today").should == date(:today)
|
87
|
+
Kronic.format(date(:today)).should == "Today"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
56
91
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
require 'rspec'
|
2
2
|
require 'timecop'
|
3
|
+
require 'active_support/core_ext'
|
3
4
|
|
4
5
|
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
|
5
6
|
require 'kronic'
|
7
|
+
|
8
|
+
module MethodVisibility
|
9
|
+
# Used to toggle whether Time.zone is available, for testing with and
|
10
|
+
# without activesupport.
|
11
|
+
def hide_class_method(method)
|
12
|
+
metaclass = (class << self; self; end)
|
13
|
+
metaclass.send :alias_method, :old_method, method
|
14
|
+
metaclass.send :remove_method, method
|
15
|
+
begin
|
16
|
+
yield
|
17
|
+
ensure
|
18
|
+
metaclass.send :alias_method, method, :old_method
|
19
|
+
metaclass.send :remove_method, :old_method
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
metadata
CHANGED
@@ -4,9 +4,8 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
|
9
|
-
version: 0.2.1
|
7
|
+
- 3
|
8
|
+
version: "0.3"
|
10
9
|
platform: ruby
|
11
10
|
authors:
|
12
11
|
- Xavier Shay
|
@@ -14,7 +13,7 @@ autorequire:
|
|
14
13
|
bindir: bin
|
15
14
|
cert_chain: []
|
16
15
|
|
17
|
-
date: 2010-09-
|
16
|
+
date: 2010-09-27 00:00:00 +01:00
|
18
17
|
default_executable:
|
19
18
|
dependencies:
|
20
19
|
- !ruby/object:Gem::Dependency
|
@@ -47,6 +46,32 @@ dependencies:
|
|
47
46
|
version: "0"
|
48
47
|
type: :development
|
49
48
|
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: activesupport
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
type: :development
|
61
|
+
version_requirements: *id003
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: tzinfo
|
64
|
+
prerelease: false
|
65
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
version: "0"
|
73
|
+
type: :development
|
74
|
+
version_requirements: *id004
|
50
75
|
description:
|
51
76
|
email:
|
52
77
|
- hello@xaviershay.com
|
@@ -63,7 +88,7 @@ files:
|
|
63
88
|
- README.rdoc
|
64
89
|
- HISTORY
|
65
90
|
- Rakefile
|
66
|
-
has_rdoc:
|
91
|
+
has_rdoc: false
|
67
92
|
homepage: http://github.com/xaviershay/kronic
|
68
93
|
licenses: []
|
69
94
|
|