I have a database column the holds timestamps in UNIX format, I am looking for a way to compare those timestamps to a timestamp from the time right now and print how many seconds/hours/days since the original timestamp.
Just to confirm I am NOT looking for a conversion from 1489757456 to 03/17/2017 # 1:30pm. I AM looking for a conversion from 1489757456 to 1m ago/2hr ago/3d ago ect.
datetime.datetime.fromtimestamp()
to convert your unix timestamp to a datetitme
datetime.datetime.now()
to get the current datetime
dateutil.relativedelta.relativedelta(dt1, dt2, ...)
to get the difference with respect to leap years.
References:
https://docs.python.org/3.6/library/datetime.html
http://dateutil.readthedocs.io/en/stable/relativedelta.html
Function, like this will generate output for hours, mins and seconds from seconds number.
def secondsToHms(d):
d = int(d);
h = math.floor(d / 3600)
m = math.floor(d % 3600 / 60)
s = math.floor(d % 3600 % 60)
htext = " hour, " if h == 1 else " hours, "
hDisplay = str(h) + htext if h > 0 else ""
mtext = " minute, " if m == 1 else " minutes, "
mDisplay = str(m) + mtext if m > 0 else ""
stext = " second" if s == 1 else " seconds"
sDisplay = str(s) + stext if s > 0 else ""
return hDisplay + mDisplay + sDisplay;
For example:
secondsToHms(344334)
>> 95.0 hours, 38.0 minutes, 54.0 seconds
So you can add your preferred formatting and also add days/months if needed in similar fashion.
In python how do I sum up the following time?
0:00:00
0:00:15
9:30:56
It depends on the form you have these times in, for example if you already have them as datetime.timedeltas, then you could just sum them up:
>>> s = datetime.timedelta(seconds=0) + datetime.timedelta(seconds=15) + datetime.timedelta(hours=9, minutes=30, seconds=56)
>>> str(s)
'9:31:11'
Using timedeltas (tested in Python 3.9):
import datetime
timeList = ['0:00:00', '0:00:15', '9:30:56']
mysum = datetime.timedelta()
for i in timeList:
(h, m, s) = i.split(':')
d = datetime.timedelta(hours=int(h), minutes=int(m), seconds=int(s))
mysum += d
print(str(mysum))
Result:
9:31:11
As a list of strings?
timeList = [ '0:00:00', '0:00:15', '9:30:56' ]
totalSecs = 0
for tm in timeList:
timeParts = [int(s) for s in tm.split(':')]
totalSecs += (timeParts[0] * 60 + timeParts[1]) * 60 + timeParts[2]
totalSecs, sec = divmod(totalSecs, 60)
hr, min = divmod(totalSecs, 60)
print "%d:%02d:%02d" % (hr, min, sec)
Result:
9:31:11
I'm really disappointed if there is not any more pythonic solution... :(
Horrible one ->
timeList = [ '0:00:00', '0:00:15', '9:30:56' ]
ttt = [map(int,i.split()[-1].split(':')) for i in timeList]
seconds=reduce(lambda x,y:x+y[0]*3600+y[1]*60+y[2],ttt,0)
#seconds == 34271
This one looks horrible too ->
zero_time = datetime.datetime.strptime('0:0:0', '%H:%M:%S')
ttt=[datetime.datetime.strptime(i, '%H:%M:%S')-zero_time for i in timeList]
delta=sum(ttt,zero_time)-zero_time
# delta==datetime.timedelta(0, 34271)
# str(delta)=='9:31:11' # this seems good, but
# if we have more than 1 day we get for example str(delta)=='1 day, 1:05:22'
Really frustrating is also this ->
sum(ttt,zero_time).strftime('%H:%M:%S') # it is only "modulo" 24 :(
I really like to see one-liner so, I tried to make one in python3 :P (good result but horrible look)
import functools
timeList = ['0:00:00','0:00:15','9:30:56','21:00:00'] # notice additional 21 hours!
sum_fnc=lambda ttt:(lambda a:'%02d:%02d:%02d' % (divmod(divmod(a,60)[0],60)+(divmod(a,60)[1],)))((lambda a:functools.reduce(lambda x,y:x+y[0]*3600+y[1]*60+y[2],a,0))((lambda a:[list(map(int,i.split()[-1].split(':'))) for i in a])(ttt)))
# sum_fnc(timeList) -> '30:40:11'
lines = ["0:00:00", "0:00:15", "9:30:56"]
total = 0
for line in lines:
h, m, s = map(int, line.split(":"))
total += 3600*h + 60*m + s
print "%02d:%02d:%02d" % (total / 3600, total / 60 % 60, total % 60)
Assuming you want to add up the seconds for a total time:
def parse_time(s):
hour, min, sec = s.split(':')
try:
hour = int(hour)
min = int(min)
sec = int(sec)
except ValueError:
# handle errors here, but this isn't a bad default to ignore errors
return 0
return hour * 60 * 60 + min * 60 + sec
print parse_time('0:00:00') + parse_time('0:00:15') + parse_time('9:30:56')
from datetime import timedelta
h = ['3:00:00','1:07:00', '4:00:00', '4:05:00', '4:10:00', '4:03:00']
def to_td(h):
ho, mi, se = h.split(':')
return timedelta(hours=int(ho), minutes=int(mi), seconds=int(se))
print(str(sum(map(to_td, h), timedelta())))
# Out[31]: 20:25:00
Naive approach (without exception handling):
#!/usr/bin/env python
def sumup(*times):
cumulative = 0
for t in times:
hours, minutes, seconds = t.split(":")
cumulative += 3600 * int(hours) + 60 * int(minutes) + int(seconds)
return cumulative
def hms(seconds):
"""Turn seconds into hh:mm:ss"""
hours = seconds / 3600
seconds -= 3600*hours
minutes = seconds / 60
seconds -= 60*minutes
return "%02d:%02d:%02d" % (hours, minutes, seconds)
if __name__ == '__main__':
print hms(sumup(*("0:00:00", "0:00:15", "9:30:56")))
# will print: 09:31:11
Bellow is a solution using list comprehension:
from datetime import timedelta
def time_sum(time: List[str]) -> timedelta:
"""
Calculates time from list of time hh:mm:ss format
"""
return sum(
[
timedelta(hours=int(ms[0]), minutes=int(ms[1]), seconds=int(ms[2]))
for t in time
for ms in [t.split(":")]
],
timedelta(),
)
Example:
time_list = ["0:00:00", "0:00:15", "9:30:56"]
total = time_sum(time_list)
print(f"Total time: {total}")
Python: I need to show file modification times in the "1 day ago", "two hours ago", format.
Is there something ready to do that? It should be in English.
The code was originally published on a blog post "Python Pretty Date function" (http://evaisse.com/post/93417709/python-pretty-date-function)
It is reproduced here as the blog account has been suspended and the page is no longer available.
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 = 0
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"
If you happen to be using Django, then new in version 1.4 is the naturaltime template filter.
To use it, first add 'django.contrib.humanize' to your INSTALLED_APPS setting in settings.py, and {% load humanize %} into the template you're using the filter in.
Then, in your template, if you have a datetime variable my_date, you can print its distance from the present by using {{ my_date|naturaltime }}, which will be rendered as something like 4 minutes ago.
Other new things in Django 1.4.
Documentation for naturaltime and other filters in the django.contrib.humanize set.
In looking for the same thing with the additional requirement that it handle future dates, I found this:
http://pypi.python.org/pypi/py-pretty/1
Example code (from site):
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'
You can also do that with arrow package
From github page:
>>> import arrow
>>> utc = arrow.utcnow()
>>> utc = utc.shift(hours=-1)
>>> utc.humanize()
'an hour ago'
There is humanize package:
>>> 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'
It supports localization l10n, internationalization i18n:
>>> _ = humanize.i18n.activate('ru_RU')
>>> print humanize.naturaltime(datetime.now() - timedelta(days=1))
день назад
>>> print humanize.naturaltime(datetime.now() - timedelta(hours=2))
2 часа назад
The answer Jed Smith linked to is good, and I used it for a year or so, but I think it could be improved in a few ways:
It's nice to be able to define each time unit in terms of the preceding unit, instead of having "magic" constants like 3600, 86400, etc. sprinkled throughout the code.
After much use, I find I don't want to go to the next unit quite so eagerly. Example: both 7 days and 13 days will show as "1 week"; I'd rather see "7 days" or "13 days" instead.
Here's what I came up with:
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'))
Notice how every tuple in intervals is easy to interpret and check: a 'minute' is 60 seconds; an 'hour' is 60 minutes; etc. The only fudge is setting weeks_per_month to its average value; given the application, that should be fine. (And note that it's clear at a glance that the last three constants multiply out to 365.242, the number of days per year.)
One downside to my function is that it doesn't do anything outside the "## units" pattern: "Yesterday", "just now", etc. are right out. Then again, the original poster didn't ask for these fancy terms, so I prefer my function for its succinctness and the readability of its numerical constants. :)
The ago package provides this. Call human on a datetime object to get a human readable description of the difference.
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
Using datetime objects with tzinfo:
def time_elapsed(etime):
# need to add tzinfo to datetime.utcnow
now = 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)
I have written a detailed blog post for the solution on http://sunilarora.org/17329071
I am posting a quick snippet here as well.
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
DAY_INCREMENTS = [
[365, "year"],
[30, "month"],
[7, "week"],
[1, "day"],
]
SECOND_INCREMENTS = [
[3600, "hour"],
[60, "minute"],
[1, "second"],
]
def time_ago(dt):
diff = datetime.now() - dt # use timezone.now() or equivalent if `dt` is timezone aware
if diff.days < 0:
return "in the future?!?"
for increment, label in DAY_INCREMENTS:
if diff.days >= increment:
increment_diff = int(diff.days / increment)
return str(increment_diff) + " " + label + plural(increment_diff) + " ago"
for increment, label in SECOND_INCREMENTS:
if diff.seconds >= increment:
increment_diff = int(diff.seconds / increment)
return str(increment_diff) + " " + label + plural(increment_diff) + " ago"
return "just now"
def plural(num):
if num != 1:
return "s"
return ""
This is the gist of #sunil 's post
>>> 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)
You can download and install from below link. It should be more helpful for you. It has been providing user friendly message from second to year.
It's well tested.
https://github.com/nareshchaudhary37/timestamp_content
Below steps to install into your virtual env.
git clone https://github.com/nareshchaudhary37/timestamp_content
cd timestamp-content
python setup.py
Here is an updated answer based on Jed Smith's implementation that properly hands both offset-naive and offset-aware datetimes. You can also give a default timezones. 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"
I've been dragging and tweaking this code from programming language to programming language for so long, I don't remember where I originally got it from. It served me well in PHP, Java, and TypeScript, and now it's time for Python.
It handles past and future dates, as well as edge cases.
def unix_time() -> int:
return int(time.time())
def pretty_time(t: int, absolute=False) -> str:
if not type(t) is int:
return "N/A"
if t == 0:
return "Never"
now = unix_time()
if t == now:
return "Now"
periods = ["second", "minute", "hour", "day", "week", "month", "year", "decade"]
lengths = [60, 60, 24, 7, 4.35, 12, 10]
diff = now - t
if absolute:
suffix = ""
else:
if diff >= 0:
suffix = "ago"
else:
diff *= -1
suffix = "remaining"
i = 0
while diff >= lengths[i] and i < len(lengths) - 1:
diff /= lengths[i]
i += 1
diff = round(diff)
if diff > 1:
periods[i] += "s"
return "{0} {1} {2}".format(diff, periods[i], suffix)
def time_ago(self):
start_time = self.date # The start date
now_time = datetime.now()
difference = int((now_time - start_time).total_seconds())
second = [1, 'seconds']
minute = [60, 'minutes']
hour = [60 * minute[0], 'hours']
day = [24 * hour[0], 'days']
week = [7 * day[0], 'weeks']
month = [4 * week[0], 'months']
year = [12 * month[0], 'years']
times = [year, month, week, day, hour, minute, second]
for time in times:
if difference >= time[0]:
time_ago = int(difference / time[0])
if time_ago <= 1:
timeframe = time[1].rstrip('s')
else:
timeframe = time[1]
time_item = str(time_ago) + ' ' + timeframe
return time_item
return 'Date Error'