If you are dealing with number of hours people work then you should take DST transitions (and the changes in UTC offset for other reasons) into account otherwise the result can be wrong (by an hour usually).
It is not trivial to find midnight correctly. See How do I get the UTC time of “midnight” for a given timezone?
To get correct results, you should specify a date in addition to the time e.g., assume the last Sunday. And you need to know the local timezone and (for portability) you need a historical timezone database such as provided by pytz
module. tzlocal
module can find your local timezone:
from datetime import datetime, timedelta
from tzlocal import get_localzone # $ pip install tzlocal
from pytz import AmbiguousTimeError
DAY = timedelta(1)
local_timezone = get_localzone() # get pytz timezone
time_format = "%H:%M"
def asktime(prompt, format):
while True:
try:
return datetime.strptime(raw_input(prompt), format)
except ValueError:
print('Invalid time format, expected %s. Try again.' % format)
def disambiguate(time, date):
d = datetime.combine(date, time).replace(tzinfo=None)
try:
return local_timezone.localize(d, is_dst=None)
except AmbiguousTimeError:
is_dst = yes_or_no('Was it summer time (%s)?' % d)
return local_timezone.localize(d, is_dst=is_dst)
# allow NonExistentTimeError to propagate
# find last Sunday
sunday = datetime.now(local_timezone)
while sunday.weekday() != 6: # Sunday is 6
sunday -= DAY
# get 'sign on', 'sign off' times
#NOTE: assume, no 24h+ shifts
signon = asktime("What time did you sign on Sunday: ", time_format)
signoff = asktime("What time did you sign off Sunday: ", time_format)
if signoff < signon: # signon is a day before (Saturday)
signon = disambiguate(signon, sunday.date() - DAY)
signoff = disambiguate(signoff, sunday)
print("Signon time %s" % signon)
print("Signoff time %s" % signoff)
diff = signoff - signon
print("The difference %s" % diff)
If 'sign on', 'sign off' times are during DST transition ("fall back") then the same local time may occur twice. Then (in addition to a date) you need to know whether it was a summer time during ('sign on', 'sign off'), to make the time unambiguous.
Where yes_or_no()
is a small utility function:
def yes_or_no(prompt):
while True:
answer = raw_input(prompt)
if answer.lower() in {'yes', 'no'}:
return answer == 'yes'
print("Please, answer 'yes' or 'no'. Try again.")
Everything is much simpler if you don't need to deal with the local timezone and you can ask for UTC time:
signon = asktime("What UTC time did you sign on Sunday: ", time_format)
signoff = asktime("What UTC time did you sign off Sunday: ", time_format)
# support 24h+ shifts
while signoff < signon: # signon is on a previous date
signon -= DAY
diff = signoff - signon
print(diff)
And that is why it is recommended to work with UTC time instead of the local time if possible (if you can ask people to provide time in UTC).
If you can't install tzlocal
module and can't use UTC time then you could use time.mktime()
to disambiguate the local time (it may be less reliable than the above method that uses the tz database), see my answer to "Find if 24 hrs have passed between datetimes - Python".