Skip to content
Draft
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
1 change: 1 addition & 0 deletions changes.d/5928.break.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed behaviour of `initial cycle point = next(...)` which could result in a time in the past, and equivalently for `previous()`. E.g. if the current time is `2024-01-01T12:30Z`, `next(--01-01)` now evaluates to `2025-01-01T00:00Z` instead of `2024-01-01T00:00Z`.
2 changes: 1 addition & 1 deletion conda-environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ dependencies:
- graphviz # for static graphing
# Note: can't pin jinja2 any higher than this until we give up on Cylc 7 back-compat
- jinja2 >=3.0,<3.1
- metomi-isodatetime >=1!3.0.0, <1!3.2.0
- metomi-isodatetime >=1!3.2.0, <1!3.3.0
- packaging
# Constrain protobuf version for compatible Scheduler-UIS comms across hosts
- protobuf >=4.24.4,<4.25.0
Expand Down
2 changes: 1 addition & 1 deletion cylc/flow/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ async def stop(
# schedule shutdown after wallclock time passes provided time
parser = TimePointParser()
schd.set_stop_clock(
int(parser.parse(clock_time).seconds_since_unix_epoch)
parser.parse(clock_time).seconds_since_unix_epoch
)
schd._update_workflow_state()
elif task is not None:
Expand Down
68 changes: 29 additions & 39 deletions cylc/flow/cycling/iso8601.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,52 @@
import contextlib
from functools import lru_cache
import re
from typing import List, Optional, TYPE_CHECKING, Tuple
from typing import (
TYPE_CHECKING,
List,
Optional,
Tuple,
)

from metomi.isodatetime.data import Calendar, CALENDAR, Duration
from metomi.isodatetime.data import (
CALENDAR,
Calendar,
)
from metomi.isodatetime.dumpers import TimePointDumper
from metomi.isodatetime.timezone import (
get_local_time_zone, get_local_time_zone_format, TimeZoneFormatMode)
from metomi.isodatetime.exceptions import IsodatetimeError
from metomi.isodatetime.parsers import ISO8601SyntaxError
from cylc.flow.time_parser import CylcTimeParser
from metomi.isodatetime.timezone import (
TimeZoneFormatMode,
get_local_time_zone,
get_local_time_zone_format,
)

from cylc.flow.cycling import (
PointBase, IntervalBase, SequenceBase, ExclusionBase, cmp
ExclusionBase,
IntervalBase,
PointBase,
SequenceBase,
cmp,
)
from cylc.flow.exceptions import (
CylcConfigError,
IntervalParsingError,
PointParsingError,
SequenceDegenerateError,
WorkflowConfigError
WorkflowConfigError,
)
from cylc.flow.wallclock import get_current_time_string
from cylc.flow.parsec.validate import IllegalValueError
from cylc.flow.time_parser import CylcTimeParser
from cylc.flow.wallclock import get_current_time_string


if TYPE_CHECKING:
from metomi.isodatetime.data import TimePoint
from metomi.isodatetime.parsers import (
DurationParser, TimePointParser, TimeRecurrenceParser)
DurationParser,
TimePointParser,
TimeRecurrenceParser,
)

CYCLER_TYPE_ISO8601 = "iso8601"
CYCLER_TYPE_SORT_KEY_ISO8601 = 1
Expand Down Expand Up @@ -706,14 +726,6 @@ def ingest_time(value: str, now: Optional[str] = None) -> str:
now = get_current_time_string()
now_point = parser.parse(now)

# correct for year in 'now' if year is the only date unit specified -
# https://github.com/cylc/cylc-flow/issues/4805#issuecomment-1103928604
if re.search(r"\(-\d{2}[);T]", value):
now_point += Duration(years=1)
# likewise correct for month if year and month are the only date units
elif re.search(r"\(-\d{4}[);T]", value):
now_point += Duration(months=1)

# perform whatever transformation is required
offset = None
if is_prev_next:
Expand Down Expand Up @@ -806,28 +818,6 @@ def prev_next(

cycle_point = timepoints[my_diff.index(min(my_diff))]

# ensure truncated dates do not have time from 'now' included' -
# https://github.com/metomi/isodatetime/issues/212
if 'T' not in value.split(')')[0]:
# NOTE: Strictly speaking we shouldn't forcefully mutate TimePoints
# in this way as they're meant to be immutable since
# https://github.com/metomi/isodatetime/pull/165, however it
# should be ok as long as the TimePoint is not used as a dict key and
# we don't call any of the TimePoint's cached methods until after we've
# finished mutating it.
cycle_point._hour_of_day = 0
cycle_point._minute_of_hour = 0
cycle_point._second_of_minute = 0
# likewise ensure month and day from 'now' are not included
# where they did not appear in the truncated datetime
if re.search(r"\(-\d{2}[);T]", value):
# case 1 - year only
cycle_point._month_of_year = 1
cycle_point._day_of_month = 1
elif re.search(r"\(-(-\d{2}|\d{4})[;T)]", value):
# case 2 - month only or year and month
cycle_point._day_of_month = 1

return cycle_point, offset


Expand Down
4 changes: 2 additions & 2 deletions cylc/flow/task_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ def get_point_as_seconds(self):
"""Compute and store my cycle point as seconds since epoch."""
if self.point_as_seconds is None:
iso_timepoint = point_parse(str(self.point))
self.point_as_seconds = int(iso_timepoint.seconds_since_unix_epoch)
self.point_as_seconds = iso_timepoint.seconds_since_unix_epoch
if iso_timepoint.time_zone.unknown:
utc_offset_hours, utc_offset_minutes = (
get_local_time_zone())
Expand Down Expand Up @@ -448,7 +448,7 @@ def get_clock_trigger_time(
else:
trigger_time = point_time + interval_parse(offset_str)

self.clock_trigger_times[offset_str] = int(
self.clock_trigger_times[offset_str] = (
trigger_time.seconds_since_unix_epoch
)
return self.clock_trigger_times[offset_str]
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ install_requires =
graphene>=2.1,<3
# Note: can't pin jinja2 any higher than this until we give up on Cylc 7 back-compat
jinja2==3.0.*
metomi-isodatetime>=1!3.0.0,<1!3.2.0
metomi-isodatetime>=1!3.2.0,<1!3.3.0
# Constrain protobuf version for compatible Scheduler-UIS comms across hosts
packaging
protobuf>=4.24.4,<4.25.0
Expand Down
6 changes: 3 additions & 3 deletions tests/unit/cycling/test_iso8601.py
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,7 @@ def test_simple(set_cycling_type):
('next(--0325)', '20110325T0000Z'),
('next(---10)', '20100810T0000Z'),
('next(---05T1200Z)', '20100905T1200Z'),
param('next(--08-08)', '20110808T0000Z', marks=pytest.mark.xfail),
('next(--08-08)', '20110808T0000Z'),
('next(T15)', '20100809T1500Z'),
('next(T-41)', '20100808T1541Z'),
]
Expand All @@ -724,7 +724,7 @@ def test_next_simple(value: str, expected: str, set_cycling_type):
('previous(--0325)', '20100325T0000Z'),
('previous(---10)', '20100710T0000Z'),
('previous(---05T1200Z)', '20100805T1200Z'),
param('previous(--08-08)', '20100808T0000Z', marks=pytest.mark.xfail),
('previous(--08-08)', '20100808T0000Z'),
('previous(T15)', '20100808T1500Z'),
('previous(T-41)', '20100808T1441Z'),
]
Expand Down Expand Up @@ -870,7 +870,7 @@ def test_weeks_days(set_cycling_type):
('previous(--1225)', '20171225T0000Z'),
('next(-2006)', '20200601T0000Z'),
('previous(-W101)', '20180305T0000Z'),
('next(-W-1; -W-3; -W-5)', '20180314T0000Z'),
('next(-W-1; -W-3; -W-5)', '20180316T0000Z'),
('next(-001; -091; -181; -271)', '20180401T0000Z'),
('previous(-365T12Z)', '20171231T1200Z'),
]
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_workflow_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def schd(
WORKFLOW_STATUS_RUNNING_TO_STOP % 4
),
(
{'stop_clock_time': int(STOP_TIME.seconds_since_unix_epoch)},
{'stop_clock_time': STOP_TIME.seconds_since_unix_epoch},
WorkflowStatus.RUNNING,
WORKFLOW_STATUS_RUNNING_TO_STOP % str(STOP_TIME)
),
Expand Down
Loading