5

Is there a function to 'reverse humanize' times?

For example, given (strings):

  • '1 minute ago'
  • '7 hours ago'
  • '5 days ago'
  • '2 months ago'

Could return (apologies for the pseudo-code):

  • datetime.now() - timedelta (1 minute), accuracy (60 seconds)
  • datetime.now() - timedelta (7 hours), accuracy (1 hour)
  • datetime.now() - timedelta (5 days), accuracy (1 day)
  • datetime.now() - timedelta (2 months), accuracy (1 month)
David Toy
  • 180
  • 2
  • 9
  • What do you mean by "accuracy"? Please provide the output you would expect for the examples that you provided. – Rob Bednark Jan 01 '15 at 06:37
  • @RobBednark You revitalising old questions? :) Accuracy ~= likely range of this time period. E.g. the time 'two days ago' actually covers a period between 48 hours and 72 hours ago. Direct conversion of two days ago would be an instant in time 48hours ago, but that fails to express that the event may have actually taken place 23hours 59min before that, (or 12 hours after that if the humanized time rounds up). Thus `convert(humanized_string`) might return `[time, accuracy(before, after)]` e.g. `convert('two days ago') = [time(48hours), accuracy(-12hours, +24hours)]` – David Toy Jan 08 '15 at 16:24

2 Answers2

2

I've been using parsedatetime and it's worked rather well for me. The home page lists some formats it can handle, e.g.:

  • in 5 minutes
  • 5 minutes from now
  • 2 hours before noon
  • 2 days from tomorrow

The major downside I've found is that it has no sense of timezones.

In case it's worth anything, here's a wrapper function I use, which always returns a datetime object regardless of whether the input string is relative (like all your examples) or fixed:

def parse_datetime(datetime_string):
    datetime_parser = parsedatetime.Calendar(parsedatetime_consts.Constants())
    timestamp = datetime_parser.parse(datetime_string)
    if len(timestamp) == 2:
        if timestamp[1] == 0:
            raise ValueError(u'Failed to parse datetime: %s' % datetime_string)
        timestamp = timestamp[0]
    return datetime.fromtimestamp(time.mktime(timestamp))
moinudin
  • 134,091
  • 45
  • 190
  • 216
  • You could try to make parsedatetime give you precision by locating the number and incrementing or decrementing it, and finding the difference between the two parsed datetimes. – Paul Fisher Jan 29 '11 at 22:13
  • @Paul Easier to check the delta between the return value and `now()`. – moinudin Jan 29 '11 at 22:22
  • I'm referring to the precision (or resolution) the OP wanted, as in, "how specifically does this specify a moment"? i.e., "in 5 minutes" has a resolution of 1 minute (it could refer to anything from about 4 to 6 minutes), whereas "2 months ago" has a resolution of about 30 days since it could refer to any time during that month. – Paul Fisher Jan 30 '11 at 05:36
  • @Paul Yes, I understood you. "in 2 hours" will return the same minutes and seconds as `now()`. Perhaps that helps you understand the method I suggested? – moinudin Jan 30 '11 at 08:42
2

Can you not just write a simple implementation yourself such as:

import datetime

def parsedatetime(str_val):

  parts = str_val.split(' ')

  if len(parts) != 3 and parts[2] != 'ago':
     raise Exception("can't parse %s" % str_val)

  try:
     interval = int(parts[0])
  except ValueError,e :
     raise Exception("can't parse %s" % str_val)

  desc = parts[1]

  if 'second' in desc:
     td = datetime.timedelta(seconds=interval)
  elif 'minute' in desc:
     td = datetime.timedelta(minutes=interval)
  elif 'hour' in desc:
     td = datetime.timedelta(minutes=interval*60)
  elif 'day' in desc:
     td = datetime.timedelta(days=interval)
  else:
     raise Exception("cant parse %s" % str_val)

   answer = datetime.datetime.now - td
   return answer

The input doesn't look that varied.

Philluminati
  • 2,649
  • 2
  • 25
  • 32