2012-09-24 7 views
8

tengo cadenas que muestran una fecha en el siguiente formato:pitón 'x Hace días' a fecha y hora

x minutes/hours/days/months/years ago 

tengo que analizar que a una fecha y hora usando pitón.

Parece que dateutil no puede hacerlo.

¿Hay alguna manera de hacerlo?

+1

Parece que 'puede dateutil' manejarlo en el formato que Lattyware y yo prescribimos, simplemente sustituyendo 'dateutil.relativedelta' por' datetime.timedelta'. (ver mi actualización y enlace). – mgilson

Respuesta

7

Claro que puedes hacerlo. Solo necesita un timedelta.

s = "3 days ago" 
parsed_s = [s.split()[:2]] 
time_dict = dict((fmt,float(amount)) for amount,fmt in parsed_s) 
dt = datetime.timedelta(**time_dict) 
past_time = datetime.datetime.now() - dt 

Como acotación al margen, parece que tiene un dateutilrelativedelta que actúa como un timedelta, pero el constructor también acepta months y years en los argumentos (y aparentemente los argumentos deben ser números enteros).

+0

relativedelta funciona mejor, aunque no funciona con un flotador, necesita 'amount' para ser un int. – applechief

+0

@chaft - Gracias. Supongo que tiene sentido. Si bien es concebible que se pueda llegar a una definición hace 3 meses, probablemente no sea concebible decirlo hace 3.5 meses y tenga una fecha única. – mgilson

6

Esto se puede hacer fácilmente con timedelta s:

import datetime 

def string_to_delta(string_delta): 
    value, unit, _ = string_delta.split() 
    return datetime.timedelta(**{unit: float(value)}) 

Producción:

>>> string_to_delta("20 hours ago") 
datetime.timedelta(0, 72000) 

Aunque esto requerirá algún trabajo extra para hacer frente a meses/años - como la adición de un mes a una fecha es una operación ambigua, pero debería ser una simple adición si sabes lo que quieres que signifique.

Para obtener un tiempo real, simplemente tome el delta lejos de datetime.datetime.now().

+0

Su división es un poco más elegante que la mía. +1. (aunque yo usaría 'float (n)' - timedelta parece manejarlo bien y permitiría el uso de 'hace 3.5 días'). – mgilson

+0

@mgilson: Gracias por la sugerencia: no conocía los flotadores manipulados '' timedelta''. –

+0

Tenía que probarlo para ver - Bastante bien :). – mgilson

0

solución completamente exagerada pero necesitaba algo más flexible:

def string_to_delta(relative): 
    #using simplistic year (no leap months are 30 days long. 
    #WARNING: 12 months != 1 year 
    unit_mapping = [('mic', 'microseconds', 1), 
        ('millis', 'microseconds', 1000), 
        ('sec', 'seconds', 1), 
        ('day', 'days', 1), 
        ('week', 'days', 7), 
        ('mon', 'days', 30), 
        ('year', 'days', 365)] 
    try: 
     tokens = relative.lower().split(' ') 
     past = False 
     if tokens[-1] == 'ago': 
      past = True 
      tokens = tokens[:-1] 
     elif tokens[0] == 'in': 
      tokens = tokens[1:] 


     units = dict(days = 0, seconds = 0, microseconds = 0) 
     #we should always get pairs, if not we let this die and throw an exception 
     while len(tokens) > 0: 
      value = tokens.pop(0) 
      if value == 'and': #just skip this token 
       continue 
      else: 
       value = float(value) 

      unit = tokens.pop(0) 
      for match, time_unit, time_constant in unit_mapping: 
       if unit.startswith(match): 
        units[time_unit] += value * time_constant 
     return datetime.timedelta(**units), past 

    except Exception as e: 
     raise ValueError("Don't know how to parse %s: %s" % (relative, e)) 

Esto puede analizar cosas como:

  • 2 days ago
  • in 60 seconds
  • 2 DAY and 4 Secs
  • in 1 year, 1 Month, 2 days and 4 MICRO
  • 2 Weeks 4 secs ago
  • 7 millis ago

Un enorme pero : Simplifica mes y año a 30 y 365 días, respectivamente. No siempre es lo que quiere, aunque es suficiente para algunos casos.

1

Dado que sus argumentos son algo así como hace 2 días, 3 meses atrás, 2 años atrás. La siguiente función podría ser útil para obtener la fecha exacta de los argumentos.Primero tiene que importar los siguientes utilidades fecha

import datetime 
from dateutil.relativedelta import relativedelta 

A continuación, aplicar la función a continuación

def get_past_date(str_days_ago): 
    TODAY = datetime.date.today() 
    splitted = str_days_ago.split() 
    if len(splitted) == 1 and splitted[0].lower() == 'today': 
     return str(TODAY.isoformat()) 
    elif len(splitted) == 1 and splitted[0].lower() == 'yesterday': 
     date = TODAY - relativedelta(days=1) 
     return str(date.isoformat()) 
    elif splitted[1].lower() in ['hour', 'hours', 'hr', 'hrs', 'h']: 
     date = datetime.datetime.now() - relativedelta(hours=int(splitted[0])) 
     return str(date.date().isoformat()) 
    elif splitted[1].lower() in ['day', 'days', 'd']: 
     date = TODAY - relativedelta(days=int(splitted[0])) 
     return str(date.isoformat()) 
    elif splitted[1].lower() in ['wk', 'wks', 'week', 'weeks', 'w']: 
     date = TODAY - relativedelta(weeks=int(splitted[0])) 
     return str(date.isoformat()) 
    elif splitted[1].lower() in ['mon', 'mons', 'month', 'months', 'm']: 
     date = TODAY - relativedelta(months=int(splitted[0])) 
     return str(date.isoformat()) 
    elif splitted[1].lower() in ['yrs', 'yr', 'years', 'year', 'y']: 
     date = TODAY - relativedelta(years=int(splitted[0])) 
     return str(date.isoformat()) 
    else: 
     return "Wrong Argument format" 

A continuación, puede llamar a la función como esta:

print get_past_date('5 hours ago') 
print get_past_date('yesterday') 
print get_past_date('3 days ago') 
print get_past_date('4 months ago') 
print get_past_date('2 years ago') 
print get_past_date('today') 
Cuestiones relacionadas