Dates
Baseline resolves two dates for every page, datePublished and dateModified, from a list of sources you can change. One resolver answers for the JSON-LD graph, the Open Graph tags, the sitemap, the page context and the content graph, so no two surfaces can disagree about when a page was published.
It does not use Eleventy's page.date.
Why it exists
Eleventy gives every page a page.date, and when nothing in your front matter says otherwise it fills that in from the file's creation time on disk. On your laptop that is roughly when you made the file. On CI it is the moment the repository was cloned.
So a site that sets no dates gets, on every deploy, a datePublished that is identical across every page, to the millisecond, and that moves forward each time you build. It usually lands after the modified date. Search engines discount a sitemap that claims every URL changed on every deploy.
The second problem is quieter. page.date is always an instant, a precise moment. But 2026-04-28 is not a moment, it is a day. Turning it into one picks midnight in some time zone, and the date then shifts by a day for anyone formatting it in another. Nothing in Eleventy carries the difference.
Baseline keeps both facts: where a date came from, and whether it is a day or a moment.
Date or instant
Every resolved date is one of two kinds.
| Kind | Example | Written as |
|---|---|---|
| Calendar date | 2026-04-28 |
the day, unchanged |
| Instant | 2026-04-28T14:30:00+02:00 |
converted to UTC, 2026-04-28T12:30:00Z |
A calendar date is never widened into a timestamp for an output that accepts a day. Both the sitemap's <lastmod> and schema.org's datePublished take a date on its own, so that is what they get.
Writing a date with no quotes in YAML front matter gives you a calendar date:
---
datePublished: 2026-04-28
---
Add a time and you have an instant. If that time carries no offset, it is read in the site's time zone:
---
datePublished: 2026-04-28T14:30
---
The source lists
Each field has an ordered list of sources. Baseline tries them in order and the first one that yields a usable date wins.
| Field | Default sources |
|---|---|
datePublished |
datePublished, date, :filename |
dateModified |
dateModified, modified, updated, :git-modified, :datePublished |
A name with no colon is a front-matter key, read from the page's data. A name with a colon is derived.
| Source | Where it comes from |
|---|---|
:filename |
A YYYY-MM-DD anywhere in the file name, as a calendar date |
:git-modified |
The last commit to touch the file, as an instant |
:git-created |
The first commit to touch the file, as an instant |
:datePublished |
The published date this page just resolved to |
:default |
Expands to the built-in list for that field |
Nothing is invented
If no source answers, the field stays empty. The page gets no datePublished, the sitemap entry gets no <lastmod>, and the JSON-LD omits the property. An absent date is correct; a wrong one is not, and datePublished is optional on a WebPage anyway.
That is why :git-created is not in the published default. A file's first commit is when it entered the repository, which is not when it was published. A page that sat behind draft: true for three weeks would be dated three weeks before anyone could read it.
Changing them
options.dates takes either field. :default expands to the built-in list in place, so you can add a key without restating the chain:
eleventyConfig.addPlugin(baseline(settings, {
dates: {
datePublished: ['pubDate', ':default']
}
}));
That tries your own pubDate key first, then falls back to everything Baseline would have tried.
File modification time is not a source, and Baseline will refuse it by name if you ask. On CI it is the clone time rather than the date the content changed, which is the problem this page opens with.
Time zone
settings.timeZone is optional and defaults to UTC.
export default {
timeZone: 'Europe/Amsterdam'
};
It only affects one thing: a date-time written without an offset. A value that carries its own offset is converted from that, and a calendar date is not a moment, so neither is touched by it.
Any IANA time zone name works, along with the aliases and casings your platform accepts, because Baseline validates by handing the value to Intl and seeing whether it is taken. UTC, utc, GMT and Etc/UTC all resolve to UTC. Node ships the time zone database, so nothing needs installing.
A bare offset like +02:00 is refused. It has no daylight saving, so it would be wrong for half the year, which makes it a value rather than a zone. Use the name of the place.
Git as a source
:git-modified and :git-created come from one git log walk, run once per build and cached, rather than one call per file.
They read the author date, not the committer date. Rebasing rewrites committer dates on commits whose content never changed, which would move a page's modified date for a reason that has nothing to do with the page.
Git is skipped entirely in two cases. Resolution falls through to the next source, and Baseline says one line about it at the start of the build:
- No repository. Nothing to read.
- A shallow clone. GitHub Actions checks out at depth 1 by default, which makes every file report the same commit. A uniform wrong answer is worse than none, so Baseline treats the git sources as empty. Fetch the full history if you want them:
fetch-depth: 0inactions/checkout.
Neither line appears unless a source list actually names :git-created or :git-modified, so a site that does not use them never hears about git.
Where the resolved dates appear
| Surface | What it carries |
|---|---|
_pageContext.page.datePublished / .dateModified |
The resolved strings, for templates |
JSON-LD datePublished / dateModified |
On the WebPage and Article nodes |
article:published_time / article:modified_time |
Open Graph, on article-type pages |
Sitemap <lastmod> |
The resolved modified date, or your sitemap.lastmod if you set one |
_navigator.nodes[url].datePublished / .dateModified |
On every content graph node |
Each is handed a finished string. No template formats a date, so there is no second place one can be reinterpreted.
Baseline does not format dates for display. There is no date filter and no
dateStylesetting yet. For a human-readable date, reach forIntl.DateTimeFormatwith the zone and locale Baseline already resolved for the page.
Scheduling is not handled
There is no publishDate or expiryDate, and no clamp forcing a modified date to fall after a published one, because a scheduled post can legitimately be edited before it goes out. To keep a page out of a build, use draft: true, which the drafts preprocessor skips in production.
See also
- Site settings for
settings.timeZonealongside the rest of the settings argument. - Config export for
options.datesalongside the other options. - Page context for the object the resolved dates land on.
- SEO graph for the JSON-LD and Open Graph surfaces that read them.