Calendars: the thing that turns a duration into a date

A calendar states which days and hours are worked. Every duration in a schedule is a count of working time, every date is a position, and the calendar is the only thing that converts between them — so "five days" is not a fixed quantity and two activities of identical duration on different calendars finish on different days.

This page is about what calendars do to a schedule: which quantity is counted on which calendar, how activity, project and resource calendars differ, what a lag is counted on, and what happens when a calendar changes between updates. How a calendar is encoded in a P6 export is a separate subject, and inside clndr_data owns it.

Working days and calendar days are different currencies

A contract states durations in calendar days as often as in working days. A schedule states them in working days almost always. Mixing the two is the cheapest way to produce a wrong number that looks right.

Three rules follow, and the third gets broken quietly. A duration is a count, not a span: ten working days occupies fourteen calendar days on a Monday-to-Friday week and eleven on a six-day week, the count unchanged and the finish date moved. A date difference is a span, not a count: converting elapsed days back to working days needs a calendar. And counts taken on different calendars must not be added — on a network using several, which is most real networks, "three days here plus four days there" is not seven of anything. When this project reports a finish movement in working days it names the calendar the count was taken on, because on a mixed network the choice is arbitrary and it changes the number.

Shifts, hours per day, and the divisor

A calendar states shifts — clock times on each working day. It also, separately, states an hours-per-day figure, and the two can disagree.

That is not a corruption. The hours-per-day figure is the divisor every duration in the file converts through: P6 stores durations in hours, and the duration in days is the hours divided by that figure. The shifts describe when work happens. A file can declare eight hours a day while its shifts describe ten.

Three things follow that a reviewer can check. A day may carry several shifts and the gap between them is unpaid — 08:00–12:00 plus 13:00–17:00 is an eight-hour day, not a nine-hour one, and ignoring the gap drifts every intra-day timestamp by the length of the lunch break. A shift finishing at or before its start is overnight, not zero-length. And when the declared figure and the shifts disagree, the direction matters: this project reports it by name either way, uses the shifts where they run longer than the declared day because the stored dates in such files are consistent with them, and keeps the declared figure where they read shorter, because every way of misreading a week biases the shift figure low and never high.

Fractional durations

A duration that is not a whole number of working days has to go somewhere. This project rounds up: an activity needing 33 hours on an eight-hour calendar occupies five days, because you cannot schedule a crew for a fraction of a shift and then hand the space over. It is stated here because it is otherwise invisible to anyone reading a report. The obvious alternative — adding a day to every fractional duration, as a heuristic to recover what a whole-day axis loses — was built and measured here, made agreement with P6 substantially worse, and was refused on that measurement. See what this does not do and is this real?.

Exceptions: a shutdown and a make-up day are the same field

A calendar's exceptions are dated departures from its weekly pattern, and the only thing separating the two kinds is whether the exception carries shifts:

Exception Meaning
Carries at least one shift A working exception — a make-up Saturday, or a day worked short
Carries no shift at all A shutdown — a holiday, or one day of a Christmas closure

Both are exception records, and a reader that looks in the wrong place turns every make-up day into a shutdown — silently, and in the direction that makes the schedule look later than the contractor planned it. That failure and seven others are in inside clndr_data.

What matters at the scheduling level is that exceptions have no downstream symptom. Lose a two-week December shutdown and every date, every float value and every delay attribution moves together; the schedule still reads as entirely reasonable, and nothing in the output points at the calendar. That is why a calendar inventory is the first thing to ask for and the last thing anybody does.

Activity, project and resource calendars

A P6 export records a calendar's kind, and the three kinds do different jobs.

Kind What it governs Where it comes from
Global Shared working weeks and shutdowns, inherited by project calendars The calendar table, flagged as global
Project The project's own working pattern, including its default for activities that name none Derived from a global calendar and storing only its own departures
Resource When a named crew or item of plant is available, independently of the activity's calendar The resource table's own calendar reference

Three consequences. Every activity names its own calendar and the project's default is a fallback — an activity naming no calendar, or naming one the file does not contain, falls back to it, and this project reports the second case by name, because an activity silently rehomed onto a different working week is a date change with no visible cause. A project calendar stores departures, not the whole pattern: the shared shutdown calendar can live entirely in the parent record, so reading only the child's own definition works days the project does not; the parent link is a field P6 stored rather than a figure it computed, so following it is a decoding step and not a borrowed answer, and the chain can be several deep. And a resource calendar can change a levelling answer without touching the network — a crew on a Monday-to-Friday calendar is not available on a Saturday even when the activity's calendar works it, so the logic and the critical path are unchanged while the resource-feasible answer moves.

Which quantity is counted on which calendar

Almost every "why does this number disagree" question resolves to a row here.

Quantity Counted on
An activity's duration That activity's own calendar
An activity's total float and free float That activity's own calendar
A relationship's lag Whichever calendar the project's lag calendar setting names — predecessor, successor, project default, or elapsed time
A constraint date Resolved to a boundary on the constrained activity's calendar
The project start and finish Instants, not counts; a date, not a number of days
A reported finish movement A calendar that has to be named, because on a mixed network the choice is arbitrary

The lag row is the one that surprises people, because the governing calendar is a project-level setting rather than a property of the relationship: change the setting and every lagged link in the file changes meaning with no edit to a single link. Lags and leads works that through, with a worked example where one start-to-start lag lands on a Friday counted one way and the following Monday counted the other.

When a calendar changes between updates

Changing a calendar between two updates expands or contracts the apparent project duration with no logic change, no duration change, and no work moving. Add a shutdown week and the forecast finish slips a week. Make a past non-working day working and it pulls in.

The awkward part is that the legitimate and illegitimate versions are the same edit. Recording a granted weather day by making a past working day non-working is ordinary, correct practice. Absorbing a slip by quietly adding holidays reaches forward past the status date and moves a forecast. In the exported file those two look identical: a changed exception record on a named calendar.

Published update-review practice — AACE RP 53R-06 is the document, and its method rather than its wording is described here — asks that each calendar change be documented, and treats a change reaching forward past the status date differently from one confined to the past. That test is a judgment about intent, so this project's rule names the changes and leaves the determination to the reviewer: which calendars were added, which removed, and which altered in pattern or hours since the prior version. It does not conclude that a change was improper, nor that an undocumented one was innocent.

A related trap: two updates whose activities have moved between calendars is not the same case as two updates whose calendars have changed. Both move dates, and only the second shows up in a calendar inventory.

NYSDOT names calendars expressly among the ways a schedule can appear to share total float while not doing so: a restrictive enough calendar absorbs time that would otherwise show as float. The position who owns the float takes, and this page does not duplicate, is that a calendar reflecting a genuine seasonal restriction and one written to absorb float are the same object in the export — so an inventory is evidence and not a finding, because the distinction is intent.

What the specifications say about calendars

Less than they say about constraints and lags, and mostly about disclosure and default settings.

Document The requirement, in shape Where
SCDOT Time periods fixed at 8 hours a day and 2,000 a year, among a list of required default settings Default Values
NYSDOT Over-restrictive calendars named as a float-suppression mechanism Item 639
WisDOT Calendar properties checked as part of the agency's own review filters §108.4.4.3 and the review guidance
MnDOT Calendars among the network content the narrative must account for §1803.3C.1

Where a specification fixes hours per day, note what that binds: the divisor every duration converts through — not necessarily the shifts, which can still describe something else. The two disagreeing is a reportable state, not an error to be silently resolved.

What a calendar inventory shows

The thing to ask for before arguing about a date. Every calendar, with its name, kind, working week, declared hours per day and the hours its shifts actually describe. Every exception, dated and classified as a shutdown or a working exception. Which activities use each calendar — one nobody uses is not a finding and one carrying the whole driving chain is. What changed since the prior update. And which calendars were read completely, with each failure named: an empty definition, an unreadable one, a week with fewer than seven days parsed, a calendar on which nobody ever works. Those are reported by name rather than absorbed into a default week, because a default week is the failure mode that produces a plausible schedule finishing early.

What this tool does, and what it refuses

It reads each calendar's weekly pattern, shift times and dated exceptions, follows the parent-calendar chain, and states what it found — including where a calendar's declared hours disagree with its own shifts, and where a calendar could not be read at all. It computes every duration, float value and lag on the calendar that governs it, names that calendar beside any day count it reports, and identifies calendar changes between two versions.

It discloses what it cannot see. The work pattern here is day-granular, so a six-hour Saturday is worked as a whole day and the overstatement is stated in the output rather than corrected in silence.

It refuses to decide whether a calendar is over-restrictive, whether a calendar change was proper, or whether a shutdown is excusable. Those are determinations for the reviewing authority.

Source: web/pages/schedule-calendars.md. Source commit date: 2026-09-11.

See it in practice

Follow the evidence, from the schedule to the finding.

Explore the sample