Skip to content

feat(dates): accept time-of-day on --due-date and --start-date#77

Merged
krodak merged 1 commit into
krodak:mainfrom
thecodeartificerX:feat/iso-datetime-support
May 14, 2026
Merged

feat(dates): accept time-of-day on --due-date and --start-date#77
krodak merged 1 commit into
krodak:mainfrom
thecodeartificerX:feat/iso-datetime-support

Conversation

@thecodeartificerX
Copy link
Copy Markdown
Contributor

What

--due-date and --start-date currently accept only YYYY-MM-DD and always write due_date_time / start_date_time as false. That makes it impossible to set tasks at a specific time of day from the CLI, even though the ClickUp REST API supports it natively.

This PR extends parseDueDate to accept three input shapes, fully backward compatible:

Input Treated as *_date_time
2025-03-15 midnight in the user's ClickUp timezone false
2025-03-15T14:30 (or T14:30:00) wall clock in the user's ClickUp timezone true
2025-03-15T14:30:00+08:00 or ...Z absolute ISO 8601 instant (ignores tz hint) true

When a time component is present, the API payload sets due_date_time / start_date_time to true, so ClickUp displays the task with time-of-day and the Google Calendar integration emits a timed event instead of an all-day event.

Why

This is the gap that triggered the work: planning the day with hourly time blocks is impossible from cup today, because every task lands on the calendar as an all-day event. Users have to fall back to the ClickUp UI or hit the REST API directly. With this patch, cup create -n 'Deep work' --due-date 2025-03-15T16:00 and cup update <id> --start-date 2025-03-15T14:30 --due-date 2025-03-15T16:00 work end to end and the events appear at the right time on the calendar.

Implementation notes

  • parseDueDate(value, timezone) now returns { ms: number; hasTime: boolean } instead of number. Callers (update, create, bulk, goals) destructure and propagate hasTime to due_date_time / start_date_time.
  • The existing IANA-timezone offset technique used for dateToTimezoneMs is generalised to arbitrary wall-clock times via wallClockToTimezoneMs. The old function is removed since it was a special case of the new one (hour=minute=second=0).
  • Range-checks on HH/MM/SS reject inputs like T25:00 cleanly.
  • Help text on --due-date / --start-date updated for both create and update.

Tests

  • 9 new unit tests in tests/unit/commands/update.test.ts covering date-only, local datetime, datetime + IANA tz (Australia/Perth), full ISO with Z, full ISO with +08:00, and malformed inputs (out-of-range time, missing minutes).
  • 3 new buildUpdatePayload tests confirming due_date_time=true / start_date_time=true are emitted when a time component is present.
  • Full unit suite: 1107 / 1111 passing. The 4 failures (config.test.ts > respects XDG_CONFIG_HOME, interactive.test.ts > showDetailsAndOpen, two completion-doc-sync tests) are pre-existing on main and unrelated to this change — verified by stashing the patch and re-running.

Compatibility

Backward compatible at the CLI surface: every existing YYYY-MM-DD invocation produces the same payload it did before (*_date_time: false). Only callers that opt into the new time-of-day syntax see the new behaviour.

The parseDueDate function signature change (now { ms, hasTime }) is internal — it's not re-exported as a public API. All in-repo callers are updated in the same PR.

Previously, --due-date and --start-date only accepted YYYY-MM-DD and always
wrote the task with due_date_time/start_date_time = false. This made it
impossible to set tasks at a specific time of day from the CLI, even though
the ClickUp API natively supports it.

This extends parseDueDate to accept three input shapes, backward compatible:

  - YYYY-MM-DD                       (existing behavior; date-only)
  - YYYY-MM-DDTHH:MM[:SS]            (wall clock in user's ClickUp timezone)
  - YYYY-MM-DDTHH:MM[:SS]±HH:MM | Z  (absolute ISO 8601 instant)

When the input includes a time component, the payload's due_date_time /
start_date_time is set to true, so ClickUp displays the task with time and
the Google Calendar integration emits a timed event rather than an all-day one.

Changes:
  - parseDueDate now returns { ms, hasTime } instead of number; callers
    (update, create, bulk, goals) use the new shape.
  - Reuses the existing IANA-timezone offset technique, generalized from
    midnight-only to arbitrary wall-clock times.
  - Range-checks HH/MM/SS to reject e.g. "T25:00".
  - Help text on --due-date / --start-date updated in update + create.
  - 9 new unit tests covering date-only, local datetime, datetime+tz, full
    ISO with Z, full ISO with explicit offset, and malformed inputs.

Tests: 70/70 update tests pass; full suite 1107 passing with 4 pre-existing
failures (config/interactive/completion-doc-sync) untouched by this change.
@krodak krodak merged commit b3364ca into krodak:main May 14, 2026
1 check passed
@krodak
Copy link
Copy Markdown
Owner

krodak commented May 14, 2026

Thanks for this — clean refactor with excellent test coverage. The ParsedDate {ms, hasTime} struct is exactly the right design for ClickUp's due_date_time field, and the three-format parser handles timezone semantics correctly across all cases.

Merged and shipping in v1.28.0 with docs updates for the new formats.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants