diff --git a/cylc/flow/rundb.py b/cylc/flow/rundb.py index 1b04c43d3a2..cbe9c02e013 100644 --- a/cylc/flow/rundb.py +++ b/cylc/flow/rundb.py @@ -452,7 +452,9 @@ def close(self) -> None: def connect(self) -> sqlite3.Connection: """Connect to the database.""" if self.conn is None: - self.conn = sqlite3.connect(self.db_file_name, self.CONN_TIMEOUT) + self.conn = sqlite3.connect( + self.db_file_name, timeout=self.CONN_TIMEOUT + ) return self.conn def create_tables(self): diff --git a/cylc/flow/wallclock.py b/cylc/flow/wallclock.py index c5b56419796..811f99d788a 100644 --- a/cylc/flow/wallclock.py +++ b/cylc/flow/wallclock.py @@ -16,11 +16,17 @@ """Wall clock related utilities.""" from calendar import timegm -from datetime import datetime, timedelta, timezone -from typing import Dict, Optional +from datetime import ( + datetime, + timedelta, + timezone, +) from metomi.isodatetime.timezone import ( - get_local_time_zone_format, get_local_time_zone, TimeZoneFormatMode) + TimeZoneFormatMode, + get_local_time_zone, + get_local_time_zone_format, +) DATE_TIME_FORMAT_BASIC = "%Y%m%dT%H%M%S" @@ -39,7 +45,6 @@ TIME_FORMAT_EXTENDED_SUB_SECOND = "%H:%M:%S.%f" TIME_ZONE_STRING_UTC = "Z" -TIME_ZONE_UTC_UTC_OFFSET = (0, 0) TIME_ZONE_LOCAL_INFO = { "hours": get_local_time_zone()[0], @@ -51,8 +56,8 @@ } TIME_ZONE_UTC_INFO = { - "hours": TIME_ZONE_UTC_UTC_OFFSET[0], - "minutes": TIME_ZONE_UTC_UTC_OFFSET[1], + "hours": 0, + "minutes": 0, "string_basic": TIME_ZONE_STRING_UTC, "string_extended": TIME_ZONE_STRING_UTC } @@ -70,8 +75,9 @@ def set_utc_mode(mode): _FLAGS['utc_mode'] = bool(mode) -def now(override_use_utc=None): - """Return a current-time datetime.datetime and a UTC timezone flag. +def now(override_use_utc: bool | None = None) -> tuple[datetime, bool]: + """Return a current-time, timezone-aware datetime.datetime and a flag + indicating whether it is UTC or not. Keyword arguments: override_use_utc (default None) - a boolean (or None) that, if @@ -81,9 +87,9 @@ def now(override_use_utc=None): """ if override_use_utc or (override_use_utc is None and _FLAGS['utc_mode']): - return datetime.utcnow(), False + return datetime.now(timezone.utc), False else: - return datetime.now(), True + return datetime.now().astimezone(), True def get_current_time_string(display_sub_seconds=False, override_use_utc=None, @@ -112,10 +118,9 @@ def get_current_time_string(display_sub_seconds=False, override_use_utc=None, def get_time_string( date_time: datetime, display_sub_seconds: bool = False, - override_use_utc: Optional[bool] = None, + override_use_utc: bool | None = None, use_basic_format: bool = False, date_time_is_local: bool = False, - custom_time_zone_info: Optional[Dict] = None, ): """Return a string representing the current system time. @@ -135,40 +140,10 @@ def get_time_string( date_time_is_local: Indicates that the date_time argument object is in the local time zone, not UTC. - custom_time_zone_info: - A dictionary that enforces a particular time zone: - - .. code-block:: python - { - "hours": _hours, # offset from UTC - "minutes": _minutes, # offset from utc - "string_basic": _string, # timezone designators - "string_extened": _string - } - - Usage of ``string_basic`` or ``string_extended`` is - switched by ``use_basic_format``. """ - time_zone_string = None local_tz = get_local_time_zone() - if custom_time_zone_info is not None: - custom_hours = custom_time_zone_info["hours"] - custom_minutes = custom_time_zone_info["minutes"] - if use_basic_format: - custom_string = custom_time_zone_info["string_basic"] - else: - custom_string = custom_time_zone_info["string_extended"] - if date_time_is_local: - date_time_hours, date_time_minutes = local_tz - else: - date_time_hours, date_time_minutes = (0, 0) - diff_hours = custom_hours - date_time_hours - diff_minutes = custom_minutes - date_time_minutes - date_time = date_time + timedelta( - hours=diff_hours, minutes=diff_minutes) - time_zone_string = custom_string - elif override_use_utc or (override_use_utc is None and _FLAGS['utc_mode']): + if override_use_utc or (override_use_utc is None and _FLAGS['utc_mode']): time_zone_string = TIME_ZONE_STRING_UTC if date_time_is_local: date_time = date_time - timedelta( @@ -198,9 +173,11 @@ def get_time_string( return date_time_string + time_zone_string -def get_time_string_from_unix_time(unix_time, display_sub_seconds=False, - use_basic_format=False, - custom_time_zone_info=None): +def get_time_string_from_unix_time( + unix_time: float, + display_sub_seconds: bool = False, + use_basic_format: bool = False, +) -> str: """Convert a unix timestamp into a local time zone datetime.datetime. Arguments: @@ -213,20 +190,15 @@ def get_time_string_from_unix_time(unix_time, display_sub_seconds=False, use_basic_format (default False) - a boolean that, if True, represents the date/time without "-" or ":" delimiters. This is most useful for filenames where ":" may cause problems. - custom_time_zone_info (default None) - a dictionary that enforces - a particular time zone. It looks like {"hours": _hours, - "minutes": _minutes, "string": _string} where _hours and _minutes - are the hours and minutes offset from UTC and _string is the string - to use as the time zone designator. """ - date_time = datetime.fromtimestamp(unix_time, timezone.utc) - return get_time_string(date_time, - display_sub_seconds=display_sub_seconds, - use_basic_format=use_basic_format, - override_use_utc=None, - date_time_is_local=False, - custom_time_zone_info=custom_time_zone_info) + return get_time_string( + datetime.fromtimestamp(unix_time, timezone.utc), + display_sub_seconds=display_sub_seconds, + use_basic_format=use_basic_format, + override_use_utc=None, + date_time_is_local=False, + ) def get_unix_time_from_time_string(datetime_string): diff --git a/tests/conftest.py b/tests/conftest.py index 3f31def2c9d..4fa4fe13d84 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -278,7 +278,7 @@ def set_timezone(monkeypatch): Will use a very implausible timezone if none is provided. """ - def patch(time_zone: str = 'XXX-19:17'): + def patch(time_zone: str): monkeypatch.setenv('TZ', time_zone) time.tzset() diff --git a/tests/unit/cycling/test_iso8601.py b/tests/unit/cycling/test_iso8601.py index 7455e27f0ac..ea2a8de11a9 100644 --- a/tests/unit/cycling/test_iso8601.py +++ b/tests/unit/cycling/test_iso8601.py @@ -14,7 +14,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from datetime import datetime +from datetime import ( + datetime, + timezone, +) import pytest from pytest import param @@ -892,10 +895,10 @@ def test_next_simple_no_now(set_cycling_type): point = "next(T00Z)+P1D" output = ingest_time(point, my_now) - current_time = datetime.utcnow() + current_time = datetime.now(timezone.utc) # my_now is None, but ingest_time will have used a similar time, and # the returned value must be after current_time - output_time = datetime.strptime(output, "%Y%m%dT%H%MZ") + output_time = datetime.strptime(output, "%Y%m%dT%H%M%z") assert current_time < output_time diff --git a/tests/unit/test_wallclock.py b/tests/unit/test_wallclock.py index 83ffbf6d07f..f9302e00619 100644 --- a/tests/unit/test_wallclock.py +++ b/tests/unit/test_wallclock.py @@ -14,11 +14,16 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from datetime import datetime +from datetime import ( + datetime, + timedelta, + timezone, +) + +from metomi.isodatetime.data import CALENDAR import pytest from pytest import param -from metomi.isodatetime.data import CALENDAR from cylc.flow.wallclock import ( get_current_time_string, get_time_string, @@ -68,12 +73,28 @@ def test_get_unix_time_from_time_string_error(value, error): get_unix_time_from_time_string(value) +@pytest.mark.parametrize('tz_info', [ + pytest.param(None, id="naive"), + pytest.param(timezone.utc, id="utc-tz-aware"), + pytest.param(timezone(timedelta(hours=5)), id="custom-tz-aware"), +]) +def test_get_time_string_tzinfo(tz_info, set_timezone): + """Basic check it handles naive and timezone-aware datetime objects. + + Currently we just ignore the timezone information in the datetime object. + """ + set_timezone('UTC') + assert get_time_string( + datetime(2077, 2, 8, 13, 42, 39, 123456, tz_info) + ) == '2077-02-08T13:42:39Z' + + def test_get_current_time_string(set_timezone): """It reacts to local time zone changes. https://github.com/cylc/cylc-flow/issues/6701 """ - set_timezone() + set_timezone('XXX-19:17') res = get_current_time_string() assert res[-6:] == '+19:17' @@ -95,46 +116,12 @@ def test_get_current_time_string(set_timezone): ), param( datetime(2000, 12, 13, 15, 30, 12, 123456), - { - 'custom_time_zone_info': { - 'hours': 0, - 'minutes': -20, - 'string_basic': 'XXX+00:20', - }, - 'use_basic_format': True - }, - '20001213T151012XXX+00:20', - id='custom_time_zone_info_string_basic', - ), - param( - datetime(2000, 12, 13, 15, 30, 12, 123456), - { - 'custom_time_zone_info': { - 'hours': 0, - 'minutes': -20, - 'string_extended': ':UK/Exeter', - }, - 'use_basic_format': False - }, - '2000-12-13T15:10:12:UK/Exeter', - id='custom_time_zone_info_string_extended', - ), - param( - datetime(2000, 12, 13, 15, 30, 12, 123456), - { - 'custom_time_zone_info': { - 'hours': 0, - 'minutes': -20, - 'string_extended': ':UK/Exeter', - }, - 'use_basic_format': False, - 'date_time_is_local': True, - }, - '2000-12-12T19:53:12:UK/Exeter', - id='date_time_is_local', + {'use_basic_format': True}, + '20001214T104712+1917', + id='basic_format', ), ), ) def test_get_time_string(set_timezone, arg, kwargs, expect): - set_timezone() + set_timezone('XXX-19:17') assert get_time_string(arg, **kwargs) == expect diff --git a/tests/unit/tui/test_util.py b/tests/unit/tui/test_util.py index 5d1a7f10e38..fb85858407a 100644 --- a/tests/unit/tui/test_util.py +++ b/tests/unit/tui/test_util.py @@ -17,7 +17,8 @@ from datetime import ( datetime, - timedelta + timedelta, + timezone ) from unittest.mock import Mock @@ -171,7 +172,7 @@ def test_get_task_icon( start_time = None if start_offset is not None: start_time = get_time_string( - datetime.utcnow() - timedelta(seconds=start_offset) + datetime.now(timezone.utc) - timedelta(seconds=start_offset) ) assert ( (