pythondatetimetimezonepython-datetimepytz

Local start and end of day in UTC


I would like to find out what the start and end time of a specific day is expressed in UTC and in Python.

For instance:

Now I would like to get these times in UTC:

I do not want to set the timezone programatically - I want to get it from my system.

I find this easy to do in Swift as every date is timezone aware, but I can't get my head around on how to do this in Python. The reason why I need to do this is because I want to get all the data within a specific (local) day from my database, which contains UTC timestamps.

I've tried this:

from datetime import datetime, time
import pytz

start_of_day = datetime.combine(datetime.now(), time.min)
end_of_day = datetime.combine(datetime.now(), time.max)
print(start_of_day) 
print(end_of_day) 
print(start_of_day.astimezone().tzinfo)
print(end_of_day.astimezone().tzinfo)

start_of_day = pytz.utc.localize(start_of_day)
end_of_day = pytz.utc.localize(end_of_day)
print(start_of_day) 
print(end_of_day) 
print(start_of_day.astimezone().tzinfo)
print(end_of_day.astimezone().tzinfo)

which gives the following output:

2023-10-29 00:00:00
2023-10-29 23:59:59.999999
BST
GMT
2023-10-29 00:00:00+00:00
2023-10-29 23:59:59.999999+00:00
BST
GMT

while I would expect, something like (I guess UTC might also be GMT):

2023-10-29 00:00:00
2023-10-29 23:59:59.999999
CEST
CET
2023-10-28 22:00:00+00:00
2023-10-29 22:59:59.999999+00:00
UTC
UTC

Not only are the times wrong, but the timezones are also weird.


Solution

  • pytz's localize just sets the time zone, it doesn't convert like astimezone does.

    Here's a slightly modified version of your code. We can use the standard lib datetime.timezone.utc to set UTC, to make things a bit clearer (pytz is deprecated since Python 3.9 btw.). See also comments in the code for some explanation.

    from datetime import datetime, time, timezone
    
    start_of_day = datetime.combine(datetime.now(), time.min)
    end_of_day = datetime.combine(datetime.now(), time.max)
    print(start_of_day) # naive datetime here...
    print(end_of_day) 
    print(start_of_day.astimezone().tzinfo)
    print(end_of_day.astimezone().tzinfo)
    
    start_of_day = start_of_day.astimezone(timezone.utc) # convert / make aware, UTC
    end_of_day = end_of_day.astimezone(timezone.utc)
    print(start_of_day) 
    print(end_of_day) 
    print(start_of_day.tzinfo) # datetime object is already aware here, no need for astimezone
    print(end_of_day.tzinfo)
    

    On my system that is configured to use time zone "Europe/Berlin" the output is

    2023-10-29 00:00:00 # naive datetime, but "silently" on UTC+2
    2023-10-29 23:59:59.999999 # same but UTC+1
    CEST
    CET
    # Note that the conversion from local to UTC applies the UTC offset:
    2023-10-28 22:00:00+00:00 # was on UTC+2, so clock moves back 2h for UTC
    2023-10-29 22:59:59.999999+00:00 # was UTC+1, clock back 1h
    UTC
    UTC