4

I would like to infer in Python if a date is the actual day of the year in which the hour is changed due to DST (Daylight Saving Time).

With the library pytz you can localize a datetime and the actual DST change is correctly done. Furthermore, there is the method dst() of the library datetime that allows you to infer if an actual date is in summer or winter time (example). However, I would like to infer the actual day in which a DST change is made.

Concretely, I would need a function (for example is_dst_change(date, timezone)) that receives a date and returns True only for those days of the year that do have an hour change. For example:

import pytz
import datetime

def is_dst_change(day, timezone):
    # Localize
    loc_day = pytz.timezone(timezone).localize(day)

    # Pseudocode: Infer if a date is the actual day in which the dst hour change is made
    if loc_day is dst_change_day:
        return True
    else:
        return False

# In the timezone 'Europe/Madrid', the days in which the DST change is made in 2021 are 28/03/2021 and 31/10/2021
is_dst_change(day=datetime.datetime(year=2021, month=3, day=28), timezone = 'Europe/Madrid')  # This should return True
is_dst_change(day=datetime.datetime(year=2021, month=10, day=31), timezone = 'Europe/Madrid')  # This should return True
is_dst_change(day=datetime.datetime(year=2021, month=2, day=1), timezone = 'Europe/Madrid')  # This should return False
is_dst_change(day=datetime.datetime(year=2021, month=7, day=1), timezone = 'Europe/Madrid')  # This should return False

Thus, in the above example the only days of 2021 for which the function is_dst_change(day, timezone='Europe/Madrid') will return True are 28/03/2021 and 31/10/2021. For the rest of the days of the year 2021, it must return False. Is there a way to infer this with Python?

IgnacioGaBo
  • 168
  • 11
  • Arizona and New Mexico are in the same timezone, but Arizona doesn't change to Daylight Savings Time. – Barmar Oct 07 '21 at 15:23
  • @Barmar I think this is mitigated by using the `[Country]/[City]` timezone identifiers, no? As opposed to using the more broad timezone-only identifiers like `EST`/`EDT`, `PST`/`PDT`? – esqew Oct 07 '21 at 15:34
  • @esqew That's correct, I hadn't thought of that case. – Barmar Oct 07 '21 at 15:38
  • @Barmar you'll have to use [IANA time zone name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) here – FObersteiner Oct 07 '21 at 17:00
  • @esqew abbreviations like EST/EDT, PST/PDT are not *time zones* in a geographical sense, they just tell you UTC offsets of certain time zones during a certain part of the year. And many of them are ambiguous, just search for BST for example. – FObersteiner Oct 07 '21 at 17:03
  • @MrFuppes Thanks for pointing that out; I had a hunch there was some detail about those I was missing in my comment. – esqew Oct 07 '21 at 17:04

2 Answers2

2

If today's UTC offset is different than tomorrow's, then today is a DST change.

def is_dst_change(day: datetime.datetime, timezone):
    # Override time to midnight
    day = day.replace(hour=0, minute=0, second=0, microsecond=0)
    tz = pytz.timezone(timezone)
    return(tz.utcoffset(day+datetime.timedelta(days=1)) != 
            tz.utcoffset(day))

"Today" is defined in this code as midnight to midnight.

Peter
  • 14,559
  • 35
  • 55
  • Beat me to it! [Repl.it demo](https://replit.com/@esqew/AwkwardBitesizedRevisioncontrol#main.py) I think this is likely the most balanced way to determine this, barring an ultra-optimized `is_dst_change`-esque function being provided by `pytz` OOTB. – esqew Oct 07 '21 at 15:36
  • 1
    If `day` is a datetime object and the hour attribute is *after* the DST change, I think this won't give a correct result. You might want to normalize to the date. – FObersteiner Oct 07 '21 at 15:54
1

You can make use of datetime.dst() (a change in UTC offset is not necessarily a DST transition):

from datetime import datetime, time, timedelta
from zoneinfo import ZoneInfo # Python 3.9+

def is_date_of_DSTtransition(dt: datetime, zone: str) -> bool:
    """
    check if the date part of a datetime object falls on the date
    of a DST transition.
    """
    _d = datetime.combine(dt.date(), time.min).replace(tzinfo=ZoneInfo(zone))
    return _d.dst() != (_d+timedelta(1)).dst()

e.g. for tz Europe/Berlin:

for d in range(366):
    if is_date_of_DSTtransition(datetime(2021, 1, 1) + timedelta(d), "Europe/Berlin"):
        print((datetime(2021, 1, 1) + timedelta(d)).date())
# 2021-03-28
# 2021-10-31

Note: I'm using zoneinfo here instead of pytz; for legacy code, there is a pytz deprecation shim. Here's a pytz version anyway (needs an additional normalize):

import pytz
def is_date_of_DSTtransition(dt: datetime, zone: str) -> bool:
    _d = pytz.timezone(zone).localize(datetime.combine(dt.date(), time.min))
    return _d.dst() != pytz.timezone(zone).normalize(_d+timedelta(1)).dst()
FObersteiner
  • 22,500
  • 8
  • 42
  • 72