Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elegant way to adjust date timezones in Python

I'm based in the UK, and grappling with summer time BST and timezones.

Here's my code:

TIME_OFFSET = 1 # 0 for GMT, 1 for BST

def RFC3339_to_localHHMM(input):
                 # Take an XML date (2013-04-08T22:35:00Z)
                 # return e.g. 08/04 23:35
                 return (datetime.datetime.strptime(input, '%Y-%m-%dT%H:%M:%SZ') +
datetime.timedelta(hours=TIME_OFFSET)).strftime('%d/%m %H:%M')

Setting a variable like this feels very wrong, but I can't find any elegant way to achieve the above without hideous amounts of code. Am I missing something, and is there no way to (for example) read the system timezone?

like image 997
Chris Avatar asked Jan 14 '23 17:01

Chris


2 Answers

To convert UTC to given timezone:

from datetime import datetime
import pytz

local_tz = pytz.timezone("Europe/London") # time zone name from Olson database

def utc_to_local(utc_dt):
    return utc_dt.replace(tzinfo=pytz.utc).astimezone(local_tz)

rfc3339s = "2013-04-08T22:35:00Z"
utc_dt = datetime.strptime(rfc3339s, '%Y-%m-%dT%H:%M:%SZ')
local_dt = utc_to_local(utc_dt)
print(local_dt.strftime('%d/%m %H:%M')) # -> 08/04 23:35

See also How to convert a python utc datetime to a local datetime using only python standard library?.

like image 149
jfs Avatar answered Jan 16 '23 06:01

jfs


You seem to be asking a few separate questions here.

First, if you only care about your own machine's current local timezone, you don't need to know what it is. Just use the local-to-UTC functions. There are a few holes in the API, but even if you can't find the function you need, you can always just get from local to UTC or vice-versa by going through the POSIX timestamp and the fromtimestamp and utcfromtimestamp methods.

If you want to be able to deal with any timezone, see the top of the docs for the difference between aware and naive objects, but basically: an aware object is one that knows its timezone. So, that's what you need. The problem is that, as the docs say:

Note that no concrete tzinfo classes are supplied by the datetime module. Supporting timezones at whatever level of detail is required is up to the application.

The easiest way to support timezones is to install and use the third-party library pytz.

Meanwhile, as strftime() and strptime() Behavior sort-of explains, strptime always returns a naive object. You then have to call replace and/or astimezone (depending on whether the string was a UTC time or a local time) to get an aware object imbued with the right timezone.

But, even with all this, you still need to know what local timezone you're in, which means you still need a constant. In other words:

TIMEZONE = pytz.timezone('Europe/London')
def RFC3339_to_localHHMM(input):
    # Take an XML date (2013-04-08T22:35:00Z)
    # return e.g. 08/04 23:35
    utc_naive = datetime.datetime.strptime(input, '%Y-%m-%dT%H:%M:%SZ')
    utc = utc_naive.replace(pytz.utc)
    bst = utc.astimezone(TIMEZONE)
    return bst.strftime('%d/%m %H:%M')

So, how do you get the OS to give you the local timezone? Well, that's different for different platforms, and Python has nothing built in to help. But there are a few different third-party libraries that do, such as dateutil. For example:

def RFC3339_to_localHHMM(input):
    # Take an XML date (2013-04-08T22:35:00Z)
    # return e.g. 08/04 23:35
    utc = datetime.datetime.strptime(input, '%Y-%m-%dT%H:%M:%SZ')
    bst = utc.astimezone(dateutil.tz.tzlocal())
    return bst.strftime('%d/%m %H:%M')

But now we've come full circle. If all you wanted was the local timezone, you didn't really need the timezone at all (at least for your simple use case). So, this is only necessary if you need to support any timezone, and also want to be able to, e.g., default to your local timezone (without having to write two copies of all of your code for the aware and naive cases).

(Also, if you're going to use dateutil in the first place, you might want to use it for more than just getting the timezone—it can basically replacing everything you're doing with both datetime and pytz.)

Of course there are other options besides these libraries—search PyPI, Google, and/or the ActiveState recipes.

like image 25
abarnert Avatar answered Jan 16 '23 05:01

abarnert