Naturaltime with minutes, hours, days and weeks - python

I use naturaltime in my Django application.
How can I display only time in minutes, then hours, then days, then weeks?
Here is my code:
{{ obj.pub_date|naturaltime }}

I use two custom filters to solve a very similar problem using babel and pytz. I write "Today" or "Yesterday" plus the time. You're welcome to use my code any way you like.
My template code is two usages, one for writing "Today", "Yesterday", or the date if it was even earlier.
{{ scored_document.fields.10.value|format_date_human(locale='en') }}
Then this tag writes the actual time of day
{{ scored_document.fields.10.value|datetimeformat_list(hour=scored_document.fields.17.value|int ,minute =scored_document.fields.18.value|int, timezoneinfo=timezoneinfo, locale=locale) }}
The two corresponding functions are
MONTHS = ('Jan.', 'Feb.', 'Mar.', 'April.', 'May.', 'June.',
'July.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.')
FORMAT = '%H:%M / %d-%m-%Y'
def format_date_human(to_format, locale='en', timezoneinfo='Asia/Calcutta'):
tzinfo = timezone(timezoneinfo)
now = datetime.now()
#logging.info('delta: %s', str((now - to_format).days))
#logging.info('delta2: %s', str((datetime.date(now)-datetime.date(to_format)).days))
if datetime.date(to_format) == datetime.date(now):
date_str = _('Today')
elif (now - to_format).days == 1:
date_str = _('Yesterday')
else:
month = MONTHS[to_format.month - 1]
date_str = '{0} {1}'.format(to_format.day, _(month))
time_str = format_time(to_format, 'H:mm', tzinfo=tzinfo, locale=locale)
return "{0}".format(date_str, time_str)
def datetimeformat_list(date, hour, minute, locale='en', timezoneinfo='Asia/Calcutta'):
import datetime as DT
import pytz
utc = pytz.utc
to_format = DT.datetime(int(date.year), int(date.month), int(date.day), int(hour), int(minute))
utc_date = utc.localize(to_format)
tzone = pytz.timezone(timezoneinfo)
tzone_date = utc_date.astimezone(tzone)
time_str = format_time(tzone_date, 'H:mm')
return "{0}".format(time_str)

Related

Why is my timezone datetime wrong?

I use this code to format my time but the time that comes out is 5 hours wrong. I should be 06 something in calcutta now and it formats the time now as 01... something. What is wrong with the code?
def datetimeformat_viewad(to_format, locale='en', timezoneinfo='Asia/Calcutta'):
tzinfo = timezone(timezoneinfo)
month = MONTHS[to_format.month - 1]
input = pytz.timezone(timezoneinfo).localize(
datetime(int(to_format.year), int(to_format.month), int(to_format.day), int(to_format.hour), int(to_format.minute)))
date_str = '{0} {1}'.format(input.day, _(month))
time_str = format_time(input, 'H:mm', tzinfo=tzinfo, locale=locale)
return "{0} {1}".format(date_str, time_str)
Update
This code worked which was according to the answer below.
def datetimeformat_viewad(to_format, locale='en', timezoneinfo='Asia/Calcutta'):
import datetime as DT
import pytz
utc = pytz.utc
to_format = DT.datetime(int(to_format.year), int(to_format.month), int(to_format.day), int(to_format.hour), int(to_format.minute))
utc_date = utc.localize(to_format)
tzone = pytz.timezone(timezoneinfo)
tzone_date = utc_date.astimezone(tzone)
month = MONTHS[int(tzone_date.month) - 1]
time_str = format_time(tzone_date, 'H:mm')
date_str = '{0} {1}'.format(tzone_date.day, _(month))
return "{0} {1}".format(date_str, time_str)
It sounds like to_format is a naive datetime in UTC time.
You want to convert it to Calcutta time.
To do this, you localize to_format to UTC time1, and then use astimezone to convert that timezone-aware time to Calcutta time:
import datetime as DT
import pytz
utc = pytz.utc
to_format = DT.datetime(2015,7,17,1,0)
print(to_format)
# 2015-07-17 01:00:00
utc_date = utc.localize(to_format)
print(utc_date)
# 2015-07-17 01:00:00+00:00
timezoneinfo = 'Asia/Calcutta'
tzone = pytz.timezone(timezoneinfo)
tzone_date = utc_date.astimezone(tzone)
print(tzone_date)
# 2015-07-17 06:30:00+05:30
1The tzone.localize method does not convert between timezones. It
interprets the given localtime as one given in tzone. So if to_format is
meant to be interpreted as a UTC time, then use utc.localize to convert the
naive datetime to a timezone-aware UTC time.

Displaying a timedelta object in a django template

I'm having trouble getting my django template to display a timedelta object consistently. I tried using the time filter in my template, but nothing is displayed when I do this. The timedelta object is shown as follows on the errors page if I use Assert False:
time datetime.timedelta(0, 38, 132827)
This displays the time difference as:
0:00:38.132827
I would like to only show the hours, minutes, and seconds for each timedelta object. Does anyone have any suggestions on how I can do this?
I followed Peter's advice and wrote a custom template filter.
Here's the steps I took.
First I followed this guide to create a custom template filter.
Be sure to read this section on code layout.
Here's my filter code
from django import template
register = template.Library()
#register.filter()
def smooth_timedelta(timedeltaobj):
"""Convert a datetime.timedelta object into Days, Hours, Minutes, Seconds."""
secs = timedeltaobj.total_seconds()
timetot = ""
if secs > 86400: # 60sec * 60min * 24hrs
days = secs // 86400
timetot += "{} days".format(int(days))
secs = secs - days*86400
if secs > 3600:
hrs = secs // 3600
timetot += " {} hours".format(int(hrs))
secs = secs - hrs*3600
if secs > 60:
mins = secs // 60
timetot += " {} minutes".format(int(mins))
secs = secs - mins*60
if secs > 0:
timetot += " {} seconds".format(int(secs))
return timetot
Then in my template I did
{% load smooth_timedelta %}
{% timedeltaobject|smooth_timedelta %}
Example output
You can try remove the microseconds from the timedelta object, before sending it to the template:
time = time - datetime.timedelta(microseconds=time.microseconds)
I don't think there's anything built in, and timedeltas don't directly expose their hour and minute values. but this package includes a timedelta custom filter tag that might help:
http://pydoc.net/django-timedeltafield/0.7.10/
As far as I know you have to write you're own template tag for this. Below is the one I've concocted based on the Django core timesince/timeuntil code that should output what you're after:
#register.simple_tag
def duration( duration ):
"""
Usage: {% duration timedelta %}
Returns seconds duration as weeks, days, hours, minutes, seconds
Based on core timesince/timeuntil
"""
def seconds_in_units(seconds):
"""
Returns a tuple containing the most appropriate unit for the
number of seconds supplied and the value in that units form.
>>> seconds_in_units(7700)
(2, 'hour')
"""
unit_totals = OrderedDict()
unit_limits = [
("week", 7 * 24 * 3600),
("day", 24 * 3600),
("hour", 3600),
("minute", 60),
("second", 1)
]
for unit_name, limit in unit_limits:
if seconds >= limit:
amount = int(float(seconds) / limit)
if amount != 1:
unit_name += 's' # dodgy pluralisation
unit_totals[unit_name] = amount
seconds = seconds - ( amount * limit )
return unit_totals;
if duration:
if isinstance( duration, datetime.timedelta ):
if duration.total_seconds > 0:
unit_totals = seconds_in_units( duration.total_seconds() )
return ', '.join([str(v)+" "+str(k) for (k,v) in unit_totals.iteritems()])
return 'None'
from datetime import datetime
start = datetime.now()
taken = datetime.now() - start
str(taken)
'0:03:08.243773'
str(taken).split('.')[0]
'0:03:08'
The advice to write your own custom template tag is 100% the right way to go. You'll have complete control and can format it anyway you like. BUT -- if you're lazy and want a quick solution using builtin django facilities, you can use a hackey technique using the built-in timesince tag.
Basically, subtract your timedelta from the current time and drop it into your template. For example
import datetime
import django.template
tdelta = datetime.timedelta(hours=5, minutes=10)
tm = datetime.datetime.utcnow() - tdelta
django_engine = django.template.engines['django']
template = django_engine.from_string("My delta {{ tm|timesince }}")
print(template.render({'tm': tm})
Execute the above code in ./manage.py shell and the output is:
My delta 5 hours, 10 minutes

returning different time frames from datetime

I am parsing a file this way :
for d in csvReader:
print datetime.datetime.strptime(d["Date"]+"-"+d["Time"], "%d-%b-%Y-%H:%M:%S.%f").date()
date() returns : 2000-01-08, which is correct
time() returns : 06:20:00, which is also correct
How would I go about returning informations like "date+time" or "date+hours+minutes"
EDIT
Sorry I should have been more precise, here is what I am trying to achieve :
lmb = lambda d: datetime.datetime.strptime(d["Date"]+"-"+d["Time"], "%d-%b-%Y-%H:%M:%S.%f").date()
daily_quotes = {}
for k, g in itertools.groupby(csvReader, key = lmb):
lowBids = []
highBids = []
openBids = []
closeBids = []
for i in g:
lowBids.append(float(i["Low Bid"]))
highBids.append(float(i["High Bid"]))
openBids.append(float(i["Open Bid"]))
closeBids.append(float(i["Close Bid"]))
dayMin = min(lowBids)
dayMax = max(highBids)
open = openBids[0]
close = closeBids[-1]
daily_quotes[k.strftime("%Y-%m-%d")] = [dayMin,dayMax,open,close]
As you can see, right now I'm grouping values by day, I would like to group them by hour ( for which I would need date + hour ) or minutes ( date + hour + minute )
thanks in advance !
Don't use the date method of the datetime object you're getting from strptime. Instead, apply strftime directly to the return from strptime, which gets you access to all the member fields, including year, month, day, hour, minute, seconds, etc...
d = {"Date": "01-Jan-2000", "Time": "01:02:03.456"}
dt = datetime.datetime.strptime(d["Date"]+"-"+d["Time"], "%d-%b-%Y-%H:%M:%S.%f")
print dt.strftime("%Y-%m-%d-%H-%M-%S")

What's the most elegant way to get the end of the day (datetime)?

I'm currently writing some reporting code that allows users to optionally specify a date range. The way it works (simplified), is:
A user (optionally) specifies a year.
A user (optionally) specifies a month.
A user (optionally) specifies a day.
Here's a code snippet, along with comments describing what I'd like to do:
from datetime import datetime, timedelta
# ...
now = datetime.now()
start_time = now.replace(hour=0, minute=0, second=0, microsecond=0)
stop_time = now
# If the user enters no year, month, or day--then we'll simply run a
# report that only spans the current day (from the start of today to now).
if options['year']:
start_time = start_time.replace(year=options['year'], month=0, day=0)
stop_time = stop_time.replace(year=options['year'])
# If the user specifies a year value, we should set stop_time to the last
# day / minute / hour / second / microsecond of the year, that way we'll
# only generate reports from the start of the specified year, to the end
# of the specified year.
if options['month']:
start_time = start_time.replace(month=options['month'], day=0)
stop_time = stop_time.replace(month=options['month'])
# If the user specifies a month value, then set stop_time to the last
# day / minute / hour / second / microsecond of the specified month, that
# way we'll only generate reports for the specified month.
if options['day']:
start_time = start_time.replace(day=options['day'])
stop_time = stop_time.replace(day=options['day'])
# If the user specifies a day value, then set stop_time to the last moment of
# the current day, so that reports ONLY run on the current day.
I'm trying to find the most elegant way to write the code above--I've been trying to find a way to do it with timedelta, but can't seem to figure it out. Any advice would be appreciated.
To set the stop_time, advance start_time one year, month or day as appropriate, then subtract one timedelta(microseconds=1)
if options['year']:
start_time = start_time.replace(year=options['year'], month=1, day=1)
stop_time = stop_time.replace(year=options['year']+1)-timedelta(microseconds=1)
elif options['month']:
start_time = start_time.replace(month=options['month'], day=1)
months=options['month']%12+1
stop_time = stop_time.replace(month=months,day=1)-timedelta(microseconds=1)
else:
start_time = start_time.replace(day=options['day'])
stop_time = stop_time.replace(day=options['day'])+timedelta(days=1,microseconds=-1)
Using dict.get can simplify your code. It is a bit cleaner than using datetime.replace and timedelta objects.
Here's something to get you started:
from datetime import datetime
options = dict(month=5, day=20)
now = datetime.now()
start_time = datetime(year=options.get('year', now.year),
month=options.get('month', 1),
day=options.get('day', 1)
hour=0,
minute=0,
second=0)
stop_time = datetime(year=options.get('year', now.year),
month=options.get('month', now.month),
day=options.get('day', now.day),
hour=now.hour,
minute=now.minute,
second=now.second)
today = datetime.date.today()
begintime = today.strftime("%Y-%m-%d 00:00:00")
endtime = today.strftime("%Y-%m-%d 23:59:59")
from datetime import datetime, date, timedelta
def get_current_timestamp():
return int(datetime.now().timestamp())
def get_end_today_timestamp():
# get 23:59:59
result = datetime.combine(date.today() + timedelta(days=1), datetime.min.time())
return int(result.timestamp()) - 1
def get_datetime_from_timestamp(timestamp):
return datetime.fromtimestamp(timestamp)
end_today = get_datetime_from_timestamp(get_end_today_timestamp())
date = datetime.strftime('<input date str>')
date.replace(hour=0, minute=0, second=0, microsecond=0) # now we get begin of the day
date += timedelta(days=1, microseconds=-1) # now end of the day
After looking at some of the answers here, and not really finding anything extremely elegant, I did some poking around the standard library and found my current solution (which I like quite well): dateutil.
Here's how I implemented it:
from datetime import date
from dateutil.relativedelta import relativedelta
now = date.today()
stop_time = now + relativedelta(days=1)
start_time = date(
# NOTE: I'm not doing dict.get() since in my implementation, these dict
# keys are guaranteed to exist.
year = options['year'] or now.year,
month = options['month'] or now.month,
day = options['day'] or now.day
)
if options['year']:
start_time = date(year=options['year'] or now.year, month=1, day=1)
stop_time = start_time + relativedelta(years=1)
if options['month']:
start_time = date(
year = options['year'] or now.year,
month = options['month'] or now.month,
day = 1
)
stop_time = start_time + relativedelta(months=1)
if options['day']:
start_time = date(
year = options['year'] or now.year,
month = options['month'] or now.month,
day = options['day'] or now.day,
)
stop_time = start_time + relativedelta(days=1)
# ... do stuff with start_time and stop_time here ...
What I like about this implementation, is that python's dateutil.relativedata.relativedata works really well on edge cases. It gets the days/months/years correct. If I have month=12, and do relativedata(months=1), it'll increment the year and set the month to 1 (works nicely).
Also: in the above implementation, if the user specifies none of the optional dates (year, month, or day)--we'll fallback to a nice default (start_time = this morning, stop_time = tonight), that way we'll default to doing stuff for the current day only.
Thanks to everyone for their answers--they were helpful in my research.

How to increment a datetime by one day?

How to increment the day of a datetime?
for i in range(1, 35)
date = datetime.datetime(2003, 8, i)
print(date)
But I need pass through months and years correctly? Any ideas?
date = datetime.datetime(2003,8,1,12,4,5)
for i in range(5):
date += datetime.timedelta(days=1)
print(date)
Incrementing dates can be accomplished using timedelta objects:
import datetime
datetime.datetime.now() + datetime.timedelta(days=1)
Look up timedelta objects in the Python docs: http://docs.python.org/library/datetime.html
All of the current answers are wrong in some cases as they do not consider that timezones change their offset relative to UTC. So in some cases adding 24h is different from adding a calendar day.
Proposed solution
The following solution works for Samoa and keeps the local time constant.
def add_day(today):
"""
Add a day to the current day.
This takes care of historic offset changes and DST.
Parameters
----------
today : timezone-aware datetime object
Returns
-------
tomorrow : timezone-aware datetime object
"""
today_utc = today.astimezone(datetime.timezone.utc)
tz = today.tzinfo
tomorrow_utc = today_utc + datetime.timedelta(days=1)
tomorrow_utc_tz = tomorrow_utc.astimezone(tz)
tomorrow_utc_tz = tomorrow_utc_tz.replace(hour=today.hour,
minute=today.minute,
second=today.second)
return tomorrow_utc_tz
Tested Code
# core modules
import datetime
# 3rd party modules
import pytz
# add_day methods
def add_day(today):
"""
Add a day to the current day.
This takes care of historic offset changes and DST.
Parameters
----------
today : timezone-aware datetime object
Returns
-------
tomorrow : timezone-aware datetime object
"""
today_utc = today.astimezone(datetime.timezone.utc)
tz = today.tzinfo
tomorrow_utc = today_utc + datetime.timedelta(days=1)
tomorrow_utc_tz = tomorrow_utc.astimezone(tz)
tomorrow_utc_tz = tomorrow_utc_tz.replace(hour=today.hour,
minute=today.minute,
second=today.second)
return tomorrow_utc_tz
def add_day_datetime_timedelta_conversion(today):
# Correct for Samoa, but dst shift
today_utc = today.astimezone(datetime.timezone.utc)
tz = today.tzinfo
tomorrow_utc = today_utc + datetime.timedelta(days=1)
tomorrow_utc_tz = tomorrow_utc.astimezone(tz)
return tomorrow_utc_tz
def add_day_dateutil_relativedelta(today):
# WRONG!
from dateutil.relativedelta import relativedelta
return today + relativedelta(days=1)
def add_day_datetime_timedelta(today):
# WRONG!
return today + datetime.timedelta(days=1)
# Test cases
def test_samoa(add_day):
"""
Test if add_day properly increases the calendar day for Samoa.
Due to economic considerations, Samoa went from 2011-12-30 10:00-11:00
to 2011-12-30 10:00+13:00. Hence the country skipped 2011-12-30 in its
local time.
See https://stackoverflow.com/q/52084423/562769
A common wrong result here is 2011-12-30T23:59:00-10:00. This date never
happened in Samoa.
"""
tz = pytz.timezone('Pacific/Apia')
today_utc = datetime.datetime(2011, 12, 30, 9, 59,
tzinfo=datetime.timezone.utc)
today_tz = today_utc.astimezone(tz) # 2011-12-29T23:59:00-10:00
tomorrow = add_day(today_tz)
return tomorrow.isoformat() == '2011-12-31T23:59:00+14:00'
def test_dst(add_day):
"""Test if add_day properly increases the calendar day if DST happens."""
tz = pytz.timezone('Europe/Berlin')
today_utc = datetime.datetime(2018, 3, 25, 0, 59,
tzinfo=datetime.timezone.utc)
today_tz = today_utc.astimezone(tz) # 2018-03-25T01:59:00+01:00
tomorrow = add_day(today_tz)
return tomorrow.isoformat() == '2018-03-26T01:59:00+02:00'
to_test = [(add_day_dateutil_relativedelta, 'relativedelta'),
(add_day_datetime_timedelta, 'timedelta'),
(add_day_datetime_timedelta_conversion, 'timedelta+conversion'),
(add_day, 'timedelta+conversion+dst')]
print('{:<25}: {:>5} {:>5}'.format('Method', 'Samoa', 'DST'))
for method, name in to_test:
print('{:<25}: {:>5} {:>5}'
.format(name,
test_samoa(method),
test_dst(method)))
Test results
Method : Samoa DST
relativedelta : 0 0
timedelta : 0 0
timedelta+conversion : 1 0
timedelta+conversion+dst : 1 1
Here is another method to add days on date using dateutil's relativedelta.
from datetime import datetime
from dateutil.relativedelta import relativedelta
print 'Today: ',datetime.now().strftime('%d/%m/%Y %H:%M:%S')
date_after_month = datetime.now()+ relativedelta(day=1)
print 'After a Days:', date_after_month.strftime('%d/%m/%Y %H:%M:%S')
Output:
Today: 25/06/2015 20:41:44
After a Days: 01/06/2015 20:41:44
Most Simplest solution
from datetime import timedelta, datetime
date = datetime(2003,8,1,12,4,5)
for i in range(5):
date += timedelta(days=1)
print(date)
This was a straightforward solution for me:
from datetime import timedelta, datetime
today = datetime.today().strftime("%Y-%m-%d")
tomorrow = datetime.today() + timedelta(1)
You can also import timedelta so the code is cleaner.
from datetime import datetime, timedelta
date = datetime.now() + timedelta(seconds=[delta_value])
Then convert to date to string
date = date.strftime('%Y-%m-%d %H:%M:%S')
Python one liner is
date = (datetime.now() + timedelta(seconds=[delta_value])).strftime('%Y-%m-%d %H:%M:%S')
A short solution without libraries at all. :)
d = "8/16/18"
day_value = d[(d.find('/')+1):d.find('/18')]
tomorrow = f"{d[0:d.find('/')]}/{int(day_value)+1}{d[d.find('/18'):len(d)]}".format()
print(tomorrow)
# 8/17/18
Make sure that "string d" is actually in the form of %m/%d/%Y so that you won't have problems transitioning from one month to the next.

Categories

Resources