6

For certain reasons, my employer does not want to use pip to install third party packages and wants me to use packages only hosted on trusty. Thus, I now cannot use pytz in my code. How would I go about checking if a certain date in a timezone is in DST? Here's my original code using pytz.

    import pytz
    import datetime
    ...

    target_date = datetime.datetime.strptime(arg_date, "%Y-%m-%d")
    time_zone = pytz.timezone('US/Eastern')
    dst_date = time_zone.localize(target_date, is_dst=None)
    est_hour = 24
    if bool(dst_date.dst()) is True:
        est_hour -= 4
    else:
        est_hour -= 5
DeadCake
  • 467
  • 1
  • 7
  • 18

4 Answers4

4

In the general case this is a complex problem that is not amenable to a hand-rolled solution. Relying on an external module that is vetted and maintained by someone dedicated to the task, such as pytz, is the only sane option.

However given the constraint that you're only interested in U.S. time zones, Eastern in particular, it's possible to write a simple function. It is obviously only good for the current (2016) rules, which last changed in 2007 and might change again at any time. Those rules state that DST starts on the second Sunday in March and ends on the first Sunday in November.

This code is based on my algorithm for finding a particular day of a month.

def is_dst(dt):
    if dt.year < 2007:
        raise ValueError()
    dst_start = datetime.datetime(dt.year, 3, 8, 2, 0)
    dst_start += datetime.timedelta(6 - dst_start.weekday())
    dst_end = datetime.datetime(dt.year, 11, 1, 2, 0)
    dst_end += datetime.timedelta(6 - dst_end.weekday())
    return dst_start <= dt < dst_end
Community
  • 1
  • 1
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
3

There is a new capability starting with Python 3.9: the built-in zoneinfo module. If your OS has time zone information built in, this module will access it without needing any additional installed packages.

>>> import zoneinfo
>>> time_zone = zoneinfo.ZoneInfo('US/Eastern')
>>> dst_date = datetime.datetime(2022, 1, 1, tzinfo=time_zone)
>>> dst_date
datetime.datetime(2022, 1, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='US/Eastern'))
>>> dst_date.dst()
datetime.timedelta(0)
>>> dst_date2 = datetime.datetime(2022, 3, 15, tzinfo=time_zone)
>>> dst_date2
datetime.datetime(2022, 3, 15, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='US/Eastern'))
>>> dst_date2.dst()
datetime.timedelta(seconds=3600)
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • Clean solution which saved me a few minutes, still in 2022! Especially relevant for Django 4.0+ developers. Thanks – scūriolus Oct 13 '22 at 11:55
0

Install pytz without using pip.

DST is arbitrary and chosen by legislation in different regions, you can't really calculate it - look at the pytz source for US/Eastern, for example, it's literally a list of hard-coded dates when DST changes for the next twenty years.

You could do that yourself, pulling the data from the same source that pytz does ( ZoneInfo / [or this link] (http://www.iana.org/time-zones) ). or from your OS implementation of tz if it has one...

but (unless it's a licensing reason) get your employer to look at the pytz source and confirm that it's acceptably harmless and approve it for use.

TessellatingHeckler
  • 27,511
  • 4
  • 48
  • 87
  • Thanks for the suggestion. The reason they don't want to use pip or any package manager in general is because according to them, the TechOps team is too small to manage package managers for every language and having to deal with updating packages due to security updates across every language "is a pain for them". I doubt they would approve installing pytz without pip. Hopefully they're reasonable and approve it for use. – DeadCake Aug 02 '16 at 15:13
0

This simple function worked for me:

from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
def is_dst(date:str or datetime, format:str = "%Y-%m-%d", zonename:str = "Europe/Paris"):
# print("checking date", date)
if isinstance(date, str):
    date = datetime.strptime(date, format)
if date.month==3 or date.month==10:
    zone = ZoneInfo(zonename)
    date_1 = date.astimezone(zone) + timedelta(hours=1)
    date_2 = date.astimezone(zone) + timedelta(hours=3)
    # print("\t", date_1, "\n\t", date_2)
    return not date_1.utcoffset() == date_2.utcoffset()

It basically checks if there's a mismatch between the UTC-offset whenever that happens, and I added the if date.month==3 or date.month==10 so it only checks for the months that we know will have DST beforehand (e.g., March and October).

So is_dst("2023-03-26") returns True, is_dst("2023-10-29") returns also True and any other date within 2023 returns False.