When subtracting a datetime that was converted from string, total_seconds() is wrong

Viewed 229

The total_seconds() is incorrect when I do this:

from datetime import timedelta, datetime
from pytz import timezone

timezone = timezone('Australia/Sydney')
startDate = datetime.now(timezone)
dateStr = '2020-05-18 20:12:30' # In our brain we know this time is in Sydney Time
endDate = datetime.strptime(dateStr, '%Y-%m-%d %H:%M:%S').replace(tzinfo=timezone)

diff = endDate - startDate
print(diff.total_seconds()) # incorrect answer

When both datetime objects are datetime objects originally, and you substract them, they are right

from datetime import timedelta, datetime
from pytz import timezone

timezone = timezone('Australia/Sydney')
startDate = datetime.now(timezone)
endDate = datetime.now(timezone) + timedelta(hours=2, seconds=32)

diff = endDate - startDate
print(diff.total_seconds()) # correct answer

How can I fix my issue ?

1 Answers

So it seems like all things in the horrible world of date-times, timezones and offsets this is one of these weird and wonderful things. the issue seems to stem from the fact that pytz.timezone will return a timezone object with several timezones.

{
(datetime.timedelta(seconds=36300), datetime.timedelta(0), 'LMT'): <DstTzInfo 'Australia/Sydney' LMT+10:05:00 STD>, 
(datetime.timedelta(seconds=36000), datetime.timedelta(0), 'AEST'): <DstTzInfo 'Australia/Sydney' AEST+10:00:00 STD>, 
(datetime.timedelta(seconds=39600), datetime.timedelta(seconds=3600), 'AEDT'): <DstTzInfo 'Australia/Sydney' AEDT+11:00:00 DST>
}

It seems when you are passing the timezone to the now method it's picking the timezone from your choice of 3 based on probably some local TZINFO in your setup. However, when passing the timezone to replace, it's just picking the LMT which is different by 300. A quick mention about LMT:

Local Mean Time Today: While Local Mean Time does not directly determine civil time these days, it is still used to make sure our clocks follow the Sun as closely as possible. UT1, a version of Universal Time is the Local Mean Time at the prime meridian in Greenwich, London. It is one of the components used to calculate Coordinated Universal Time (UTC), the time scale used to determine local times worldwide.

LMT is also used by astronomers around the world to time their observations.

Essentially your issue spans from datetime.now() acting on the local timezone and datetime.replace() acting on the LMT timezone. So as I mentioned in my post create your dates consistently either create them both via replace (although you will still be off by 5 mins in terms of actual time, the difference will be correct.)

UPDATE

If you want both datetime objects to be in local Sydney time then you can create your stardate as you did before using datetime.now(). But you should create your end date from your timezone objects asking it to localize it for you like.

from datetime import datetime
from pytz import timezone

timezone = timezone('Australia/Sydney')
startDate = datetime.now(timezone)
dateStr = '2020-05-18 18:52:30' # In our brain we know this time is in Sydney Time
endDate = timezone.localize(datetime.strptime(dateStr, '%Y-%m-%d %H:%M:%S'))
print(startDate, endDate, sep="\n")
diff = endDate - startDate
print(diff.total_seconds())

OUTPUT

2020-05-18 18:51:24.722614+10:00
2020-05-18 18:52:30+10:00
65.277386
Related