¿Formato de hora fácil de usar en Python?

Python: Necesito mostrar los tiempos de modificación de archivos en el formato “Hace 1 día”, “Hace dos horas”.

¿Hay algo listo para hacer eso? Debería estar en inglés.

El código se publicó originalmente en la publicación del blog “Python Pretty Date function” ( http://evaisse.com/post/93417709/python-pretty-date-function )

Se reproduce aquí ya que la cuenta del blog ha sido suspendida y la página ya no está disponible.

def pretty_date(time=False): """ Get a datetime object or a int() Epoch timestamp and return a pretty string like 'an hour ago', 'Yesterday', '3 months ago', 'just now', etc """ from datetime import datetime now = datetime.now() if type(time) is int: diff = now - datetime.fromtimestamp(time) elif isinstance(time,datetime): diff = now - time elif not time: diff = now - now second_diff = diff.seconds day_diff = diff.days if day_diff < 0: return '' if day_diff == 0: if second_diff < 10: return "just now" if second_diff < 60: return str(second_diff) + " seconds ago" if second_diff < 120: return "a minute ago" if second_diff < 3600: return str(second_diff / 60) + " minutes ago" if second_diff < 7200: return "an hour ago" if second_diff < 86400: return str(second_diff / 3600) + " hours ago" if day_diff == 1: return "Yesterday" if day_diff < 7: return str(day_diff) + " days ago" if day_diff < 31: return str(day_diff / 7) + " weeks ago" if day_diff < 365: return str(day_diff / 30) + " months ago" return str(day_diff / 365) + " years ago" 

Si está usando Django , entonces la novedad en la versión 1.4 es el filtro de plantilla naturaltime .

Para usarlo, primero agregue 'django.contrib.humanize' a su configuración INSTALLED_APPS en settings.py, y {% load humanize %} en la plantilla en la que está usando el filtro.

Luego, en su plantilla, si tiene una variable datetime my_date , puede imprimir su distancia desde el presente utilizando {{ my_date|naturaltime }} , que se procesará como 4 minutes ago .

Otras cosas nuevas en Django 1.4.

Documentación para el tiempo naturaltime y otros filtros en el conjunto django.contrib.humanize .

Al buscar lo mismo con el requisito adicional de que manejara fechas futuras, encontré esto: http://pypi.python.org/pypi/py-pretty/1

Código de ejemplo (del sitio):

 from datetime import datetime, timedelta now = datetime.now() hrago = now - timedelta(hours=1) yesterday = now - timedelta(days=1) tomorrow = now + timedelta(days=1) dayafter = now + timedelta(days=2) import pretty print pretty.date(now) # 'now' print pretty.date(hrago) # 'an hour ago' print pretty.date(hrago, short=True) # '1h ago' print pretty.date(hrago, asdays=True) # 'today' print pretty.date(yesterday, short=True) # 'yest' print pretty.date(tomorrow) # 'tomorrow' 

La respuesta a la que se vinculó Jed Smith es buena, y la usé durante un año más o menos, pero creo que podría mejorarse de varias maneras:

  • Es bueno poder definir cada unidad de tiempo en términos de la unidad anterior, en lugar de tener constantes “mágicas” como 3600, 86400, etc. esparcidas por todo el código.
  • Después de mucho uso, descubro que no quiero ir a la siguiente unidad con tanta ilusión. Ejemplo: los 7 días y los 13 días se mostrarán como “1 semana”; Prefiero ver “7 días” o “13 días” en su lugar.

Esto es lo que se me ocurrió:

 def PrettyRelativeTime(time_diff_secs): # Each tuple in the sequence gives the name of a unit, and the number of # previous units which go into it. weeks_per_month = 365.242 / 12 / 7 intervals = [('minute', 60), ('hour', 60), ('day', 24), ('week', 7), ('month', weeks_per_month), ('year', 12)] unit, number = 'second', abs(time_diff_secs) for new_unit, ratio in intervals: new_number = float(number) / ratio # If the new number is too small, don't go to the next unit. if new_number < 2: break unit, number = new_unit, new_number shown_num = int(number) return '{} {}'.format(shown_num, unit + ('' if shown_num == 1 else 's')) 

Observe cómo cada tupla en intervals es fácil de interpretar y verificar: un 'minute' es de 60 segundos; una 'hour' es de 60 minutos; etc. El único fudge es establecer weeks_per_month a su valor promedio; Dada la aplicación, eso debería estar bien. (Y tenga en cuenta que, a simple vista, es claro que las últimas tres constantes se multiplican a 365.242, el número de días por año).

Una desventaja de mi función es que no hace nada fuera del patrón de "## unidades": "Ayer", "justo ahora", etc. Por otra parte, el póster original no solicitó estos términos sofisticados, por lo que prefiero mi función por su carácter conciso y la legibilidad de sus constantes numéricas. 🙂

También puedes hacer eso con el paquete de flechas .

Desde la página github :

 >>> import arrow >>> utc = arrow.utcnow() >>> utc = utc.replace(hours=-1) >>> local.humanize() 'an hour ago' 

El paquete ago proporciona esto. Llame a human en un objeto de datetime y datetime para obtener una descripción legible por humanos de la diferencia.

 from ago import human from datetime import datetime from datetime import timedelta ts = datetime.now() - timedelta(days=1, hours=5) print(human(ts)) # 1 day, 5 hours ago print(human(ts, precision=1)) # 1 day ago 

Hay paquete de humanize :

 >>> from datetime import datetime, timedelta >>> import humanize # $ pip install humanize >>> humanize.naturaltime(datetime.now() - timedelta(days=1)) 'a day ago' >>> humanize.naturaltime(datetime.now() - timedelta(hours=2)) '2 hours ago' 

Es compatible con la localización l10n , la internacionalización i18n :

 >>> _ = humanize.i18n.activate('ru_RU') >>> print humanize.naturaltime(datetime.now() - timedelta(days=1)) день назад >>> print humanize.naturaltime(datetime.now() - timedelta(hours=2)) 2 часа назад 

He escrito un blog detallado para la solución en http://sunilarora.org/17329071 También estoy publicando un fragmento rápido aquí.

 from datetime import datetime from dateutil.relativedelta import relativedelta def get_fancy_time(d, display_full_version = False): """Returns a user friendly date format d: some datetime instace in the past display_second_unit: True/False """ #some helpers lambda's plural = lambda x: 's' if x > 1 else '' singular = lambda x: x[:-1] #convert pluran (years) --> to singular (year) display_unit = lambda unit, name: '%s %s%s'%(unit, name, plural(unit)) if unit > 0 else '' #time units we are interested in descending order of significance tm_units = ['years', 'months', 'days', 'hours', 'minutes', 'seconds'] rdelta = relativedelta(datetime.utcnow(), d) #capture the date difference for idx, tm_unit in enumerate(tm_units): first_unit_val = getattr(rdelta, tm_unit) if first_unit_val > 0: primary_unit = display_unit(first_unit_val, singular(tm_unit)) if display_full_version and idx < len(tm_units)-1: next_unit = tm_units[idx + 1] second_unit_val = getattr(rdelta, next_unit) if second_unit_val > 0: secondary_unit = display_unit(second_unit_val, singular(next_unit)) return primary_unit + ', ' + secondary_unit return primary_unit return None 

Usando objetos datetime con tzinfo:

 def time_elapsed(etime): # need to add tzinfo to datetime.utcnow now = datetime.datetime.utcnow().replace(tzinfo=etime.tzinfo) opened_for = (now - etime).total_seconds() names = ["seconds","minutes","hours","days","weeks","months"] modulos = [ 1,60,3600,3600*24,3600*24*7,3660*24*30] values = [] for m in modulos[::-1]: values.append(int(opened_for / m)) opened_for -= values[-1]*m pretty = [] for i,nm in enumerate(names[::-1]): if values[i]!=0: pretty.append("%i %s" % (values[i],nm)) return " ".join(pretty) 

Esta es la esencia de la publicación de @sunil.

 >>> from datetime import datetime >>> from dateutil.relativedelta import relativedelta >>> then = datetime(2003, 9, 17, 20, 54, 47, 282310) >>> relativedelta(then, datetime.now()) relativedelta(years=-11, months=-3, days=-9, hours=-18, minutes=-17, seconds=-8, microseconds=+912664) 

Puede descargar e instalar desde el siguiente enlace. Debería ser más útil para usted. Ha estado proporcionando un mensaje fácil de usar desde el segundo hasta el año.

Está bien probado.

https://github.com/nareshchaudhary37/timestamp_content

A continuación los pasos para instalar en su env virtual.

 git clone https://github.com/nareshchaudhary37/timestamp_content cd timestamp-content python setup.py 

Aquí hay una respuesta actualizada basada en la implementación de Jed Smith que entrega correctamente tanto el tiempo de datos ingenuo como el compensado. También puedes dar un huso horario predeterminado. Python 3.5+.

 import datetime def pretty_date(time=None, default_timezone=datetime.timezone.utc): """ Get a datetime object or a int() Epoch timestamp and return a pretty string like 'an hour ago', 'Yesterday', '3 months ago', 'just now', etc """ # Assumes all timezone naive dates are UTC if time.tzinfo is None or time.tzinfo.utcoffset(time) is None: if default_timezone: time = time.replace(tzinfo=default_timezone) now = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc) if type(time) is int: diff = now - datetime.fromtimestamp(time) elif isinstance(time, datetime.datetime): diff = now - time elif not time: diff = now - now second_diff = diff.seconds day_diff = diff.days if day_diff < 0: return '' if day_diff == 0: if second_diff < 10: return "just now" if second_diff < 60: return str(second_diff) + " seconds ago" if second_diff < 120: return "a minute ago" if second_diff < 3600: return str(second_diff / 60) + " minutes ago" if second_diff < 7200: return "an hour ago" if second_diff < 86400: return str(second_diff / 3600) + " hours ago" if day_diff == 1: return "Yesterday" if day_diff < 7: return str(day_diff) + " days ago" if day_diff < 31: return str(day_diff / 7) + " weeks ago" if day_diff < 365: return str(day_diff / 30) + " months ago" return str(day_diff / 365) + " years ago"