Skip to content

Commit 65ae56b

Browse files
committed
Add function for determining whether a date falls within British Summer Time.
1 parent 6ccd64e commit 65ae56b

File tree

2 files changed

+100
-0
lines changed

2 files changed

+100
-0
lines changed

domdf_python_tools/dates.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
# stdlib
5454
import datetime
5555
import sys
56+
import time
5657
import typing
5758
from collections import OrderedDict
5859
from types import ModuleType
@@ -69,6 +70,7 @@
6970
"calc_easter",
7071
"month_short_names",
7172
"month_full_names",
73+
"is_bst",
7274
]
7375

7476

@@ -318,6 +320,43 @@ def get_timezone(tz: str, date: Optional[datetime.datetime] = None) -> Optional[
318320
return pytz.timezone(tz).localize(d).tzinfo
319321

320322

323+
def is_bst(the_date: Union[time.struct_time, datetime.date]) -> bool:
324+
"""
325+
Calculates whether the given day falls within British Summer Time.
326+
327+
This function should also be applicable to other timezones which change to summer time on the same date (e.g. Central European Summer Time).
328+
329+
.. note::
330+
331+
This function does not consider the time of day,
332+
and therefore does not handle the fact that the time changes at 1 AM GMT.
333+
It also does not account for historic deviations from the current norm.
334+
335+
.. versionadded:: 3.5.0
336+
337+
:param the_date: A :class:`time.struct_time`, :class:`datetime.date`
338+
or :class:`datetime.datetime` representing the target date.
339+
340+
:returns: :py:obj:`True` if the date falls within British Summer Time, :py:obj:`False` otherwise.
341+
"""
342+
343+
if isinstance(the_date, datetime.date):
344+
the_date = the_date.timetuple()
345+
346+
day, month, dow = the_date.tm_mday, the_date.tm_mon, (the_date.tm_wday + 1) % 7
347+
348+
if 3 > month > 10:
349+
return False
350+
elif 3 < month < 10:
351+
return True
352+
elif month == 3:
353+
return day - dow >= 25
354+
elif month == 10:
355+
return day - dow < 25
356+
else:
357+
return False
358+
359+
321360
_pytz_functions = ["get_utc_offset", "get_timezone"]
322361

323362
try:

tests/test_dates.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,3 +325,64 @@ def test_check_date():
325325
)
326326
def test_calc_easter(date):
327327
assert dates.calc_easter(date.year) == date
328+
329+
330+
@pytest.mark.parametrize(
331+
"the_date, result",
332+
[
333+
(date(month=3, day=2, year=2019), False),
334+
(date(month=4, day=7, year=2020), True),
335+
(date(month=8, day=17, year=2015), True),
336+
(date(month=12, day=25, year=2030), False),
337+
(date(month=3, day=29, year=2019), False),
338+
(date(month=3, day=30, year=2019), False),
339+
(date(month=3, day=31, year=2019), True),
340+
(date(month=4, day=1, year=2019), True),
341+
(date(month=10, day=25, year=2019), True),
342+
(date(month=10, day=26, year=2019), True),
343+
(date(month=10, day=27, year=2019), False),
344+
(date(month=10, day=28, year=2019), False),
345+
(date(month=3, day=27, year=2020), False),
346+
(date(month=3, day=28, year=2020), False),
347+
(date(month=3, day=29, year=2020), True),
348+
(date(month=3, day=30, year=2020), True),
349+
(date(month=10, day=23, year=2020), True),
350+
(date(month=10, day=24, year=2020), True),
351+
(date(month=10, day=25, year=2020), False),
352+
(date(month=10, day=26, year=2020), False),
353+
(date(month=3, day=26, year=2021), False),
354+
(date(month=3, day=27, year=2021), False),
355+
(date(month=3, day=28, year=2021), True),
356+
(date(month=3, day=29, year=2021), True),
357+
(date(month=10, day=29, year=2021), True),
358+
(date(month=10, day=30, year=2021), True),
359+
(date(month=10, day=31, year=2021), False),
360+
(date(month=11, day=1, year=2021), False),
361+
(date(month=3, day=25, year=2022), False),
362+
(date(month=3, day=26, year=2022), False),
363+
(date(month=3, day=27, year=2022), True),
364+
(date(month=3, day=28, year=2022), True),
365+
(date(month=10, day=28, year=2022), True),
366+
(date(month=10, day=29, year=2022), True),
367+
(date(month=10, day=30, year=2022), False),
368+
(date(month=10, day=31, year=2022), False),
369+
(date(month=3, day=24, year=2023), False),
370+
(date(month=3, day=25, year=2023), False),
371+
(date(month=3, day=26, year=2023), True),
372+
(date(month=3, day=27, year=2023), True),
373+
(date(month=10, day=27, year=2023), True),
374+
(date(month=10, day=28, year=2023), True),
375+
(date(month=10, day=29, year=2023), False),
376+
(date(month=10, day=30, year=2023), False),
377+
(date(month=3, day=29, year=2024), False),
378+
(date(month=3, day=30, year=2024), False),
379+
(date(month=3, day=31, year=2024), True),
380+
(date(month=4, day=1, year=2024), True),
381+
(date(month=10, day=25, year=2024), True),
382+
(date(month=10, day=26, year=2024), True),
383+
(date(month=10, day=27, year=2024), False),
384+
(date(month=10, day=28, year=2024), False),
385+
]
386+
)
387+
def test_is_bst(the_date, result: bool):
388+
assert dates.is_bst(the_date) is result

0 commit comments

Comments
 (0)