Module: Doing::ChronifyString
- Included in:
- String
- Defined in:
- lib/doing/chronify/string.rb
Overview
Chronify methods for strings
Instance Method Summary collapse
-
#chronify(**options) ⇒ DateTime
Converts input string into a Time object when input takes on the following formats: - interval format e.g.
-
#chronify_qty ⇒ Integer
Converts simple strings into seconds that can be added to a Time object.
-
#expand_date_tags(additional_tags = nil) ⇒ Object
Convert (chronify) natural language dates within configured date tags (tags whose value is expected to be a date).
- #is_range? ⇒ Boolean
-
#split_date_range ⇒ Array<DateTime>
Splits a range string and returns an array of DateTime objects as [start, end].
-
#time_string(format: :dhm) ⇒ Object
Convert DD:HH:MM to a natural language string.
-
#to_seconds ⇒ Integer
Convert DD:HH:MM to seconds.
Instance Method Details
#chronify(**options) ⇒ DateTime
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'
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/doing/chronify/string.rb', line 27 def chronify(**) 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 = "#{[:context].to_s} #{date_string}" if date_string =~ Types::REGEX_TIME && [:context] res = Chronic.parse(date_string, { guess: .fetch(:guess, :begin), context: .fetch(:future, false) ? :future : :past, ambiguous_time_range: 8 }) Doing.logger.debug('Parser:', %(date/time string "#{self}" interpreted as #{res})) end res end |
#chronify_qty ⇒ Integer
Converts simple strings into seconds that can be added to a Time object
Input string can be HH:MM or XX[dhm][XXhm][XXm]
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# 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 |
#expand_date_tags(additional_tags = nil) ⇒ Object
Convert (chronify) natural language dates within configured date tags (tags whose value is expected to be a date). Modifies string in place.
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/doing/chronify/string.rb', line 130 def ( = nil) iso_rx = /\d{4}-\d\d-\d\d \d\d:\d\d/ = [ 'start(?:ed)?', 'beg[ia]n', 'done', 'finished', 'completed?', 'waiting', 'defer(?:red)?' ] if = = .split(/ *, */) if .is_a?(String) .map! do |tag| tag.sub(/^@/, '').gsub(/\((?!\?:)(.*?)\)/, '(?:\1)').strip end .concat().uniq! end done_rx = /(?<=^| )@(?<tag>#{.join('|')})\((?<date>.*?)\)/i gsub!(done_rx) do m = Regexp.last_match t = m['tag'] d = m['date'] future = t =~ /^(done|complete)/ ? false : true parsed_date = d =~ iso_rx ? Time.parse(d) : d.chronify(guess: :begin, future: future) parsed_date.nil? ? m[0] : "@#{t}(#{parsed_date.strftime('%F %R')})" end end |
#is_range? ⇒ Boolean
164 165 166 |
# File 'lib/doing/chronify/string.rb', line 164 def is_range? self =~ / (to|through|thru|(un)?til|-+) / end |
#split_date_range ⇒ Array<DateTime>
Splits a range string and returns an array of DateTime objects as [start, end]. If only one date is given, end time is nil.
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# 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 = date_string.strip.chronify(guess: :end) 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 |
#time_string(format: :dhm) ⇒ Object
Convert DD:HH:MM to a natural language string
117 118 119 |
# File 'lib/doing/chronify/string.rb', line 117 def time_string(format: :dhm) to_seconds.time_string(format: format) end |
#to_seconds ⇒ Integer
Convert DD:HH:MM to seconds
101 102 103 104 105 106 107 108 109 110 |
# 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 |