stamp 0.1.2 → 0.1.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/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
|
[](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.
|