views:

846

answers:

5

Is there a gem or something to parse strings like "4h 30m" "1d 4h" -- sort of like the estimates in JIRA or task planners, maybe, with internationalization?

+1  A: 

Parse into what though?

This will parse into a Hash:

"4h 30m".split(/\s/).each{|i| h[i.gsub(/\d+/,"")] = i.gsub(/\w/,"")}

Sorry. not familiar with JIRA....

Rob
+7  A: 

You can use chronic. It can parse pretty much everything you trhow at it, including "yesterday", "last week" etc.

Update: As the OP points out in the comment, Chronic is for dates, not timespans. See my other answer.

August Lilleaas
Dear jesus christ, chronic is FUCKING AWESOME.But it's not really what I wanted. I wanted duration instead of date/time.
mannicken
+3  A: 

I wrote this method that does it pretty well

  def parse_duration(dur)
    duration = 0

    number_tokens = dur.gsub(/[a-z]/i,"").split
    times = dur.gsub(/[\.0-9]/,"").split

    if number_tokens.size != times.size
      raise "unrecognised duration!"
    else
      dur_tokens = number_tokens.zip(times)

      for d in dur_tokens
        number_part = d[0].to_f
        time_part = d[1]

        case time_part.downcase
        when "h","hour","hours"
          duration += number_part.hours
        when "m","minute","minutes","min","mins"
          duration += number_part.minutes
        when "d","day","days"
          duration += number_part.days
        when "w","week","weeks"
          duration += number_part.weeks
        when "month", "months"
          duration += number_part.months
        when "y", "year", "years"
          duration += number_part.years
        else
          raise "unrecognised duration!"
        end

      end

    end

    duration
  end
DanSingerman
+8  A: 

Posting a 2nd answer, as chronic (which my original answer suggested) doesn't give you timespans but timestamps.

Here's my go on a parser.

class TimeParser
  TOKENS = {
    "m" => (60),
    "h" => (60 * 60),
    "d" => (60 * 60 * 24)
  }

  attr_reader :time

  def initialize(input)
    @input = input
    @time = 0
    parse
  end

  def parse
    @input.scan(/(\d+)(\w)/).each do |amount, measure|
      @time += amount.to_i * TOKENS[measure]
    end
  end
end

The strategy is fairly simple. Split "5h" into ["5", "h"], define how many seconds "h" represents (TOKENS), and add that amount to @time.

TimeParser.new("1m").time
# => 60

TimeParser.new("1m wtf lol").time
# => 60

TimeParser.new("4h 30m").time
# => 16200

TimeParser.new("1d 4h").time
# => 100800

It shouldn't be too hard making it handle "1.5h" either, seeing the codebase is as simple as it is.

August Lilleaas
+1  A: 

chronic_duration does this.

srboisvert