stamp 0.6.0 → 0.7.0
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.
- checksums.yaml +5 -5
- data/.github/workflows/ci.yml +18 -0
- data/LICENSE.txt +1 -1
- data/README.md +40 -3
- data/Rakefile +6 -6
- data/lib/stamp/core.rb +73 -0
- data/lib/stamp/disambiguator.rb +17 -15
- data/lib/stamp/emitters/am_pm.rb +10 -8
- data/lib/stamp/emitters/composite.rb +11 -1
- data/lib/stamp/emitters/delegate.rb +12 -8
- data/lib/stamp/emitters/lookup.rb +12 -11
- data/lib/stamp/emitters/ordinal.rb +10 -2
- data/lib/stamp/emitters/string.rb +6 -0
- data/lib/stamp/emitters/twelve_hour.rb +38 -0
- data/lib/stamp/emitters/two_digit.rb +18 -9
- data/lib/stamp/emitters/utc_offset.rb +27 -0
- data/lib/stamp/refinement.rb +21 -0
- data/lib/stamp/translator.rb +38 -31
- data/lib/stamp/version.rb +1 -1
- data/lib/stamp.rb +3 -49
- data/stamp.gemspec +10 -4
- metadata +16 -18
- data/.travis.yml +0 -9
- data/cucumber.yml +0 -3
- data/features/stamp.feature +0 -123
- data/features/step_definitions/stamp_steps.rb +0 -30
- data/features/support/env.rb +0 -14
- data/lib/stamp/emitters/modifiable.rb +0 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f41c8f35a8e2cd599d4cff288999cadec0daecd942e963617a97c30fe2b6af16
|
|
4
|
+
data.tar.gz: 7f2da591b6b8c66fc6862b93bf93db1e86aac7212f4a043abfde0e6e0d3d4a1e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1cf409a95607824e5758705c2f152270e9294549bafcbb32e74f877575bed567310899170c9ae88c5c4ab411ac666c7f54bef133b67d3375fc5822e938b9b1e7
|
|
7
|
+
data.tar.gz: 9755ca6a5e9d1ff3c8a48e0adede18577699187985240580e77ba93c8912e5feb1ebf6197b31a3451394f1800e344ad3dac248fbdbcc5fec7a18be681d6004ae
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
test:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
strategy:
|
|
9
|
+
fail-fast: false
|
|
10
|
+
matrix:
|
|
11
|
+
ruby: ["3.1", "3.2", "3.3", "3.4"]
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: ruby/setup-ruby@v1
|
|
15
|
+
with:
|
|
16
|
+
ruby-version: ${{ matrix.ruby }}
|
|
17
|
+
bundler-cache: true
|
|
18
|
+
- run: bundle exec rake
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Format dates and times based on human-friendly examples, not arcane
|
|
4
4
|
[strftime](http://strfti.me) directives.
|
|
5
5
|
|
|
6
|
-
[](https://github.com/jeremyw/stamp/actions/workflows/ci.yml)
|
|
7
7
|
|
|
8
8
|
## Installation
|
|
9
9
|
|
|
@@ -58,6 +58,44 @@ time.stamp("Jan 1 at 01:00 AM") #=> "Jun 9 at 08:52 PM"
|
|
|
58
58
|
time.stamp("23:59 UTC") #=> "20:52 PST"
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
+
UTC offsets are recognized too, in both `+HHMM` and `+HH:MM` styles:
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
time.stamp("23:59 +00:00") #=> "20:52 -05:00"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Without monkey patches
|
|
68
|
+
|
|
69
|
+
If you'd rather not patch `Date` and `Time`, use the module function:
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
require "stamp/core" # instead of "stamp"
|
|
73
|
+
|
|
74
|
+
Stamp.format(Date.new(2011, 6, 9), "Jan 1, 1999") #=> "Jun 9, 2011"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Or a refinement, scoped to the files that opt in:
|
|
78
|
+
|
|
79
|
+
```ruby
|
|
80
|
+
require "stamp/refinement"
|
|
81
|
+
using Stamp::Refinement
|
|
82
|
+
|
|
83
|
+
Date.new(2011, 6, 9).stamp("Jan 1, 1999") #=> "Jun 9, 2011"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Converting examples to strftime formats
|
|
87
|
+
|
|
88
|
+
When you need the equivalent strftime string — for `Time::DATE_FORMATS`,
|
|
89
|
+
I18n configs, or APIs that want a format rather than a formatted value:
|
|
90
|
+
|
|
91
|
+
```ruby
|
|
92
|
+
Stamp.strftime_format("Jan 1, 1999") #=> "%b %-d, %Y"
|
|
93
|
+
Stamp.strftime_format("23:59:59") #=> "%H:%M:%S"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Ordinal days (e.g. "1st") have no strftime equivalent and raise an
|
|
97
|
+
`ArgumentError`.
|
|
98
|
+
|
|
61
99
|
## Features
|
|
62
100
|
|
|
63
101
|
* Abbreviated and full names of months and weekdays are recognized.
|
|
@@ -102,5 +140,4 @@ Time.now.to_s(:military) #=> "16 July 15:35"
|
|
|
102
140
|
|
|
103
141
|
## Copyright
|
|
104
142
|
|
|
105
|
-
Copyright (c) 2011 Jeremy Weiskotten
|
|
106
|
-
further details.
|
|
143
|
+
Copyright (c) 2011-2026 Jeremy Weiskotten. See LICENSE.txt for further details.
|
data/Rakefile
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
require 'bundler/gem_tasks'
|
|
2
|
-
require '
|
|
2
|
+
require 'rake/testtask'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
t.
|
|
4
|
+
Rake::TestTask.new(:test) do |t|
|
|
5
|
+
t.libs << "test"
|
|
6
|
+
t.libs << "lib"
|
|
7
|
+
t.pattern = "test/**/*_test.rb"
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
task :default => :
|
|
10
|
+
task :default => :test
|
data/lib/stamp/core.rb
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require "date"
|
|
2
|
+
require "time"
|
|
3
|
+
|
|
4
|
+
require "stamp/emitters/am_pm"
|
|
5
|
+
require "stamp/emitters/ambiguous"
|
|
6
|
+
require "stamp/emitters/composite"
|
|
7
|
+
require "stamp/emitters/delegate"
|
|
8
|
+
require "stamp/emitters/lookup"
|
|
9
|
+
require "stamp/emitters/ordinal"
|
|
10
|
+
require "stamp/emitters/string"
|
|
11
|
+
require "stamp/emitters/twelve_hour"
|
|
12
|
+
require "stamp/emitters/two_digit"
|
|
13
|
+
require "stamp/emitters/utc_offset"
|
|
14
|
+
require "stamp/disambiguator"
|
|
15
|
+
require "stamp/translator"
|
|
16
|
+
require "stamp/version"
|
|
17
|
+
|
|
18
|
+
module Stamp
|
|
19
|
+
# Formats a date/time using a human-friendly example as a template,
|
|
20
|
+
# without monkey patching Date or Time.
|
|
21
|
+
#
|
|
22
|
+
# @param [Date|Time|DateTime] target the date/time to format
|
|
23
|
+
# @param [String] example a human-friendly date/time example
|
|
24
|
+
#
|
|
25
|
+
# @return [String] the formatted date or time
|
|
26
|
+
#
|
|
27
|
+
# @example
|
|
28
|
+
# Stamp.format(Date.new(2012, 12, 21), "Jan 1, 1999") #=> "Dec 21, 2012"
|
|
29
|
+
def self.format(target, example)
|
|
30
|
+
emitters(example).format(target)
|
|
31
|
+
rescue NoMethodError => e
|
|
32
|
+
raise e if target.respond_to?(e.name)
|
|
33
|
+
raise ArgumentError,
|
|
34
|
+
"#{example.inspect} requires ##{e.name}, which #{target.class} doesn't implement (e.g. a Date can't format time-of-day examples)"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Translates a human-friendly example into a strftime format string.
|
|
38
|
+
#
|
|
39
|
+
# @param [String] example a human-friendly date/time example
|
|
40
|
+
#
|
|
41
|
+
# @return [String] the equivalent strftime format
|
|
42
|
+
#
|
|
43
|
+
# @raise [ArgumentError] if the example uses a feature strftime can't
|
|
44
|
+
# express (e.g. ordinal days like "1st")
|
|
45
|
+
#
|
|
46
|
+
# @example
|
|
47
|
+
# Stamp.strftime_format("Jan 1, 1999") #=> "%b %-d, %Y"
|
|
48
|
+
def self.strftime_format(example)
|
|
49
|
+
emitters(example).strftime_format
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Memoizes the emitters for each example to improve performance.
|
|
53
|
+
def self.emitters(example)
|
|
54
|
+
cache = (@emitters ||= {})
|
|
55
|
+
cache.clear if cache.size >= 1000 # ponytail: crude cap to bound memory; LRU if it ever matters
|
|
56
|
+
cache[example] ||= Disambiguator.new(Translator.new.translate(example)).disambiguate!
|
|
57
|
+
end
|
|
58
|
+
private_class_method :emitters
|
|
59
|
+
|
|
60
|
+
# Formats a date/time using a human-friendly example as a template.
|
|
61
|
+
#
|
|
62
|
+
# @param [String] example a human-friendly date/time example
|
|
63
|
+
#
|
|
64
|
+
# @return [String] the formatted date or time
|
|
65
|
+
#
|
|
66
|
+
# @example
|
|
67
|
+
# Date.new(2012, 12, 21).stamp("Jan 1, 1999") #=> "Dec 21, 2012"
|
|
68
|
+
def stamp(example)
|
|
69
|
+
Stamp.format(self, example)
|
|
70
|
+
end
|
|
71
|
+
alias :stamp_like :stamp
|
|
72
|
+
alias :format_like :stamp
|
|
73
|
+
end
|
data/lib/stamp/disambiguator.rb
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
module Stamp
|
|
2
|
+
class Disambiguator
|
|
3
|
+
attr_reader :emitters
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
def initialize(emitters)
|
|
6
|
+
@emitters = emitters
|
|
7
|
+
end
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
def disambiguate!
|
|
10
|
+
emitters.replace_each! do |emitter|
|
|
11
|
+
disambiguate(emitter)
|
|
12
|
+
end
|
|
11
13
|
end
|
|
12
|
-
end
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
private
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
def disambiguate(emitter)
|
|
18
|
+
if emitter.respond_to?(:disambiguate)
|
|
19
|
+
emitter.disambiguate(emitters)
|
|
20
|
+
else
|
|
21
|
+
emitter
|
|
22
|
+
end
|
|
21
23
|
end
|
|
22
24
|
end
|
|
23
25
|
end
|
data/lib/stamp/emitters/am_pm.rb
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
module Stamp
|
|
2
2
|
module Emitters
|
|
3
3
|
class AmPm
|
|
4
|
-
|
|
4
|
+
attr_reader :strftime_directive
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@modifier = block
|
|
6
|
+
# @param [Array<String>]values for am pm text
|
|
7
|
+
def initialize(values, strftime_directive)
|
|
8
|
+
@values = values
|
|
9
|
+
@strftime_directive = strftime_directive
|
|
11
10
|
end
|
|
12
11
|
|
|
13
12
|
def format(target)
|
|
14
|
-
|
|
13
|
+
@values[target.hour < 12 ? 0 : 1]
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
def field
|
|
18
17
|
nil
|
|
19
18
|
end
|
|
19
|
+
|
|
20
|
+
UPPERCASE = new(%w(AM PM), "%p")
|
|
21
|
+
LOWERCASE = new(%w(am pm), "%P")
|
|
20
22
|
end
|
|
21
23
|
end
|
|
22
|
-
end
|
|
24
|
+
end
|
|
@@ -17,7 +17,17 @@ module Stamp
|
|
|
17
17
|
result
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
# Builds the equivalent strftime format string.
|
|
21
|
+
#
|
|
22
|
+
# @raise [ArgumentError] if any emitter has no strftime equivalent
|
|
23
|
+
def strftime_format
|
|
24
|
+
emitters.map do |e|
|
|
25
|
+
e.strftime_directive ||
|
|
26
|
+
raise(ArgumentError, "#{e.class.name.split('::').last} (e.g. \"1st\") has no strftime equivalent")
|
|
27
|
+
end.join
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def <<(emitter)
|
|
21
31
|
Array(emitter).each { |e| emitters << e }
|
|
22
32
|
end
|
|
23
33
|
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
module Stamp
|
|
2
2
|
module Emitters
|
|
3
3
|
class Delegate
|
|
4
|
-
|
|
4
|
+
attr_reader :field, :strftime_directive
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
# @param [field] the field to be formatted (e.g. +:month+, +:year+)
|
|
9
|
-
def initialize(field, &block)
|
|
6
|
+
# @param [Symbol|String] field the field to be formatted (e.g. +:month+, +:year+)
|
|
7
|
+
def initialize(field, strftime_directive)
|
|
10
8
|
@field = field
|
|
11
|
-
@
|
|
9
|
+
@strftime_directive = strftime_directive
|
|
12
10
|
end
|
|
13
11
|
|
|
12
|
+
# @param [Date|Time|DateTime] target the date to be formatted
|
|
14
13
|
def format(target)
|
|
15
|
-
|
|
14
|
+
target.send(field)
|
|
16
15
|
end
|
|
16
|
+
|
|
17
|
+
ZONE = new(:zone, "%Z")
|
|
18
|
+
YEAR = new(:year, "%Y")
|
|
19
|
+
MONTH = new(:month, "%-m")
|
|
20
|
+
DAY = new(:day, "%-d")
|
|
17
21
|
end
|
|
18
22
|
end
|
|
19
|
-
end
|
|
23
|
+
end
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
module Stamp
|
|
2
2
|
module Emitters
|
|
3
3
|
class Lookup
|
|
4
|
-
attr_reader :field
|
|
4
|
+
attr_reader :field, :strftime_directive
|
|
5
5
|
|
|
6
|
-
# @param [
|
|
7
|
-
# @param [
|
|
8
|
-
|
|
9
|
-
def initialize(field, lookup=nil)
|
|
6
|
+
# @param [Symbol|String] field the field to be formatted (e.g. +:month+, +:year+)
|
|
7
|
+
# @param [Array<String>] lookup an array of the string values to be formatted (e.g. +Date::DAYNAMES+)
|
|
8
|
+
def initialize(field, lookup, strftime_directive)
|
|
10
9
|
@field = field
|
|
11
10
|
@lookup = lookup
|
|
11
|
+
@strftime_directive = strftime_directive
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def format(target)
|
|
@@ -16,12 +16,13 @@ module Stamp
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def lookup(value)
|
|
19
|
-
|
|
20
|
-
@lookup.call(value)
|
|
21
|
-
else
|
|
22
|
-
@lookup[value]
|
|
23
|
-
end
|
|
19
|
+
@lookup[value]
|
|
24
20
|
end
|
|
21
|
+
|
|
22
|
+
MONTH = new(:month, Date::MONTHNAMES, "%B")
|
|
23
|
+
ABBR_MONTH = new(:month, Date::ABBR_MONTHNAMES, "%b")
|
|
24
|
+
DAY = new(:wday, Date::DAYNAMES, "%A")
|
|
25
|
+
ABBR_DAY = new(:wday, Date::ABBR_DAYNAMES, "%a")
|
|
25
26
|
end
|
|
26
27
|
end
|
|
27
|
-
end
|
|
28
|
+
end
|
|
@@ -3,11 +3,12 @@ module Stamp
|
|
|
3
3
|
class Ordinal
|
|
4
4
|
attr_reader :field
|
|
5
5
|
|
|
6
|
-
# @param [
|
|
6
|
+
# @param [Symbol|String] field the field to be formatted (e.g. +:month+, +:year+)
|
|
7
7
|
def initialize(field)
|
|
8
8
|
@field = field
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
# @param [Date|Time|DateTime] target the date to be formatted
|
|
11
12
|
def format(target)
|
|
12
13
|
ordinalize(target.send(field))
|
|
13
14
|
end
|
|
@@ -26,6 +27,13 @@ module Stamp
|
|
|
26
27
|
end
|
|
27
28
|
end
|
|
28
29
|
end
|
|
30
|
+
|
|
31
|
+
# strftime has no directive for ordinals ("1st", "22nd", ...)
|
|
32
|
+
def strftime_directive
|
|
33
|
+
nil
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
DAY = new(:day)
|
|
29
37
|
end
|
|
30
38
|
end
|
|
31
|
-
end
|
|
39
|
+
end
|
|
@@ -15,6 +15,12 @@ module Stamp
|
|
|
15
15
|
value << emitter.value
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
# Literal text, with % escaped so strftime passes it through verbatim.
|
|
19
|
+
def strftime_directive
|
|
20
|
+
# value can be nil (unmatched optional capture groups become tokens)
|
|
21
|
+
value.to_s.gsub("%", "%%")
|
|
22
|
+
end
|
|
23
|
+
|
|
18
24
|
def field
|
|
19
25
|
nil
|
|
20
26
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Stamp
|
|
2
|
+
module Emitters
|
|
3
|
+
# Emits hours as a two-digit number with a leading
|
|
4
|
+
# zero if necessary.
|
|
5
|
+
class TwelveHour
|
|
6
|
+
attr_reader :leading_zero
|
|
7
|
+
|
|
8
|
+
# @param [String|Symbol] field the field to be formatted (e.g. +:month+, +:year+)
|
|
9
|
+
# @param [Hash] options the options for output
|
|
10
|
+
# @option options [String] :leading_zero (default: false) pass true add a leading 0 to output
|
|
11
|
+
def initialize(options = {})
|
|
12
|
+
@leading_zero = options[:leading_zero]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @param [Date|Time|DateTime] target the date to be formatted
|
|
16
|
+
def format(target)
|
|
17
|
+
value = target.hour
|
|
18
|
+
value = ((value - 1) % 12) + 1
|
|
19
|
+
if leading_zero && (value < 10)
|
|
20
|
+
"0#{value}"
|
|
21
|
+
else
|
|
22
|
+
value
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def field
|
|
27
|
+
:hour
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def strftime_directive
|
|
31
|
+
leading_zero ? "%I" : "%-I"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
LEADING_ZERO = new(leading_zero: true)
|
|
35
|
+
NON_LEADING_ZERO = new(leading_zero: false)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -3,20 +3,29 @@ module Stamp
|
|
|
3
3
|
# Emits the given field as a two-digit number with a leading
|
|
4
4
|
# zero if necessary.
|
|
5
5
|
class TwoDigit
|
|
6
|
-
|
|
6
|
+
attr_reader :field, :strftime_directive
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
# @param [field] the field to be formatted (e.g. +:month+, +:year+)
|
|
11
|
-
def initialize(field, &block)
|
|
8
|
+
# @param [String|Symbol] field the field to be formatted (e.g. +:month+, +:year+)
|
|
9
|
+
def initialize(field, strftime_directive)
|
|
12
10
|
@field = field
|
|
13
|
-
@
|
|
11
|
+
@strftime_directive = strftime_directive
|
|
14
12
|
end
|
|
15
13
|
|
|
16
14
|
def format(target)
|
|
17
|
-
value =
|
|
18
|
-
|
|
15
|
+
value = target.send(field) % 100
|
|
16
|
+
# by definition, leading_zero option is true
|
|
17
|
+
if value < 10
|
|
18
|
+
"0#{value}"
|
|
19
|
+
else
|
|
20
|
+
value
|
|
21
|
+
end
|
|
19
22
|
end
|
|
23
|
+
YEAR = new(:year, "%y")
|
|
24
|
+
MONTH = new(:month, "%m")
|
|
25
|
+
DAY = new(:day, "%d")
|
|
26
|
+
HOUR = new(:hour, "%H")
|
|
27
|
+
MIN = new(:min, "%M")
|
|
28
|
+
SEC = new(:sec, "%S")
|
|
20
29
|
end
|
|
21
30
|
end
|
|
22
|
-
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Stamp
|
|
2
|
+
module Emitters
|
|
3
|
+
# Emits the target's UTC offset, e.g. "-0500" or "-05:00".
|
|
4
|
+
class UtcOffset
|
|
5
|
+
# @param [String] directive the strftime directive to delegate to
|
|
6
|
+
# (+"%z"+ or +"%:z"+)
|
|
7
|
+
def initialize(directive)
|
|
8
|
+
@directive = directive
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def format(target)
|
|
12
|
+
target.strftime(@directive)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def strftime_directive
|
|
16
|
+
@directive
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def field
|
|
20
|
+
nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
PLAIN = new("%z")
|
|
24
|
+
COLON = new("%:z")
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require "stamp/core"
|
|
2
|
+
|
|
3
|
+
module Stamp
|
|
4
|
+
# An alternative to the default Date/Time monkey patches:
|
|
5
|
+
#
|
|
6
|
+
# require "stamp/refinement"
|
|
7
|
+
# using Stamp::Refinement
|
|
8
|
+
#
|
|
9
|
+
# Date.today.stamp("Jan 1, 1999")
|
|
10
|
+
module Refinement
|
|
11
|
+
[Date, Time].each do |klass|
|
|
12
|
+
refine klass do
|
|
13
|
+
def stamp(example)
|
|
14
|
+
Stamp.format(self, example)
|
|
15
|
+
end
|
|
16
|
+
alias_method :stamp_like, :stamp
|
|
17
|
+
alias_method :format_like, :stamp
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/stamp/translator.rb
CHANGED
|
@@ -5,7 +5,7 @@ module Stamp
|
|
|
5
5
|
# http://en.wikipedia.org/wiki/List_of_time_zone_abbreviations
|
|
6
6
|
TIME_ZONE_ABBREVIATIONS = %w{
|
|
7
7
|
ACDT ACST ACT ADT AEDT AEST AFT AKDT AKST AMST AMT ART AST AWDT AWST AZOST AZT
|
|
8
|
-
BDT BIOT BIT BOT BRT BST
|
|
8
|
+
BDT BIOT BIT BOT BRT BST BTT CAT CCT CDT CEDT CEST CET CHADT CHAST CHOT ChST CHUT
|
|
9
9
|
CIST CIT CKT CLST CLT COST COT CST CT CVT CWST CXT DAVT DDUT DFT EASST EAST EAT
|
|
10
10
|
ECT EDT EEDT EEST EET EGST EGT EIT EST FET FJT FKST FKT FNT GALT GAMT GET GFT
|
|
11
11
|
GILT GIT GMT GST GYT HADT HAEC HAST HKT HMT HOVT HST ICT IDT IOT IRDT IRKT IRST
|
|
@@ -28,6 +28,10 @@ module Stamp
|
|
|
28
28
|
|
|
29
29
|
TIME_REGEXP = /(\d{1,2})(:)(\d{2})(\s*)(:)?(\d{2})?(\s*)?([ap]m)?/i
|
|
30
30
|
|
|
31
|
+
# UTC offsets like "+05:00" or "-0500". The lookbehind/lookahead keep
|
|
32
|
+
# date fragments like "12-31-1999" from matching; hours are capped at 14.
|
|
33
|
+
UTC_OFFSET_REGEXP = /(?<![-\d])[+-](?:0\d|1[0-4]):?[0-5]\d(?!\d)/
|
|
34
|
+
|
|
31
35
|
MERIDIAN_LOWER_REGEXP = /^(a|p)m$/
|
|
32
36
|
MERIDIAN_UPPER_REGEXP = /^(A|P)M$/
|
|
33
37
|
|
|
@@ -38,12 +42,18 @@ module Stamp
|
|
|
38
42
|
OBVIOUS_DAY = 13..31
|
|
39
43
|
OBVIOUS_YEAR = 32..99
|
|
40
44
|
|
|
41
|
-
TWO_DIGIT_YEAR_EMITTER = Emitters::TwoDigit.new(:year) { |year| year % 100 }
|
|
42
|
-
TWO_DIGIT_MONTH_EMITTER = Emitters::TwoDigit.new(:month)
|
|
43
|
-
TWO_DIGIT_DAY_EMITTER = Emitters::TwoDigit.new(:day)
|
|
44
|
-
HOUR_TO_12_HOUR = lambda { |h| ((h - 1) % 12) + 1 }
|
|
45
|
-
|
|
46
45
|
def translate(example)
|
|
46
|
+
# extract any substrings that look like UTC offsets, like "+05:00" or
|
|
47
|
+
# "-0500", before the time logic can mistake them for clock times
|
|
48
|
+
before, offset_example, after = example.partition(UTC_OFFSET_REGEXP)
|
|
49
|
+
unless offset_example.empty?
|
|
50
|
+
emitters = Emitters::Composite.new
|
|
51
|
+
emitters << translate(before) unless before.empty?
|
|
52
|
+
emitters << (offset_example.include?(":") ? Emitters::UtcOffset::COLON : Emitters::UtcOffset::PLAIN)
|
|
53
|
+
emitters << translate(after) unless after.empty?
|
|
54
|
+
return emitters
|
|
55
|
+
end
|
|
56
|
+
|
|
47
57
|
# extract any substrings that look like times, like "23:59" or "8:37 am"
|
|
48
58
|
before, time_example, after = example.partition(TIME_REGEXP)
|
|
49
59
|
|
|
@@ -76,20 +86,20 @@ module Stamp
|
|
|
76
86
|
def time_emitter(token)
|
|
77
87
|
case token
|
|
78
88
|
when MERIDIAN_LOWER_REGEXP
|
|
79
|
-
Emitters::AmPm
|
|
89
|
+
Emitters::AmPm::LOWERCASE
|
|
80
90
|
|
|
81
91
|
when MERIDIAN_UPPER_REGEXP
|
|
82
|
-
Emitters::AmPm
|
|
92
|
+
Emitters::AmPm::UPPERCASE
|
|
83
93
|
|
|
84
94
|
when TWO_DIGIT_REGEXP
|
|
85
95
|
Emitters::Ambiguous.new(
|
|
86
96
|
two_digit_hour_emitter(token),
|
|
87
|
-
Emitters::TwoDigit
|
|
88
|
-
Emitters::TwoDigit
|
|
97
|
+
Emitters::TwoDigit::MIN,
|
|
98
|
+
Emitters::TwoDigit::SEC)
|
|
89
99
|
|
|
90
100
|
when ONE_DIGIT_REGEXP
|
|
91
101
|
# 12-hour clock without leading zero
|
|
92
|
-
Emitters::
|
|
102
|
+
Emitters::TwelveHour::NON_LEADING_ZERO
|
|
93
103
|
end
|
|
94
104
|
end
|
|
95
105
|
|
|
@@ -97,57 +107,54 @@ module Stamp
|
|
|
97
107
|
case token.to_i
|
|
98
108
|
when OBVIOUS_24_HOUR
|
|
99
109
|
# 24-hour clock
|
|
100
|
-
Emitters::TwoDigit
|
|
110
|
+
Emitters::TwoDigit::HOUR
|
|
101
111
|
else
|
|
102
112
|
# 12-hour clock with leading zero
|
|
103
|
-
Emitters::
|
|
113
|
+
Emitters::TwelveHour::LEADING_ZERO
|
|
104
114
|
end
|
|
105
115
|
end
|
|
106
116
|
|
|
107
117
|
def date_emitter(token)
|
|
108
118
|
case token
|
|
109
119
|
when MONTHNAMES_REGEXP
|
|
110
|
-
Emitters::Lookup
|
|
120
|
+
Emitters::Lookup::MONTH
|
|
111
121
|
|
|
112
122
|
when ABBR_MONTHNAMES_REGEXP
|
|
113
|
-
Emitters::Lookup
|
|
123
|
+
Emitters::Lookup::ABBR_MONTH
|
|
114
124
|
|
|
115
125
|
when DAYNAMES_REGEXP
|
|
116
|
-
Emitters::Lookup
|
|
126
|
+
Emitters::Lookup::DAY
|
|
117
127
|
|
|
118
128
|
when ABBR_DAYNAMES_REGEXP
|
|
119
|
-
Emitters::Lookup
|
|
129
|
+
Emitters::Lookup::ABBR_DAY
|
|
120
130
|
|
|
121
131
|
when TIMEZONE_REGEXP
|
|
122
|
-
Emitters::Delegate
|
|
132
|
+
Emitters::Delegate::ZONE
|
|
123
133
|
|
|
124
134
|
when FOUR_DIGIT_REGEXP
|
|
125
|
-
Emitters::Delegate
|
|
135
|
+
Emitters::Delegate::YEAR
|
|
126
136
|
|
|
127
137
|
when ORDINAL_DAY_REGEXP
|
|
128
|
-
Emitters::Ordinal
|
|
138
|
+
Emitters::Ordinal::DAY
|
|
129
139
|
|
|
130
140
|
when TWO_DIGIT_REGEXP
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
case value
|
|
141
|
+
case token.to_i
|
|
134
142
|
when OBVIOUS_DAY
|
|
135
|
-
|
|
143
|
+
Emitters::TwoDigit::DAY
|
|
136
144
|
when OBVIOUS_YEAR
|
|
137
|
-
|
|
145
|
+
Emitters::TwoDigit::YEAR
|
|
138
146
|
else
|
|
139
147
|
Emitters::Ambiguous.new(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
148
|
+
Emitters::TwoDigit::MONTH,
|
|
149
|
+
Emitters::TwoDigit::DAY,
|
|
150
|
+
Emitters::TwoDigit::YEAR)
|
|
143
151
|
end
|
|
144
152
|
|
|
145
153
|
when ONE_DIGIT_REGEXP
|
|
146
154
|
Emitters::Ambiguous.new(
|
|
147
|
-
Emitters::Delegate
|
|
148
|
-
Emitters::Delegate
|
|
155
|
+
Emitters::Delegate::MONTH,
|
|
156
|
+
Emitters::Delegate::DAY)
|
|
149
157
|
end
|
|
150
158
|
end
|
|
151
|
-
|
|
152
159
|
end
|
|
153
160
|
end
|
data/lib/stamp/version.rb
CHANGED
data/lib/stamp.rb
CHANGED
|
@@ -1,50 +1,4 @@
|
|
|
1
|
-
require "
|
|
2
|
-
require "time"
|
|
1
|
+
require "stamp/core"
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
require "stamp/emitters/ambiguous"
|
|
7
|
-
require "stamp/emitters/composite"
|
|
8
|
-
require "stamp/emitters/delegate"
|
|
9
|
-
require "stamp/emitters/lookup"
|
|
10
|
-
require "stamp/emitters/ordinal"
|
|
11
|
-
require "stamp/emitters/string"
|
|
12
|
-
require "stamp/emitters/two_digit"
|
|
13
|
-
require "stamp/disambiguator"
|
|
14
|
-
require "stamp/translator"
|
|
15
|
-
require "stamp/version"
|
|
16
|
-
|
|
17
|
-
module Stamp
|
|
18
|
-
# Formats a date/time using a human-friendly example as a template.
|
|
19
|
-
#
|
|
20
|
-
# @param [String] example a human-friendly date/time example
|
|
21
|
-
# @param [Hash] options
|
|
22
|
-
# @option options [Boolean] :memoize (true)
|
|
23
|
-
#
|
|
24
|
-
# @return [String] the formatted date or time
|
|
25
|
-
#
|
|
26
|
-
# @example
|
|
27
|
-
# Date.new(2012, 12, 21).stamp("Jan 1, 1999") #=> "Dec 21, 2012"
|
|
28
|
-
def stamp(example)
|
|
29
|
-
memoize_stamp_emitters(example).format(self)
|
|
30
|
-
end
|
|
31
|
-
alias :stamp_like :stamp
|
|
32
|
-
alias :format_like :stamp
|
|
33
|
-
|
|
34
|
-
private
|
|
35
|
-
|
|
36
|
-
# Memoizes the set of emitter objects for the given +example+ in
|
|
37
|
-
# order to improve performance.
|
|
38
|
-
def memoize_stamp_emitters(example)
|
|
39
|
-
@@memoized_stamp_emitters ||= {}
|
|
40
|
-
@@memoized_stamp_emitters[example] ||= stamp_emitters(example)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def stamp_emitters(example)
|
|
44
|
-
emitters = Translator.new.translate(example)
|
|
45
|
-
Disambiguator.new(emitters).disambiguate!
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
Date.send(:include, ::Stamp)
|
|
50
|
-
Time.send(:include, ::Stamp)
|
|
3
|
+
Date.include(::Stamp)
|
|
4
|
+
Time.include(::Stamp)
|
data/stamp.gemspec
CHANGED
|
@@ -10,12 +10,18 @@ Gem::Specification.new do |s|
|
|
|
10
10
|
s.homepage = "https://github.com/jeremyw/stamp"
|
|
11
11
|
s.summary = %Q{Date and time formatting for humans.}
|
|
12
12
|
s.description = %Q{Format dates and times based on human-friendly examples, not arcane strftime directives.}
|
|
13
|
+
s.license = "MIT"
|
|
13
14
|
|
|
14
|
-
s.
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
s.metadata = {
|
|
16
|
+
"source_code_uri" => "https://github.com/jeremyw/stamp",
|
|
17
|
+
"bug_tracker_uri" => "https://github.com/jeremyw/stamp/issues"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
s.required_ruby_version = ">= 3.1"
|
|
21
|
+
|
|
22
|
+
s.files = `git ls-files`.split("\n").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
17
23
|
s.require_paths = ["lib"]
|
|
18
24
|
|
|
19
|
-
s.add_development_dependency '
|
|
25
|
+
s.add_development_dependency 'minitest'
|
|
20
26
|
s.add_development_dependency 'rake'
|
|
21
27
|
end
|
metadata
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: stamp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jeremy Weiskotten
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-08-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: minitest
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
@@ -46,35 +46,37 @@ executables: []
|
|
|
46
46
|
extensions: []
|
|
47
47
|
extra_rdoc_files: []
|
|
48
48
|
files:
|
|
49
|
+
- ".github/workflows/ci.yml"
|
|
49
50
|
- ".gitignore"
|
|
50
|
-
- ".travis.yml"
|
|
51
51
|
- ".yardopts"
|
|
52
52
|
- CONTRIBUTING.md
|
|
53
53
|
- Gemfile
|
|
54
54
|
- LICENSE.txt
|
|
55
55
|
- README.md
|
|
56
56
|
- Rakefile
|
|
57
|
-
- cucumber.yml
|
|
58
|
-
- features/stamp.feature
|
|
59
|
-
- features/step_definitions/stamp_steps.rb
|
|
60
|
-
- features/support/env.rb
|
|
61
57
|
- lib/stamp.rb
|
|
58
|
+
- lib/stamp/core.rb
|
|
62
59
|
- lib/stamp/disambiguator.rb
|
|
63
60
|
- lib/stamp/emitters/am_pm.rb
|
|
64
61
|
- lib/stamp/emitters/ambiguous.rb
|
|
65
62
|
- lib/stamp/emitters/composite.rb
|
|
66
63
|
- lib/stamp/emitters/delegate.rb
|
|
67
64
|
- lib/stamp/emitters/lookup.rb
|
|
68
|
-
- lib/stamp/emitters/modifiable.rb
|
|
69
65
|
- lib/stamp/emitters/ordinal.rb
|
|
70
66
|
- lib/stamp/emitters/string.rb
|
|
67
|
+
- lib/stamp/emitters/twelve_hour.rb
|
|
71
68
|
- lib/stamp/emitters/two_digit.rb
|
|
69
|
+
- lib/stamp/emitters/utc_offset.rb
|
|
70
|
+
- lib/stamp/refinement.rb
|
|
72
71
|
- lib/stamp/translator.rb
|
|
73
72
|
- lib/stamp/version.rb
|
|
74
73
|
- stamp.gemspec
|
|
75
74
|
homepage: https://github.com/jeremyw/stamp
|
|
76
|
-
licenses:
|
|
77
|
-
|
|
75
|
+
licenses:
|
|
76
|
+
- MIT
|
|
77
|
+
metadata:
|
|
78
|
+
source_code_uri: https://github.com/jeremyw/stamp
|
|
79
|
+
bug_tracker_uri: https://github.com/jeremyw/stamp/issues
|
|
78
80
|
post_install_message:
|
|
79
81
|
rdoc_options: []
|
|
80
82
|
require_paths:
|
|
@@ -83,19 +85,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
83
85
|
requirements:
|
|
84
86
|
- - ">="
|
|
85
87
|
- !ruby/object:Gem::Version
|
|
86
|
-
version: '
|
|
88
|
+
version: '3.1'
|
|
87
89
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
88
90
|
requirements:
|
|
89
91
|
- - ">="
|
|
90
92
|
- !ruby/object:Gem::Version
|
|
91
93
|
version: '0'
|
|
92
94
|
requirements: []
|
|
93
|
-
|
|
94
|
-
rubygems_version: 2.2.2
|
|
95
|
+
rubygems_version: 3.0.3.1
|
|
95
96
|
signing_key:
|
|
96
97
|
specification_version: 4
|
|
97
98
|
summary: Date and time formatting for humans.
|
|
98
|
-
test_files:
|
|
99
|
-
- features/stamp.feature
|
|
100
|
-
- features/step_definitions/stamp_steps.rb
|
|
101
|
-
- features/support/env.rb
|
|
99
|
+
test_files: []
|
data/.travis.yml
DELETED
data/cucumber.yml
DELETED
data/features/stamp.feature
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
@stamp
|
|
2
|
-
Feature: Stamping a date
|
|
3
|
-
In order to format dates in a more programmer-friendly way
|
|
4
|
-
the stamp method
|
|
5
|
-
formats a date given a human-readable example.
|
|
6
|
-
|
|
7
|
-
@date
|
|
8
|
-
Scenario Outline: Formatting dates by example
|
|
9
|
-
Given the date September 8, 2011
|
|
10
|
-
When I stamp the example "<example>"
|
|
11
|
-
Then I produce "<output>"
|
|
12
|
-
|
|
13
|
-
Examples:
|
|
14
|
-
| example | output |
|
|
15
|
-
| January | September |
|
|
16
|
-
| Jan | Sep |
|
|
17
|
-
| Jan 1 | Sep 8 |
|
|
18
|
-
| Jan 01 | Sep 08 |
|
|
19
|
-
| Jan 10 | Sep 08 |
|
|
20
|
-
| Jan 1, 1999 | Sep 8, 2011 |
|
|
21
|
-
| Jan 12, 1999 | Sep 08, 2011 |
|
|
22
|
-
| 1 Jan 1999 | 8 Sep 2011 |
|
|
23
|
-
| 1/1/1999 | 9/8/2011 |
|
|
24
|
-
| 31/01/2013 | 08/09/2011 |
|
|
25
|
-
| 13 January 1999 | 08 September 2011 |
|
|
26
|
-
| Monday | Thursday |
|
|
27
|
-
| Tue, Jan 1 | Thu, Sep 8 |
|
|
28
|
-
| Tuesday, January 1, 1999 | Thursday, September 8, 2011 |
|
|
29
|
-
| 01/1999 | 09/2011 |
|
|
30
|
-
| 01/01 | 09/08 |
|
|
31
|
-
| 01/31 | 09/08 |
|
|
32
|
-
| 01/99 | 09/11 |
|
|
33
|
-
| 01/01/1999 | 09/08/2011 |
|
|
34
|
-
| 12/31/99 | 09/08/11 |
|
|
35
|
-
| 31/12 | 08/09 |
|
|
36
|
-
| 31/12/99 | 08/09/11 |
|
|
37
|
-
| 31-Jan-1999 | 08-Sep-2011 |
|
|
38
|
-
| 1999-01-01 | 2011-09-08 |
|
|
39
|
-
| 1999-12-31 | 2011-09-08 |
|
|
40
|
-
| DOB: 12-31-1999 | DOB: 09-08-2011 |
|
|
41
|
-
|
|
42
|
-
@date
|
|
43
|
-
Scenario Outline: Formatting dates with ordinal days
|
|
44
|
-
Given the date <date>
|
|
45
|
-
When I stamp the example "<example>"
|
|
46
|
-
Then I produce "<output>"
|
|
47
|
-
|
|
48
|
-
Examples:
|
|
49
|
-
| date | example | output |
|
|
50
|
-
| Jan 1, 1999 | July 4th | January 1st |
|
|
51
|
-
| Jan 2, 1999 | Dec 3rd | Jan 2nd |
|
|
52
|
-
| Jan 3, 1999 | Dec 2nd | Jan 3rd |
|
|
53
|
-
| Jan 4, 1999 | Jul 1st | Jan 4th |
|
|
54
|
-
| Jan 5, 1999 | Dec 1st | Jan 5th |
|
|
55
|
-
| Jan 6, 1999 | Dec 1st | Jan 6th |
|
|
56
|
-
| Jan 7, 1999 | Dec 1st | Jan 7th |
|
|
57
|
-
| Jan 8, 1999 | Dec 1st | Jan 8th |
|
|
58
|
-
| Jan 9, 1999 | Dec 1st | Jan 9th |
|
|
59
|
-
| Jan 10, 1999 | Dec 1st | Jan 10th |
|
|
60
|
-
| Jan 11, 1999 | Dec 1st | Jan 11th |
|
|
61
|
-
| Jan 12, 1999 | Dec 1st | Jan 12th |
|
|
62
|
-
| Jan 13, 1999 | Dec 1st | Jan 13th |
|
|
63
|
-
| Jan 14, 1999 | Dec 1st | Jan 14th |
|
|
64
|
-
| Jan 20, 1999 | Dec 1st | Jan 20th |
|
|
65
|
-
| Jan 21, 1999 | Dec 1st | Jan 21st |
|
|
66
|
-
| Jan 22, 1999 | Dec 1st | Jan 22nd |
|
|
67
|
-
| Jan 23, 1999 | Dec 1st | Jan 23rd |
|
|
68
|
-
| Jan 24, 1999 | Dec 1st | Jan 24th |
|
|
69
|
-
| Jan 1, 1999 | 4th of July | 1st of January |
|
|
70
|
-
| Jan 1, 1999 | 4th of July, 1999 | 1st of January, 1999 |
|
|
71
|
-
|
|
72
|
-
@time
|
|
73
|
-
Scenario Outline: Formatting times by example
|
|
74
|
-
Given the time zone is "EST"
|
|
75
|
-
And the time February 8, 2011 at 13:31:27
|
|
76
|
-
When I stamp the example "<example>"
|
|
77
|
-
Then I produce "<output>"
|
|
78
|
-
|
|
79
|
-
Examples:
|
|
80
|
-
| example | output |
|
|
81
|
-
| 8:59 am | 1:31 pm |
|
|
82
|
-
| 8:59am | 1:31pm |
|
|
83
|
-
| 08:59 AM | 01:31 PM |
|
|
84
|
-
| 08:59 PM | 01:31 PM |
|
|
85
|
-
| 23:59 | 13:31 |
|
|
86
|
-
| 8:59:59 am | 1:31:27 pm |
|
|
87
|
-
| 08:59:59 AM | 01:31:27 PM |
|
|
88
|
-
| 08:59:59 PM | 01:31:27 PM |
|
|
89
|
-
| 23:59:59 | 13:31:27 |
|
|
90
|
-
| 8:59 PST | 1:31 EST |
|
|
91
|
-
|
|
92
|
-
@date
|
|
93
|
-
@time
|
|
94
|
-
Scenario Outline: Formatting dates and times by example
|
|
95
|
-
Given the time September 8, 2011 at 13:31:27
|
|
96
|
-
When I stamp the example "<example>"
|
|
97
|
-
Then I produce "<output>"
|
|
98
|
-
|
|
99
|
-
Examples:
|
|
100
|
-
| example | output |
|
|
101
|
-
| Jan 1, 1999 8:59 am | Sep 8, 2011 1:31 pm |
|
|
102
|
-
| 08:59 AM 1999-12-31 | 01:31 PM 2011-09-08 |
|
|
103
|
-
| Date: Jan 1, 1999 Time: 8:59 am | Date: Sep 8, 2011 Time: 1:31 pm |
|
|
104
|
-
|
|
105
|
-
Scenario: strftime directives just get passed through
|
|
106
|
-
Given the date December 21, 2012
|
|
107
|
-
When I stamp the example "John Cusack was in a movie about Jan (%-m) %e, %Y, but it wasn't very good."
|
|
108
|
-
Then I produce "John Cusack was in a movie about Dec (%-m) %e, %Y, but it wasn't very good."
|
|
109
|
-
|
|
110
|
-
Scenario: Plain text just gets passed through
|
|
111
|
-
Given the date June 1, 1926
|
|
112
|
-
When I stamp the example "Marilyn Monroe was born on January 9, 1999."
|
|
113
|
-
Then I produce "Marilyn Monroe was born on June 1, 1926."
|
|
114
|
-
|
|
115
|
-
Scenario Outline: Aliases for the stamp method
|
|
116
|
-
Given the date December 9, 2011
|
|
117
|
-
When I call "<alias>" with "1999-01-31"
|
|
118
|
-
Then I produce "2011-12-09"
|
|
119
|
-
|
|
120
|
-
Examples:
|
|
121
|
-
| alias |
|
|
122
|
-
| stamp_like |
|
|
123
|
-
| format_like |
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
module StampStepHelpers
|
|
2
|
-
def month(month_name)
|
|
3
|
-
Date::MONTHNAMES.index(month_name) || Date::ABBR_MONTHNAMES.index(month_name)
|
|
4
|
-
end
|
|
5
|
-
end
|
|
6
|
-
World(StampStepHelpers)
|
|
7
|
-
|
|
8
|
-
Given /^the date (\w+) (\d+), (\d{4})$/ do |month_name, day, year|
|
|
9
|
-
@target = Date.new(year.to_i, month(month_name), day.to_i)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
Given /^the time (\w+) (\d+), (\d+) at (\d{2}):(\d{2}):(\d{2})$/ do |month_name, day, year, hours, minutes, seconds|
|
|
13
|
-
@target = Time.local(year.to_i, month(month_name), day.to_i, hours.to_i, minutes.to_i, seconds.to_i)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
Given /^the time zone is "(.*?)"$/ do |zone|
|
|
17
|
-
ENV['TZ'] = zone
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
When /^I stamp the example "([^"]*)"$/ do |example|
|
|
21
|
-
@stamped = @target.stamp(example)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
Then /^I produce "([^"]*)"$/ do |expected|
|
|
25
|
-
assert_equal expected.strip, @stamped.strip
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
When /^I call "([^"]*)" with "([^"]*)"$/ do |method, arg|
|
|
29
|
-
@stamped = @target.send(method, arg)
|
|
30
|
-
end
|
data/features/support/env.rb
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
require 'bundler'
|
|
2
|
-
begin
|
|
3
|
-
Bundler.setup(:default, :development)
|
|
4
|
-
rescue Bundler::BundlerError => e
|
|
5
|
-
$stderr.puts e.message
|
|
6
|
-
$stderr.puts "Run `bundle install` to install missing gems"
|
|
7
|
-
exit e.status_code
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
|
|
11
|
-
require 'stamp'
|
|
12
|
-
|
|
13
|
-
require 'test/unit/assertions'
|
|
14
|
-
World(Test::Unit::Assertions)
|