Python: given the current time in UTC, how do you determine the start and end time of a day in a specific time zone?

I play with the Google App Engine, and I found out that the time zone is tied to UTC. I want to determine the start and end time of the current day for the user's local time zone. So, basically, given the current time in UTC, how do you determine the start and end times of the current day, taking into account the transition to summer savings.

I have a clumsy sample code. Please note that I understand that if I manually enter the date, I can also specify the date tomorrow, but these are examples, and I want to determine it programmatically. My main problem is that if I add timedelta to datetime with a timezone and then normalize it (as suggested in pytz docs), I get a datetime that is hourly when switching to daylight saving time.

Not mentioned in the code, but ultimately the goal is to convert these times to UTC, so it is important to know the time zone.

#!/usr/bin/python import datetime from pytz.gae import pytz hobart_tz = pytz.timezone('Australia/Hobart') utc_dt = pytz.utc.localize(datetime.datetime.utcnow()) hobart_dt = utc_dt.astimezone(hobart_tz) # create a new datetime for the start of the day and add a day to it to get tomorrow. today_start = datetime.datetime(hobart_dt.year, hobart_dt.month, hobart_dt.day) today_start = hobart_tz.localize(today_start) today_end = hobart_tz.normalize(today_start + datetime.timedelta(days=1)) print 'today:', today_start print ' next:', today_end print # gives: # today: 2011-08-28 00:00:00+10:00 # next: 2011-08-29 00:00:00+10:00 # but say today is a daylight savings changeover. # after normalisation, we are off by an hour. dst_finish_2011 = datetime.datetime(2011, 4, 3) # this would come from hobart_dt dst_finish_2011 = hobart_tz.localize(dst_finish_2011) next = hobart_tz.normalize(dst_finish_2011 + datetime.timedelta(days=1)) print '2011-04-03:', dst_finish_2011 print '2011-04-04:', next # expect 2011-04-04 00:00:00+10:00 print # gives # 2011-04-03: 2011-04-03 00:00:00+11:00 # 2011-04-04: 2011-04-03 23:00:00+10:00 (wrong) dst_start_2011 = datetime.datetime(2011, 10, 2) # this would come from hobart_dt dst_start_2011 = hobart_tz.localize(dst_start_2011) next = hobart_tz.normalize(dst_start_2011 + datetime.timedelta(days=1)) print '2011-10-02:', dst_start_2011 print '2011-10-03:', next # expect 2011-10-03 00:00:00+11:00 print # gives # 2011-10-02: 2011-10-02 00:00:00+10:00 # 2011-10-03: 2011-10-03 01:00:00+11:00 (wrong) # I guess we could ignore the timezone and localise *after* ? dst_finish_2011 = datetime.datetime(2011, 4, 3) # this would come from hobart_dt next = dst_finish_2011 + datetime.timedelta(days=1) # now localise dst_finish_2011 = hobart_tz.localize(dst_finish_2011) next = hobart_tz.localize(next) print '2011-04-03:', dst_finish_2011 print '2011-04-04:', next # expect 2011-04-04 00:00:00+10:00 print # gives # 2011-04-03: 2011-04-03 00:00:00+11:00 # 2011-04-04: 2011-04-04 00:00:00+10:00 
+3
python timezone datetime utc
Aug 28
source share
4 answers

I believe that you get this result because you are adding one day instead of 86400 seconds. There is no single, always correct equivalence between days and seconds. For example, if pytz forced one day to "really" be 86400 seconds, then adding one day to the date December 31 or June 30 would sometimes lead to the fact that the seconds field of the result would be "one second", because after a few years these days had 86,401 seconds. (Perhaps in the future they may have 86402 or even 86,399 seconds.)

Thus, adding one day means simply increasing the day by one, transferring it to the month and year, if necessary, but without changing the time of day fields. Try adding 86400 seconds and make sure you get the desired result.

0
Aug 29 2018-11-21T00:
source share
β€” -

To find out the start time of the day (midnight) and the end time of the day (tomorrow) in the local time zone, knowing the UTC time:

 #!/usr/bin/env python from datetime import datetime, time, timedelta import pytz # $ pip install pytz from tzlocal import get_localzone # $ pip install tzlocal tz = get_localzone() # get the local timezone as pytz.timezone now = datetime.now(pytz.utc) # some UTC time dt = now.astimezone(tz) # the same time in the local timezone today = dt.date() # today in the local timezone (naive date object) midnight = datetime.combine(today, time()) # midnight in the local timezone aware_midnight = tz.localize(midnight, is_dst=None) # raise exception # for ambiguous or # non-existing # times tomorrow = midnight + timedelta(1) aware_tomorrow = tz.localize(tomorrow, is_dst=None) def print_time(aware_dt, fmt="%Y-%m-%d %H:%M:%S %Z%z"): print(aware_dt.strftime(fmt)) utc_dt = aware_dt.astimezone(pytz.utc) # the same time in UTC print(utc_dt.strftime(fmt)) print_time(aware_midnight) print_time(aware_tomorrow) 

Exit

 2014-09-01 00:00:00 EST+1000 2014-08-31 14:00:00 UTC+0000 2014-09-02 00:00:00 EST+1000 2014-09-01 14:00:00 UTC+0000 

see also

  • How to get UTC "midnight" for a given time zone? - only midnight (without tomorrow)
  • How can I subtract a day from a python date? - about the difference between day and night.
  • python - datetime with a time zone before the era - demonstrate how easy it is to make the wrong answer (for some dates, time zones).
0
Sep 01 '14 at 11:44
source share

After a little experiment and thinking, I believe that I have a solution for you. My previous answer was incorrect, as you indicated; the timedelta object for days = 1 is basically the same as for seconds = 86400 (except when it comes to seconds).

One way, as I would recommend, to increase the date without taking into account the time of day, is to use the datetime.date object instead of the datetime.datetime object:

 >>> oneday = datetime.timedelta(days=1) >>> d = datetime.date(2011,4,3) >>> str(d + oneday) '2011-04-04' 

You can then add the time of day to form a complete datetime.datetime object in which you know that the time of day fields do not change from your original value.

Another method that I would feel safe using is to temporarily work with β€œnaive” dates. Therefore, the policy for adding time zones is not applied when adding timedelta .

 >>> hob = pytz.timezone('Australia/Hobart') >>> dstlast = datetime.datetime(2011,4,3) >>> str(dstlast) '2011-04-03 00:00:00' >>> dstlasthob = hob.localize(dstlast) >>> str(dstlasthob) '2011-04-03 00:00:00+11:00' >>> oneday = datetime.timedelta(days=1) >>> str(hob.normalize(dstlasthob + oneday)) '2011-04-03 23:00:00+10:00' >>> nextday = hob.localize(dstlasthob.replace(tzinfo=None) + oneday) >>> str(nextday) '2011-04-04 00:00:00+10:00' 

I tested this method for dates that have leap seconds in them (one example is 2008-12-31) and the result has a time of 00:00:00. This may actually be wrong, I'm not sure, but this is what you want :-)

-one
Aug 30 '11 at 15:45
source share

The following code attempts to get the time of midnight; if the time zone adjustment fails, it will reset again before midnight with a new zone offset.

 def DayStartEnd(localized_dt): tz = localized_dt.tzinfo start = tz.normalize(datetime.datetime(localized_dt.year,localized_dt.month,localized_dt.day,0,0,0,0,tz)) after_midnight = start.hour*60*60 + start.minute*60 + start.second if start.day != localized_dt.day: start += datetime.timedelta(seconds = 24*60*60 - after_midnight) elif after_midnight != 0: start -= datetime.timedelta(seconds = after_midnight) end = tz.normalize(start + datetime.timedelta(hours=24)) after_midnight = end.hour*60*60 + end.minute*60 + end.second if end.day == localized_dt.day: end += datetime.timedelta(seconds = 24*60*60 - after_midnight) elif after_midnight != 0: end -= datetime.timedelta(seconds = after_midnight) return start,end >>> hobart_tz = pytz.timezone('Australia/Hobart') >>> dst_finish_2011 = datetime.datetime(2011, 4, 3) >>> dst_finish_2011 = hobart_tz.localize(dst_finish_2011) >>> start,end = DayStartEnd(dst_finish_2011) >>> print start,end 2011-04-03 00:00:00+11:00 2011-04-04 00:00:00+10:00 >>> dst_start_2011 = datetime.datetime(2011, 10, 2) >>> dst_start_2011 = hobart_tz.localize(dst_start_2011) >>> start,end = DayStartEnd(dst_start_2011) >>> print start,end 2011-10-02 00:00:00+10:00 2011-10-03 00:00:00+11:00 
-one
Aug 30 '11 at 10:24
source share



All Articles