Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion cylc/flow/rundb.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
88 changes: 30 additions & 58 deletions cylc/flow/wallclock.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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],
Expand All @@ -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
}
Expand All @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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.

Expand All @@ -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(
Expand Down Expand Up @@ -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:
Expand All @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
9 changes: 6 additions & 3 deletions tests/unit/cycling/test_iso8601.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from datetime import datetime
from datetime import (
datetime,
timezone,
)

import pytest
from pytest import param
Expand Down Expand Up @@ -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


Expand Down
69 changes: 28 additions & 41 deletions tests/unit/test_wallclock.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

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,
Expand Down Expand Up @@ -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'

Expand All @@ -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
5 changes: 3 additions & 2 deletions tests/unit/tui/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@

from datetime import (
datetime,
timedelta
timedelta,
timezone
)
from unittest.mock import Mock

Expand Down Expand Up @@ -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 (
(
Expand Down
Loading