I have a piece of code that provided two timezone-aware Python datetime(s) tries to figure out if they are exactly the beginning and the end of the same day.
For most of the cases, checking that the difference (the timedelta) between the two dates is 23 hours, 59 minutes, 59 seconds and 999999 microseconds, works fine:
from datetime import datetime, time, timedelta, date
import pytz
ny = pytz.timezone('America/New_York')
day=date(2019, 3, 11)
start_naive=datetime.combine(day, time.min)
start=ny.localize(start_naive)
end_naive=datetime.combine(day, time.max)
end=ny.localize(end_naive)
same_day_check = timedelta(hours=time.max.hour,
minutes=time.max.minute,
seconds=time.max.second,
microseconds=time.max.microsecond)
print(f"Same day?")
(end - start) == same_day_check # This outputs True
⚠️ However...
On daylight savings time dates, a day doesn't last 23 hours, 59 min. 59 sec. and 999999 microseconds but +1 hour or -1 hour (in those dates, "a day" can last 22 hours, 59 min.. or 24 hour, 59 min...):
# ...
day=date(2019, 3, 10) # DST start in NY timezone
# ...
same_day_check = timedelta(hours=time.max.hour,
minutes=time.max.minute,
seconds=time.max.second,
microseconds=time.max.microsecond)
print(f"Same day?")
(end - start) == same_day_check # Now this is `False`
print(end-start) # This outputs 22:59:59.999999
Is there a more reliable way than this? Using timedelta like described above is clearly not a reliable solution.
Related
I am trying to write a script that runs continuously in the background to countdown to a repeated weekly event. For example, it should tell me how many days, hours, and minutes it will take to reach the specified time.
I know how to do it if I had a specific date and time.
import datetime
delta = datetime.datetime(2018, 5, 5, 8) - datetime.datetime.now()
But what if I don't have a specific date and time? Can datetime let me choose the day of the week?
EDIT:
i.e. Some pseudocode like this is what I need.
delta = datetime(Thursday 8 PM) - datetime.datetime.now()
#returns datetime or timedelta in days, hours, minutes
EDIT:
Thanks Ethan, i appreciate your constructive advice.
I wrote a small script which should do what you want:
import datetime
import time
wanted_day = 'thursday'
wanted_time = 8
list = [['monday', 0],['tuesday', 1],['wednesday', 2],['thursday', 3],['friday', 4],['saturday', 5],['sunday', 6]]
for i in list:
if wanted_day == i[0]:
number_wanted_day = i[1]
# today delivers the actual day
today = datetime.datetime.today().weekday()
# delta_days describes how many days are left until the wanted day
delta_days = number_wanted_day - today
# time delivers the actual time
time = time.localtime(time.time())
if wanted_time > time[3]:
delta_hours = wanted_time - time[3]
delta_mins = 59 - time[4]
delta_secs = 59 - time[5]
else:
delta_days = delta_days - 1
delta_hours = 23 - time[3] + wanted_time
delta_mins = 59 - time[4]
delta_secs = 59 - time[5]
print [delta_days, delta_hours, delta_mins, delta_secs]
The output looks like this then:
[2, 21, 3, 49]
2 is the number of days, 21 the number of hours, 3 the number of mins and 49 the number of secs (I used thursday 8 am as wanted time).
You just need to input the time in the format 0-23, with am and pm you would need to adapt it a bit
Have a program to get a date count down. I don't want to print the milliseconds, please help here is my program in python:
import time
import datetime
while (datetime.datetime.now() != datetime.datetime (2018,5,5,19,30)):
print (datetime.datetime (2018,5,5,19,30) - datetime.datetime.now())
time.sleep(1.0)
This is my current output: 54 days, 3:54:53.603289
Would like: 54 days, 3:54:53, but don't know how to do it.
You are looking for datetime.replace(microsecond=0), which will:
Return a datetime with the same attributes, except for those
attributes given new values by whichever keyword arguments are
specified.
import time
import datetime
while (datetime.datetime.now() != datetime.datetime (2018,5,5,19,30)):
print (datetime.datetime (2018,5,5,19,30) - datetime.datetime.now().replace(microsecond=0))
time.sleep(1.0)
Output:
54 days, 3:49:31
54 days, 3:49:30
54 days, 3:49:29
54 days, 3:49:28
54 days, 3:49:27
54 days, 3:49:26
54 days, 3:49:25
54 days, 3:49:24
54 days, 3:49:23
As you have found, there are no good formatting options for a timedelta object. I would recommend you pre-calculate an end_time and also use < rather than waiting for the times to match. In the event your PC is busy at the exact second, your loop might miss the match. By using < it will also catch it:
from datetime import datetime
import time
end_time = datetime(2018, 5, 5, 19, 30)
while datetime.now() < end_time:
print(end_time - datetime.now().replace(microsecond=0))
time.sleep(1.0)
I have two timestamps which are stored in float format:
tms1 = 1479081600.0
tms2 = 1482105600.0
Upon calculating the difference I get
tms2 - tms1
3024000.0
How do I go about displaying this time difference of 3024000 into a readable format in days, months or years? (The answer is 35 days between 14 Nov 2016 to 19 Dec 2016 using an online unix time difference calculator)
You can use (after importing datetime)
datetime.timedelta(seconds=3024000).days
which is
35
You should use timedelta as this is a time delta - a difference in time, not an absolute time. A full representation can also be obtained by coercing a timedelta to a string:
print(datetime.timedelta(seconds=3024000))
Gives the output:
35 days, 0:00:00
Note that you don't need an online calculator for anything - datetime comes with batteries included. You could do:
import datetime
date_format = "%d %b %Y"
start_date = datetime.datetime.strptime("14 Nov 2016", date_format)
end_date = datetime.datetime.strptime("19 Dec 2016", date_format)
print(start_date == datetime.datetime.fromtimestamp(1479081600))
print(start_date)
print(end_date.strftime("%d/%m/%Y"))
diff = end_date - start_date
print(diff)
print(diff.days)
which outputs:
True
2016-11-14 00:00:00
19/12/2016
35 days, 0:00:00
35
Note that diff here is identical to the original timedelta object, but is dynamically created from datetimes rather than statically constructed. I've also demonstrated the fact that you can build a datetime from a timestamp, should you wish, and I've also taken the liberty of demonstrating strftime and the like to illustrate the power of datetime. I highly recommend the datetime approach over an arithmetic approach as it's a lot more readable and extensible.
This answer is pretty lightweight, which isn't necessarily bad, as often you might not need any more functionality than it provides, but if the timedelta between two days is less than 24 hours, it will round down to 0 days, for example. It also can't handle timezones. If you need either of those, see the legendary Raymond's awesome answer
Just subtracting seconds doesn't help you know whether a day boundary has been crossed, so it is necessary to convert the timestamps to datetime objects before computing the days.
Add since the timezone can affect what the calendar day is for a UTC timestamp, you may need a tzinfo object as well.
Once the calendar dates are known, a little calendar math is needed to compute the difference in years, months, and days:
from datetime import timedelta, datetime
def time_diff(start_timestamp, end_timestamp, tz=None):
""" Return time difference in years, months, and days.
If *tz* is None, the timestamp is converted to the platform’s local date
and time. Otherwise, *tz* should be an instance of a *tzinfo* subclass.
"""
# Determine whether we're going forward or backward in time
ago = ''
if end_timestamp < start_timestamp:
ago = 'ago'
start_timestamp, end_timestamp = end_timestamp, start_timestamp
# Compute the calendar dates from the timestamps
d1 = datetime.fromtimestamp(start_timestamp, tz)
d2 = datetime.fromtimestamp(end_timestamp, tz)
# Advance d1 day-by-day until the day is at or above d2.day
days = 0
while d2.day < d1.day:
days += 1
d1 += timedelta(days=1)
# Now compute the day difference
days += d2.day - d1.day
# Compute the totals months difference and express in years and months
total_months = (d2.year * 12 + d2.month) - (d1.year * 12 + d1.month)
years, months = divmod(total_months, 12)
# format the output
plural = lambda n: '' if n == 1 else 's'
return '%d year%s, %d month%s, and %d day%s %s' % (
years, plural(years), months, plural(months), days, plural(days), ago)
Here is an example of how to use the function:
from datetime import tzinfo
class GMT1(tzinfo):
# Example tzinfo subclass taken from the Python docs
def utcoffset(self, dt):
return timedelta(hours=1)
def dst(self, dt):
return timedelta(0)
def tzname(self,dt):
return "Europe/Prague"
print(time_diff(1479081600.0, 1482105600.0, tz=GMT1()))
This outputs:
0 years, 1 month, and 5 days
I am trying to write a program to determine the time and date corresponding to a elapsed number of seconds since 00: 00: 00 on 1 January 2016.
But i wanted my result in specific format.
i.e should be the corresponding hour (in military time), minute, second, day of the month, month name, year, and day of the week name (Sunday – Saturday).
for example,output should look like the following
23:59:32 2 January 2018 Tuesday
i tried to code this
import time
start = time.time()
#do something
end = time.time()
temp = end-start
print(temp)
hours = temp//3600
temp = temp - 3600*hours
minutes = temp//60
seconds = temp - 60*minutes
print('%d:%d:%d' %(hours,minutes,seconds))
But i could only get the hours, minutes and seconds part,
Is there any way i can get the month,year,and week name too ?
Also i'm trying to handle leap years too.since a leap year has 366 days with 29 days in February. Leap years are years that are evenly divisible by 4, with the exception of those evenly divisible by 100 but not 400.
To format a datetime you can use strftime().
import datetime
my_time = datetime.datetime(year=2018, month=1, day=2, hour=23, minute=59,second=32)
print (my_time.strftime("%X %-d %B %Y %A"))
If you want to change the format use this table for reference
You want to use the Datetime Module. Handling dates and times is trickier than it appears! That's why it's good that Python has datetime handling built-in. Datetime and timedelta objects account for leap years and leap seconds, and they handle operations like addition and subtraction 'intuitively'.
import datetime
def convert_seconds_since_jan_1(seconds):
JAN_1_2001 = datetime.datetime(year = 2001, month = 1, day = 1)
added_time = datetime.timedelta(seconds = seconds)
return (JAN_1_2001+added_time)
Getting the weekday is simply a matter of string formatting. Remember that weekdays are modulo 7!
I am writing a module that calculates the half hour trading period in a given day. The trading periods start on the half hour and are consecutively numbered from 1 (starting 00:00) to 48 (starting 23:30). Normally there are 48 trading periods in a day, but on the day that daylight savings starts there are 46 and on the day it ends there are 50.
The code below works on all 'normal' days, but fails to give correct trading period numbers on days when daylight savings starts or ends because datetime.replace() in the code below uses the same UTC time offset for the start of the day. On days when daylight savings changes this assumption is incorrect.
Is it possible for datetime.replace() to set the time at 00:00 for 'wall clock' time, so that the time difference matches what you get if you set a stopwatch at 00:00 then counted the number of half hour intervals it would match correctly for all days? I have not found an automatic way to do this.
An example:
Daylight savings ended in New Zealand on 5th April 2015 at 03:00 (2015-05-04 14:00Z). Therefore the hour 02:00-02:59 (2015-05-04 14:00Z - 2015-05-04 14:59Z) was repeated in 'wall clock' time. Therefore in New Zealand it took 18000 seconds to reach 4am on the 5th April 2015, as daylight savings time ended. On 28th September 2014 it took 10800 seconds, as daylight savings time started.
#staticmethod
def half_hour_from_utc(time_utc = None):
# Get the time as NZ civil time.
time_nz = time_utc.astimezone(pytz.timezone('Pacific/Auckland'))
# Get the time tuple for the start of the day. This is done by keeping
# the date the same but setting the hours, minutes, seconds and
# microseconds to zero.
time_start_of_day = time_nz.replace(hour = 0, minute = 0, second = 0, microsecond = 0)
# Get total number of seconds. The half hour period is the number of
# 1800 second periods that have passed + 1
total_secs = int((time_nz - time_start_of_day).total_seconds())
half_hour = 1 + total_secs // 1800
print('%s %s %s %s\n' % (time_nz, time_start_of_day, total_secs, half_hour))
The issue is the .replace() call that may return a non-normalized datetime value i.e., tzinfo may be wrong for the midnight. See How do I get the UTC time of “midnight” for a given timezone?
from datetime import datetime, time as datetime_time, timedelta
import pytz # $ pip install pytz
def half_hour_from_utc(time_utc, tz=pytz.timezone('Pacific/Auckland')):
time_nz = time_utc.astimezone(tz) # no need to call normalize() here
midnight = datetime.combine(time_nz, datetime_time(0, 0)) # naive
time_start_of_day = tz.localize(midnight, is_dst=None) # aware
return 1 + (time_nz - time_start_of_day) // timedelta(minutes=30) # Python 3
To emulate 1 + td // timedelta(minutes=30) on Python 2:
td = time_nz - time_start_of_day
assert td.days == 0
return 1 + td.seconds // 1800
If DST transition may happen at midnight in the given timezone then you could use a minimum for the start of the day:
time_start_of_day = min(tz.localize(midnight, is_dst=False),
tz.localize(midnight, is_dst=True))
Note: it works even if 00:00 time does not exist in the given timezone on a given date: to find the difference only the corresponding utc time matters.
You should use normalize() and localize() when dealing with wall clock arithmetic:
def half_hour_from_utc(time_utc = None):
tz = pytz.timezone('Pacific/Auckland')
time_nz = tz.normalize(time_utc)
time_start_of_day = tz.localize(datetime.datetime(time_nz.year, time_nz.month, time_nz.day))
total_secs = int((time_nz - time_start_of_day).total_seconds())
half_hour = 1 + total_secs // 1800
print('%s %s %s %s\n' % (time_nz, time_start_of_day, total_secs, half_hour))
normalize() = convert datetime with timezone to datetime with another timezone.
localize() = convert datetime without timezone to datetime with timezone.
These methods take into account the necessary logic for computing the correct datetime with timezone.