5
votes

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)
2
What do you mean by "accuracy"? Please provide the output you would expect for the examples that you provided. - Rob Bednark
@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

2 Answers

2
votes

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))
1
votes

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.