Small speed up to frequently called datetime functions (#85399)
parent
45eb1efc6f
commit
d81febd3f4
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
|||
import bisect
|
||||
from contextlib import suppress
|
||||
import datetime as dt
|
||||
from functools import partial
|
||||
import platform
|
||||
import re
|
||||
import time
|
||||
|
@ -98,9 +99,10 @@ def get_time_zone(time_zone_str: str) -> dt.tzinfo | None:
|
|||
return None
|
||||
|
||||
|
||||
def utcnow() -> dt.datetime:
|
||||
"""Get now in UTC time."""
|
||||
return dt.datetime.now(UTC)
|
||||
# We use a partial here since it is implemented in native code
|
||||
# and avoids the global lookup of UTC
|
||||
utcnow: partial[dt.datetime] = partial(dt.datetime.now, UTC)
|
||||
utcnow.__doc__ = "Get now in UTC time."
|
||||
|
||||
|
||||
def now(time_zone: dt.tzinfo | None = None) -> dt.datetime:
|
||||
|
@ -466,8 +468,8 @@ def _datetime_ambiguous(dattim: dt.datetime) -> bool:
|
|||
return _datetime_exists(dattim) and dattim.utcoffset() != opposite_fold.utcoffset()
|
||||
|
||||
|
||||
def __monotonic_time_coarse() -> float:
|
||||
"""Return a monotonic time in seconds.
|
||||
def __gen_monotonic_time_coarse() -> partial[float]:
|
||||
"""Return a function that provides monotonic time in seconds.
|
||||
|
||||
This is the coarse version of time_monotonic, which is faster but less accurate.
|
||||
|
||||
|
@ -477,13 +479,16 @@ def __monotonic_time_coarse() -> float:
|
|||
|
||||
https://lore.kernel.org/lkml/20170404171826.25030-1-marc.zyngier@arm.com/
|
||||
"""
|
||||
return time.clock_gettime(CLOCK_MONOTONIC_COARSE)
|
||||
# We use a partial here since its implementation is in native code
|
||||
# which allows us to avoid the overhead of the global lookup
|
||||
# of CLOCK_MONOTONIC_COARSE.
|
||||
return partial(time.clock_gettime, CLOCK_MONOTONIC_COARSE)
|
||||
|
||||
|
||||
monotonic_time_coarse = time.monotonic
|
||||
with suppress(Exception):
|
||||
if (
|
||||
platform.system() == "Linux"
|
||||
and abs(time.monotonic() - __monotonic_time_coarse()) < 1
|
||||
and abs(time.monotonic() - __gen_monotonic_time_coarse()()) < 1
|
||||
):
|
||||
monotonic_time_coarse = __monotonic_time_coarse
|
||||
monotonic_time_coarse = __gen_monotonic_time_coarse()
|
||||
|
|
|
@ -5,7 +5,7 @@ import asyncio
|
|||
from collections import OrderedDict
|
||||
from collections.abc import Awaitable, Callable, Collection
|
||||
from contextlib import contextmanager
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
import functools as ft
|
||||
from io import StringIO
|
||||
import json
|
||||
|
@ -396,7 +396,7 @@ def async_fire_time_changed_exact(
|
|||
approach, as this is only for testing.
|
||||
"""
|
||||
if datetime_ is None:
|
||||
utc_datetime = date_util.utcnow()
|
||||
utc_datetime = datetime.now(timezone.utc)
|
||||
else:
|
||||
utc_datetime = date_util.as_utc(datetime_)
|
||||
|
||||
|
@ -418,7 +418,7 @@ def async_fire_time_changed(
|
|||
for an exact microsecond, use async_fire_time_changed_exact.
|
||||
"""
|
||||
if datetime_ is None:
|
||||
utc_datetime = date_util.utcnow()
|
||||
utc_datetime = datetime.now(timezone.utc)
|
||||
else:
|
||||
utc_datetime = date_util.as_utc(datetime_)
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ from datetime import datetime, timedelta
|
|||
import json
|
||||
from unittest.mock import patch, sentinel
|
||||
|
||||
from freezegun import freeze_time
|
||||
import pytest
|
||||
from sqlalchemy import text
|
||||
|
||||
|
@ -973,6 +972,7 @@ def test_state_changes_during_period_multiple_entities_single_test(hass_recorder
|
|||
hist[entity_id][0].state == value
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2039-01-19 03:14:07.555555-00:00")
|
||||
async def test_get_full_significant_states_past_year_2038(
|
||||
async_setup_recorder_instance: SetupRecorderInstanceT,
|
||||
hass: ha.HomeAssistant,
|
||||
|
@ -980,29 +980,29 @@ async def test_get_full_significant_states_past_year_2038(
|
|||
"""Test we can store times past year 2038."""
|
||||
await async_setup_recorder_instance(hass, {})
|
||||
past_2038_time = dt_util.parse_datetime("2039-01-19 03:14:07.555555-00:00")
|
||||
hass.states.async_set("sensor.one", "on", {"attr": "original"})
|
||||
state0 = hass.states.get("sensor.one")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with freeze_time(past_2038_time):
|
||||
hass.states.async_set("sensor.one", "on", {"attr": "original"})
|
||||
state0 = hass.states.get("sensor.one")
|
||||
await hass.async_block_till_done()
|
||||
hass.states.async_set("sensor.one", "on", {"attr": "new"})
|
||||
state1 = hass.states.get("sensor.one")
|
||||
await async_wait_recording_done(hass)
|
||||
hass.states.async_set("sensor.one", "on", {"attr": "new"})
|
||||
state1 = hass.states.get("sensor.one")
|
||||
|
||||
def _get_entries():
|
||||
with session_scope(hass=hass) as session:
|
||||
return history.get_full_significant_states_with_session(
|
||||
hass,
|
||||
session,
|
||||
past_2038_time - timedelta(days=365),
|
||||
past_2038_time + timedelta(days=365),
|
||||
entity_ids=["sensor.one"],
|
||||
significant_changes_only=False,
|
||||
)
|
||||
await async_wait_recording_done(hass)
|
||||
|
||||
states = await recorder.get_instance(hass).async_add_executor_job(_get_entries)
|
||||
sensor_one_states: list[State] = states["sensor.one"]
|
||||
assert sensor_one_states[0] == state0
|
||||
assert sensor_one_states[1] == state1
|
||||
assert sensor_one_states[0].last_changed == past_2038_time
|
||||
assert sensor_one_states[0].last_updated == past_2038_time
|
||||
def _get_entries():
|
||||
with session_scope(hass=hass) as session:
|
||||
return history.get_full_significant_states_with_session(
|
||||
hass,
|
||||
session,
|
||||
past_2038_time - timedelta(days=365),
|
||||
past_2038_time + timedelta(days=365),
|
||||
entity_ids=["sensor.one"],
|
||||
significant_changes_only=False,
|
||||
)
|
||||
|
||||
states = await recorder.get_instance(hass).async_add_executor_job(_get_entries)
|
||||
sensor_one_states: list[State] = states["sensor.one"]
|
||||
assert sensor_one_states[0] == state0
|
||||
assert sensor_one_states[1] == state1
|
||||
assert sensor_one_states[0].last_changed == past_2038_time
|
||||
assert sensor_one_states[0].last_updated == past_2038_time
|
||||
|
|
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
|||
import asyncio
|
||||
from collections.abc import AsyncGenerator, Callable, Generator
|
||||
from contextlib import asynccontextmanager
|
||||
import datetime
|
||||
import functools
|
||||
import gc
|
||||
import itertools
|
||||
|
@ -78,6 +79,14 @@ asyncio.set_event_loop_policy(runner.HassEventLoopPolicy(False))
|
|||
asyncio.set_event_loop_policy = lambda policy: None
|
||||
|
||||
|
||||
def _utcnow():
|
||||
"""Make utcnow patchable by freezegun."""
|
||||
return datetime.datetime.now(datetime.timezone.utc)
|
||||
|
||||
|
||||
dt_util.utcnow = _utcnow
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
"""Register custom pytest options."""
|
||||
parser.addoption("--dburl", action="store", default="sqlite://")
|
||||
|
|
Loading…
Reference in New Issue