From bf2d40adfed9b9689bcbb61b18306fa1098f74eb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 8 May 2021 00:46:26 -0500 Subject: [PATCH] Migrate from pytz to python-dateutil (#49643) Co-authored-by: Paulus Schoutsen --- .../components/homeassistant/triggers/time.py | 10 +- .../components/hvv_departures/sensor.py | 7 +- .../components/input_datetime/__init__.py | 8 +- homeassistant/components/radarr/sensor.py | 8 +- homeassistant/components/tod/binary_sensor.py | 108 ++++++---------- homeassistant/components/zamg/sensor.py | 8 +- homeassistant/core.py | 16 +-- homeassistant/package_constraints.txt | 2 +- homeassistant/util/dt.py | 92 +++++--------- requirements.txt | 2 +- setup.py | 2 +- tests/common.py | 2 +- tests/components/climacell/test_sensor.py | 4 +- tests/components/climacell/test_weather.py | 4 +- tests/components/config/test_core.py | 4 +- .../generic_thermostat/test_climate.py | 34 ++--- tests/components/history_stats/test_sensor.py | 3 +- tests/components/jewish_calendar/__init__.py | 8 +- .../jewish_calendar/test_binary_sensor.py | 10 +- .../components/jewish_calendar/test_sensor.py | 18 +-- .../pvpc_hourly_pricing/test_config_flow.py | 5 +- .../pvpc_hourly_pricing/test_sensor.py | 5 +- tests/components/recorder/models_original.py | 2 +- tests/components/recorder/test_init.py | 2 +- tests/components/recorder/test_models.py | 27 ++-- tests/components/sun/test_trigger.py | 2 +- tests/components/tod/test_binary_sensor.py | 76 ++++------- tests/components/withings/common.py | 4 +- tests/components/withings/test_sensor.py | 8 +- tests/components/xiaomi_miio/test_vacuum.py | 18 +-- tests/components/zwave/test_init.py | 6 +- tests/helpers/test_condition.py | 2 +- tests/helpers/test_event.py | 41 ++---- tests/helpers/test_template.py | 3 +- tests/test_config.py | 10 +- tests/test_core.py | 5 +- tests/util/test_dt.py | 120 ++++++++++++++---- 37 files changed, 325 insertions(+), 361 deletions(-) diff --git a/homeassistant/components/homeassistant/triggers/time.py b/homeassistant/components/homeassistant/triggers/time.py index 69f01672078..6668672732e 100644 --- a/homeassistant/components/homeassistant/triggers/time.py +++ b/homeassistant/components/homeassistant/triggers/time.py @@ -95,8 +95,14 @@ async def async_attach_trigger(hass, config, action, automation_info): if has_date: # If input_datetime has date, then track point in time. - trigger_dt = dt_util.DEFAULT_TIME_ZONE.localize( - datetime(year, month, day, hour, minute, second) + trigger_dt = datetime( + year, + month, + day, + hour, + minute, + second, + tzinfo=dt_util.DEFAULT_TIME_ZONE, ) # Only set up listener if time is now or in the future. if trigger_dt >= dt_util.now(): diff --git a/homeassistant/components/hvv_departures/sensor.py b/homeassistant/components/hvv_departures/sensor.py index 5bc70c7a3b4..a3df466da74 100644 --- a/homeassistant/components/hvv_departures/sensor.py +++ b/homeassistant/components/hvv_departures/sensor.py @@ -4,13 +4,12 @@ import logging from aiohttp import ClientConnectorError from pygti.exceptions import InvalidAuth -from pytz import timezone from homeassistant.components.sensor import SensorEntity from homeassistant.const import ATTR_ATTRIBUTION, ATTR_ID, DEVICE_CLASS_TIMESTAMP from homeassistant.helpers import aiohttp_client from homeassistant.util import Throttle -from homeassistant.util.dt import utcnow +from homeassistant.util.dt import get_time_zone, utcnow from .const import ATTRIBUTION, CONF_STATION, DOMAIN, MANUFACTURER @@ -28,6 +27,7 @@ ATTR_DELAY = "delay" ATTR_NEXT = "next" PARALLEL_UPDATES = 0 +BERLIN_TIME_ZONE = get_time_zone("Europe/Berlin") _LOGGER = logging.getLogger(__name__) @@ -60,12 +60,11 @@ class HVVDepartureSensor(SensorEntity): @Throttle(MIN_TIME_BETWEEN_UPDATES) async def async_update(self, **kwargs): """Update the sensor.""" - departure_time = utcnow() + timedelta( minutes=self.config_entry.options.get("offset", 0) ) - departure_time_tz_berlin = departure_time.astimezone(timezone("Europe/Berlin")) + departure_time_tz_berlin = departure_time.astimezone(BERLIN_TIME_ZONE) payload = { "station": self.config_entry.data[CONF_STATION], diff --git a/homeassistant/components/input_datetime/__init__.py b/homeassistant/components/input_datetime/__init__.py index f423367019e..84a7fb89fe1 100644 --- a/homeassistant/components/input_datetime/__init__.py +++ b/homeassistant/components/input_datetime/__init__.py @@ -224,8 +224,8 @@ class InputDatetime(RestoreEntity): dt_util.DEFAULT_TIME_ZONE ) else: - self._current_datetime = dt_util.DEFAULT_TIME_ZONE.localize( - current_datetime + self._current_datetime = current_datetime.replace( + tzinfo=dt_util.DEFAULT_TIME_ZONE ) @classmethod @@ -388,8 +388,8 @@ class InputDatetime(RestoreEntity): if not time: time = self._current_datetime.time() - self._current_datetime = dt_util.DEFAULT_TIME_ZONE.localize( - py_datetime.datetime.combine(date, time) + self._current_datetime = py_datetime.datetime.combine( + date, time, dt_util.DEFAULT_TIME_ZONE ) self.async_write_ha_state() diff --git a/homeassistant/components/radarr/sensor.py b/homeassistant/components/radarr/sensor.py index 542ff285261..fda7a37756b 100644 --- a/homeassistant/components/radarr/sensor.py +++ b/homeassistant/components/radarr/sensor.py @@ -3,7 +3,6 @@ from datetime import datetime, timedelta import logging import time -from pytz import timezone import requests import voluptuous as vol @@ -26,6 +25,7 @@ from homeassistant.const import ( HTTP_OK, ) import homeassistant.helpers.config_validation as cv +from homeassistant.util import dt as dt_util _LOGGER = logging.getLogger(__name__) @@ -112,7 +112,6 @@ class RadarrSensor(SensorEntity): self.ssl = "https" if conf.get(CONF_SSL) else "http" self._state = None self.data = [] - self._tz = timezone(str(hass.config.time_zone)) self.type = sensor_type self._name = SENSOR_TYPES[self.type][0] if self.type == "diskspace": @@ -177,8 +176,9 @@ class RadarrSensor(SensorEntity): def update(self): """Update the data for the sensor.""" - start = get_date(self._tz) - end = get_date(self._tz, self.days) + time_zone = dt_util.get_time_zone(self.hass.config.time_zone) + start = get_date(time_zone) + end = get_date(time_zone, self.days) try: res = requests.get( ENDPOINTS[self.type].format( diff --git a/homeassistant/components/tod/binary_sensor.py b/homeassistant/components/tod/binary_sensor.py index a0fed1f8032..4fd9a3b8bf9 100644 --- a/homeassistant/components/tod/binary_sensor.py +++ b/homeassistant/components/tod/binary_sensor.py @@ -1,8 +1,8 @@ """Support for representing current time of the day as binary sensors.""" from datetime import datetime, timedelta import logging +from typing import Callable -import pytz import voluptuous as vol from homeassistant.components.binary_sensor import PLATFORM_SCHEMA, BinarySensorEntity @@ -70,6 +70,7 @@ class TodSensor(BinarySensorEntity): self._before_offset = before_offset self._before = before self._after = after + self._unsub_update: Callable[[], None] = None @property def should_poll(self): @@ -81,59 +82,37 @@ class TodSensor(BinarySensorEntity): """Return the name of the sensor.""" return self._name - @property - def after(self): - """Return the timestamp for the beginning of the period.""" - return self._time_after - - @property - def before(self): - """Return the timestamp for the end of the period.""" - return self._time_before - @property def is_on(self): """Return True is sensor is on.""" - if self.after < self.before: - return self.after <= self.current_datetime < self.before + if self._time_after < self._time_before: + return self._time_after <= dt_util.utcnow() < self._time_before return False - @property - def current_datetime(self): - """Return local current datetime according to hass configuration.""" - return dt_util.utcnow() - - @property - def next_update(self): - """Return the next update point in the UTC time.""" - return self._next_update - @property def extra_state_attributes(self): """Return the state attributes of the sensor.""" + time_zone = dt_util.get_time_zone(self.hass.config.time_zone) return { - ATTR_AFTER: self.after.astimezone(self.hass.config.time_zone).isoformat(), - ATTR_BEFORE: self.before.astimezone(self.hass.config.time_zone).isoformat(), - ATTR_NEXT_UPDATE: self.next_update.astimezone( - self.hass.config.time_zone - ).isoformat(), + ATTR_AFTER: self._time_after.astimezone(time_zone).isoformat(), + ATTR_BEFORE: self._time_before.astimezone(time_zone).isoformat(), + ATTR_NEXT_UPDATE: self._next_update.astimezone(time_zone).isoformat(), } def _naive_time_to_utc_datetime(self, naive_time): """Convert naive time from config to utc_datetime with current day.""" # get the current local date from utc time - current_local_date = self.current_datetime.astimezone( - self.hass.config.time_zone - ).date() - # calculate utc datetime corecponding to local time - utc_datetime = self.hass.config.time_zone.localize( - datetime.combine(current_local_date, naive_time) - ).astimezone(tz=pytz.UTC) - return utc_datetime + current_local_date = ( + dt_util.utcnow() + .astimezone(dt_util.get_time_zone(self.hass.config.time_zone)) + .date() + ) + # calculate utc datetime corresponding to local time + return dt_util.as_utc(datetime.combine(current_local_date, naive_time)) - def _calculate_initial_boudary_time(self): + def _calculate_boudary_time(self): """Calculate internal absolute time boundaries.""" - nowutc = self.current_datetime + nowutc = dt_util.utcnow() # If after value is a sun event instead of absolute time if is_sun_event(self._after): # Calculate the today's event utc time or @@ -177,43 +156,34 @@ class TodSensor(BinarySensorEntity): self._time_after += self._after_offset self._time_before += self._before_offset - def _turn_to_next_day(self): - """Turn to to the next day.""" - if is_sun_event(self._after): - self._time_after = get_astral_event_next( - self.hass, self._after, self._time_after - self._after_offset - ) - self._time_after += self._after_offset - else: - # Offset is already there - self._time_after += timedelta(days=1) - - if is_sun_event(self._before): - self._time_before = get_astral_event_next( - self.hass, self._before, self._time_before - self._before_offset - ) - self._time_before += self._before_offset - else: - # Offset is already there - self._time_before += timedelta(days=1) - async def async_added_to_hass(self): """Call when entity about to be added to Home Assistant.""" - self._calculate_initial_boudary_time() + self._calculate_boudary_time() self._calculate_next_update() - self._point_in_time_listener(dt_util.now()) + + @callback + def _clean_up_listener(): + if self._unsub_update is not None: + self._unsub_update() + self._unsub_update = None + + self.async_on_remove(_clean_up_listener) + + self._unsub_update = event.async_track_point_in_utc_time( + self.hass, self._point_in_time_listener, self._next_update + ) def _calculate_next_update(self): """Datetime when the next update to the state.""" - now = self.current_datetime - if now < self.after: - self._next_update = self.after + now = dt_util.utcnow() + if now < self._time_after: + self._next_update = self._time_after return - if now < self.before: - self._next_update = self.before + if now < self._time_before: + self._next_update = self._time_before return - self._turn_to_next_day() - self._next_update = self.after + self._calculate_boudary_time() + self._next_update = self._time_after @callback def _point_in_time_listener(self, now): @@ -221,6 +191,6 @@ class TodSensor(BinarySensorEntity): self._calculate_next_update() self.async_write_ha_state() - event.async_track_point_in_utc_time( - self.hass, self._point_in_time_listener, self.next_update + self._unsub_update = event.async_track_point_in_utc_time( + self.hass, self._point_in_time_listener, self._next_update ) diff --git a/homeassistant/components/zamg/sensor.py b/homeassistant/components/zamg/sensor.py index 2e2d07cea62..36c9a5fa380 100644 --- a/homeassistant/components/zamg/sensor.py +++ b/homeassistant/components/zamg/sensor.py @@ -7,7 +7,6 @@ import logging import os from aiohttp.hdrs import USER_AGENT -import pytz import requests import voluptuous as vol @@ -28,7 +27,7 @@ from homeassistant.const import ( __version__, ) import homeassistant.helpers.config_validation as cv -from homeassistant.util import Throttle +from homeassistant.util import Throttle, dt as dt_util _LOGGER = logging.getLogger(__name__) @@ -41,6 +40,7 @@ CONF_STATION_ID = "station_id" DEFAULT_NAME = "zamg" MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=10) +VIENNA_TIME_ZONE = dt_util.get_time_zone("Europe/Vienna") SENSOR_TYPES = { "pressure": ("Pressure", PRESSURE_HPA, "LDstat hPa", float), @@ -187,7 +187,7 @@ class ZamgData: date, time = self.data.get("update_date"), self.data.get("update_time") if date is not None and time is not None: return datetime.strptime(date + time, "%d-%m-%Y%H:%M").replace( - tzinfo=pytz.timezone("Europe/Vienna") + tzinfo=VIENNA_TIME_ZONE ) @classmethod @@ -208,7 +208,7 @@ class ZamgData: """Get the latest data from ZAMG.""" if self.last_update and ( self.last_update + timedelta(hours=1) - > datetime.utcnow().replace(tzinfo=pytz.utc) + > datetime.utcnow().replace(tzinfo=dt_util.UTC) ): return # Not time to update yet; data is only hourly diff --git a/homeassistant/core.py b/homeassistant/core.py index c22526474a4..067afb23c8a 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -1531,7 +1531,7 @@ class Config: self.longitude: float = 0 self.elevation: int = 0 self.location_name: str = "Home" - self.time_zone: datetime.tzinfo = dt_util.UTC + self.time_zone: str = "UTC" self.units: UnitSystem = METRIC_SYSTEM self.internal_url: str | None = None self.external_url: str | None = None @@ -1621,17 +1621,13 @@ class Config: Async friendly. """ - time_zone = dt_util.UTC.zone - if self.time_zone and getattr(self.time_zone, "zone"): - time_zone = getattr(self.time_zone, "zone") - return { "latitude": self.latitude, "longitude": self.longitude, "elevation": self.elevation, "unit_system": self.units.as_dict(), "location_name": self.location_name, - "time_zone": time_zone, + "time_zone": self.time_zone, "components": self.components, "config_dir": self.config_dir, # legacy, backwards compat @@ -1651,7 +1647,7 @@ class Config: time_zone = dt_util.get_time_zone(time_zone_str) if time_zone: - self.time_zone = time_zone + self.time_zone = time_zone_str dt_util.set_default_time_zone(time_zone) else: raise ValueError(f"Received invalid time zone {time_zone_str}") @@ -1721,17 +1717,13 @@ class Config: async def async_store(self) -> None: """Store [homeassistant] core config.""" - time_zone = dt_util.UTC.zone - if self.time_zone and getattr(self.time_zone, "zone"): - time_zone = getattr(self.time_zone, "zone") - data = { "latitude": self.latitude, "longitude": self.longitude, "elevation": self.elevation, "unit_system": self.units.name, "location_name": self.location_name, - "time_zone": time_zone, + "time_zone": self.time_zone, "external_url": self.external_url, "internal_url": self.internal_url, } diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 2b5546ac53c..71f3a75eac9 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -24,8 +24,8 @@ paho-mqtt==1.5.1 pillow==8.1.2 pip>=8.0.3,<20.3 pyroute2==0.5.18 +python-dateutil==2.8.1 python-slugify==4.0.1 -pytz>=2021.1 pyyaml==5.4.1 requests==2.25.1 ruamel.yaml==0.15.100 diff --git a/homeassistant/util/dt.py b/homeassistant/util/dt.py index b0c6cb21fec..a144918713e 100644 --- a/homeassistant/util/dt.py +++ b/homeassistant/util/dt.py @@ -4,20 +4,16 @@ from __future__ import annotations from contextlib import suppress import datetime as dt import re -from typing import Any, cast +from typing import Any import ciso8601 -import pytz -import pytz.exceptions as pytzexceptions -import pytz.tzinfo as pytzinfo +from dateutil import tz from homeassistant.const import MATCH_ALL DATE_STR_FORMAT = "%Y-%m-%d" -NATIVE_UTC = dt.timezone.utc -UTC = pytz.utc -DEFAULT_TIME_ZONE: dt.tzinfo = pytz.utc - +UTC = dt.timezone.utc +DEFAULT_TIME_ZONE: dt.tzinfo = dt.timezone.utc # Copyright (c) Django Software Foundation and individual contributors. # All rights reserved. @@ -37,7 +33,6 @@ def set_default_time_zone(time_zone: dt.tzinfo) -> None: """ global DEFAULT_TIME_ZONE # pylint: disable=global-statement - # NOTE: Remove in the future in favour of typing assert isinstance(time_zone, dt.tzinfo) DEFAULT_TIME_ZONE = time_zone @@ -48,15 +43,12 @@ def get_time_zone(time_zone_str: str) -> dt.tzinfo | None: Async friendly. """ - try: - return pytz.timezone(time_zone_str) - except pytzexceptions.UnknownTimeZoneError: - return None + return tz.gettz(time_zone_str) def utcnow() -> dt.datetime: """Get now in UTC time.""" - return dt.datetime.now(NATIVE_UTC) + return dt.datetime.now(UTC) def now(time_zone: dt.tzinfo | None = None) -> dt.datetime: @@ -72,7 +64,7 @@ def as_utc(dattim: dt.datetime) -> dt.datetime: if dattim.tzinfo == UTC: return dattim if dattim.tzinfo is None: - dattim = DEFAULT_TIME_ZONE.localize(dattim) # type: ignore + dattim = dattim.replace(tzinfo=DEFAULT_TIME_ZONE) return dattim.astimezone(UTC) @@ -93,14 +85,14 @@ def as_local(dattim: dt.datetime) -> dt.datetime: if dattim.tzinfo == DEFAULT_TIME_ZONE: return dattim if dattim.tzinfo is None: - dattim = UTC.localize(dattim) + dattim = dattim.replace(tzinfo=DEFAULT_TIME_ZONE) return dattim.astimezone(DEFAULT_TIME_ZONE) def utc_from_timestamp(timestamp: float) -> dt.datetime: """Return a UTC time from a timestamp.""" - return UTC.localize(dt.datetime.utcfromtimestamp(timestamp)) + return dt.datetime.utcfromtimestamp(timestamp).replace(tzinfo=UTC) def start_of_local_day(dt_or_d: dt.date | dt.datetime | None = None) -> dt.datetime: @@ -112,9 +104,7 @@ def start_of_local_day(dt_or_d: dt.date | dt.datetime | None = None) -> dt.datet else: date = dt_or_d - return DEFAULT_TIME_ZONE.localize( # type: ignore - dt.datetime.combine(date, dt.time()) - ) + return dt.datetime.combine(date, dt.time(), tzinfo=DEFAULT_TIME_ZONE) # Copyright (c) Django Software Foundation and individual contributors. @@ -239,6 +229,12 @@ def parse_time_expression(parameter: Any, min_value: int, max_value: int) -> lis return res +def _dst_offset_diff(dattim: dt.datetime) -> dt.timedelta: + """Return the offset when crossing the DST barrier.""" + delta = dt.timedelta(hours=24) + return (dattim + delta).utcoffset() - (dattim - delta).utcoffset() # type: ignore[operator] + + def find_next_time_expression_time( now: dt.datetime, # pylint: disable=redefined-outer-name seconds: list[int], @@ -312,38 +308,28 @@ def find_next_time_expression_time( result = result.replace(hour=next_hour) - if result.tzinfo is None: + if result.tzinfo in (None, UTC): return result - # Now we need to handle timezones. We will make this datetime object - # "naive" first and then re-convert it to the target timezone. - # This is so that we can call pytz's localize and handle DST changes. - tzinfo: pytzinfo.DstTzInfo = UTC if result.tzinfo == NATIVE_UTC else result.tzinfo - result = result.replace(tzinfo=None) - - try: - result = tzinfo.localize(result, is_dst=None) - except pytzexceptions.AmbiguousTimeError: + if tz.datetime_ambiguous(result): # This happens when we're leaving daylight saving time and local # clocks are rolled back. In this case, we want to trigger # on both the DST and non-DST time. So when "now" is in the DST # use the DST-on time, and if not, use the DST-off time. - use_dst = bool(now.dst()) - result = tzinfo.localize(result, is_dst=use_dst) - except pytzexceptions.NonExistentTimeError: + fold = 1 if now.dst() else 0 + if result.fold != fold: + result = result.replace(fold=fold) + + if not tz.datetime_exists(result): # This happens when we're entering daylight saving time and local # clocks are rolled forward, thus there are local times that do # not exist. In this case, we want to trigger on the next time # that *does* exist. # In the worst case, this will run through all the seconds in the # time shift, but that's max 3600 operations for once per year - result = result.replace(tzinfo=tzinfo) + dt.timedelta(seconds=1) - return find_next_time_expression_time(result, seconds, minutes, hours) - - result_dst = cast(dt.timedelta, result.dst()) - now_dst = cast(dt.timedelta, now.dst()) or dt.timedelta(0) - if result_dst >= now_dst: - return result + return find_next_time_expression_time( + result + dt.timedelta(seconds=1), seconds, minutes, hours + ) # Another edge-case when leaving DST: # When now is in DST and ambiguous *and* the next trigger time we *should* @@ -351,23 +337,11 @@ def find_next_time_expression_time( # For example: if triggering on 2:30 and now is 28.10.2018 2:30 (in DST) # we should trigger next on 28.10.2018 2:30 (out of DST), but our # algorithm above would produce 29.10.2018 2:30 (out of DST) + if tz.datetime_ambiguous(now): + check_result = find_next_time_expression_time( + now + _dst_offset_diff(now), seconds, minutes, hours + ) + if tz.datetime_ambiguous(check_result): + return check_result - # Step 1: Check if now is ambiguous - try: - tzinfo.localize(now.replace(tzinfo=None), is_dst=None) - return result - except pytzexceptions.AmbiguousTimeError: - pass - - # Step 2: Check if result of (now - DST) is ambiguous. - check = now - now_dst - check_result = find_next_time_expression_time(check, seconds, minutes, hours) - try: - tzinfo.localize(check_result.replace(tzinfo=None), is_dst=None) - return result - except pytzexceptions.AmbiguousTimeError: - pass - - # OK, edge case does apply. We must override the DST to DST-off - check_result = tzinfo.localize(check_result.replace(tzinfo=None), is_dst=False) - return check_result + return result diff --git a/requirements.txt b/requirements.txt index 475ece2b866..c134926c6fa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ PyJWT==1.7.1 cryptography==3.3.2 pip>=8.0.3,<20.3 python-slugify==4.0.1 -pytz>=2021.1 +python-dateutil==2.8.1 pyyaml==5.4.1 requests==2.25.1 ruamel.yaml==0.15.100 diff --git a/setup.py b/setup.py index 4791b0815f1..71b3d66046c 100755 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ REQUIRES = [ "cryptography==3.3.2", "pip>=8.0.3,<20.3", "python-slugify==4.0.1", - "pytz>=2021.1", + "python-dateutil==2.8.1", "pyyaml==5.4.1", "requests==2.25.1", "ruamel.yaml==0.15.100", diff --git a/tests/common.py b/tests/common.py index e3a4a714edf..952350fe68c 100644 --- a/tests/common.py +++ b/tests/common.py @@ -270,7 +270,7 @@ async def async_test_home_assistant(loop, load_registries=True): hass.config.latitude = 32.87336 hass.config.longitude = -117.22743 hass.config.elevation = 0 - hass.config.time_zone = date_util.get_time_zone("US/Pacific") + hass.config.time_zone = "US/Pacific" hass.config.units = METRIC_SYSTEM hass.config.media_dirs = {"local": get_test_config_dir("media")} hass.config.skip_pip = True diff --git a/tests/components/climacell/test_sensor.py b/tests/components/climacell/test_sensor.py index 44fc163848b..d06742ba209 100644 --- a/tests/components/climacell/test_sensor.py +++ b/tests/components/climacell/test_sensor.py @@ -7,7 +7,6 @@ from typing import Any from unittest.mock import patch import pytest -import pytz from homeassistant.components.climacell.config_flow import ( _get_config_schema, @@ -18,6 +17,7 @@ from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.const import ATTR_ATTRIBUTION from homeassistant.core import HomeAssistant, State, callback from homeassistant.helpers.entity_registry import async_get +from homeassistant.util import dt as dt_util from .const import API_V3_ENTRY_DATA, API_V4_ENTRY_DATA @@ -59,7 +59,7 @@ async def _setup(hass: HomeAssistant, config: dict[str, Any]) -> State: """Set up entry and return entity state.""" with patch( "homeassistant.util.dt.utcnow", - return_value=datetime(2021, 3, 6, 23, 59, 59, tzinfo=pytz.UTC), + return_value=datetime(2021, 3, 6, 23, 59, 59, tzinfo=dt_util.UTC), ): data = _get_config_schema(hass)(config) config_entry = MockConfigEntry( diff --git a/tests/components/climacell/test_weather.py b/tests/components/climacell/test_weather.py index 02aa65a350e..fa1ef9dc490 100644 --- a/tests/components/climacell/test_weather.py +++ b/tests/components/climacell/test_weather.py @@ -7,7 +7,6 @@ from typing import Any from unittest.mock import patch import pytest -import pytz from homeassistant.components.climacell.config_flow import ( _get_config_schema, @@ -46,6 +45,7 @@ from homeassistant.components.weather import ( from homeassistant.const import ATTR_ATTRIBUTION, ATTR_FRIENDLY_NAME from homeassistant.core import HomeAssistant, State, callback from homeassistant.helpers.entity_registry import async_get +from homeassistant.util import dt as dt_util from .const import API_V3_ENTRY_DATA, API_V4_ENTRY_DATA @@ -70,7 +70,7 @@ async def _setup(hass: HomeAssistant, config: dict[str, Any]) -> State: """Set up entry and return entity state.""" with patch( "homeassistant.util.dt.utcnow", - return_value=datetime(2021, 3, 6, 23, 59, 59, tzinfo=pytz.UTC), + return_value=datetime(2021, 3, 6, 23, 59, 59, tzinfo=dt_util.UTC), ): data = _get_config_schema(hass)(config) config_entry = MockConfigEntry( diff --git a/tests/components/config/test_core.py b/tests/components/config/test_core.py index 361fceab565..b58b572e230 100644 --- a/tests/components/config/test_core.py +++ b/tests/components/config/test_core.py @@ -57,7 +57,7 @@ async def test_websocket_core_update(hass, client): assert hass.config.elevation != 25 assert hass.config.location_name != "Huis" assert hass.config.units.name != CONF_UNIT_SYSTEM_IMPERIAL - assert hass.config.time_zone.zone != "America/New_York" + assert hass.config.time_zone != "America/New_York" assert hass.config.external_url != "https://www.example.com" assert hass.config.internal_url != "http://example.com" @@ -91,7 +91,7 @@ async def test_websocket_core_update(hass, client): assert hass.config.internal_url == "http://example.local" assert len(mock_set_tz.mock_calls) == 1 - assert mock_set_tz.mock_calls[0][1][0].zone == "America/New_York" + assert mock_set_tz.mock_calls[0][1][0] == dt_util.get_time_zone("America/New_York") async def test_websocket_core_update_not_admin(hass, hass_ws_client, hass_admin_user): diff --git a/tests/components/generic_thermostat/test_climate.py b/tests/components/generic_thermostat/test_climate.py index f5a27ac8b97..a7f42fffbd8 100644 --- a/tests/components/generic_thermostat/test_climate.py +++ b/tests/components/generic_thermostat/test_climate.py @@ -4,7 +4,6 @@ from os import path from unittest.mock import patch import pytest -import pytz import voluptuous as vol from homeassistant import config as hass_config @@ -37,6 +36,7 @@ import homeassistant.core as ha from homeassistant.core import DOMAIN as HASS_DOMAIN, CoreState, State, callback from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component +from homeassistant.util import dt as dt_util from homeassistant.util.unit_system import METRIC_SYSTEM from tests.common import ( @@ -691,9 +691,7 @@ async def test_temp_change_ac_trigger_on_not_long_enough(hass, setup_comp_4): async def test_temp_change_ac_trigger_on_long_enough(hass, setup_comp_4): """Test if temperature change turn ac on.""" - fake_changed = datetime.datetime( - 1918, 11, 11, 11, 11, 11, tzinfo=datetime.timezone.utc - ) + fake_changed = datetime.datetime(1918, 11, 11, 11, 11, 11, tzinfo=dt_util.UTC) with patch( "homeassistant.helpers.condition.dt_util.utcnow", return_value=fake_changed ): @@ -719,9 +717,7 @@ async def test_temp_change_ac_trigger_off_not_long_enough(hass, setup_comp_4): async def test_temp_change_ac_trigger_off_long_enough(hass, setup_comp_4): """Test if temperature change turn ac on.""" - fake_changed = datetime.datetime( - 1918, 11, 11, 11, 11, 11, tzinfo=datetime.timezone.utc - ) + fake_changed = datetime.datetime(1918, 11, 11, 11, 11, 11, tzinfo=dt_util.UTC) with patch( "homeassistant.helpers.condition.dt_util.utcnow", return_value=fake_changed ): @@ -801,9 +797,7 @@ async def test_temp_change_ac_trigger_on_not_long_enough_2(hass, setup_comp_5): async def test_temp_change_ac_trigger_on_long_enough_2(hass, setup_comp_5): """Test if temperature change turn ac on.""" - fake_changed = datetime.datetime( - 1918, 11, 11, 11, 11, 11, tzinfo=datetime.timezone.utc - ) + fake_changed = datetime.datetime(1918, 11, 11, 11, 11, 11, tzinfo=dt_util.UTC) with patch( "homeassistant.helpers.condition.dt_util.utcnow", return_value=fake_changed ): @@ -829,9 +823,7 @@ async def test_temp_change_ac_trigger_off_not_long_enough_2(hass, setup_comp_5): async def test_temp_change_ac_trigger_off_long_enough_2(hass, setup_comp_5): """Test if temperature change turn ac on.""" - fake_changed = datetime.datetime( - 1918, 11, 11, 11, 11, 11, tzinfo=datetime.timezone.utc - ) + fake_changed = datetime.datetime(1918, 11, 11, 11, 11, 11, tzinfo=dt_util.UTC) with patch( "homeassistant.helpers.condition.dt_util.utcnow", return_value=fake_changed ): @@ -919,9 +911,7 @@ async def test_temp_change_heater_trigger_on_not_long_enough(hass, setup_comp_6) async def test_temp_change_heater_trigger_on_long_enough(hass, setup_comp_6): """Test if temperature change turn heater on after min cycle.""" - fake_changed = datetime.datetime( - 1918, 11, 11, 11, 11, 11, tzinfo=datetime.timezone.utc - ) + fake_changed = datetime.datetime(1918, 11, 11, 11, 11, 11, tzinfo=dt_util.UTC) with patch( "homeassistant.helpers.condition.dt_util.utcnow", return_value=fake_changed ): @@ -938,9 +928,7 @@ async def test_temp_change_heater_trigger_on_long_enough(hass, setup_comp_6): async def test_temp_change_heater_trigger_off_long_enough(hass, setup_comp_6): """Test if temperature change turn heater off after min cycle.""" - fake_changed = datetime.datetime( - 1918, 11, 11, 11, 11, 11, tzinfo=datetime.timezone.utc - ) + fake_changed = datetime.datetime(1918, 11, 11, 11, 11, 11, tzinfo=dt_util.UTC) with patch( "homeassistant.helpers.condition.dt_util.utcnow", return_value=fake_changed ): @@ -1019,7 +1007,7 @@ async def test_temp_change_ac_trigger_on_long_enough_3(hass, setup_comp_7): _setup_sensor(hass, 30) await hass.async_block_till_done() await common.async_set_temperature(hass, 25) - test_time = datetime.datetime.now(pytz.UTC) + test_time = datetime.datetime.now(dt_util.UTC) async_fire_time_changed(hass, test_time) await hass.async_block_till_done() assert len(calls) == 0 @@ -1042,7 +1030,7 @@ async def test_temp_change_ac_trigger_off_long_enough_3(hass, setup_comp_7): _setup_sensor(hass, 20) await hass.async_block_till_done() await common.async_set_temperature(hass, 25) - test_time = datetime.datetime.now(pytz.UTC) + test_time = datetime.datetime.now(dt_util.UTC) async_fire_time_changed(hass, test_time) await hass.async_block_till_done() assert len(calls) == 0 @@ -1090,7 +1078,7 @@ async def test_temp_change_heater_trigger_on_long_enough_2(hass, setup_comp_8): _setup_sensor(hass, 20) await hass.async_block_till_done() await common.async_set_temperature(hass, 25) - test_time = datetime.datetime.now(pytz.UTC) + test_time = datetime.datetime.now(dt_util.UTC) async_fire_time_changed(hass, test_time) await hass.async_block_till_done() assert len(calls) == 0 @@ -1113,7 +1101,7 @@ async def test_temp_change_heater_trigger_off_long_enough_2(hass, setup_comp_8): _setup_sensor(hass, 30) await hass.async_block_till_done() await common.async_set_temperature(hass, 25) - test_time = datetime.datetime.now(pytz.UTC) + test_time = datetime.datetime.now(dt_util.UTC) async_fire_time_changed(hass, test_time) await hass.async_block_till_done() assert len(calls) == 0 diff --git a/tests/components/history_stats/test_sensor.py b/tests/components/history_stats/test_sensor.py index 06ba1f22f47..6e25e9e67cf 100644 --- a/tests/components/history_stats/test_sensor.py +++ b/tests/components/history_stats/test_sensor.py @@ -6,7 +6,6 @@ import unittest from unittest.mock import patch import pytest -import pytz from homeassistant import config as hass_config from homeassistant.components.history_stats import DOMAIN @@ -82,7 +81,7 @@ class TestHistoryStatsSensor(unittest.TestCase): ) def test_period_parsing(self, mock): """Test the conversion from templates to period.""" - now = datetime(2019, 1, 1, 23, 30, 0, tzinfo=pytz.utc) + now = datetime(2019, 1, 1, 23, 30, 0, tzinfo=dt_util.UTC) with patch("homeassistant.util.dt.now", return_value=now): today = Template( "{{ now().replace(hour=0).replace(minute=0).replace(second=0) }}", diff --git a/tests/components/jewish_calendar/__init__.py b/tests/components/jewish_calendar/__init__.py index 2d42458cf1b..b0279fd2748 100644 --- a/tests/components/jewish_calendar/__init__.py +++ b/tests/components/jewish_calendar/__init__.py @@ -26,7 +26,9 @@ def make_nyc_test_params(dtime, results, havdalah_offset=0): if isinstance(results, dict): time_zone = dt_util.get_time_zone("America/New_York") results = { - key: time_zone.localize(value) if isinstance(value, datetime) else value + key: value.replace(tzinfo=time_zone) + if isinstance(value, datetime) + else value for key, value in results.items() } return ( @@ -46,7 +48,9 @@ def make_jerusalem_test_params(dtime, results, havdalah_offset=0): if isinstance(results, dict): time_zone = dt_util.get_time_zone("Asia/Jerusalem") results = { - key: time_zone.localize(value) if isinstance(value, datetime) else value + key: value.replace(tzinfo=time_zone) + if isinstance(value, datetime) + else value for key, value in results.items() } return ( diff --git a/tests/components/jewish_calendar/test_binary_sensor.py b/tests/components/jewish_calendar/test_binary_sensor.py index 1f34532eeb5..b34dfdb28e4 100644 --- a/tests/components/jewish_calendar/test_binary_sensor.py +++ b/tests/components/jewish_calendar/test_binary_sensor.py @@ -179,9 +179,9 @@ async def test_issur_melacha_sensor( ): """Test Issur Melacha sensor output.""" time_zone = dt_util.get_time_zone(tzname) - test_time = time_zone.localize(now) + test_time = now.replace(tzinfo=time_zone) - hass.config.time_zone = time_zone + hass.config.time_zone = tzname hass.config.latitude = latitude hass.config.longitude = longitude @@ -214,7 +214,7 @@ async def test_issur_melacha_sensor( [ latitude, longitude, - time_zone, + tzname, HDATE_DEFAULT_ALTITUDE, diaspora, "english", @@ -270,9 +270,9 @@ async def test_issur_melacha_sensor_update( ): """Test Issur Melacha sensor output.""" time_zone = dt_util.get_time_zone(tzname) - test_time = time_zone.localize(now) + test_time = now.replace(tzinfo=time_zone) - hass.config.time_zone = time_zone + hass.config.time_zone = tzname hass.config.latitude = latitude hass.config.longitude = longitude diff --git a/tests/components/jewish_calendar/test_sensor.py b/tests/components/jewish_calendar/test_sensor.py index 8634f28d8fa..970e31c7985 100644 --- a/tests/components/jewish_calendar/test_sensor.py +++ b/tests/components/jewish_calendar/test_sensor.py @@ -163,9 +163,9 @@ async def test_jewish_calendar_sensor( ): """Test Jewish calendar sensor output.""" time_zone = dt_util.get_time_zone(tzname) - test_time = time_zone.localize(now) + test_time = now.replace(tzinfo=time_zone) - hass.config.time_zone = time_zone + hass.config.time_zone = tzname hass.config.latitude = latitude hass.config.longitude = longitude @@ -188,7 +188,9 @@ async def test_jewish_calendar_sensor( await hass.async_block_till_done() result = ( - dt_util.as_utc(time_zone.localize(result)) if isinstance(result, dt) else result + dt_util.as_utc(result.replace(tzinfo=time_zone)) + if isinstance(result, dt) + else result ) sensor_object = hass.states.get(f"sensor.test_{sensor}") @@ -506,9 +508,9 @@ async def test_shabbat_times_sensor( ): """Test sensor output for upcoming shabbat/yomtov times.""" time_zone = dt_util.get_time_zone(tzname) - test_time = time_zone.localize(now) + test_time = now.replace(tzinfo=time_zone) - hass.config.time_zone = time_zone + hass.config.time_zone = tzname hass.config.latitude = latitude hass.config.longitude = longitude @@ -559,7 +561,7 @@ async def test_shabbat_times_sensor( [ latitude, longitude, - time_zone, + tzname, HDATE_DEFAULT_ALTITUDE, diaspora, language, @@ -593,7 +595,7 @@ OMER_TEST_IDS = [ @pytest.mark.parametrize(["test_time", "result"], OMER_PARAMS, ids=OMER_TEST_IDS) async def test_omer_sensor(hass, legacy_patchable_time, test_time, result): """Test Omer Count sensor output.""" - test_time = hass.config.time_zone.localize(test_time) + test_time = test_time.replace(tzinfo=dt_util.get_time_zone(hass.config.time_zone)) with alter_time(test_time): assert await async_setup_component( @@ -627,7 +629,7 @@ DAFYOMI_TEST_IDS = [ @pytest.mark.parametrize(["test_time", "result"], DAFYOMI_PARAMS, ids=DAFYOMI_TEST_IDS) async def test_dafyomi_sensor(hass, legacy_patchable_time, test_time, result): """Test Daf Yomi sensor output.""" - test_time = hass.config.time_zone.localize(test_time) + test_time = test_time.replace(tzinfo=dt_util.get_time_zone(hass.config.time_zone)) with alter_time(test_time): assert await async_setup_component( diff --git a/tests/components/pvpc_hourly_pricing/test_config_flow.py b/tests/components/pvpc_hourly_pricing/test_config_flow.py index 31a7005c4cc..2a64d81ef98 100644 --- a/tests/components/pvpc_hourly_pricing/test_config_flow.py +++ b/tests/components/pvpc_hourly_pricing/test_config_flow.py @@ -2,12 +2,11 @@ from datetime import datetime from unittest.mock import patch -from pytz import timezone - from homeassistant import config_entries, data_entry_flow from homeassistant.components.pvpc_hourly_pricing import ATTR_TARIFF, DOMAIN from homeassistant.const import CONF_NAME from homeassistant.helpers import entity_registry as er +from homeassistant.util import dt as dt_util from .conftest import check_valid_state @@ -26,7 +25,7 @@ async def test_config_flow( - Check abort when trying to config another with same tariff - Check removal and add again to check state restoration """ - hass.config.time_zone = timezone("Europe/Madrid") + hass.config.time_zone = dt_util.get_time_zone("Europe/Madrid") mock_data = {"return_time": datetime(2019, 10, 26, 14, 0, tzinfo=date_util.UTC)} def mock_now(): diff --git a/tests/components/pvpc_hourly_pricing/test_sensor.py b/tests/components/pvpc_hourly_pricing/test_sensor.py index 2045ba52671..19f3a7aa31c 100644 --- a/tests/components/pvpc_hourly_pricing/test_sensor.py +++ b/tests/components/pvpc_hourly_pricing/test_sensor.py @@ -3,12 +3,11 @@ from datetime import datetime, timedelta import logging from unittest.mock import patch -from pytz import timezone - from homeassistant.components.pvpc_hourly_pricing import ATTR_TARIFF, DOMAIN from homeassistant.const import CONF_NAME from homeassistant.core import ATTR_NOW, EVENT_TIME_CHANGED from homeassistant.setup import async_setup_component +from homeassistant.util import dt as dt_util from .conftest import check_valid_state @@ -32,7 +31,7 @@ async def test_sensor_availability( hass, caplog, legacy_patchable_time, pvpc_aioclient_mock: AiohttpClientMocker ): """Test sensor availability and handling of cloud access.""" - hass.config.time_zone = timezone("Europe/Madrid") + hass.config.time_zone = dt_util.get_time_zone("Europe/Madrid") config = {DOMAIN: [{CONF_NAME: "test_dst", ATTR_TARIFF: "discrimination"}]} mock_data = {"return_time": datetime(2019, 10, 27, 20, 0, 0, tzinfo=date_util.UTC)} diff --git a/tests/components/recorder/models_original.py b/tests/components/recorder/models_original.py index 4c9880d9257..5f64fbda736 100644 --- a/tests/components/recorder/models_original.py +++ b/tests/components/recorder/models_original.py @@ -170,5 +170,5 @@ def _process_timestamp(ts): if ts is None: return None if ts.tzinfo is None: - return dt_util.UTC.localize(ts) + return ts.replace(tzinfo=dt_util.UTC) return dt_util.as_utc(ts) diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index a34df0a4ac2..a045bb638ce 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -604,7 +604,7 @@ def test_auto_purge(hass_recorder): # # The clock is started at 4:15am then advanced forward below now = dt_util.utcnow() - test_time = tz.localize(datetime(now.year + 2, 1, 1, 4, 15, 0)) + test_time = datetime(now.year + 2, 1, 1, 4, 15, 0, tzinfo=tz) run_tasks_at_time(hass, test_time) with patch( diff --git a/tests/components/recorder/test_models.py b/tests/components/recorder/test_models.py index 26a1e487ba8..9f32f1c5746 100644 --- a/tests/components/recorder/test_models.py +++ b/tests/components/recorder/test_models.py @@ -2,7 +2,6 @@ from datetime import datetime import pytest -import pytz from sqlalchemy import create_engine from sqlalchemy.orm import scoped_session, sessionmaker @@ -144,11 +143,11 @@ async def test_process_timestamp(): """Test processing time stamp to UTC.""" datetime_with_tzinfo = datetime(2016, 7, 9, 11, 0, 0, tzinfo=dt.UTC) datetime_without_tzinfo = datetime(2016, 7, 9, 11, 0, 0) - est = pytz.timezone("US/Eastern") + est = dt_util.get_time_zone("US/Eastern") datetime_est_timezone = datetime(2016, 7, 9, 11, 0, 0, tzinfo=est) - nst = pytz.timezone("Canada/Newfoundland") + nst = dt_util.get_time_zone("Canada/Newfoundland") datetime_nst_timezone = datetime(2016, 7, 9, 11, 0, 0, tzinfo=nst) - hst = pytz.timezone("US/Hawaii") + hst = dt_util.get_time_zone("US/Hawaii") datetime_hst_timezone = datetime(2016, 7, 9, 11, 0, 0, tzinfo=hst) assert process_timestamp(datetime_with_tzinfo) == datetime( @@ -158,13 +157,13 @@ async def test_process_timestamp(): 2016, 7, 9, 11, 0, 0, tzinfo=dt.UTC ) assert process_timestamp(datetime_est_timezone) == datetime( - 2016, 7, 9, 15, 56, tzinfo=dt.UTC + 2016, 7, 9, 15, 0, tzinfo=dt.UTC ) assert process_timestamp(datetime_nst_timezone) == datetime( - 2016, 7, 9, 14, 31, tzinfo=dt.UTC + 2016, 7, 9, 13, 30, tzinfo=dt.UTC ) assert process_timestamp(datetime_hst_timezone) == datetime( - 2016, 7, 9, 21, 31, tzinfo=dt.UTC + 2016, 7, 9, 21, 0, tzinfo=dt.UTC ) assert process_timestamp(None) is None @@ -173,13 +172,13 @@ async def test_process_timestamp_to_utc_isoformat(): """Test processing time stamp to UTC isoformat.""" datetime_with_tzinfo = datetime(2016, 7, 9, 11, 0, 0, tzinfo=dt.UTC) datetime_without_tzinfo = datetime(2016, 7, 9, 11, 0, 0) - est = pytz.timezone("US/Eastern") + est = dt_util.get_time_zone("US/Eastern") datetime_est_timezone = datetime(2016, 7, 9, 11, 0, 0, tzinfo=est) - est = pytz.timezone("US/Eastern") + est = dt_util.get_time_zone("US/Eastern") datetime_est_timezone = datetime(2016, 7, 9, 11, 0, 0, tzinfo=est) - nst = pytz.timezone("Canada/Newfoundland") + nst = dt_util.get_time_zone("Canada/Newfoundland") datetime_nst_timezone = datetime(2016, 7, 9, 11, 0, 0, tzinfo=nst) - hst = pytz.timezone("US/Hawaii") + hst = dt_util.get_time_zone("US/Hawaii") datetime_hst_timezone = datetime(2016, 7, 9, 11, 0, 0, tzinfo=hst) assert ( @@ -192,15 +191,15 @@ async def test_process_timestamp_to_utc_isoformat(): ) assert ( process_timestamp_to_utc_isoformat(datetime_est_timezone) - == "2016-07-09T15:56:00+00:00" + == "2016-07-09T15:00:00+00:00" ) assert ( process_timestamp_to_utc_isoformat(datetime_nst_timezone) - == "2016-07-09T14:31:00+00:00" + == "2016-07-09T13:30:00+00:00" ) assert ( process_timestamp_to_utc_isoformat(datetime_hst_timezone) - == "2016-07-09T21:31:00+00:00" + == "2016-07-09T21:00:00+00:00" ) assert process_timestamp_to_utc_isoformat(None) is None diff --git a/tests/components/sun/test_trigger.py b/tests/components/sun/test_trigger.py index 6e6a1f11ef9..f100fb53dc8 100644 --- a/tests/components/sun/test_trigger.py +++ b/tests/components/sun/test_trigger.py @@ -33,7 +33,7 @@ def calls(hass): def setup_comp(hass): """Initialize components.""" mock_component(hass, "group") - dt_util.set_default_time_zone(hass.config.time_zone) + hass.config.set_time_zone(hass.config.time_zone) hass.loop.run_until_complete( async_setup_component(hass, sun.DOMAIN, {sun.DOMAIN: {sun.CONF_ELEVATION: 0}}) ) diff --git a/tests/components/tod/test_binary_sensor.py b/tests/components/tod/test_binary_sensor.py index 2eb506f80f3..8b63082c36c 100644 --- a/tests/components/tod/test_binary_sensor.py +++ b/tests/components/tod/test_binary_sensor.py @@ -3,7 +3,6 @@ from datetime import datetime, timedelta from unittest.mock import patch import pytest -import pytz from homeassistant.const import STATE_OFF, STATE_ON import homeassistant.core as ha @@ -61,7 +60,7 @@ async def test_setup_no_sensors(hass): async def test_in_period_on_start(hass): """Test simple setting.""" - test_time = datetime(2019, 1, 10, 18, 43, 0, tzinfo=hass.config.time_zone) + test_time = datetime(2019, 1, 10, 18, 43, 0, tzinfo=dt_util.UTC) config = { "binary_sensor": [ { @@ -85,7 +84,7 @@ async def test_in_period_on_start(hass): async def test_midnight_turnover_before_midnight_inside_period(hass): """Test midnight turnover setting before midnight inside period .""" - test_time = datetime(2019, 1, 10, 22, 30, 0, tzinfo=hass.config.time_zone) + test_time = datetime(2019, 1, 10, 22, 30, 0, tzinfo=dt_util.UTC) config = { "binary_sensor": [ {"platform": "tod", "name": "Night", "after": "22:00", "before": "5:00"} @@ -104,9 +103,7 @@ async def test_midnight_turnover_before_midnight_inside_period(hass): async def test_midnight_turnover_after_midnight_inside_period(hass): """Test midnight turnover setting before midnight inside period .""" - test_time = hass.config.time_zone.localize( - datetime(2019, 1, 10, 21, 0, 0) - ).astimezone(pytz.UTC) + test_time = datetime(2019, 1, 10, 21, 0, 0, tzinfo=dt_util.UTC) config = { "binary_sensor": [ {"platform": "tod", "name": "Night", "after": "22:00", "before": "5:00"} @@ -140,9 +137,7 @@ async def test_midnight_turnover_after_midnight_inside_period(hass): async def test_midnight_turnover_before_midnight_outside_period(hass): """Test midnight turnover setting before midnight outside period.""" - test_time = hass.config.time_zone.localize( - datetime(2019, 1, 10, 20, 30, 0) - ).astimezone(pytz.UTC) + test_time = datetime(2019, 1, 10, 20, 30, 0, tzinfo=dt_util.UTC) config = { "binary_sensor": [ {"platform": "tod", "name": "Night", "after": "22:00", "before": "5:00"} @@ -161,9 +156,7 @@ async def test_midnight_turnover_before_midnight_outside_period(hass): async def test_midnight_turnover_after_midnight_outside_period(hass): """Test midnight turnover setting before midnight inside period .""" - test_time = hass.config.time_zone.localize( - datetime(2019, 1, 10, 20, 0, 0) - ).astimezone(pytz.UTC) + test_time = datetime(2019, 1, 10, 20, 0, 0, tzinfo=dt_util.UTC) config = { "binary_sensor": [ @@ -180,9 +173,7 @@ async def test_midnight_turnover_after_midnight_outside_period(hass): state = hass.states.get("binary_sensor.night") assert state.state == STATE_OFF - switchover_time = hass.config.time_zone.localize( - datetime(2019, 1, 11, 4, 59, 0) - ).astimezone(pytz.UTC) + switchover_time = datetime(2019, 1, 11, 4, 59, 0, tzinfo=dt_util.UTC) with patch( "homeassistant.components.tod.binary_sensor.dt_util.utcnow", return_value=switchover_time, @@ -210,9 +201,7 @@ async def test_midnight_turnover_after_midnight_outside_period(hass): async def test_from_sunrise_to_sunset(hass): """Test period from sunrise to sunset.""" - test_time = hass.config.time_zone.localize(datetime(2019, 1, 12)).astimezone( - pytz.UTC - ) + test_time = datetime(2019, 1, 12, tzinfo=dt_util.UTC) sunrise = dt_util.as_local( get_astral_event_date(hass, "sunrise", dt_util.as_utc(test_time)) ) @@ -311,9 +300,7 @@ async def test_from_sunrise_to_sunset(hass): async def test_from_sunset_to_sunrise(hass): """Test period from sunset to sunrise.""" - test_time = hass.config.time_zone.localize(datetime(2019, 1, 12)).astimezone( - pytz.UTC - ) + test_time = datetime(2019, 1, 12, tzinfo=dt_util.UTC) sunset = dt_util.as_local(get_astral_event_date(hass, "sunset", test_time)) sunrise = dt_util.as_local(get_astral_event_next(hass, "sunrise", sunset)) # assert sunset == sunrise @@ -405,13 +392,13 @@ async def test_from_sunset_to_sunrise(hass): async def test_offset(hass): """Test offset.""" - after = hass.config.time_zone.localize(datetime(2019, 1, 10, 18, 0, 0)).astimezone( - pytz.UTC - ) + timedelta(hours=1, minutes=34) + after = datetime(2019, 1, 10, 18, 0, 0, tzinfo=dt_util.UTC) + timedelta( + hours=1, minutes=34 + ) - before = hass.config.time_zone.localize(datetime(2019, 1, 10, 22, 0, 0)).astimezone( - pytz.UTC - ) + timedelta(hours=1, minutes=45) + before = datetime(2019, 1, 10, 22, 0, 0, tzinfo=dt_util.UTC) + timedelta( + hours=1, minutes=45 + ) entity_id = "binary_sensor.evening" config = { @@ -484,9 +471,9 @@ async def test_offset(hass): async def test_offset_overnight(hass): """Test offset overnight.""" - after = hass.config.time_zone.localize(datetime(2019, 1, 10, 18, 0, 0)).astimezone( - pytz.UTC - ) + timedelta(hours=1, minutes=34) + after = datetime(2019, 1, 10, 18, 0, 0, tzinfo=dt_util.UTC) + timedelta( + hours=1, minutes=34 + ) entity_id = "binary_sensor.evening" config = { "binary_sensor": [ @@ -528,9 +515,7 @@ async def test_norwegian_case_winter(hass): hass.config.latitude = 69.6 hass.config.longitude = 18.8 - test_time = hass.config.time_zone.localize(datetime(2010, 1, 1)).astimezone( - pytz.UTC - ) + test_time = datetime(2010, 1, 1, tzinfo=dt_util.UTC) sunrise = dt_util.as_local( get_astral_event_next(hass, "sunrise", dt_util.as_utc(test_time)) ) @@ -645,9 +630,7 @@ async def test_norwegian_case_summer(hass): hass.config.longitude = 18.8 hass.config.elevation = 10.0 - test_time = hass.config.time_zone.localize(datetime(2010, 6, 1)).astimezone( - pytz.UTC - ) + test_time = datetime(2010, 6, 1, tzinfo=dt_util.UTC) sunrise = dt_util.as_local( get_astral_event_next(hass, "sunrise", dt_util.as_utc(test_time)) @@ -759,9 +742,7 @@ async def test_norwegian_case_summer(hass): async def test_sun_offset(hass): """Test sun event with offset.""" - test_time = hass.config.time_zone.localize(datetime(2019, 1, 12)).astimezone( - pytz.UTC - ) + test_time = datetime(2019, 1, 12, tzinfo=dt_util.UTC) sunrise = dt_util.as_local( get_astral_event_date(hass, "sunrise", dt_util.as_utc(test_time)) + timedelta(hours=-1, minutes=-30) @@ -881,30 +862,27 @@ async def test_sun_offset(hass): async def test_dst(hass): """Test sun event with offset.""" - hass.config.time_zone = pytz.timezone("CET") - test_time = hass.config.time_zone.localize( - datetime(2019, 3, 30, 3, 0, 0) - ).astimezone(pytz.UTC) + hass.config.time_zone = "CET" + test_time = datetime(2019, 3, 30, 3, 0, 0, tzinfo=dt_util.UTC) config = { "binary_sensor": [ {"platform": "tod", "name": "Day", "after": "2:30", "before": "2:40"} ] } + # Test DST: # after 2019-03-30 03:00 CET the next update should ge scheduled # at 3:30 not 2:30 local time - # Internally the entity_id = "binary_sensor.day" - testtime = test_time with patch( "homeassistant.components.tod.binary_sensor.dt_util.utcnow", - return_value=testtime, + return_value=test_time, ): await async_setup_component(hass, "binary_sensor", config) await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) - assert state.attributes["after"] == "2019-03-31T03:30:00+02:00" - assert state.attributes["before"] == "2019-03-31T03:40:00+02:00" - assert state.attributes["next_update"] == "2019-03-31T03:30:00+02:00" + assert state.attributes["after"] == "2019-03-30T03:30:00+01:00" + assert state.attributes["before"] == "2019-03-30T03:40:00+01:00" + assert state.attributes["next_update"] == "2019-03-30T03:30:00+01:00" assert state.state == STATE_OFF diff --git a/tests/components/withings/common.py b/tests/components/withings/common.py index 4d3552bf662..aea2d0152b2 100644 --- a/tests/components/withings/common.py +++ b/tests/components/withings/common.py @@ -7,7 +7,6 @@ from urllib.parse import urlparse from aiohttp.test_utils import TestClient import arrow -import pytz from withings_api.common import ( MeasureGetMeasResponse, NotifyAppli, @@ -40,6 +39,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers.config_entry_oauth2_flow import AUTH_CALLBACK_PATH from homeassistant.setup import async_setup_component +from homeassistant.util import dt as dt_util from tests.test_util.aiohttp import AiohttpClientMocker @@ -77,7 +77,7 @@ def new_profile_config( measuregrps=[], more=False, offset=0, - timezone=pytz.UTC, + timezone=dt_util.UTC, updatetime=arrow.get(12345), ), api_response_sleep_get_summary=api_response_sleep_get_summary diff --git a/tests/components/withings/test_sensor.py b/tests/components/withings/test_sensor.py index 71e69967796..7e337da8afb 100644 --- a/tests/components/withings/test_sensor.py +++ b/tests/components/withings/test_sensor.py @@ -3,7 +3,6 @@ from typing import Any from unittest.mock import patch import arrow -import pytz from withings_api.common import ( GetSleepSummaryData, GetSleepSummarySerie, @@ -29,6 +28,7 @@ from homeassistant.components.withings.const import Measurement from homeassistant.core import HomeAssistant, State from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_registry import EntityRegistry +from homeassistant.util import dt as dt_util from .common import ComponentFactory, new_profile_config @@ -189,7 +189,7 @@ PERSON0 = new_profile_config( ), ), more=False, - timezone=pytz.UTC, + timezone=dt_util.UTC, updatetime=arrow.get("2019-08-01"), offset=0, ), @@ -198,7 +198,7 @@ PERSON0 = new_profile_config( offset=0, series=( GetSleepSummarySerie( - timezone=pytz.UTC, + timezone=dt_util.UTC, model=SleepModel.SLEEP_MONITOR, startdate=arrow.get("2019-02-01"), enddate=arrow.get("2019-02-01"), @@ -225,7 +225,7 @@ PERSON0 = new_profile_config( ), ), GetSleepSummarySerie( - timezone=pytz.UTC, + timezone=dt_util.UTC, model=SleepModel.SLEEP_MONITOR, startdate=arrow.get("2019-02-01"), enddate=arrow.get("2019-02-01"), diff --git a/tests/components/xiaomi_miio/test_vacuum.py b/tests/components/xiaomi_miio/test_vacuum.py index 23e5d8884b3..fe0466472fa 100644 --- a/tests/components/xiaomi_miio/test_vacuum.py +++ b/tests/components/xiaomi_miio/test_vacuum.py @@ -5,7 +5,6 @@ from unittest.mock import MagicMock, patch from miio import DeviceException import pytest -from pytz import utc from homeassistant.components.vacuum import ( ATTR_BATTERY_ICON, @@ -55,6 +54,7 @@ from homeassistant.const import ( STATE_ON, STATE_UNAVAILABLE, ) +from homeassistant.util import dt as dt_util from .test_config_flow import TEST_MAC @@ -106,12 +106,12 @@ def mirobo_is_got_error_fixture(): mock_timer_1 = MagicMock() mock_timer_1.enabled = True mock_timer_1.cron = "5 5 1 8 1" - mock_timer_1.next_schedule = datetime(2020, 5, 23, 13, 21, 10, tzinfo=utc) + mock_timer_1.next_schedule = datetime(2020, 5, 23, 13, 21, 10, tzinfo=dt_util.UTC) mock_timer_2 = MagicMock() mock_timer_2.enabled = False mock_timer_2.cron = "5 5 1 8 2" - mock_timer_2.next_schedule = datetime(2020, 5, 23, 13, 21, 10, tzinfo=utc) + mock_timer_2.next_schedule = datetime(2020, 5, 23, 13, 21, 10, tzinfo=dt_util.UTC) mock_vacuum.timer.return_value = [mock_timer_1, mock_timer_2] @@ -180,12 +180,12 @@ def mirobo_is_on_fixture(): mock_timer_1 = MagicMock() mock_timer_1.enabled = True mock_timer_1.cron = "5 5 1 8 1" - mock_timer_1.next_schedule = datetime(2020, 5, 23, 13, 21, 10, tzinfo=utc) + mock_timer_1.next_schedule = datetime(2020, 5, 23, 13, 21, 10, tzinfo=dt_util.UTC) mock_timer_2 = MagicMock() mock_timer_2.enabled = False mock_timer_2.cron = "5 5 1 8 2" - mock_timer_2.next_schedule = datetime(2020, 5, 23, 13, 21, 10, tzinfo=utc) + mock_timer_2.next_schedule = datetime(2020, 5, 23, 13, 21, 10, tzinfo=dt_util.UTC) mock_vacuum.timer.return_value = [mock_timer_1, mock_timer_2] @@ -255,12 +255,12 @@ async def test_xiaomi_vacuum_services(hass, caplog, mock_mirobo_is_got_error): { "enabled": True, "cron": "5 5 1 8 1", - "next_schedule": datetime(2020, 5, 23, 13, 21, 10, tzinfo=utc), + "next_schedule": datetime(2020, 5, 23, 13, 21, 10, tzinfo=dt_util.UTC), }, { "enabled": False, "cron": "5 5 1 8 2", - "next_schedule": datetime(2020, 5, 23, 13, 21, 10, tzinfo=utc), + "next_schedule": datetime(2020, 5, 23, 13, 21, 10, tzinfo=dt_util.UTC), }, ] @@ -353,12 +353,12 @@ async def test_xiaomi_specific_services(hass, caplog, mock_mirobo_is_on): { "enabled": True, "cron": "5 5 1 8 1", - "next_schedule": datetime(2020, 5, 23, 13, 21, 10, tzinfo=utc), + "next_schedule": datetime(2020, 5, 23, 13, 21, 10, tzinfo=dt_util.UTC), }, { "enabled": False, "cron": "5 5 1 8 2", - "next_schedule": datetime(2020, 5, 23, 13, 21, 10, tzinfo=utc), + "next_schedule": datetime(2020, 5, 23, 13, 21, 10, tzinfo=dt_util.UTC), }, ] diff --git a/tests/components/zwave/test_init.py b/tests/components/zwave/test_init.py index 6b1a6fe4f98..ecf5759b835 100644 --- a/tests/components/zwave/test_init.py +++ b/tests/components/zwave/test_init.py @@ -5,7 +5,6 @@ from datetime import datetime from unittest.mock import MagicMock, patch import pytest -from pytz import utc import voluptuous as vol from homeassistant.bootstrap import async_setup_component @@ -19,6 +18,7 @@ from homeassistant.components.zwave import ( from homeassistant.components.zwave.binary_sensor import get_device from homeassistant.const import ATTR_ENTITY_ID, ATTR_NAME from homeassistant.helpers import device_registry as dr, entity_registry as er +from homeassistant.util import dt as dt_util from tests.common import async_fire_time_changed, mock_registry from tests.mock.zwave import MockEntityValues, MockNetwork, MockNode, MockValue @@ -140,7 +140,7 @@ async def test_auto_heal_midnight(hass, mock_openzwave, legacy_patchable_time): network = hass.data[zwave.DATA_NETWORK] assert not network.heal.called - time = utc.localize(datetime(2017, 5, 6, 0, 0, 0)) + time = datetime(2017, 5, 6, 0, 0, 0, tzinfo=dt_util.UTC) async_fire_time_changed(hass, time) await hass.async_block_till_done() await hass.async_block_till_done() @@ -156,7 +156,7 @@ async def test_auto_heal_disabled(hass, mock_openzwave): network = hass.data[zwave.DATA_NETWORK] assert not network.heal.called - time = utc.localize(datetime(2017, 5, 6, 0, 0, 0)) + time = datetime(2017, 5, 6, 0, 0, 0, tzinfo=dt_util.UTC) async_fire_time_changed(hass, time) await hass.async_block_till_done() assert not network.heal.called diff --git a/tests/helpers/test_condition.py b/tests/helpers/test_condition.py index 11705502e77..9347d0bc025 100644 --- a/tests/helpers/test_condition.py +++ b/tests/helpers/test_condition.py @@ -27,7 +27,7 @@ def calls(hass): @pytest.fixture(autouse=True) def setup_comp(hass): """Initialize components.""" - dt_util.set_default_time_zone(hass.config.time_zone) + hass.config.set_time_zone(hass.config.time_zone) hass.loop.run_until_complete( async_setup_component(hass, sun.DOMAIN, {sun.DOMAIN: {sun.CONF_ELEVATION: 0}}) ) diff --git a/tests/helpers/test_event.py b/tests/helpers/test_event.py index b8291b97efa..e134c5e327d 100644 --- a/tests/helpers/test_event.py +++ b/tests/helpers/test_event.py @@ -2908,8 +2908,8 @@ async def test_periodic_task_entering_dst(hass): specific_runs = [] now = dt_util.utcnow() - time_that_will_not_match_right_away = timezone.localize( - datetime(now.year + 1, 3, 25, 2, 31, 0) + time_that_will_not_match_right_away = datetime( + now.year + 1, 3, 25, 2, 31, 0, tzinfo=timezone ) with patch( @@ -2924,25 +2924,25 @@ async def test_periodic_task_entering_dst(hass): ) async_fire_time_changed( - hass, timezone.localize(datetime(now.year + 1, 3, 25, 1, 50, 0, 999999)) + hass, datetime(now.year + 1, 3, 25, 1, 50, 0, 999999, tzinfo=timezone) ) await hass.async_block_till_done() assert len(specific_runs) == 0 async_fire_time_changed( - hass, timezone.localize(datetime(now.year + 1, 3, 25, 3, 50, 0, 999999)) + hass, datetime(now.year + 1, 3, 25, 3, 50, 0, 999999, tzinfo=timezone) ) await hass.async_block_till_done() assert len(specific_runs) == 0 async_fire_time_changed( - hass, timezone.localize(datetime(now.year + 1, 3, 26, 1, 50, 0, 999999)) + hass, datetime(now.year + 1, 3, 26, 1, 50, 0, 999999, tzinfo=timezone) ) await hass.async_block_till_done() assert len(specific_runs) == 0 async_fire_time_changed( - hass, timezone.localize(datetime(now.year + 1, 3, 26, 2, 50, 0, 999999)) + hass, datetime(now.year + 1, 3, 26, 2, 50, 0, 999999, tzinfo=timezone) ) await hass.async_block_till_done() assert len(specific_runs) == 1 @@ -2958,8 +2958,8 @@ async def test_periodic_task_leaving_dst(hass): now = dt_util.utcnow() - time_that_will_not_match_right_away = timezone.localize( - datetime(now.year + 1, 10, 28, 2, 28, 0), is_dst=True + time_that_will_not_match_right_away = datetime( + now.year + 1, 10, 28, 2, 28, 0, tzinfo=timezone, fold=1 ) with patch( @@ -2974,46 +2974,33 @@ async def test_periodic_task_leaving_dst(hass): ) async_fire_time_changed( - hass, - timezone.localize( - datetime(now.year + 1, 10, 28, 2, 5, 0, 999999), is_dst=False - ), + hass, datetime(now.year + 1, 10, 28, 2, 5, 0, 999999, tzinfo=timezone, fold=0) ) await hass.async_block_till_done() assert len(specific_runs) == 0 async_fire_time_changed( - hass, - timezone.localize( - datetime(now.year + 1, 10, 28, 2, 55, 0, 999999), is_dst=False - ), + hass, datetime(now.year + 1, 10, 28, 2, 55, 0, 999999, tzinfo=timezone, fold=0) ) await hass.async_block_till_done() assert len(specific_runs) == 1 async_fire_time_changed( hass, - timezone.localize( - datetime(now.year + 2, 10, 28, 2, 45, 0, 999999), is_dst=True - ), + datetime(now.year + 2, 10, 28, 2, 45, 0, 999999, tzinfo=timezone, fold=1), ) await hass.async_block_till_done() assert len(specific_runs) == 2 async_fire_time_changed( hass, - timezone.localize( - datetime(now.year + 2, 10, 28, 2, 55, 0, 999999), is_dst=True - ), + datetime(now.year + 2, 10, 28, 2, 55, 0, 999999, tzinfo=timezone, fold=1), ) await hass.async_block_till_done() assert len(specific_runs) == 2 async_fire_time_changed( - hass, - timezone.localize( - datetime(now.year + 2, 10, 28, 2, 55, 0, 999999), is_dst=True - ), + hass, datetime(now.year + 2, 10, 28, 2, 55, 0, 999999, tzinfo=timezone, fold=1) ) await hass.async_block_till_done() assert len(specific_runs) == 2 @@ -3224,7 +3211,7 @@ async def test_async_track_point_in_time_cancel(hass): await asyncio.sleep(0.2) assert len(times) == 1 - assert times[0].tzinfo.zone == "US/Hawaii" + assert "US/Hawaii" in str(times[0].tzinfo) async def test_async_track_entity_registry_updated_event(hass): diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index 704194cd49b..48ef6f25b67 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -5,7 +5,6 @@ import random from unittest.mock import patch import pytest -import pytz import voluptuous as vol from homeassistant.components import group @@ -763,7 +762,7 @@ def test_render_with_possible_json_value_non_string_value(hass): hass, ) value = datetime(2019, 1, 18, 12, 13, 14) - expected = str(pytz.utc.localize(value)) + expected = str(value.replace(tzinfo=dt_util.UTC)) assert tpl.async_render_with_possible_json_value(value) == expected diff --git a/tests/test_config.py b/tests/test_config.py index 76218ab5bf2..2ca46e2c1e9 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -374,7 +374,7 @@ async def test_loading_configuration_from_storage(hass, hass_storage): assert hass.config.elevation == 10 assert hass.config.location_name == "Home" assert hass.config.units.name == CONF_UNIT_SYSTEM_METRIC - assert hass.config.time_zone.zone == "Europe/Copenhagen" + assert hass.config.time_zone == "Europe/Copenhagen" assert hass.config.external_url == "https://www.example.com" assert hass.config.internal_url == "http://example.local" assert len(hass.config.allowlist_external_dirs) == 3 @@ -405,7 +405,7 @@ async def test_loading_configuration_from_storage_with_yaml_only(hass, hass_stor assert hass.config.elevation == 10 assert hass.config.location_name == "Home" assert hass.config.units.name == CONF_UNIT_SYSTEM_METRIC - assert hass.config.time_zone.zone == "Europe/Copenhagen" + assert hass.config.time_zone == "Europe/Copenhagen" assert len(hass.config.allowlist_external_dirs) == 3 assert "/etc" in hass.config.allowlist_external_dirs assert hass.config.media_dirs == {"mymedia": "/usr"} @@ -463,7 +463,7 @@ async def test_override_stored_configuration(hass, hass_storage): assert hass.config.elevation == 10 assert hass.config.location_name == "Home" assert hass.config.units.name == CONF_UNIT_SYSTEM_METRIC - assert hass.config.time_zone.zone == "Europe/Copenhagen" + assert hass.config.time_zone == "Europe/Copenhagen" assert len(hass.config.allowlist_external_dirs) == 3 assert "/etc" in hass.config.allowlist_external_dirs assert hass.config.config_source == config_util.SOURCE_YAML @@ -493,7 +493,7 @@ async def test_loading_configuration(hass): assert hass.config.elevation == 25 assert hass.config.location_name == "Huis" assert hass.config.units.name == CONF_UNIT_SYSTEM_IMPERIAL - assert hass.config.time_zone.zone == "America/New_York" + assert hass.config.time_zone == "America/New_York" assert hass.config.external_url == "https://www.example.com" assert hass.config.internal_url == "http://example.local" assert len(hass.config.allowlist_external_dirs) == 3 @@ -525,7 +525,7 @@ async def test_loading_configuration_temperature_unit(hass): assert hass.config.elevation == 25 assert hass.config.location_name == "Huis" assert hass.config.units.name == CONF_UNIT_SYSTEM_METRIC - assert hass.config.time_zone.zone == "America/New_York" + assert hass.config.time_zone == "America/New_York" assert hass.config.external_url == "https://www.example.com" assert hass.config.internal_url == "http://example.local" assert hass.config.config_source == config_util.SOURCE_YAML diff --git a/tests/test_core.py b/tests/test_core.py index d3283c14b84..0a205cedad1 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -9,7 +9,6 @@ from tempfile import TemporaryDirectory from unittest.mock import MagicMock, Mock, PropertyMock, patch import pytest -import pytz import voluptuous as vol from homeassistant.const import ( @@ -44,7 +43,7 @@ from homeassistant.util.unit_system import METRIC_SYSTEM from tests.common import async_capture_events, async_mock_service -PST = pytz.timezone("America/Los_Angeles") +PST = dt_util.get_time_zone("America/Los_Angeles") def test_split_entity_id(): @@ -877,7 +876,7 @@ def test_config_defaults(): assert config.longitude == 0 assert config.elevation == 0 assert config.location_name == "Home" - assert config.time_zone == dt_util.UTC + assert config.time_zone == "UTC" assert config.internal_url is None assert config.external_url is None assert config.config_source == "default" diff --git a/tests/util/test_dt.py b/tests/util/test_dt.py index 50013012201..628cb533681 100644 --- a/tests/util/test_dt.py +++ b/tests/util/test_dt.py @@ -16,17 +16,12 @@ def teardown(): def test_get_time_zone_retrieves_valid_time_zone(): """Test getting a time zone.""" - time_zone = dt_util.get_time_zone(TEST_TIME_ZONE) - - assert time_zone is not None - assert time_zone.zone == TEST_TIME_ZONE + assert dt_util.get_time_zone(TEST_TIME_ZONE) is not None def test_get_time_zone_returns_none_for_garbage_time_zone(): """Test getting a non existing time zone.""" - time_zone = dt_util.get_time_zone("Non existing time zone") - - assert time_zone is None + assert dt_util.get_time_zone("Non existing time zone") is None def test_set_default_time_zone(): @@ -35,8 +30,7 @@ def test_set_default_time_zone(): dt_util.set_default_time_zone(time_zone) - # We cannot compare the timezones directly because of DST - assert time_zone.zone == dt_util.now().tzinfo.zone + assert dt_util.now().tzinfo is time_zone def test_utcnow(): @@ -239,35 +233,111 @@ def test_find_next_time_expression_time_dst(): return dt_util.find_next_time_expression_time(dt, seconds, minutes, hours) # Entering DST, clocks are rolled forward - assert tz.localize(datetime(2018, 3, 26, 2, 30, 0)) == find( - tz.localize(datetime(2018, 3, 25, 1, 50, 0)), 2, 30, 0 + assert datetime(2018, 3, 26, 2, 30, 0, tzinfo=tz) == find( + datetime(2018, 3, 25, 1, 50, 0, tzinfo=tz), 2, 30, 0 ) - assert tz.localize(datetime(2018, 3, 26, 2, 30, 0)) == find( - tz.localize(datetime(2018, 3, 25, 3, 50, 0)), 2, 30, 0 + assert datetime(2018, 3, 26, 2, 30, 0, tzinfo=tz) == find( + datetime(2018, 3, 25, 3, 50, 0, tzinfo=tz), 2, 30, 0 ) - assert tz.localize(datetime(2018, 3, 26, 2, 30, 0)) == find( - tz.localize(datetime(2018, 3, 26, 1, 50, 0)), 2, 30, 0 + assert datetime(2018, 3, 26, 2, 30, 0, tzinfo=tz) == find( + datetime(2018, 3, 26, 1, 50, 0, tzinfo=tz), 2, 30, 0 ) # Leaving DST, clocks are rolled back - assert tz.localize(datetime(2018, 10, 28, 2, 30, 0), is_dst=False) == find( - tz.localize(datetime(2018, 10, 28, 2, 5, 0), is_dst=False), 2, 30, 0 + assert datetime(2018, 10, 28, 2, 30, 0, tzinfo=tz, fold=0) == find( + datetime(2018, 10, 28, 2, 5, 0, tzinfo=tz, fold=0), 2, 30, 0 ) - assert tz.localize(datetime(2018, 10, 28, 2, 30, 0), is_dst=False) == find( - tz.localize(datetime(2018, 10, 28, 2, 55, 0), is_dst=True), 2, 30, 0 + assert datetime(2018, 10, 28, 2, 30, 0, tzinfo=tz, fold=0) == find( + datetime(2018, 10, 28, 2, 5, 0, tzinfo=tz), 2, 30, 0 ) - assert tz.localize(datetime(2018, 10, 28, 4, 30, 0), is_dst=False) == find( - tz.localize(datetime(2018, 10, 28, 2, 55, 0), is_dst=True), 4, 30, 0 + assert datetime(2018, 10, 28, 2, 30, 0, tzinfo=tz, fold=1) == find( + datetime(2018, 10, 28, 2, 55, 0, tzinfo=tz), 2, 30, 0 ) - assert tz.localize(datetime(2018, 10, 28, 2, 30, 0), is_dst=True) == find( - tz.localize(datetime(2018, 10, 28, 2, 5, 0), is_dst=True), 2, 30, 0 + assert datetime(2018, 10, 28, 2, 30, 0, tzinfo=tz, fold=1) == find( + datetime(2018, 10, 28, 2, 55, 0, tzinfo=tz, fold=0), 2, 30, 0 ) - assert tz.localize(datetime(2018, 10, 29, 2, 30, 0)) == find( - tz.localize(datetime(2018, 10, 28, 2, 55, 0), is_dst=False), 2, 30, 0 + assert datetime(2018, 10, 28, 4, 30, 0, tzinfo=tz, fold=0) == find( + datetime(2018, 10, 28, 2, 55, 0, tzinfo=tz, fold=1), 4, 30, 0 + ) + + assert datetime(2018, 10, 28, 2, 30, 0, tzinfo=tz, fold=1) == find( + datetime(2018, 10, 28, 2, 5, 0, tzinfo=tz, fold=1), 2, 30, 0 + ) + + assert datetime(2018, 10, 28, 2, 30, 0, tzinfo=tz, fold=1) == find( + datetime(2018, 10, 28, 2, 55, 0, tzinfo=tz, fold=0), 2, 30, 0 + ) + + +def test_find_next_time_expression_time_dst_chicago(): + """Test daylight saving time for find_next_time_expression_time.""" + tz = dt_util.get_time_zone("America/Chicago") + dt_util.set_default_time_zone(tz) + + def find(dt, hour, minute, second): + """Call test_find_next_time_expression_time.""" + seconds = dt_util.parse_time_expression(second, 0, 59) + minutes = dt_util.parse_time_expression(minute, 0, 59) + hours = dt_util.parse_time_expression(hour, 0, 23) + + return dt_util.find_next_time_expression_time(dt, seconds, minutes, hours) + + # Entering DST, clocks are rolled forward + assert datetime(2021, 3, 15, 2, 30, 0, tzinfo=tz) == find( + datetime(2021, 3, 14, 1, 50, 0, tzinfo=tz), 2, 30, 0 + ) + + assert datetime(2021, 3, 15, 2, 30, 0, tzinfo=tz) == find( + datetime(2021, 3, 14, 3, 50, 0, tzinfo=tz), 2, 30, 0 + ) + + assert datetime(2021, 3, 15, 2, 30, 0, tzinfo=tz) == find( + datetime(2021, 3, 14, 1, 50, 0, tzinfo=tz), 2, 30, 0 + ) + + assert datetime(2021, 3, 14, 3, 30, 0, tzinfo=tz) == find( + datetime(2021, 3, 14, 1, 50, 0, tzinfo=tz), 3, 30, 0 + ) + + # Leaving DST, clocks are rolled back + assert datetime(2021, 11, 7, 2, 30, 0, tzinfo=tz, fold=0) == find( + datetime(2021, 11, 7, 2, 5, 0, tzinfo=tz, fold=0), 2, 30, 0 + ) + + assert datetime(2021, 11, 7, 2, 30, 0, tzinfo=tz) == find( + datetime(2021, 11, 7, 2, 5, 0, tzinfo=tz), 2, 30, 0 + ) + + assert datetime(2021, 11, 7, 2, 30, 0, tzinfo=tz, fold=0) == find( + datetime(2021, 11, 7, 2, 5, 0, tzinfo=tz), 2, 30, 0 + ) + + assert datetime(2021, 11, 7, 2, 30, 0, tzinfo=tz, fold=1) == find( + datetime(2021, 11, 7, 2, 10, 0, tzinfo=tz), 2, 30, 0 + ) + + assert datetime(2021, 11, 7, 2, 30, 0, tzinfo=tz, fold=1) == find( + datetime(2021, 11, 7, 2, 30, 0, tzinfo=tz, fold=0), 2, 30, 0 + ) + + assert datetime(2021, 11, 8, 2, 30, 0, tzinfo=tz, fold=1) == find( + datetime(2021, 11, 7, 2, 55, 0, tzinfo=tz, fold=0), 2, 30, 0 + ) + + assert datetime(2021, 11, 7, 4, 30, 0, tzinfo=tz, fold=0) == find( + datetime(2021, 11, 7, 2, 55, 0, tzinfo=tz, fold=1), 4, 30, 0 + ) + + assert datetime(2021, 11, 7, 2, 30, 0, tzinfo=tz, fold=1) == find( + datetime(2021, 11, 7, 2, 5, 0, tzinfo=tz, fold=1), 2, 30, 0 + ) + + assert datetime(2021, 11, 8, 2, 30, 0, tzinfo=tz) == find( + datetime(2021, 11, 7, 2, 55, 0, tzinfo=tz, fold=0), 2, 30, 0 )