lib/third_base/date.rb in third_base-1.1.1 vs lib/third_base/date.rb in third_base-1.2.0
- old
+ new
@@ -41,11 +41,12 @@
DEFAULT_PARSERS[:us] = [[%r{\A(\d\d?)[-./ ](\d\d?)[-./ ](\d\d(?:\d\d)?)\z}o, proc{|m| {:civil=>[two_digit_year(m[3]), m[1].to_i, m[2].to_i]}}],
[%r{\A(\d\d?)/(\d?\d)\z}o, proc{|m| {:civil=>[Time.now.year, m[1].to_i, m[2].to_i]}}],
[%r{\A#{MONTHNAME_RE_PATTERN}[-./ ](\d\d?)(?:st|nd|rd|th)?,?(?:[-./ ](-?(?:\d\d(?:\d\d)?)))?\z}io, proc{|m| {:civil=>[m[3] ? two_digit_year(m[3]) : Time.now.year, MONTH_NUM_MAP[m[1].downcase], m[2].to_i]}}],
[%r{\A(\d\d?)(?:st|nd|rd|th)?[-./ ]#{MONTHNAME_RE_PATTERN}[-./ ](-?\d{4})\z}io, proc{|m| {:civil=>[m[3].to_i, MONTH_NUM_MAP[m[2].downcase], m[1].to_i]}}],
[%r{\A(-?\d{4})[-./ ]#{MONTHNAME_RE_PATTERN}[-./ ](\d\d?)(?:st|nd|rd|th)?\z}io, proc{|m| {:civil=>[m[1].to_i, MONTH_NUM_MAP[m[2].downcase], m[3].to_i]}}],
- [%r{\A#{MONTHNAME_RE_PATTERN}[-./ ](-?\d{4})\z}io, proc{|m| {:civil=>[m[2].to_i, MONTH_NUM_MAP[m[1].downcase], 1]}}]]
+ [%r{\A#{MONTHNAME_RE_PATTERN}[-./ ](-?\d{4})\z}io, proc{|m| {:civil=>[m[2].to_i, MONTH_NUM_MAP[m[1].downcase], 1]}}],
+ [%r{\A#{ABBR_DAYNAME_RE_PATTERN} #{ABBR_MONTHNAME_RE_PATTERN} (\d\d?) (-?\d{4})\z}io, proc{|m| {:civil=>[m[4].to_i, MONTH_NUM_MAP[m[2].downcase], m[3].to_i]}}]]
DEFAULT_PARSERS[:eu] = [[%r{\A(\d\d?)[-./ ](\d\d?)[-./ ](\d\d\d\d)\z}o, proc{|m| {:civil=>[m[3].to_i, m[2].to_i, m[1].to_i]}}],
[%r{\A(\d\d?)[-./ ](\d?\d)[-./ ](\d?\d)\z}o, proc{|m| {:civil=>[two_digit_year(m[1]), m[2].to_i, m[3].to_i]}}]]
DEFAULT_PARSERS[:num] = [[%r{\A\d{2,8}\z}o, proc do |m|
m = m[0]
case m.length
@@ -89,17 +90,30 @@
class << self
alias new! new
end
- # Add a parser to the parser type. re should be
- # a Regexp, and a block must be provided. The block
- # should take a single MatchData argument, a return
- # either nil specifying it could not parse the string,
- # or a hash of values to be passed to new!.
- def self.add_parser(type, re, &block)
- parser_hash[type].unshift([re, block])
+ # Add a parser to the parser type. Arguments:
+ # * type - The parser type to which to add the parser, should be a Symbol.
+ # * pattern - Can be either a Regexp or String:
+ # * String - A strptime parser regular expression is created using pattern as the format string.
+ # If a block is given, it is used. If no block is given, the parser will
+ # operate identically to strptime.
+ # * Regexp - The regular expression is used directly. In this case, a block must be provided,
+ # or an error is raised.
+ #
+ # The block, if provided, should take a single MatchData argument. It should return
+ # nil if it cannot successfully parse the string, an instance of this class, or a hash of
+ # values to be passed to new!.
+ def self.add_parser(type, pattern, &block)
+ if pattern.is_a?(String)
+ pattern, blk = strptime_pattern_and_block(pattern)
+ block ||= blk
+ else
+ raise(ArgumentError, 'must provide block for Regexp parser') unless block_given?
+ end
+ parser_hash[type].unshift([pattern, block])
end
# Add a parser type to the list of parser types.
# Should be used if you want to add your own parser
# types.
@@ -142,11 +156,11 @@
def self.parse(str, opts={})
s = str.strip
parsers(opts[:parser_types]) do |pattern, block|
if m = pattern.match(s)
if res = block.call(m)
- return new!(res)
+ return res.is_a?(Hash) ? new!(res) : res
end
end
end
raise ArgumentError, 'invalid date'
end
@@ -164,28 +178,19 @@
end
# Parse the string using the provided format (or the default format).
# Raises an ArgumentError if the format does not match the string.
def self.strptime(str, fmt=strptime_default)
- blocks = []
+ pattern, block = strptime_pattern_and_block(fmt)
s = str.strip
- date_hash = {}
- pattern = Regexp.escape(expand_strptime_format(fmt)).gsub(STRFTIME_RE) do |x|
- pat, *blks = _strptime_part(x[1..1])
- blocks += blks
- pat
- end
- if m = /#{pattern}/i.match(s)
- m.to_a[1..-1].zip(blocks) do |x, blk|
- blk.call(date_hash, x)
- end
- new_from_parts(date_hash)
+ if m = pattern.match(s)
+ block.call(m)
else
raise ArgumentError, 'invalid date'
end
end
-
+
# Returns a date with the current year, month, and date.
def self.today
t = Time.now
civil(t.year, t.mon, t.day)
end
@@ -281,19 +286,36 @@
def self.strptime_default
'%Y-%m-%d'
end
+ def self.strptime_pattern_and_block(fmt)
+ blocks = []
+ pattern = Regexp.escape(expand_strptime_format(fmt)).gsub(STRFTIME_RE) do |x|
+ pat, *blks = _strptime_part(x[1..1])
+ blocks += blks
+ pat
+ end
+ block = proc do |m|
+ h = {}
+ m.to_a[1..-1].zip(blocks) do |x, blk|
+ blk.call(h, x)
+ end
+ new_from_parts(h)
+ end
+ [/\A#{pattern}\z/i, block]
+ end
+
def self.two_digit_year(y)
y = if y.length == 2
y = y.to_i
(y < 69 ? 2000 : 1900) + y
else
y.to_i
end
end
- private_class_method :_expand_strptime_format, :_strptime_part, :default_parser_hash, :default_parser_list, :expand_strptime_format, :new_from_parts, :parser_hash, :parser_list, :parsers, :parsers_for_family, :strptime_default, :two_digit_year
+ private_class_method :_expand_strptime_format, :_strptime_part, :default_parser_hash, :default_parser_list, :expand_strptime_format, :new_from_parts, :parser_hash, :parser_list, :parsers, :parsers_for_family, :strptime_default, :strptime_pattern_and_block, :two_digit_year
reset_parsers!
# Public Instance Methods