Skip to content

Build invoice deadline engine with multi-timezone countdown, business-hours awareness, and expiry callbacks #186

@Kingsman-99

Description

@Kingsman-99

Description

Build a comprehensive deadline management module that provides human-readable countdowns in multiple timezones, skips non-business hours when computing "effective" time remaining, fires registered expiry callbacks when a deadline passes, and integrates with the existing watcher.ts polling system.

Acceptance Criteria

  • Create src/deadlineEngine.ts exporting DeadlineEngine class
  • getCountdown(deadline: number, options?: CountdownOptions): CountdownResult — returns { expired: boolean, display: string, secondsRemaining: number, localDisplay: string }
  • CountdownOptions supports: timezone?: string (IANA, e.g. "America/New_York"), businessHours?: { start: number, end: number, days: number[] }
  • When businessHours is set, secondsRemaining counts only business-hours seconds remaining (e.g. a deadline 10 hours away at 5pm Friday = only next Monday morning hours count)
  • registerExpiryCallback(deadline: number, invoiceId: string, cb: (invoiceId: string) => void): () => void — returns unsubscribe function; callback fires once when deadline passes
  • Callbacks are managed via setInterval internally; the interval is shared (one per DeadlineEngine instance), not one per callback
  • destroy() clears all callbacks and the interval
  • Exported from src/index.ts
  • Tests: business-hours calculation across weekend, callback fires after mock time advance, unsubscribe prevents callback, multiple callbacks on same deadline all fire, destroy clears all

Context

  • isExpired in src/utils.ts is the existing baseline
  • src/watcher.ts uses polling — DeadlineEngine should be usable standalone or alongside it
  • Business hours must handle DST correctly using Intl.DateTimeFormat — do not use moment.js or external date libraries

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    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