diff --git a/python_modules/dagster/dagster/_utils/schedules.py b/python_modules/dagster/dagster/_utils/schedules.py index 1cc0aad39806a..d1132e8a7bd5c 100644 --- a/python_modules/dagster/dagster/_utils/schedules.py +++ b/python_modules/dagster/dagster/_utils/schedules.py @@ -637,7 +637,7 @@ def cron_string_iterator( # Croniter >= 1.4 returns 3 items cron_parts, nth_weekday_of_month, *_ = CroniterShim.expand(cron_string) - is_numeric = [len(part) == 1 and part[0] != "*" for part in cron_parts] + is_numeric = [len(part) == 1 and isinstance(part[0], int) for part in cron_parts] is_wildcard = [len(part) == 1 and part[0] == "*" for part in cron_parts] all_numeric_minutes = len(cron_parts[0]) > 0 and all( @@ -657,7 +657,7 @@ def cron_string_iterator( if ( all(is_numeric[0:3]) and all(is_wildcard[3:]) - and int(cron_parts[2][0]) <= MAX_DAY_OF_MONTH_WITH_GUARANTEED_MONTHLY_INTERVAL + and cron_parts[2][0] <= MAX_DAY_OF_MONTH_WITH_GUARANTEED_MONTHLY_INTERVAL ): # monthly known_schedule_type = ScheduleType.MONTHLY elif all(is_numeric[0:2]) and is_numeric[4] and all(is_wildcard[2:4]): # weekly @@ -668,16 +668,16 @@ def cron_string_iterator( known_schedule_type = ScheduleType.HOURLY if is_numeric[1]: - expected_hour = int(cron_parts[1][0]) + expected_hour = cron_parts[1][0] if all_numeric_minutes: - expected_minutes = [int(cron_part) for cron_part in cron_parts[0]] + expected_minutes = [cron_part for cron_part in cron_parts[0]] if is_numeric[2]: - expected_day = int(cron_parts[2][0]) + expected_day = cron_parts[2][0] if is_numeric[4]: - expected_day_of_week = int(cron_parts[4][0]) + expected_day_of_week = cron_parts[4][0] if known_schedule_type: start_datetime = datetime.datetime.fromtimestamp( diff --git a/python_modules/dagster/dagster_tests/scheduler_tests/test_cron_string_iterator.py b/python_modules/dagster/dagster_tests/scheduler_tests/test_cron_string_iterator.py index e678c55d69540..ec18a2f52ed42 100644 --- a/python_modules/dagster/dagster_tests/scheduler_tests/test_cron_string_iterator.py +++ b/python_modules/dagster/dagster_tests/scheduler_tests/test_cron_string_iterator.py @@ -582,3 +582,38 @@ def test_reversed_dst_transition_advances(execution_timezone, cron_string, times prev_time = next_time start_timestamp = start_timestamp - timestamp_interval + + +def test_last_day_of_month_cron_schedule(): + # L means last day of month + execution_timezone = "Europe/Berlin" + cron_string = "*/15 13 L * *" + + expected_datetimes = [ + create_datetime(2023, 10, 31, 13, 0, 0, tz="Europe/Berlin"), + create_datetime(2023, 10, 31, 13, 15, 0, tz="Europe/Berlin"), + create_datetime(2023, 10, 31, 13, 30, 0, tz="Europe/Berlin"), + create_datetime(2023, 10, 31, 13, 45, 0, tz="Europe/Berlin"), + create_datetime(2023, 11, 30, 13, 0, 0, tz="Europe/Berlin"), + create_datetime(2023, 11, 30, 13, 15, 0, tz="Europe/Berlin"), + create_datetime(2023, 11, 30, 13, 30, 0, tz="Europe/Berlin"), + create_datetime(2023, 11, 30, 13, 45, 0, tz="Europe/Berlin"), + create_datetime(2023, 12, 31, 13, 0, 0, tz="Europe/Berlin"), + create_datetime(2023, 12, 31, 13, 15, 0, tz="Europe/Berlin"), + create_datetime(2023, 12, 31, 13, 30, 0, tz="Europe/Berlin"), + create_datetime(2023, 12, 31, 13, 45, 0, tz="Europe/Berlin"), + ] + + start_timestamp = expected_datetimes[0].timestamp() - 1 + + cron_iter = cron_string_iterator(start_timestamp, cron_string, execution_timezone) + + for i in range(len(expected_datetimes)): + assert next(cron_iter) == expected_datetimes[i] + + end_timestamp = expected_datetimes[-1].timestamp() + 1 + + cron_iter = reverse_cron_string_iterator(end_timestamp, cron_string, execution_timezone) + + for i in range(len(expected_datetimes)): + assert next(cron_iter) == expected_datetimes[-(i + 1)]