Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
  • 5 commits
  • 17 files changed
  • 0 commit comments
  • 2 contributors
View
1  .gitignore
@@ -3,3 +3,4 @@
.yardoc
doc
pkg/*
+/Gemfile.lock
View
7 .travis.yml
@@ -5,9 +5,4 @@ rvm:
- 2.0.0
- ree
- rbx
- - jruby
-
-matrix:
- allow_failures:
- - rvm: 1.8.7
- - rvm: ree
+ - jruby
View
11 CONTRIBUTING.md
@@ -0,0 +1,11 @@
+## Contributing to stamp
+
+* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
+* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
+* Fork the project
+* Run `bundle install`
+* Run `rake` to execute the cucumber specs and make sure they all pass
+* Start a feature/bugfix branch
+* Commit and push until you are happy with your contribution
+* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
+* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
View
56 README.md
@@ -1,7 +1,7 @@
# stamp
Format dates and times based on human-friendly examples, not arcane
-strftime directives.
+[strftime](http://strfti.me) directives.
[![Build Status](https://secure.travis-ci.org/jeremyw/stamp.png)](http://travis-ci.org/jeremyw/stamp)
@@ -15,7 +15,7 @@ Your Ruby dates and times get a powerful new method: `stamp`.
You might be concerned that "stamp" isn't descriptive enough for developers
reading your code who aren't familiar with this gem. If that's the case, the
-following aliases are available:
+following aliases are provided:
* `stamp_like`
* `format_like`
@@ -27,11 +27,11 @@ and weekday parts you'd like, and your date will be formatted accordingly:
```ruby
date = Date.new(2011, 6, 9)
-date.stamp("March 1, 1999") #=> "June 9, 2011"
-date.stamp("Jan 1, 1999") #=> "Jun 9, 2011"
+date.stamp("March 1, 1999") #=> "June 9, 2011"
+date.stamp("Jan 1, 1999") #=> "Jun 9, 2011"
date.stamp("Jan 01") #=> "Jun 09"
-date.stamp("Sunday, May 1, 2000") #=> "Thursday, June 9, 2011"
-date.stamp("Sun Aug 5") #=> "Thu Jun 9"
+date.stamp("Sunday, May 1, 2000") #=> "Thursday, June 9, 2011"
+date.stamp("Sun Aug 5") #=> "Thu Jun 9"
date.stamp("12/31/99") #=> "06/09/11"
date.stamp("DOB: 12/31/2000") #=> "DOB: 06/09/2011"
```
@@ -50,11 +50,11 @@ hours, minutes, and seconds when it sees colon-separated values.
```ruby
time = Time.utc(2011, 6, 9, 20, 52, 30)
-time.stamp("3:00 AM") #=> " 8:52 PM"
+time.stamp("3:00 AM") #=> "8:52 PM"
time.stamp("01:00:00 AM") #=> "08:52:30 PM"
time.stamp("23:59") #=> "20:52"
time.stamp("23:59:59") #=> "20:52:30"
-time.stamp("Jan 1 at 01:00 AM") #=> "Jun 9 at 08:52 PM"
+time.stamp("Jan 1 at 01:00 AM") #=> "Jun 9 at 08:52 PM"
time.stamp("23:59 UTC") #=> "20:52 PST"
```
@@ -76,53 +76,29 @@ For example, "01/09" could refer to January 9, September 1, or
January 2009. More explicit examples include "12/31", "31/12", and "12/99".
Using unambiguous values will also help people who read the code in the
-future understand your intent.
+future, including yourself, understand your intent.
### Rails Integration
-Stamp makes it easy to configure your application's common date and time
-formats in a more self-documenting way with the `strftime_format` method:
+Stamp makes it easy to configure your Rails application's common date and time
+formats in a more self-documenting way with `DATE_FORMATS`:
```ruby
-# config/initializers/time_formats.rb
-Date::DATE_FORMATS[:short] = Stamp.strftime_format("Mon Jan 1")
-Time::DATE_FORMATS[:military] = Stamp.strftime_format("23:59")
+# config/initializers/date_formats.rb
+Date::DATE_FORMATS[:short] = Proc.new { |date| date.stamp("Sun Jan 5") }
+Time::DATE_FORMATS[:military] = Proc.new { |time| time.stamp("5 January 23:59") }
```
To use your formats:
```ruby
Date.today.to_s(:short) #=> "Sat Jul 16"
-Time.now.to_s(:military) #=> "15:35"
+Time.now.to_s(:military) #=> "16 July 15:35"
```
### Limitations
-* DateTime should inherit stamp behavior from Date, but it hasn't been thoroughly tested. Patches welcome!
-
-### Advanced Usage
-
-If you need more obscure formatting options, you can include any valid
-[strftime](http://strfti.me) directives in your example string, and they'll
-just be passed along:
-
-```ruby
-Date.today.stamp("Week #%U, 1999") #=> "Week #23, 2011"
-```
-
-Check out [http://strfti.me](http://strfti.me) for more ideas.
-
-## Contributing to stamp
-
-* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
-* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
-* Fork the project
-* Run `bundle install`
-* Run `rake` to execute the cucumber specs and make sure they all pass
-* Start a feature/bugfix branch
-* Commit and push until you are happy with your contribution
-* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
-* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
+* `DateTime` should inherit stamp behavior from `Date`, but it hasn't been thoroughly tested. Patches welcome!
## Copyright
View
25 features/stamp.feature
@@ -14,15 +14,15 @@ Feature: Stamping a date
| example | output |
| January | September |
| Jan | Sep |
- | Jan 1 | Sep 8 |
+ | Jan 1 | Sep 8 |
| Jan 01 | Sep 08 |
| Jan 10 | Sep 08 |
- | Jan 1, 1999 | Sep 8, 2011 |
+ | Jan 1, 1999 | Sep 8, 2011 |
| Jan 12, 1999 | Sep 08, 2011 |
| 13 January 1999 | 08 September 2011 |
| Monday | Thursday |
- | Tue, Jan 1 | Thu, Sep 8 |
- | Tuesday, January 1, 1999 | Thursday, September 8, 2011 |
+ | Tue, Jan 1 | Thu, Sep 8 |
+ | Tuesday, January 1, 1999 | Thursday, September 8, 2011 |
| 01/1999 | 09/2011 |
| 01/01 | 09/08 |
| 01/31 | 09/08 |
@@ -67,7 +67,8 @@ Feature: Stamping a date
@time
Scenario Outline: Formatting times by example
- Given the time September 8, 2011 at 13:31:27
+ Given the time zone is "EST"
+ And the time February 8, 2011 at 13:31:27
When I stamp the example "<example>"
Then I produce "<output>"
@@ -82,7 +83,7 @@ Feature: Stamping a date
| 08:59:59 AM | 01:31:27 PM |
| 08:59:59 PM | 01:31:27 PM |
| 23:59:59 | 13:31:27 |
- | 8:59 PST | 1:31 UTC |
+ | 8:59 PST | 1:31 EST |
@date
@time
@@ -93,19 +94,19 @@ Feature: Stamping a date
Examples:
| example | output |
- | Jan 1, 1999 8:59 am | Sep 8, 2011 1:31 pm |
+ | Jan 1, 1999 8:59 am | Sep 8, 2011 1:31 pm |
| 08:59 AM 1999-12-31 | 01:31 PM 2011-09-08 |
- | Date: Jan 1, 1999 Time: 8:59 am | Date: Sep 8, 2011 Time: 1:31 pm |
+ | Date: Jan 1, 1999 Time: 8:59 am | Date: Sep 8, 2011 Time: 1:31 pm |
Scenario: strftime directives just get passed through
Given the date December 21, 2012
- When I stamp the example "John Cusack was in a movie about %b %d, %Y, but it wasn't very good."
- Then I produce "John Cusack was in a movie about Dec 21, 2012, but it wasn't very good."
+ When I stamp the example "John Cusack was in a movie about Jan (%-m) %e, %Y, but it wasn't very good."
+ Then I produce "John Cusack was in a movie about Dec (%-m) %e, %Y, but it wasn't very good."
Scenario: Plain text just gets passed through
Given the date June 1, 1926
- When I stamp the example "Marilyn Monroe was born on January 1, 1999."
- Then I produce "Marilyn Monroe was born on June 1, 1926."
+ When I stamp the example "Marilyn Monroe was born on January 9, 1999."
+ Then I produce "Marilyn Monroe was born on June 1, 1926."
Scenario Outline: Aliases for the stamp method
Given the date December 9, 2011
View
6 features/step_definitions/stamp_steps.rb
@@ -10,7 +10,11 @@ def month(month_name)
end
Given /^the time (\w+) (\d+), (\d+) at (\d{2}):(\d{2}):(\d{2})$/ do |month_name, day, year, hours, minutes, seconds|
- @target = Time.utc(year.to_i, month(month_name), day.to_i, hours.to_i, minutes.to_i, seconds.to_i)
+ @target = Time.local(year.to_i, month(month_name), day.to_i, hours.to_i, minutes.to_i, seconds.to_i)
+end
+
+Given /^the time zone is "(.*?)"$/ do |zone|
+ ENV['TZ'] = zone
end
When /^I stamp the example "([^"]*)"$/ do |example|
View
55 lib/stamp.rb
@@ -1,53 +1,50 @@
require "date"
require "time"
+require "stamp/emitters/modifiable"
+require "stamp/emitters/am_pm"
+require "stamp/emitters/composite"
+require "stamp/emitters/delegate"
+require "stamp/emitters/lookup"
+require "stamp/emitters/ordinal"
+require "stamp/emitters/string"
+require "stamp/emitters/two_digit"
require "stamp/translator"
require "stamp/version"
module Stamp
-
- # Transforms the given example dates/time format to a format string
- # suitable for strftime.
- #
- # @param [String] example a human-friendly date/time example
- # @param [#strftime] the Date or Time to be formatted. Optional, but may
- # be used to support certain edge cases
- # @return [String] a strftime-friendly format
- #
- # @example
- # Stamp.strftime_format("Jan 1, 1999") #=> "%b %e, %Y"
- def self.strftime_format(example, target=nil)
- Stamp::StrftimeTranslator.new(target).translate(example)
- end
+ # Limits the number of formats that we memoize to prevent unbounded
+ # memory consumption.
+ MEMOIZATION_CAP = 999
# Formats a date/time using a human-friendly example as a template.
#
- # @param [String] example a human-friendly date/time example
+ # @param [String] example a human-friendly date/time example
+ # @param [Hash] options
+ # @option options [Boolean] :memoize (true)
+ #
# @return [String] the formatted date or time
#
# @example
# Date.new(2012, 12, 21).stamp("Jan 1, 1999") #=> "Dec 21, 2012"
def stamp(example)
- strftime(strftime_format(example))
+ memoize_stamp_emitters(example).format(self)
end
alias :stamp_like :stamp
alias :format_like :stamp
- # Transforms the given example date/time format to a format string
- # suitable for strftime.
- #
- # @param [String] example a human-friendly date/time example
- # @return [String] a strftime-friendly format
- #
- # @example
- # Date.today.strftime_format("Jan 1, 1999") #=> "%b %e, %Y"
- def strftime_format(example)
- # delegate to the class method, providing self as a target value to
- # support certain edge cases
- Stamp.strftime_format(example, self)
+ # Memoizes the set of emitter objects for the given +example+ in
+ # order to improve performance.
+ def memoize_stamp_emitters(example)
+ @@memoized_stamp_emitters ||= {}
+ @@memoized_stamp_emitters.clear if @@memoized_stamp_emitters.size > MEMOIZATION_CAP
+ @@memoized_stamp_emitters[example] ||= stamp_emitters(example)
end
+ def stamp_emitters(example)
+ Translator.new.translate(example)
+ end
end
Date.send(:include, ::Stamp)
-Time.send(:include, ::Stamp)
+Time.send(:include, ::Stamp)
View
22 lib/stamp/emitters/am_pm.rb
@@ -0,0 +1,22 @@
+module Stamp
+ module Emitters
+ class AmPm
+ include Modifiable
+
+ AM = 'am'
+ PM = 'pm'
+
+ def initialize(&block)
+ @modifier = block
+ end
+
+ def format(target)
+ modify(target.hour < 12 ? AM : PM)
+ end
+
+ def field
+ nil
+ end
+ end
+ end
+end
View
31 lib/stamp/emitters/composite.rb
@@ -0,0 +1,31 @@
+module Stamp
+ module Emitters
+ class Composite
+ include Enumerable
+
+ def initialize
+ @emitters = []
+ end
+
+ def format(target)
+ # NOTE using #each to build string because benchmarking shows
+ # that it's ~20% faster than .map.join('')
+ result = ''
+ @emitters.each { |e| result << e.format(target).to_s }
+ result
+ end
+
+ def <<(emitter)
+ if emitter.is_a?(Enumerable)
+ emitter.each { |e| @emitters << e }
+ else
+ @emitters << emitter
+ end
+ end
+
+ def each(&block)
+ @emitters.each(&block)
+ end
+ end
+ end
+end
View
19 lib/stamp/emitters/delegate.rb
@@ -0,0 +1,19 @@
+module Stamp
+ module Emitters
+ class Delegate
+ include Modifiable
+
+ attr_reader :field
+
+ # @param [field] the field to be formatted (e.g. +:month+, +:year+)
+ def initialize(field, &block)
+ @field = field
+ @modifier = block
+ end
+
+ def format(target)
+ modify(target.send(field))
+ end
+ end
+ end
+end
View
27 lib/stamp/emitters/lookup.rb
@@ -0,0 +1,27 @@
+module Stamp
+ module Emitters
+ class Lookup
+ attr_reader :field
+
+ # @param [field] the field to be formatted (e.g. +:month+, +:year+)
+ # @param [lookup] an array of the string values to be formatted (e.g. +Date::DAYNAMES+)
+ # or a +call+able that returns the formatted value
+ def initialize(field, lookup=nil)
+ @field = field
+ @lookup = lookup
+ end
+
+ def format(target)
+ lookup(target.send(field))
+ end
+
+ def lookup(value)
+ if @lookup.respond_to?(:call)
+ @lookup.call(value)
+ else
+ @lookup[value]
+ end
+ end
+ end
+ end
+end
View
9 lib/stamp/emitters/modifiable.rb
@@ -0,0 +1,9 @@
+module Modifiable
+ def modify(value)
+ if @modifier
+ @modifier.call(value)
+ else
+ value
+ end
+ end
+end
View
31 lib/stamp/emitters/ordinal.rb
@@ -0,0 +1,31 @@
+module Stamp
+ module Emitters
+ class Ordinal
+ attr_reader :field
+
+ # @param [field] the field to be formatted (e.g. +:month+, +:year+)
+ def initialize(field)
+ @field = field
+ end
+
+ def format(target)
+ ordinalize(target.send(field))
+ end
+
+ # Cribbed from ActiveSupport::Inflector
+ # https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb
+ def ordinalize(number)
+ number.to_s + if (11..13).include?(number % 100)
+ 'th'
+ else
+ case number % 10
+ when 1; 'st'
+ when 2; 'nd'
+ when 3; 'rd'
+ else 'th'
+ end
+ end
+ end
+ end
+ end
+end
View
23 lib/stamp/emitters/string.rb
@@ -0,0 +1,23 @@
+module Stamp
+ module Emitters
+ class String
+ attr_reader :value
+
+ def initialize(value)
+ @value = value
+ end
+
+ def format(target)
+ value
+ end
+
+ def <<(emitter)
+ value << emitter.value
+ end
+
+ def field
+ nil
+ end
+ end
+ end
+end
View
22 lib/stamp/emitters/two_digit.rb
@@ -0,0 +1,22 @@
+module Stamp
+ module Emitters
+ # Emits the given field as a two-digit number with a leading
+ # zero if necessary.
+ class TwoDigit
+ include Modifiable
+
+ attr_reader :field
+
+ # @param [field] the field to be formatted (e.g. +:month+, +:year+)
+ def initialize(field, &block)
+ @field = field
+ @modifier = block
+ end
+
+ def format(target)
+ value = modify(target.send(field))
+ value < 10 ? "0#{value}" : value
+ end
+ end
+ end
+end
View
149 lib/stamp/translator.rb
@@ -1,13 +1,27 @@
module Stamp
- class StrftimeTranslator
+ class Translator
+
+ # Full list of time zone abbreviations from
+ # http://en.wikipedia.org/wiki/List_of_time_zone_abbreviations
+ TIME_ZONE_ABBREVIATIONS = %w{
+ ACDT ACST ACT ADT AEDT AEST AFT AKDT AKST AMST AMT ART AST AWDT AWST AZOST AZT
+ BDT BIOT BIT BOT BRT BST BTTCAT CCT CDT CEDT CEST CET CHADT CHAST CHOT ChST CHUT
+ CIST CIT CKT CLST CLT COST COT CST CT CVT CWST CXT DAVT DDUT DFT EASST EAST EAT
+ ECT EDT EEDT EEST EET EGST EGT EIT EST FET FJT FKST FKT FNT GALT GAMT GET GFT
+ GILT GIT GMT GST GYT HADT HAEC HAST HKT HMT HOVT HST ICT IDT IOT IRDT IRKT IRST
+ IST JST KGT KOST KRAT KST LHST LINT MAGT MART MAWT MDT MET MEST MHT MIST MIT MMT
+ MSK MST MUT MVT MYT NCT NDT NFT NPT NST NT NUT NZDT NZST OMST ORAT PDT PET PETT
+ PGT PHOT PHT PKT PMDT PMST PONT PST RET ROTT SAKT SAMT SAST SBT SCT SGT SLT SRT
+ SST SYOT TAHT THA TFT TJT TKT TLT TMT TOT TVT UCT ULAT UTC UYST UYT UZT VET VLAT
+ VOLT VOST VUT WAKT WAST WAT WEDT WEST WET WST YAKT YEKT
+ }
+
+ TIMEZONE_REGEXP = /^(#{TIME_ZONE_ABBREVIATIONS.join('|')})$/
MONTHNAMES_REGEXP = /^(#{Date::MONTHNAMES.compact.join('|')})$/i
ABBR_MONTHNAMES_REGEXP = /^(#{Date::ABBR_MONTHNAMES.compact.join('|')})$/i
DAYNAMES_REGEXP = /^(#{Date::DAYNAMES.join('|')})$/i
ABBR_DAYNAMES_REGEXP = /^(#{Date::ABBR_DAYNAMES.join('|')})$/i
- # Full list of time zone abbreviations from http://en.wikipedia.org/wiki/List_of_time_zone_abbreviations
- TIMEZONE_REGEXP = /^(ACDT|ACST|ACT|ADT|AEDT|AEST|AFT|AKDT|AKST|AMST|AMT|ART|AST|AST|AST|AST|AWDT|AWST|AZOST|AZT|BDT|BIOT|BIT|BOT|BRT|BST|BST|BTT|CAT|CCT|CDT|CDT|CEDT|CEST|CET|CHADT|CHAST|CHOT|ChST|CHUT|CIST|CIT|CKT|CLST|CLT|COST|COT|CST|CST|CST|CST|CST|CT|CVT|CWST|CXT|DAVT|DDUT|DFT|EASST|EAST|EAT|ECT|ECT|EDT|EEDT|EEST|EET|EGST|EGT|EIT|EST|EST|FET|FJT|FKST|FKT|FNT|GALT|GAMT|GET|GFT|GILT|GIT|GMT|GST|GST|GYT|HADT|HAEC|HAST|HKT|HMT|HOVT|HST|ICT|IDT|IOT|IRDT|IRKT|IRST|IST|IST|IST|JST|KGT|KOST|KRAT|KST|LHST|LHST|LINT|MAGT|MART|MAWT|MDT|MET|MEST|MHT|MIST|MIT|MMT|MSK|MST|MST|MST|MUT|MVT|MYT|NCT|NDT|NFT|NPT|NST|NT|NUT|NZDT|NZST|OMST|ORAT|PDT|PET|PETT|PGT|PHOT|PHT|PKT|PMDT|PMST|PONT|PST|PST|RET|ROTT|SAKT|SAMT|SAST|SBT|SCT|SGT|SLT|SRT|SST|SST|SYOT|TAHT|THA|TFT|TJT|TKT|TLT|TMT|TOT|TVT|UCT|ULAT|UTC|UYST|UYT|UZT|VET|VLAT|VOLT|VOST|VUT|WAKT|WAST|WAT|WEDT|WEST|WET|WST|YAKT|YEKT)$/
-
ONE_DIGIT_REGEXP = /^\d{1}$/
TWO_DIGIT_REGEXP = /^\d{2}$/
FOUR_DIGIT_REGEXP = /^\d{4}$/
@@ -25,146 +39,115 @@ class StrftimeTranslator
OBVIOUS_DAYS = 13..31
OBVIOUS_24_HOUR = 13..23
+ TWO_DIGIT_YEAR_EMITTER = Emitters::TwoDigit.new(:year) { |year| year % 100 }
+ TWO_DIGIT_MONTH_EMITTER = Emitters::TwoDigit.new(:month)
+ TWO_DIGIT_DAY_EMITTER = Emitters::TwoDigit.new(:day)
+ HOUR_TO_12_HOUR = lambda { |h| ((h - 1) % 12) + 1 }
+
OBVIOUS_DATE_MAP = {
- OBVIOUS_YEARS => '%y',
- OBVIOUS_MONTHS => '%m',
- OBVIOUS_DAYS => '%d'
+ OBVIOUS_YEARS => TWO_DIGIT_YEAR_EMITTER,
+ OBVIOUS_MONTHS => TWO_DIGIT_MONTH_EMITTER,
+ OBVIOUS_DAYS => TWO_DIGIT_DAY_EMITTER
}
TWO_DIGIT_DATE_SUCCESSION = {
- :month => '%d',
- :day => '%y',
- :year => '%m'
+ :month => TWO_DIGIT_DAY_EMITTER,
+ :day => TWO_DIGIT_YEAR_EMITTER,
+ :year => TWO_DIGIT_MONTH_EMITTER
}
TWO_DIGIT_TIME_SUCCESSION = {
- :hour => '%M',
- :minute => '%S'
+ :hour => Emitters::TwoDigit.new(:min),
+ :min => Emitters::TwoDigit.new(:sec)
}
- def initialize(target_date_or_time)
- @target = target_date_or_time
- end
-
- # Cribbed from ActiveSupport to format ordinal days (1st, 2nd, 23rd etc).
- def ordinalize(number)
- if (11..13).include?(number.to_i % 100)
- "#{number}th"
- else
- case number.to_i % 10
- when 1; "#{number}st"
- when 2; "#{number}nd"
- when 3; "#{number}rd"
- else "#{number}th"
- end
- end
- end
-
def translate(example)
# extract any substrings that look like times, like "23:59" or "8:37 am"
before, time_example, after = example.partition(TIME_REGEXP)
- # transform any date tokens to strftime directives
- words = strftime_directives(before.split(/\b/)) do |token, previous_part|
- strftime_date_directive(token, previous_part)
+ # build emitters from the example date
+ emitters = Emitters::Composite.new
+ emitters << build_emitters(before.split(/\b/)) do |token, previous_part|
+ date_emitter(token, previous_part)
end
- # transform the example time string to strftime directives
+ # build emitters from the example time
unless time_example.empty?
time_parts = time_example.scan(TIME_REGEXP).first
- words += strftime_directives(time_parts) do |token, previous_part|
- strftime_time_directive(token, previous_part)
+ emitters << build_emitters(time_parts) do |token, previous_part|
+ time_emitter(token, previous_part)
end
end
# recursively process any remaining text
- words << translate(after) unless after.empty?
- words.join
- end
-
- # Returns symbolic date part for given strftime directive.
- def date_part(strftime_directive)
- case strftime_directive
- when '%b', '%B', '%m'
- :month
- when '%d', '%e'
- :day
- when '%y', '%Y'
- :year
- when '%H', '%I', '%l'
- :hour
- when '%M'
- :minute
- when '%S'
- :second
- end
+ emitters << translate(after) unless after.empty?
+ emitters
end
- # Transforms tokens that look like date/time parts to strftime directives.
- def strftime_directives(tokens)
+ # Transforms tokens that look like date/time parts to emitter objects.
+ def build_emitters(tokens)
previous_part = nil
tokens.map do |token|
- directive = yield(token, previous_part)
- previous_part = date_part(directive) unless directive.nil?
- directive || token
+ emitter = yield(token, previous_part)
+ previous_part = emitter.field unless emitter.nil?
+
+ emitter || Emitters::String.new(token)
end
end
- def strftime_time_directive(token, previous_part)
+ def time_emitter(token, previous_part)
case token
when MERIDIAN_LOWER_REGEXP
- if RUBY_VERSION =~ /^1.8/ && @target.is_a?(Time)
- # 1.8.7 doesn't implement %P
- @target.strftime("%p").downcase
- else
- '%P'
- end
+ Emitters::AmPm.new
when MERIDIAN_UPPER_REGEXP
- '%p'
+ Emitters::AmPm.new { |v| v.upcase }
when TWO_DIGIT_REGEXP
TWO_DIGIT_TIME_SUCCESSION[previous_part] ||
case token.to_i
when OBVIOUS_24_HOUR
- '%H' # 24-hour clock
+ # 24-hour clock
+ Emitters::TwoDigit.new(:hour)
else
- '%I' # 12-hour clock with leading zero
+ # 12-hour clock with leading zero
+ Emitters::TwoDigit.new(:hour, &HOUR_TO_12_HOUR)
end
when ONE_DIGIT_REGEXP
- '%l' # hour without leading zero
+ # 12-hour clock without leading zero
+ Emitters::Delegate.new(:hour, &HOUR_TO_12_HOUR)
end
end
- def strftime_date_directive(token, previous_part)
+ def date_emitter(token, previous_part)
case token
when MONTHNAMES_REGEXP
- '%B'
+ Emitters::Lookup.new(:month, Date::MONTHNAMES)
when ABBR_MONTHNAMES_REGEXP
- '%b'
+ Emitters::Lookup.new(:month, Date::ABBR_MONTHNAMES)
when DAYNAMES_REGEXP
- '%A'
+ Emitters::Lookup.new(:wday, Date::DAYNAMES)
when ABBR_DAYNAMES_REGEXP
- '%a'
+ Emitters::Lookup.new(:wday, Date::ABBR_DAYNAMES)
when TIMEZONE_REGEXP
- '%Z'
+ Emitters::Delegate.new(:zone)
when FOUR_DIGIT_REGEXP
- '%Y'
+ Emitters::Delegate.new(:year)
when ORDINAL_DAY_REGEXP
- ordinalize(@target.day)
+ Emitters::Ordinal.new(:day)
when TWO_DIGIT_REGEXP
value = token.to_i
obvious_mappings =
- OBVIOUS_DATE_MAP.reject { |k,v| date_part(v) == previous_part }
+ OBVIOUS_DATE_MAP.reject { |k,v| v.field == previous_part }
obvious_directive = obvious_mappings.find do |range, directive|
break directive if range === value
@@ -174,10 +157,10 @@ def strftime_date_directive(token, previous_part)
# disambiguate based on context
obvious_directive ||
TWO_DIGIT_DATE_SUCCESSION[previous_part] ||
- '%m'
+ TWO_DIGIT_MONTH_EMITTER
when ONE_DIGIT_REGEXP
- '%e' # day without leading zero
+ Emitters::Delegate.new(:day)
end
end
View
2  lib/stamp/version.rb
@@ -1,3 +1,3 @@
module Stamp
- VERSION = "0.4.0"
+ VERSION = "0.5.0"
end

No commit comments for this range

Something went wrong with that request. Please try again.