Cleanest and most Pythonic way to get tomorrow's date? - python

What is the cleanest and most Pythonic way to get tomorrow's date? There must be a better way than to add one to the day, handle days at the end of the month, etc.

datetime.date.today() + datetime.timedelta(days=1) should do the trick

timedelta can handle adding days, seconds, microseconds, milliseconds, minutes, hours, or weeks.
>>> import datetime
>>> today = datetime.date.today()
>>> today
datetime.date(2009, 10, 1)
>>> today + datetime.timedelta(days=1)
datetime.date(2009, 10, 2)
>>> datetime.date(2009,10,31) + datetime.timedelta(hours=24)
datetime.date(2009, 11, 1)
As asked in a comment, leap days pose no problem:
>>> datetime.date(2004, 2, 28) + datetime.timedelta(days=1)
datetime.date(2004, 2, 29)
>>> datetime.date(2004, 2, 28) + datetime.timedelta(days=2)
datetime.date(2004, 3, 1)
>>> datetime.date(2005, 2, 28) + datetime.timedelta(days=1)
datetime.date(2005, 3, 1)

No handling of leap seconds tho:
>>> from datetime import datetime, timedelta
>>> dt = datetime(2008,12,31,23,59,59)
>>> str(dt)
'2008-12-31 23:59:59'
>>> # leap second was added at the end of 2008,
>>> # adding one second should create a datetime
>>> # of '2008-12-31 23:59:60'
>>> str(dt+timedelta(0,1))
'2009-01-01 00:00:00'
>>> str(dt+timedelta(0,2))
'2009-01-01 00:00:01'
darn.
EDIT - #Mark: The docs say "yes", but the code says "not so much":
>>> time.strptime("2008-12-31 23:59:60","%Y-%m-%d %H:%M:%S")
(2008, 12, 31, 23, 59, 60, 2, 366, -1)
>>> time.mktime(time.strptime("2008-12-31 23:59:60","%Y-%m-%d %H:%M:%S"))
1230789600.0
>>> time.gmtime(time.mktime(time.strptime("2008-12-31 23:59:60","%Y-%m-%d %H:%M:%S")))
(2009, 1, 1, 6, 0, 0, 3, 1, 0)
>>> time.localtime(time.mktime(time.strptime("2008-12-31 23:59:60","%Y-%m-%d %H:%M:%S")))
(2009, 1, 1, 0, 0, 0, 3, 1, 0)
I would think that gmtime or localtime would take the value returned by mktime and given me back the original tuple, with 60 as the number of seconds. And this test shows that these leap seconds can just fade away...
>>> a = time.mktime(time.strptime("2008-12-31 23:59:60","%Y-%m-%d %H:%M:%S"))
>>> b = time.mktime(time.strptime("2009-01-01 00:00:00","%Y-%m-%d %H:%M:%S"))
>>> a,b
(1230789600.0, 1230789600.0)
>>> b-a
0.0

Even the basic time module can handle this:
import time
time.localtime(time.time() + 24*3600)

For people who are dealing with servers Time Stamp
To get yesterday Time Stamp:
yesterdaytimestamp = datetime.datetime.today() + datetime.timedelta(days=-1)
To get Today Time Stamp:
currenttimestamp = datetime.datetime.now().timestamp()
To get Tomorrow Time Stamp:
tomorrowtimestamp = datetime.datetime.today() + datetime.timedelta(days=1)
To print:
print('\n Yesterday TimeStamp is : ', yesterdaytimestamp.timestamp(),
'\n Today TimeStamp is :', currenttimestamp,
'\n Tomorrow TimeStamp is: ', tomorrowtimestamp.timestamp())
The output:
Yesterday TimeStamp is : 1632842904.110993
Today TimeStamp is : 1632929304.111022
Tomorrow TimeStamp is : 1633015704.11103

There's nothing at all wrong with using today() as shown in the selected answer if that is the extent of your needs.
datetime.date.today() + datetime.timedelta(days=1)
Alternatively, if you or someone else working with your code might need more precision in handling tomorrow's date, consider using datetime.now() instead of today(). This will certainly allow for simpler, more readable code:
datetime.datetime.now() + datetime.timedelta(days=1)
This returns something like:
datetime.datetime(2022, 2, 17, 19, 50, 19, 984925)
The advantage is that you can now work with datetime attributes in a concise, human readable way:
class datetime.datetime
A combination of a date and a time. Attributes: year, month, day, hour, minute, second, microsecond, and tzinfo.
Examples
You can easily convert this to a date object withdate():
import datetime
tomorrow = datetime.datetime.now() + datetime.timedelta(days=1)
print(f"Tomorrow's date is {tomorrow.date()}")
tomorrow.date() is easy to use and it is very clear to anyone reading your code that it is returning the date for tomorrow. The output for the above looks like so:
Tomorrow's date is 2022-02-17
If later in your code you only need the date number for the day, you can now use tomorrow.day:
print(f"Tomorrow is the {tomorrow.day}rd")
Which will return something like:
Tomorrow is the 17rd
That's a silly example, but you can see how having access to these attributes can be useful and keep your code readable as well. It can be easily understood that tomorrow.day returns the day number.
Need to work with the exact time tomorrow's date begins? You can now replace the hours, minutes, seconds, and microseconds:
# Replace all attributes except day with 0.
midnight = tomorrow.replace(
hour=0,
minute=0,
second=0,
microsecond=0)
# Print midnight as the beginning of tomorrow's date.
print(f"{midnight}")
Reading the above code, it should be apparent which attributes of tomorrow are being replaced. When midnight is printed, it will output:
2022-02-17 00:00:00
Need to know the time left until tomorrow's date? Now something like that is possible, simple, and readable:
print(f"{midnight - datetime.datetime.now()}")
The output is the time to the microsecond that tomorrow's date begins:
3:14:28.158331
There are many ways people might wish to handle tomorrow's date. By ensuring these attributes are available from the beginning, you can write more readable code and avoid unnecessary work later.

For the case you only want to calculate the timestamp
import time
tomorrow = (int(time.time() / 86400) + 1) * 86400

Related

Adding a timedelta to a skyfield Time

The skyfield Almanach documentation
uses this code to define the points in time between which to compute sunrise & sunset:
t0 = ts.utc(2018, 9, 12, 4)
t1 = ts.utc(2018, 9, 13, 4)
What if I just wanted to use one (start) date and set the next date to be exactly one day after? I can't just add one to the day argument since this would not be correct at the end of the month.
Using Python's datetime I could do this using
from datetime import datetime, timedelta
datetime(2019, 1, 31, 12) + timedelta(days=1)
# datetime.datetime(2019, 2, 1, 12, 0)
but I can't find anything like timedelta in the skyfield API documentation.
What if I just wanted to use one (start) date and set the next date to be exactly one day after? I can't just add one to the day argument since this would not be correct at the end of the month.
Happily, you can just add one to the day! As the documentation says:
https://rhodesmill.org/skyfield/time.html
"you are free to provide out-of-range values and leave it to Skyfield to work out the correct result"
>>> from skyfield.api import load
>>> ts = load.timescale()
[#################################] 100% deltat.data
>>> t = ts.utc(2018, 2, 28 + 1)
>>> t.utc_jpl()
'A.D. 2018-Mar-01 00:00:00.0000 UT'
You can use datetime's timedelta and convert back between datetime and skyfield's Time objects like this:
t0 = ts.utc(2019, 1, 31, 12)
t1 = ts.utc(t0.utc_datetime() + timedelta(days=1))
# Print
t1.utc_iso()
# '2019-02-01T12:00:00Z'
While certainly not beautiful, this allows you to use all the features of Python's datetime.

Aware DateTime object not adapting to change in DST

I wrote a generator in python that yields a new day of data at each call from a pandas DataFrame. My DataFrame is unix timestamp indexed. My first attempt at a code worked as follows (df is the dataframe, tz is a pytz.timezone (Europe/Amsterdam in my case):
def interval_generator(df, tz):
today = datetime.datetime.fromtimestamp(df.index.min(), tz)
last_day = datetime.datetime.fromtimestamp(df.index.max(), tz)
while today <= last_day:
tomorrow = today + datetime.timedelta(days=1)
yield df.loc[tz.localize(today).timestamp():tz.localize(tomorrow).timestamp() - 1]
today = tomorrow
However when running my code I noticed that the DateTime object has the weird behaviour of really sticking with the timezone it was initially attached to (especially the incremented hour). Example of the (in my eyes) weird behaviour:
import datetime
import pytz
tz = pytz.timezone('Europe/Amsterdam')
# This is when daylight saving times stops in the Netherlands in 2015.
t1 = datetime.datetime(2015, 10, 25, 0, 0)
t2 = t1 + datetime.timedelta(days=1)
t1_localized = tz.localize(t1)
t2_localized = tz.localize(t2)
t2_loc_incremented = t1_localized + datetime.timedelta(days=1)
When printing the output of these final three variables you get:
>>> t1_localized
datetime.datetime(2015, 10, 25, 0, 0, tzinfo=<DstTzInfo 'Europe/Amsterdam' CEST+2:00:00 DST>)
>>> t2_localized
datetime.datetime(2015, 10, 26, 0, 0, tzinfo=<DstTzInfo 'Europe/Amsterdam' CET+1:00:00 STD>)
>>> t2_loc_incremented
datetime.datetime(2015, 10, 26, 0, 0, tzinfo=<DstTzInfo 'Europe/Amsterdam' CEST+2:00:00 DST>)
More importantly for my code, the timestamp for both versions of t2 is different:
>>> t2_localized.timestamp()
1445814000.0
>>> t2_loc_incremented.timestamp()
1445810400.0
I solved this in my generator function with the following workaround:
def interval_generator(df, tz):
today = datetime.datetime.fromtimestamp(df.index.min(), tz=tz).strftime('%Y-%m-%d')
today = datetime.datetime.strptime(today, '%Y-%m-%d')
last_day = datetime.datetime.fromtimestamp(df.index.max(), tz=tz).strftime('%Y-%m-%d')
last_day = datetime.datetime.strptime(last_day, '%Y-%m-%d')
while today <= last_day:
tomorrow = today + datetime.timedelta(days=1)
yield df.loc[tz.localize(today).timestamp():tz.localize(tomorrow).timestamp() - 1]
today = tomorrow
Which basically gets me the desired functionality, but I can't help but wonder whether there isn't a better way to deal with this issue. Is there any good alternative for my problem? Is this considered a bug of the datetime module? (I am using python 3.4) I tried googling, but could not find anything

How to find the time until a certain date in Python, for a specific TimeZone?

I want to create a script that outputs the amount of time remaining (as "X hours, Y minutes, Z seconds remaining") until 27th August 9 AM (in IST).
This is what I tried:
import datetime
delta = datetime.datetime(2015, 8, 27) - datetime.datetime.now()
total_seconds = delta.total_seconds()
s = int(total_seconds)
print s
This prints 4439, which is the wrong answer. I'm guessing it's a timezone issue. My timezone is Asia/Kolkata and the correct answer should be 36720.
How can this be done?
Try this instead, I suspect the int() messes with the total_seconds object.
import datetime
delta = datetime.datetime(2015, 8, 27) - datetime.datetime.now()
total_seconds = (delta).total_seconds()
print total_seconds

How do I find the nth day of the next month in Python?

I am trying to get the date delta by subtracting today's date from the nth day of the next month.
delta = nth_of_next_month - todays_date
print delta.days
How do you get the date object for the 1st (or 2nd, 3rd.. nth) day of the next month. I tried taking the month number from the date object and increasing it by 1. Which is obviously a dumb idea because 12 + 1 = 13. I also tried adding one month to today and tried to get to the first of the month. I am sure that there is a much more efficient way of doing this.
The dateutil library is useful for this:
from dateutil.relativedelta import relativedelta
from datetime import datetime
# Where day is the day you want in the following month
dt = datetime.now() + relativedelta(months=1, day=20)
This should be straightforward unless I'm missing something in your question:
import datetime
now = datetime.datetime.now()
nth_day = 5
next_month = now.month + 1 if now.month < 12 else 1 # February
year = now.year if now.month < 12 else now.year+1
nth_of_next_month = datetime.datetime(year, next_month, nth_day)
print(nth_of_next_month)
Result:
2014-02-05 00:00:00
Using dateutil as suggested in another answer is a much better idea than this, though.
Another alternative is to use delorean library:
Delorean is a library that provides easy and convenient datetime
conversions in Python.
>>> from delorean import Delorean
>>> d = Delorean()
>>> d.next_month()
Delorean(datetime=2014-02-15 18:51:14.325350+00:00, timezone=UTC)
>>> d.next_month().next_day(2)
Delorean(datetime=2014-02-17 18:51:14.325350+00:00, timezone=UTC)
My approach to calculating the next month without external libraries:
def nth_day_of_next_month(dt, n):
return dt.replace(
year=dt.year + (dt.month // 12), # +1 for december, +0 otherwise
month=(dt.month % 12) + 1, # december becomes january
day=n)
This works for both datetime.datetime() and datetime.date() objects.
Demo:
>>> import datetime
>>> def nth_day_of_next_month(dt, n):
... return dt.replace(year=dt.year + (dt.month // 12), month=(dt.month % 12) + 1, day=n)
...
>>> nth_day_of_next_month(datetime.datetime.now(), 4)
datetime.datetime(2014, 2, 4, 19, 20, 51, 177860)
>>> nth_day_of_next_month(datetime.date.today(), 18)
datetime.date(2014, 2, 18)
Without using any external library, this can be achived as follows
from datetime import datetime, timedelta
def nth_day_of_next_month(n):
today = datetime.now()
next_month_dt = today + timedelta(days=32-today.day)
return next_month_dt.replace(day=n)

How to find next day's Unix timestamp for same hour, including DST, in Python?

In Python, I can find the Unix time stamp of a local time, knowing the time zone, like this (using pytz):
>>> import datetime as DT
>>> import pytz
>>> mtl = pytz.timezone('America/Montreal')
>>> naive_time3 = DT.datetime.strptime('2013/11/03', '%Y/%m/%d')
>>> naive_time3
datetime.datetime(2013, 11, 3, 0, 0)
>>> localized_time3 = mtl.localize(naive_time3)
>>> localized_time3
datetime.datetime(2013, 11, 3, 0, 0, tzinfo=<DstTzInfo 'America/Montreal' EDT-1 day, 20:00:00 DST>)
>>> localized_time3.timestamp()
1383451200.0
So far, so good. naive_time is not aware of the time zone, whereas localized_time knows its midnight on 2013/11/03 in Montréal, so the (UTC) Unix time stamp is good. This time zone is also my local time zone and this time stamp seems right:
$ date -d #1383451200
Sun Nov 3 00:00:00 EDT 2013
Now, clocks were adjusted one hour backward November 3rd at 2:00 here in Montréal, so we gained an extra hour that day. This means that there were, here, 25 hours between 2013/11/03 and 2013/11/04. This shows it:
>>> naive_time4 = DT.datetime.strptime('2013/11/04', '%Y/%m/%d')
>>> localized_time4 = mtl.localize(naive_time4)
>>> localized_time4
datetime.datetime(2013, 11, 4, 0, 0, tzinfo=<DstTzInfo 'America/Montreal' EST-1 day, 19:00:00 STD>)
>>> (localized_time4.timestamp() - localized_time3.timestamp()) / 3600
25.0
Now, I'm looking for an easy way to get the localized_time4 object from localized_time3, knowing I want to get the next localized day at the same hour (here, midnight). I tried timedelta, but I believe it's not aware of time zones or DST:
>>> localized_time4td = localized_time3 + DT.timedelta(1)
>>> localized_time4td
datetime.datetime(2013, 11, 4, 0, 0, tzinfo=<DstTzInfo 'America/Montreal' EDT-1 day, 20:00:00 DST>)
>>> (localized_time4td.timestamp() - localized_time3.timestamp()) / 3600
24.0
My purpose is to get informations about log entries that are stored with their Unix timestamp for each local day. Of course, if I use localized_time3.timestamp() and add 24 * 3600 here (which will be the same as localized_time4td.timestamp()), I will miss all log entries that happened between localized_time4td.timestamp() and localized_time4td.timestamp() + 3600.
In other words, the function or method I'm looking for should know when to add 25 hours, 24 hours or 23 hours sometimes to a Unix time stamp, depending on when DST shifts happen.
Without using a new package:
def add_day(x):
d = x.date()+DT.timedelta(1)
return mtl.localize(x.replace(year=d.year, month=d.month, day=d.day, tzinfo=None))
Full script:
import datetime as DT
import pytz
import calendar
mtl = pytz.timezone('America/Montreal')
naive_time3 = DT.datetime.strptime('2013/11/03', '%Y/%m/%d')
print repr(naive_time3)
#datetime.datetime(2013, 11, 3, 0, 0)
localized_time3 = mtl.localize(naive_time3)
print repr(localized_time3)
#datetime.datetime(2013, 11, 3, 0, 0, tzinfo=<DstTzInfo 'America/Montreal' EDT-1 day, 20:00:00 DST>)
print calendar.timegm(localized_time3.utctimetuple())
#1383451200.0
def add_day(x):
d = x.date()+DT.timedelta(1)
return mtl.localize(x.replace(year=d.year, month=d.month, day=d.day, tzinfo=None))
print repr(add_day(localized_time3))
#datetime.datetime(2013, 11, 4, 0, 0, tzinfo=<DstTzInfo 'America/Montreal' EST-1 day, 19:00:00 STD>)
(calendar is for Python2.)
I gradually provide several solutions with the most robust solution at the very end of this answer that tries to handle the following issues:
utc offset due to DST
past dates when the local timezone might have had different utc offset due to reason unrelated to DST. dateutil and stdlib solutions fail here on some systems, notably Windows
ambiguous times during DST (don't know whether Arrow provides interface to handle it)
non-existent times during DST (the same)
To find POSIX timestamp for tomorrow's midnight (or other fixed hour) in a given timezone, you could use code from How do I get the UTC time of “midnight” for a given timezone?:
from datetime import datetime, time, timedelta
import pytz
DAY = timedelta(1)
tz = pytz.timezone('America/Montreal')
tomorrow = datetime(2013, 11, 3).date() + DAY
midnight = tz.localize(datetime.combine(tomorrow, time(0, 0)), is_dst=None)
timestamp = (midnight - datetime(1970, 1, 1, tzinfo=pytz.utc)).total_seconds()
dt.date() method returns the same naive date for both naive and timezone-aware dt objects.
The explicit formula for timestamp is used to support Python version before Python 3.3. Otherwise .timestamp() method could be used in Python 3.3+.
To avoid ambiguity in parsing input dates during DST transitions that are unavoidable for .localize() method unless you know is_dst parameter, you could use Unix timestamps stored with the dates:
from datetime import datetime, time, timedelta
import pytz
DAY = timedelta(1)
tz = pytz.timezone('America/Montreal')
local_dt = datetime.fromtimestamp(timestamp_from_the_log, tz)
tomorrow = local_dt.date() + DAY
midnight = tz.localize(datetime.combine(tomorrow, time(0, 0)), is_dst=None)
timestamp = (midnight - datetime(1970, 1, 1, tzinfo=pytz.utc)).total_seconds()
To support other fixed hours (not only midnight):
tomorrow = local_dt.replace(tzinfo=None) + DAY # tomorrow, same time
dt_plus_day = tz.localize(tomorrow, is_dst=None)
timestamp = dt_plus_day.timestamp() # use the explicit formula before Python 3.3
is_dst=None raises an exception if the result date is ambiguous or non-existent. To avoid exception, you could choose the time that is closest to the previous date from yesterday (same DST state i.e., is_dst=local_dt.dst()):
from datetime import datetime, time, timedelta
import pytz
DAY = timedelta(1)
tz = pytz.timezone('America/Montreal')
local_dt = datetime.fromtimestamp(timestamp_from_the_log, tz)
tomorrow = local_dt.replace(tzinfo=None) + DAY
dt_plus_day = tz.localize(tomorrow, is_dst=local_dt.dst())
dt_plus_day = tz.normalize(dt_plus_day) # to detect non-existent times
timestamp = (dt_plus_day - datetime(1970, 1, 1, tzinfo=pytz.utc)).total_seconds()
.localize() respects given time even if it is non-existent, therefore .normalize() is required to fix the time. You could raise an exception here if normalize() method changes its input (non-existent time detected in this case) for consistency with other code examples.
(Thanks to #rdodev for pointing me to Arrow).
Using Arrow, this operation becomes easy:
>>> import arrow
>>> import datetime as DT
>>> lt3 = arrow.get(DT.datetime(2013, 11, 3), 'America/Montreal')
>>> lt3
<Arrow [2013-11-03T00:00:00-04:00]>
>>> lt4 = arrow.get(DT.datetime(2013, 11, 4), 'America/Montreal')
>>> lt4
<Arrow [2013-11-04T00:00:00-05:00]>
>>> lt4.timestamp - (lt3.replace(days=1).timestamp)
0
>>> (lt3.replace(days=1).timestamp - lt3.timestamp) / 3600
25.0
Using Arrow's replace method, singular unit names replace that property while plural adds to it. So lt3.replace(days=1) is November 4th, 2013 while lt3.replace(day=1) is November 1st, 2013.
Here an alternative based on dateutil:
>>> # In Spain we changed DST 10/26/2013
>>> import datetime
>>> import dateutil.tz
>>> # tzlocal gets the timezone of the computer
>>> dt1 = datetime.datetime(2013, 10, 26, 14, 00).replace(tzinfo=dateutil.tz.tzlocal())
>>> print dt1
2013-10-26 14:00:00+02:00
>>> dt2 = dt1 + datetime.timedelta(1)
>>> print dt2
2013-10-27 14:00:00+01:00
# see if we hace 25 hours of difference
>>> import time
>>> (time.mktime(dt2.timetuple()) - time.mktime(dt1.timetuple())) / 3600.0
25.0
>>> (float(dt2.strftime('%s')) - float(dt1.strftime('%s'))) / 3600 # the same
25.0

Categories