lib/validates_timeliness/formats.rb in validates_timeliness-1.1.7 vs lib/validates_timeliness/formats.rb in validates_timeliness-2.0.0
- old
+ new
@@ -122,17 +122,17 @@
{ 'n' => [ /n{1}/, '(\d{1,2})', :min ] },
{ 'ss' => [ /s{2,}/, '(\d{2})', :sec ] },
{ 's' => [ /s{1}/, '(\d{1,2})', :sec ] },
{ 'u' => [ /u{1,}/, '(\d{1,6})', :usec ] },
{ 'ampm' => [ /ampm/, '((?:[aApP])\.?[mM]\.?)', :meridian ] },
- { 'zo' => [ /zo/, '(?:[+-]\d{2}:?\d{2})'] },
+ { 'zo' => [ /zo/, '([+-]\d{2}:?\d{2})', :offset ] },
{ 'tz' => [ /tz/, '(?:[A-Z]{1,4})' ] },
{ '_' => [ /_/, '\s?' ] }
]
- # Arguments whichs will be passed to the format proc if matched in the
- # time string. The key must should the key from the format tokens. The array
+ # Arguments which will be passed to the format proc if matched in the
+ # time string. The key must be the key from the format tokens. The array
# consists of the arry position of the arg, the arg name, and the code to
# place in the time array slot. The position can be nil which means the arg
# won't be placed in the array.
#
# The code can be used to manipulate the arg value if required, otherwise
@@ -144,10 +144,11 @@
:day => [2, 'd', 'd'],
:hour => [3, 'h', 'full_hour(h,md)'],
:min => [4, 'n', 'n'],
:sec => [5, 's', 's'],
:usec => [6, 'u', 'microseconds(u)'],
+ :offset => [7, 'z', 'offset_in_seconds(z)'],
:meridian => [nil, 'md', nil]
}
class << self
@@ -158,25 +159,33 @@
end
# Loop through format expressions for type and call proc on matches. Allow
# pre or post match strings to exist if strict is false. Otherwise wrap
# regexp in start and end anchors.
- # Returns 7 part time array.
- def parse(string, type, strict=true)
+ # Returns time array if matches a format, nil otherwise.
+ def parse(string, type, options={})
return string unless string.is_a?(String)
+ options.reverse_merge!(:strict => true)
+ sets = if options[:format]
+ [ send("#{type}_expressions").assoc(options[:format]) ]
+ else
+ expression_set(type, string)
+ end
+
matches = nil
- exp, processor = expression_set(type, string).find do |regexp, proc|
- full = /\A#{regexp}\Z/ if strict
+ processor = sets.each do |format, regexp, proc|
+ full = /\A#{regexp}\Z/ if options[:strict]
full ||= case type
when :date then /\A#{regexp}/
when :time then /#{regexp}\Z/
when :datetime then /\A#{regexp}\Z/
end
- matches = full.match(string.strip)
+ break(proc) if matches = full.match(string.strip)
end
- processor.call(*matches[1..7]) if matches
+ last = options[:include_offset] ? 8 : 7
+ processor.call(*matches[1..last]) if matches
end
# Delete formats of specified type. Error raised if format not found.
def remove_formats(type, *remove_formats)
remove_formats.each do |format|
@@ -204,12 +213,11 @@
index = before ? formats.index(before) : -1
formats.insert(index, format)
end
compile_format_expressions
end
-
-
+
# Removes formats where the 1 or 2 digit month comes first, to eliminate
# formats which are ambiguous with the European style of day then month.
# The mmm token is ignored as its not ambigous.
def remove_us_formats
us_format_regexp = /\Am{1,2}[^m]/
@@ -244,26 +252,21 @@
# Generates a proc which when executed maps the regexp capture groups to a
# proc argument based on order captured. A time array is built using the proc
# argument in the position indicated by the first element of the proc arg
# array.
#
- # Examples:
- #
- # 'yyyy-mm-dd hh:nn' => lambda {|y,m,d,h,n| md||=0; [unambiguous_year(y),month_index(m),d,full_hour(h,md),n,nil,nil].map {|i| i.to_i } }
- # 'dd/mm/yyyy h:nn_ampm' => lambda {|d,m,y,h,n,md| md||=0; [unambiguous_year(y),month_index(m),d,full_hour(h,md),n,nil,nil].map {|i| i.to_i } }
- #
def format_proc(order)
arg_map = format_proc_args
args = order.invert.sort.map {|p| arg_map[p[1]][1] }
arr = [nil] * 7
order.keys.each {|k| i = arg_map[k][0]; arr[i] = arg_map[k][2] unless i.nil? }
- proc_string = "lambda {|#{args.join(',')}| md||=nil; [#{arr.map {|i| i.nil? ? 'nil' : i }.join(',')}].map {|i| i.to_i } }"
+ proc_string = "lambda {|#{args.join(',')}| md||=nil; [#{arr.map {|i| i.nil? ? 'nil' : i }.join(',')}].map {|i| i.is_a?(Float) ? i : i.to_i } }"
eval proc_string
end
def compile_formats(formats)
- formats.map { |format| regexp, format_proc = format_expression_generator(format) }
+ formats.map { |format| [ format, *format_expression_generator(format) ] }
end
# Pick expression set and combine date and datetimes for
# datetime attributes to allow date string as datetime
def expression_set(type, string)
@@ -310,9 +313,16 @@
defined?(I18n) ? I18n.t('date.abbr_month_names') : Date::ABBR_MONTHNAMES
end
def microseconds(usec)
(".#{usec}".to_f * 1_000_000).to_i
+ end
+
+ def offset_in_seconds(offset)
+ sign = offset =~ /^-/ ? -1 : 1
+ parts = offset.scan(/\d\d/).map {|p| p.to_f }
+ parts[1] = parts[1].to_f / 60
+ (parts[0] + parts[1]) * sign * 3600
end
end
end
end