Skip to content

fix: strict date parsing — eliminate timezone ghost and invalid date crashes #3

Description

@EmanueleMinotto

Problem

The current resolver parses disabledUntil dates without format validation or timezone pinning. This causes two bugs:

  1. Invalid formats accepted silently — malformed dates (e.g. 2026-4-1, spreadsheet-formatted values) may throw at runtime or parse incorrectly depending on the Java version and locale
    1. Timezone-dependent behaviour — comparing against LocalDate.now() or new Date() without UTC pinning means a test disabled until 2026-04-01 re-enables at different wall-clock moments across CI runners in different timezones. A runner in Sydney re-enables it hours before one in London.

Proposed fix

Replace the current date parsing with a strict, UTC-pinned implementation:

private static final Pattern DATE_RE = Pattern.compile("^\\d{4}-\\d{2}-\\d{2}$");

static LocalDate parseDisabledUntil(String raw, int rowNum) {
    if (raw == null || raw.trim().isEmpty()) return null;
    if (!DATE_RE.matcher(raw.trim()).matches()) {
        throw new IllegalArgumentException(
            "[skipper] Row " + rowNum + ": invalid disabledUntil \"" + raw + "\". Use YYYY-MM-DD."
        );
    }
    // "Disabled until" means through end of that calendar day UTC.
    // Return the day AFTER so comparison is: Instant.now().isBefore(expiry)
    return LocalDate.parse(raw.trim()).plusDays(1);
}

static boolean isDisabled(Row row, int rowNum) {
    LocalDate until = parseDisabledUntil(row.getDisabledUntil(), rowNum);
    return until != null && LocalDate.now(ZoneOffset.UTC).isBefore(until);
}

Key decisions:

  • Throw on parse errors — bad data fails loudly at startup, not silently mid-run
    • UTC pinned via ZoneOffset.UTC — consistent expiry across all CI runners regardless of system timezone
      • YYYY-MM-DD strictly enforced — rejects locale-formatted dates, spreadsheet values, partial dates

Acceptance criteria

  • Any disabledUntil not matching YYYY-MM-DD throws IllegalArgumentException with the row number
  • - [ ] A test disabled until 2026-04-01 expires at the same UTC instant regardless of the JVM's default timezone
  • - [ ] Empty/null disabledUntil is treated as "not disabled"
  • - [ ] Unit tests: valid date, malformed date (throws), null/empty, timezone consistency

Effort estimate

~10 lines of logic. Unit tests recommended alongside.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions