stamp 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +1 -1
- data/README.md +5 -3
- data/features/stamp.feature +26 -25
- data/lib/stamp.rb +117 -121
- data/lib/stamp/version.rb +1 -1
- metadata +10 -5
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# stamp
|
2
2
|
|
3
|
-
Format dates
|
3
|
+
Format dates and times based on human-friendly examples, not arcane
|
4
4
|
strftime directives.
|
5
5
|
|
6
6
|
[![Build Status](http://travis-ci.org/jeremyw/stamp.png)](http://travis-ci.org/jeremyw/stamp)
|
@@ -65,7 +65,9 @@ future understand your intent.
|
|
65
65
|
### Limitations
|
66
66
|
|
67
67
|
* Time zone support hasn't been implemented. Patches welcome!
|
68
|
-
* DateTime
|
68
|
+
* DateTime should inherit stamp behavior from Date, but it hasn't been thoroughly tested. Patches welcome!
|
69
|
+
|
70
|
+
### Advanced Usage
|
69
71
|
|
70
72
|
If you need more obscure formatting options, you can include any valid
|
71
73
|
[strftime](http://strfti.me) directives in your example string, and they'll
|
@@ -75,7 +77,7 @@ just be passed along:
|
|
75
77
|
Date.today.stamp("Week #%U, 1999") # "Week #23, 2011"
|
76
78
|
````
|
77
79
|
|
78
|
-
|
80
|
+
Check out [http://strfti.me](http://strfti.me) for more ideas.
|
79
81
|
|
80
82
|
## Contributing to stamp
|
81
83
|
|
data/features/stamp.feature
CHANGED
@@ -6,34 +6,33 @@ Feature: Stamping a date
|
|
6
6
|
|
7
7
|
@date
|
8
8
|
Scenario Outline: Formatting dates by example
|
9
|
-
Given the date
|
9
|
+
Given the date September 8, 2011
|
10
10
|
When I stamp the example "<example>"
|
11
11
|
Then I produce "<output>"
|
12
12
|
And I like turtles
|
13
13
|
|
14
14
|
Examples:
|
15
15
|
| example | output |
|
16
|
-
| January |
|
17
|
-
| Jan |
|
18
|
-
| Jan 1 |
|
19
|
-
| Jan 01 |
|
20
|
-
| Jan 10 |
|
21
|
-
| Jan 1, 1999 |
|
22
|
-
| Monday |
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
| 01/
|
27
|
-
| 01/
|
28
|
-
| 01/
|
29
|
-
| 01/
|
30
|
-
|
|
31
|
-
|
|
32
|
-
| 31/12
|
33
|
-
| 31
|
34
|
-
|
|
35
|
-
|
|
36
|
-
| DOB: 12-31-1999 | DOB: 10-09-2011 |
|
16
|
+
| January | September |
|
17
|
+
| Jan | Sep |
|
18
|
+
| Jan 1 | Sep 8 |
|
19
|
+
| Jan 01 | Sep 08 |
|
20
|
+
| Jan 10 | Sep 08 |
|
21
|
+
| Jan 1, 1999 | Sep 8, 2011 |
|
22
|
+
| Monday | Thursday |
|
23
|
+
| Tue, Jan 1 | Thu, Sep 8 |
|
24
|
+
| Tuesday, January 1, 1999 | Thursday, September 8, 2011 |
|
25
|
+
| 01/1999 | 09/2011 |
|
26
|
+
| 01/01 | 09/08 |
|
27
|
+
| 01/31 | 09/08 |
|
28
|
+
| 01/99 | 09/11 |
|
29
|
+
| 01/01/1999 | 09/08/2011 |
|
30
|
+
| 12/31/99 | 09/08/11 |
|
31
|
+
| 31/12 | 08/09 |
|
32
|
+
| 31/12/99 | 08/09/11 |
|
33
|
+
| 31-Jan-1999 | 08-Sep-2011 |
|
34
|
+
| 1999-12-31 | 2011-09-08 |
|
35
|
+
| DOB: 12-31-1999 | DOB: 09-08-2011 |
|
37
36
|
|
38
37
|
@time
|
39
38
|
Scenario Outline: Formatting times by example
|
@@ -54,15 +53,17 @@ Feature: Stamping a date
|
|
54
53
|
| 08:59:59 PM | 01:31:27 PM |
|
55
54
|
| 23:59:59 | 13:31:27 |
|
56
55
|
|
56
|
+
|
57
57
|
Scenario: strftime directives just get passed through
|
58
58
|
Given the date December 21, 2012
|
59
59
|
When I stamp the example "John Cusack was in a movie about %b %d, %Y, but it wasn't very good."
|
60
60
|
Then I produce "John Cusack was in a movie about Dec 21, 2012, but it wasn't very good."
|
61
61
|
|
62
|
+
|
62
63
|
Scenario: Plain text just gets passed through
|
63
|
-
Given the date
|
64
|
-
When I stamp the example "
|
65
|
-
Then I produce "
|
64
|
+
Given the date June 1, 1926
|
65
|
+
When I stamp the example "Marilyn Monroe was born on January 1, 1999."
|
66
|
+
Then I produce "Marilyn Monroe was born on June 1, 1926."
|
66
67
|
|
67
68
|
|
68
69
|
@wip
|
data/lib/stamp.rb
CHANGED
@@ -3,148 +3,144 @@ require "date"
|
|
3
3
|
require "time"
|
4
4
|
|
5
5
|
module Stamp
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
MONTHNAMES_REGEXP = /^(#{Date::MONTHNAMES.compact.join('|')})$/i
|
7
|
+
ABBR_MONTHNAMES_REGEXP = /^(#{Date::ABBR_MONTHNAMES.compact.join('|')})$/i
|
8
|
+
DAYNAMES_REGEXP = /^(#{Date::DAYNAMES.join('|')})$/i
|
9
|
+
ABBR_DAYNAMES_REGEXP = /^(#{Date::ABBR_DAYNAMES.join('|')})$/i
|
10
|
+
|
11
|
+
ONE_DIGIT_REGEXP = /^\d{1}$/
|
12
|
+
TWO_DIGIT_REGEXP = /^\d{2}$/
|
13
|
+
FOUR_DIGIT_REGEXP = /^\d{4}$/
|
14
|
+
|
15
|
+
TIME_REGEXP = /^(\d{1,2})(:)(\d{2})(\s*)(:)?(\d{2})?(\s*)?([ap]m)?$/i
|
16
|
+
|
17
|
+
MERIDIAN_LOWER_REGEXP = /^(a|p)m$/
|
18
|
+
MERIDIAN_UPPER_REGEXP = /^(A|P)M$/
|
19
|
+
|
20
|
+
# Disambiguate based on value
|
21
|
+
OBVIOUS_YEARS = 60..99
|
22
|
+
OBVIOUS_MONTHS = 12
|
23
|
+
OBVIOUS_DAYS = 28..31
|
24
|
+
OBVIOUS_24_HOUR = 13..23
|
25
|
+
|
26
|
+
TWO_DIGIT_DATE_SUCCESSION = {
|
27
|
+
'%m' => '%d',
|
28
|
+
'%b' => '%d',
|
29
|
+
'%B' => '%d',
|
30
|
+
'%d' => '%y',
|
31
|
+
'%e' => '%y'
|
32
|
+
}
|
33
|
+
|
34
|
+
TWO_DIGIT_TIME_SUCCESSION = {
|
35
|
+
'%H' => '%M',
|
36
|
+
'%I' => '%M',
|
37
|
+
'%l' => '%M',
|
38
|
+
'%M' => '%S'
|
39
|
+
}
|
40
|
+
|
41
|
+
|
42
|
+
def stamp(example)
|
43
|
+
strftime(strftime_format(example))
|
10
44
|
end
|
11
45
|
|
12
|
-
module InstanceMethods
|
13
|
-
|
14
|
-
MONTHNAMES_REGEXP = /(#{Date::MONTHNAMES.compact.join('|')})/i
|
15
|
-
ABBR_MONTHNAMES_REGEXP = /(#{Date::ABBR_MONTHNAMES.compact.join('|')})/i
|
16
|
-
DAYNAMES_REGEXP = /(#{Date::DAYNAMES.join('|')})/i
|
17
|
-
ABBR_DAYNAMES_REGEXP = /(#{Date::ABBR_DAYNAMES.join('|')})/i
|
18
|
-
|
19
|
-
ONE_DIGIT_REGEXP = /\d{1}/
|
20
|
-
TWO_DIGIT_REGEXP = /\d{2}/
|
21
|
-
FOUR_DIGIT_REGEXP = /\d{4}/
|
22
|
-
|
23
|
-
TIME_REGEXP = /(\d{1,2})(:)(\d{2})(\s*)(:)?(\d{2})?(\s*)?([ap]m)?/i
|
24
46
|
|
25
|
-
|
26
|
-
MERIDIAN_UPPER_REGEXP = /(A|P)M/
|
47
|
+
private
|
27
48
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
49
|
+
# Transforms the given string with example dates/times to a format string
|
50
|
+
# suitable for strftime.
|
51
|
+
def strftime_format(example)
|
52
|
+
# extract any substrings that look like times, like "23:59" or "8:37 am"
|
53
|
+
before, time_example, after = example.partition(TIME_REGEXP)
|
33
54
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
'%B' => '%d',
|
38
|
-
'%d' => '%y',
|
39
|
-
'%e' => '%y'
|
40
|
-
}
|
41
|
-
|
42
|
-
TWO_DIGIT_TIME_SUCCESSION = {
|
43
|
-
'%H' => '%M',
|
44
|
-
'%I' => '%M',
|
45
|
-
'%l' => '%M',
|
46
|
-
'%M' => '%S'
|
47
|
-
}
|
48
|
-
|
49
|
-
|
50
|
-
def stamp(example)
|
51
|
-
strftime(strftime_format(example))
|
55
|
+
# transform any date tokens to strftime directives
|
56
|
+
words = strftime_directives(before.split(/\b/)) do |token, previous_directive|
|
57
|
+
strftime_date_directive(token, previous_directive)
|
52
58
|
end
|
53
59
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
def strftime_format(example)
|
60
|
-
# extract any substrings that look like times, like "23:59" or "8:37 am"
|
61
|
-
before, time_example, after = example.partition(TIME_REGEXP)
|
62
|
-
|
63
|
-
# transform any date tokens to strftime directives
|
64
|
-
words = strftime_directives(before.split(/\b/)) do |token, previous_directive|
|
65
|
-
strftime_date_directive(token, previous_directive)
|
60
|
+
# transform the example time string to strftime directives
|
61
|
+
unless time_example.empty?
|
62
|
+
time_parts = time_example.scan(TIME_REGEXP).first
|
63
|
+
words += strftime_directives(time_parts) do |token, previous_directive|
|
64
|
+
strftime_time_directive(token, previous_directive)
|
66
65
|
end
|
67
|
-
|
68
|
-
# transform the example time string to strftime directives
|
69
|
-
unless time_example.empty?
|
70
|
-
time_parts = time_example.scan(TIME_REGEXP).first
|
71
|
-
words += strftime_directives(time_parts) do |token, previous_directive|
|
72
|
-
strftime_time_directive(token, previous_directive)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
# recursively process any remaining text
|
77
|
-
words << strftime_format(after) unless after.empty?
|
78
|
-
words.join
|
79
66
|
end
|
80
67
|
|
81
|
-
#
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
68
|
+
# recursively process any remaining text
|
69
|
+
words << strftime_format(after) unless after.empty?
|
70
|
+
words.join
|
71
|
+
end
|
72
|
+
|
73
|
+
# Transforms tokens that look like date/time parts to strftime directives.
|
74
|
+
def strftime_directives(tokens)
|
75
|
+
previous_directive = nil
|
76
|
+
tokens.map do |token|
|
77
|
+
directive = yield(token, previous_directive)
|
78
|
+
previous_directive = directive unless directive.nil?
|
79
|
+
directive || token
|
89
80
|
end
|
81
|
+
end
|
90
82
|
|
91
|
-
|
92
|
-
|
93
|
-
|
83
|
+
def strftime_time_directive(token, previous_directive)
|
84
|
+
case token
|
85
|
+
when MERIDIAN_LOWER_REGEXP
|
86
|
+
if RUBY_VERSION =~ /^1.8/ && self.is_a?(Time)
|
87
|
+
# 1.8.7 doesn't implement %P
|
88
|
+
self.strftime("%p").downcase
|
89
|
+
else
|
94
90
|
'%P'
|
95
|
-
|
96
|
-
when MERIDIAN_UPPER_REGEXP
|
97
|
-
'%p'
|
98
|
-
|
99
|
-
when TWO_DIGIT_REGEXP
|
100
|
-
TWO_DIGIT_TIME_SUCCESSION[previous_directive] ||
|
101
|
-
case token.to_i
|
102
|
-
when OBVIOUS_24_HOUR
|
103
|
-
'%H' # 24-hour clock
|
104
|
-
else
|
105
|
-
'%I' # 12-hour clock with leading zero
|
106
|
-
end
|
107
|
-
|
108
|
-
when ONE_DIGIT_REGEXP
|
109
|
-
'%l' # hour without leading zero
|
110
91
|
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def strftime_date_directive(token, previous_directive)
|
114
|
-
case token
|
115
|
-
when MONTHNAMES_REGEXP
|
116
|
-
'%B'
|
117
92
|
|
118
|
-
|
119
|
-
|
93
|
+
when MERIDIAN_UPPER_REGEXP
|
94
|
+
'%p'
|
120
95
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
when ABBR_DAYNAMES_REGEXP
|
125
|
-
'%a'
|
126
|
-
|
127
|
-
when FOUR_DIGIT_REGEXP
|
128
|
-
'%Y'
|
129
|
-
|
130
|
-
when TWO_DIGIT_REGEXP
|
131
|
-
# try to discern obvious intent based on the example value
|
96
|
+
when TWO_DIGIT_REGEXP
|
97
|
+
TWO_DIGIT_TIME_SUCCESSION[previous_directive] ||
|
132
98
|
case token.to_i
|
133
|
-
when
|
134
|
-
'%
|
135
|
-
when OBVIOUS_MONTHS
|
136
|
-
'%m'
|
137
|
-
when OBVIOUS_DAYS
|
138
|
-
'%d'
|
99
|
+
when OBVIOUS_24_HOUR
|
100
|
+
'%H' # 24-hour clock
|
139
101
|
else
|
140
|
-
|
141
|
-
# disambiguate based on context
|
142
|
-
TWO_DIGIT_DATE_SUCCESSION[previous_directive] || '%m'
|
102
|
+
'%I' # 12-hour clock with leading zero
|
143
103
|
end
|
144
104
|
|
145
|
-
|
146
|
-
|
105
|
+
when ONE_DIGIT_REGEXP
|
106
|
+
'%l' # hour without leading zero
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def strftime_date_directive(token, previous_directive)
|
111
|
+
case token
|
112
|
+
when MONTHNAMES_REGEXP
|
113
|
+
'%B'
|
114
|
+
|
115
|
+
when ABBR_MONTHNAMES_REGEXP
|
116
|
+
'%b'
|
117
|
+
|
118
|
+
when DAYNAMES_REGEXP
|
119
|
+
'%A'
|
120
|
+
|
121
|
+
when ABBR_DAYNAMES_REGEXP
|
122
|
+
'%a'
|
123
|
+
|
124
|
+
when FOUR_DIGIT_REGEXP
|
125
|
+
'%Y'
|
126
|
+
|
127
|
+
when TWO_DIGIT_REGEXP
|
128
|
+
# try to discern obvious intent based on the example value
|
129
|
+
case token.to_i
|
130
|
+
when OBVIOUS_YEARS
|
131
|
+
'%y'
|
132
|
+
when OBVIOUS_MONTHS
|
133
|
+
'%m'
|
134
|
+
when OBVIOUS_DAYS
|
135
|
+
'%d'
|
136
|
+
else
|
137
|
+
# the intent isn't obvious based on the example value, so try to
|
138
|
+
# disambiguate based on context
|
139
|
+
TWO_DIGIT_DATE_SUCCESSION[previous_directive] || '%m'
|
147
140
|
end
|
141
|
+
|
142
|
+
when ONE_DIGIT_REGEXP
|
143
|
+
'%e' # day without leading zero
|
148
144
|
end
|
149
145
|
end
|
150
146
|
end
|
data/lib/stamp/version.rb
CHANGED
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stamp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
5
6
|
segments:
|
6
7
|
- 0
|
7
8
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
9
|
+
- 3
|
10
|
+
version: 0.1.3
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Jeremy Weiskotten
|
@@ -14,7 +15,7 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2011-07-
|
18
|
+
date: 2011-07-14 00:00:00 -04:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
@@ -25,6 +26,7 @@ dependencies:
|
|
25
26
|
requirements:
|
26
27
|
- - ">="
|
27
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
28
30
|
segments:
|
29
31
|
- 0
|
30
32
|
version: "0"
|
@@ -38,6 +40,7 @@ dependencies:
|
|
38
40
|
requirements:
|
39
41
|
- - ">="
|
40
42
|
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
41
44
|
segments:
|
42
45
|
- 0
|
43
46
|
version: "0"
|
@@ -83,6 +86,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
83
86
|
requirements:
|
84
87
|
- - ">="
|
85
88
|
- !ruby/object:Gem::Version
|
89
|
+
hash: 3
|
86
90
|
segments:
|
87
91
|
- 0
|
88
92
|
version: "0"
|
@@ -91,13 +95,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
95
|
requirements:
|
92
96
|
- - ">="
|
93
97
|
- !ruby/object:Gem::Version
|
98
|
+
hash: 3
|
94
99
|
segments:
|
95
100
|
- 0
|
96
101
|
version: "0"
|
97
102
|
requirements: []
|
98
103
|
|
99
104
|
rubyforge_project:
|
100
|
-
rubygems_version: 1.
|
105
|
+
rubygems_version: 1.6.2
|
101
106
|
signing_key:
|
102
107
|
specification_version: 3
|
103
108
|
summary: Date and time formatting for humans.
|