module Doing::ChronifyString
Chronify methods for strings
Public Instance Methods
Converts input string into a Time
object when input takes on the following formats: - interval format e.g. ‘1d2h30m’, ‘45m’ etc. - a semantic phrase e.g. ‘yesterday 5:30pm’ - a strftime e.g. ‘2016-03-15 15:32:04 PDT’
@param options Additional options
@option options :future [Boolean] assume future date (default: false)
@option options :guess [Symbol] :begin or :end to assume beginning or end of arbitrary time range
@return [DateTime] result
# File lib/doing/chronify/string.rb, line 27 def chronify(**options) now = Time.now raise Errors::InvalidTimeExpression, "Invalid time expression #{inspect}" if to_s.strip == '' secs_ago = if match(/^(\d+)$/) # plain number, assume minutes Regexp.last_match(1).to_i * 60 elsif (m = match(/^(?:(?<day>\d+)d)? *(?:(?<hour>\d+)h)? *(?:(?<min>\d+)m)?$/i)) # day/hour/minute format e.g. 1d2h30m [[m['day'], 24 * 3600], [m['hour'], 3600], [m['min'], 60]].map { |qty, secs| qty ? (qty.to_i * secs) : 0 }.reduce(0, :+) end if secs_ago res = now - secs_ago Doing.logger.debug('Parser:', %(date/time string "#{self}" interpreted as #{res} (#{secs_ago} seconds ago))) else date_string = dup date_string = 'today' if date_string.match(Types::REGEX_DAY) && now.strftime('%a') =~ /^#{Regexp.last_match(1)}/i date_string = "#{options[:context].to_s} #{date_string}" if date_string =~ Types::REGEX_TIME && options[:context] res = Chronic.parse(date_string, { guess: options.fetch(:guess, :begin), context: options.fetch(:future, false) ? :future : :past, ambiguous_time_range: 8 }) Doing.logger.debug('Parser:', %(date/time string "#{self}" interpreted as #{res})) end res end
Converts simple strings into seconds that can be added to a Time
object
Input string can be HH:MM or XX[[XXhm]] (1d2h30m, 45m, 1.5d, 1h20m, etc.)
@return [Integer] seconds
# File lib/doing/chronify/string.rb, line 70 def chronify_qty minutes = 0 case self.strip when /^(\d+):(\d\d)$/ minutes += Regexp.last_match(1).to_i * 60 minutes += Regexp.last_match(2).to_i when /^(\d+(?:\.\d+)?)([hmd])?/ scan(/(\d+(?:\.\d+)?)([hmd])?/).each do |m| amt = m[0] type = m[1].nil? ? 'm' : m[1] minutes += case type.downcase when 'm' amt.to_i when 'h' (amt.to_f * 60).round when 'd' (amt.to_f * 60 * 24).round else 0 end end end minutes * 60 end
# File lib/doing/chronify/string.rb, line 164 def is_range? self =~ / (to|through|thru|(un)?til|-+) / end
Splits a range string and returns an array of DateTime objects as [start, end]. If only one date is given, end time is nil.
@return [Array<DateTime>] Start and end dates as array @example Process a natural language date range “mon 3pm to mon 5pm”.split_date_range
# File lib/doing/chronify/string.rb, line 178 def split_date_range time_rx = /^(\d{1,2}(:\d{1,2})?( *(am|pm))?|midnight|noon)$/ range_rx = / (to|through|thru|(?:un)?til|-+) / date_string = dup if date_string.is_range? # Do we want to differentiate between "to" and "through"? # inclusive = date_string =~ / (through|thru|-+) / ? true : false inclusive = true dates = date_string.split(range_rx) if dates[0].strip =~ time_rx && dates[-1].strip =~ time_rx start = dates[0].strip finish = dates[-1].strip else start = dates[0].chronify(guess: :begin, future: false) finish = dates[-1].chronify(guess: inclusive ? :end : :begin, future: true) end raise Errors::InvalidTimeExpression, "Unrecognized date string (#{dates[0]})" if start.nil? raise Errors::InvalidTimeExpression, "Unrecognized date string (#{dates[-1]})" if finish.nil? else if date_string.strip =~ time_rx start = date_string.strip finish = '11:59pm' else start = date_string.strip.chronify(guess: :begin, future: false) finish = start + (24 * 60 * 60) end raise Errors::InvalidTimeExpression, 'Unrecognized date string' unless start end if start.is_a? String Doing.logger.debug('Parser:', "--from string interpreted as time span, from #{start || '12am'} to #{finish || '11:59pm'}") else Doing.logger.debug('Parser:', "date range interpreted as #{start.strftime('%F %R')} -- #{finish ? finish.strftime('%F %R') : 'now'}") end [start, finish] end
Convert DD:HH:MM to a natural language string
@param format [Symbol] The format to output (:dhm, :hm, :m, :clock, :natural)
# File lib/doing/chronify/string.rb, line 117 def time_string(format: :dhm) to_seconds.time_string(format: format) end
Convert DD:HH:MM to seconds
@return [Integer] rounded number of seconds
# File lib/doing/chronify/string.rb, line 101 def to_seconds mtch = match(/(\d+):(\d+):(\d+)/) raise Errors::DoingRuntimeError, "Invalid time string: #{self}" unless mtch h = mtch[1] m = mtch[2] s = mtch[3] (h.to_i * 60 * 60) + (m.to_i * 60) + s.to_i end