A due date that knows about weekends and observed holidays. Give it a start date and a number of days; it returns the date the clock actually runs out, the working days left, and a sentence explaining how it got there. It computes and reports — it writes nothing.
This component requires Modern controls to be enabled. Settings → Updates → Preview → Modern controls and themes
Push the start into November and watch Thanksgiving and Veterans Day fall inside the window — the due date moves two extra days.
A due date that knows about weekends, holidays, and the difference between them.
Give it a start date and a number of days. It returns the date the clock actually runs out, how many working days are left, and a sentence explaining how it got there. It renders a card, and it exposes every number it computed so you can draw your own.
It does not write anything. No status column, no stored due date, no reminder. It computes and reports; what that means is yours.
DateAdd(start, 30, TimeUnit.Days) is wrong for most statutory and contractual
clocks, and the three reasons stack up.
Weekends do not count. Thirty working days from 3 August is 14 September, not 2 September.
Holidays do not count either, so the answer depends on which months the window crosses. A thirty day clock starting in October crosses three federal holidays; the same clock starting in February crosses one. Same allowance, different date.
And a holiday is observed, not celebrated. Independence Day on a Saturday is observed on the Friday. The office is closed on the third, so the third is the day that does not count. An app that skips the fourth tells someone the office is open when it is shut.
The gap grows with the window. At thirty days it is usually about twelve. At a hundred and five working days from 17 August 2026 the naive answer is 30 November and the real one is 20 January, fifty one days later. Every one of them is a day somebody thought they had.
| Property | Type | Default | Description |
|---|---|---|---|
StartDate | DateAndTime | Today() | The event the clock runs from. The start date itself is never counted, so day one is the next working day |
Days | Number | 30 | How many days the clock allows. Capped at 400 internally |
CompletedDate | DateAndTime | blank | Blank while open. Set it and the status reads Complete and the countdown stops |
Label | Text | "Due" | What the deadline is for, shown above the date |
Holidays | Table | one sentinel row | HolidayName and HolidayDate, where the date is the observed date. Leave it and US federal holidays are used |
Config | Record | see below | Behaviour and look |
Theme | Table | Dark and Light | One row per mode |
| Key | Default | Description |
|---|---|---|
CountMode | "business" | business or calendar |
WorkDays | "1,2,3,4,5" | Day indexes, Sunday is 0. A Sunday to Thursday week is "0,1,2,3,4" |
DueSoonDays | 3 | Working days remaining at which the status turns amber |
ShowBreakdown | true | The sentence explaining the date, and the rule above it |
ShowStats | true | The three tiles at the bottom |
ShowProgress | true | The elapsed time bar |
Compact | false | Label, pill and date only. No countdown, breakdown, bar or tiles |
DarkMode | false | Picks a row of the Theme table |
Accent | "" | Hex string. Blank uses the theme accent |
Radius | 12 | Corner radius |
Card height is 236 expanded and 92 compact. The component sets it from Compact,
so leave Height alone unless you are hiding sections individually.
| Property | Type | Description |
|---|---|---|
DueDate | DateAndTime | The answer |
StatusKey | Text | done, overdue, due-today, due-soon, on-track. Branch on this, not on the label |
StatusLabel | Text | The same thing in words, in whichever unit the mode counts |
IsOverdue | Boolean | Past due and not completed |
DaysRemaining | Number | Calendar days, negative when overdue |
WorkDaysRemaining | Number | Working days. This is the number people plan against |
SkippedDays | Number | Non working days inside the window. Zero in calendar mode |
SkippedHolidays | Number | Observed holidays inside the window, in either mode |
NextWorkDay | DateAndTime | The next working day after today |
Breakdown | Text | The sentence that explains the date |
FederalHolidays | Table | The built in table, so you can join your own closures to it |
| Event | Description |
|---|---|
OnSelect | The card was tapped |
Label and status pill across the top. The pill is a coloured dot and a word, not colour alone, so the state survives being printed and being read by somebody who cannot separate the hues.
The date, large, as Wed, Jan 20, 2027.
The countdown, in the unit the mode is counting: working days in business mode, calendar days in calendar mode. Overdue is always calendar days in both, because how late something is gets counted the way a person counts it.
The breakdown sentence. 105 working days from Aug 17 lands on Jan 20. 51 days skipped (7 holidays). This is the part that stops somebody rebuilding the calculation in a spreadsheet.
A progress bar, elapsed time over the whole span. Empty before the clock starts, full once it is overdue or complete.
Three tiles for the numbers the sentence refers to: work days remaining, skipped, holidays. A number somebody can point at settles an argument faster than a paragraph does.
No state at all. Every output derives from the inputs, so OnReset does
nothing and the component draws correctly the moment it is pasted. There is no load
step and no host wiring beyond passing the inputs.
The search is bounded. Adding N working days generates a window of candidate
dates, keeps the working ones, and takes the Nth. The window is Days * 2 + 14,
which is always enough because a five day week needs about 1.4 times the target and
the extra fortnight covers a run of closures. Days is capped at 400, since an
unbounded search is a hang rather than an error, and working day precision on a
date two years out is false precision anyway.
The holiday table covers three years, the year of StartDate and the two
either side, so a window crossing New Year still finds January's holidays. A 105
day clock from August reaches New Year's Day and MLK Day of the following year; a
two year table would come back two days early and say nothing about why.
The observed rule is applied to the five fixed date holidays and the nth weekday computed for the rest. Independence Day 2026 falls on a Saturday, so the table carries Friday 3 July.
The progress bar measures elapsed over span, not remaining over allowance. The second one silently assumes the clock started today, and a start date three months out makes the fraction negative.
WorkDaysRemaining is not DaysRemaining. Three calendar days over a long
weekend is zero working days. Showing the calendar count is how a deadline gets
missed by somebody who was watching the countdown.
The working week is configurable. Sunday to Thursday is the working week across much of the Gulf. Hard coding Saturday and Sunday is what stops a component being usable outside the US, and it costs one Config key to avoid.
An empty holiday table is a sentinel, not Table(). An empty table literal has
no columns, so anything reading HolidayDate off it fails to compile. The default
carries one row dated 1900, and rows dated 1900 are treated as absent.
| Situation | How it is set up |
|---|---|
| Thirty days to respond to a notice | Days: 30, business mode, default holidays |
| Five business days to cure a deficiency | Days: 5, DueSoonDays: 2, compact |
| Ninety calendar days to close out an award | CountMode: "calendar" |
| Materials due ten days before a panel | StartDate is the panel date minus the window, or compute backwards in the host |
| SLA clock on a support ticket | Days: 2, DueSoonDays: 1, compact in the list |
| Recertification due annually | Days: 365, calendar mode |
| A Gulf region deployment | WorkDays: "0,1,2,3,4" |
| Agency shutdown week | Pass your closures joined to FederalHolidays |
When not to use it. If the clock runs in hours rather than days, or if the deadline is a fixed date somebody typed in, this is the wrong tool. It answers one question, and it is only interesting when the answer is not obvious.
Compact mode is the one for a gallery — a single row, no breakdown. The detail is for a detail screen.
Label: =ThisItem.Title
StartDate: =ThisItem.AwardDate
Days: =ThisItem.DaysAllowed
Height: =92
Config: |-
={
CountMode: "business",
DueSoonDays: 3,
ShowBreakdown: false,
ShowStats: false,
ShowProgress: false,
Compact: true,
DarkMode: gblDarkMode,
Accent: "", Radius: 10
}Join your closures to FederalHolidays rather than restating them, so the two can't drift apart.
Holidays: =Table(
{ HolidayName: "Agency closure",
HolidayDate: Date(2026, 12, 24) },
{ HolidayName: "Agency closure",
HolidayDate: Date(2026, 12, 26) }
) & cmpDeadline1.FederalHolidaysStore it, don't compute it on read — a later holiday-table fix shouldn't move a date someone was already given.
Patch(
'Requests',
LookUp('Requests', ID = gblRequestId),
{ DueDate: cmpDeadline1.DueDate }
)| Column | Type | Notes |
|---|---|---|
StartDate | Date | The event the clock runs from |
DaysAllowed | Number | |
DueDate | Date, indexed | Written once, then filterable |
CompletedDate | Date | Blank while open |
Index DueDate. Filtering on a stored indexed date between two values is delegable
at any list size; anything computed at render time is capped at your delegation
limit and fails silently past it.
Recompute the stored date deliberately, in a Flow, when StartDate or
DaysAllowed changes. Recomputing on every read means a holiday table correction
quietly moves dates people are working to.
Set Width by hand. A pasted component instance arrives at 600 regardless of
what the YAML says. Height comes through intact.
Pass every Config key, even the ones you are leaving at their defaults. A
Record input's Default does not reach a pasted instance, so the component Coalesces
each key internally, and passing the full record is what a real host should look
like.
The instance syntax is Control: CanvasComponent with a ComponentName
sibling, not Control: cmpDeadline.
Set the start to 17 August 2026 with 105 days in working mode. The card reads
Wed, Jan 20, 2027, 51 days skipped, 7 holidays, and the comparison panel shows
DateAdd fifty one days short.
Switch to calendar days. The date moves back to 30 November, skipped drops to zero, and the holiday count drops to four, because the shorter window misses Christmas, New Year and MLK Day.
Pull the start back to 2 July and Independence Day appears in the list as Friday 3 July, not the fourth. That is the observed rule, and it is the detail most implementations get wrong.
To see the working week matter, use 26 July 2026 with 10 days. Monday to Friday gives Friday 7 August; Sunday to Thursday gives Sunday 9 August. Most starts give the same answer either way, since both weeks have five working days, so pick a start that lands on a weekend to see them separate.
One email when something new ships. Unsubscribe anytime.
Use the toolbar to format · or type markdown directly