python-babel / babel

The official repository for Babel, the Python Internationalization Library
http://babel.pocoo.org/
BSD 3-Clause "New" or "Revised" License
1.29k stars 432 forks source link

No daylight saving time applied for Europe/London timezone #1082

Closed warrickball closed 1 month ago

warrickball commented 1 month ago

Overview Description

If I convert a datetime object to the timezone Europe/London, the time is (largely) unchanged, with no daylight saving applied. The conversion works correctly for a few other timezones I tried (GB, Europe/Berlin, Africa/Johannesburg).

I also get an unexpected 1 minute offset.

Reproducibility

>>> from datetime import datetime
>>> from babel.dates import get_timezone
>>> t = datetime(2024, 5, 9, 12, 30)
>>> t
datetime.datetime(2024, 5, 9, 12, 30)
>>> t.replace(tzinfo=get_timezone()).astimezone(get_timezone('Europe/London'))
datetime.datetime(2024, 5, 9, 12, 30, tzinfo=<DstTzInfo 'Europe/London' LMT-1 day, 23:59:00 STD>)
>>> t.replace(tzinfo=get_timezone()).astimezone(get_timezone('GB'))
datetime.datetime(2024, 5, 9, 13, 31, tzinfo=<DstTzInfo 'GB' BST+1:00:00 DST>)
>>> t.replace(tzinfo=get_timezone()).astimezone(get_timezone('Europe/Berlin'))
datetime.datetime(2024, 5, 9, 14, 31, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>)
>>> t.replace(tzinfo=get_timezone()).astimezone(get_timezone('Africa/Johannesburg'))
datetime.datetime(2024, 5, 9, 14, 31, tzinfo=<DstTzInfo 'Africa/Johannesburg' SAST+2:00:00 STD>)

Additional Information

Babel 2.15.0 via pip, pytz 2024.1 via package manager (dnf), Python 3.12.3, Fedora 39.

jun66j5 commented 1 month ago

I'm unsure what is your local timezone but datetime.astimezone() should be used rather than datetime.replace(tzinfo=...).

>>> from datetime import datetime
>>> from babel.dates import get_timezone
>>> t = datetime(2024, 5, 9, 12, 30)
>>> localtz = get_timezone()
>>> localtz
<DstTzInfo 'Asia/Tokyo' LMT+9:19:00 STD>
>>> 
>>> t.astimezone(localtz)
datetime.datetime(2024, 5, 9, 12, 30, tzinfo=<DstTzInfo 'Asia/Tokyo' JST+9:00:00 STD>)
>>> t.astimezone(localtz).astimezone(get_timezone('Europe/London'))
datetime.datetime(2024, 5, 9, 4, 30, tzinfo=<DstTzInfo 'Europe/London' BST+1:00:00 DST>)
>>> 
>>> t.replace(tzinfo=localtz)
datetime.datetime(2024, 5, 9, 12, 30, tzinfo=<DstTzInfo 'Asia/Tokyo' LMT+9:19:00 STD>)
>>> t.replace(tzinfo=localtz).astimezone(get_timezone('Europe/London'))
datetime.datetime(2024, 5, 9, 4, 11, tzinfo=<DstTzInfo 'Europe/London' BST+1:00:00 DST>)
akx commented 1 month ago

What is your end goal here? 🤔

Also note that get_timezone() is just a simple wrapper for pytz.timezone() (if pytz is installed) or zoneinfo.ZoneInfo(); there's nothing Babel-specific about this, really...

warrickball commented 1 month ago

This is something I isolated from a MkDocs plugin I've used called mkdocs-git-revision-date-localized-plugin. Specifically, these four lines (dates.py:29-32) are used to generate the local timestamp that appears on the pages:

    utc_revision_date = datetime.fromtimestamp(int(unix_timestamp), tz=timezone.utc)
    loc_revision_date = utc_revision_date.replace(
        tzinfo=get_timezone("UTC")
    ).astimezone(get_timezone(time_zone))

I accept that this might be deeper than Babel but I started by digging into the code for the plugin. I'll see if I can reproduce directly with pytz.timezone or zoneinfo.ZoneInfo.

warrickball commented 1 month ago

I'm happy to close this as a misuse of the functions that doesn't actually appear anywhere. The code in the plugin doesn't reproduce the error anyway, presumably, because it uses .replace(tzinfo=get_timezone("UTC")) rather than .replace(tzinfo=get_timezone()).

jun66j5 commented 1 month ago

    utc_revision_date = datetime.fromtimestamp(int(unix_timestamp), tz=timezone.utc)

    loc_revision_date = utc_revision_date.replace(

        tzinfo=get_timezone("UTC")

    ).astimezone(get_timezone(time_zone))

You could change that code like the following, simply:

loc_revision_date = datetime.fromtimestamp(int(unix_timestamp), tz=get_timezone(time_zone))