Article Published Date and JSON-LD Date Mismatch

The visible "Published / Updated" date does not match `datePublished` / `dateModified` in JSON-LD. Why this is more than a cosmetic bug.

Your article’s byline says “Published May 17, 2026.” View source: JSON-LD has "datePublished": "2024-03-08". Rich Results Test passes (technically valid), but the Article rich result with the publication date doesn’t appear in SERPs. Or the SERP shows a different date than your byline. Google uses datePublished for freshness signals and dateModified for the “Updated” chip in results. When those disagree with the visible byline, Google trusts neither and may suppress the rich result entirely.

This article walks through why dates drift, how to align them, and the trap of bumping dateModified on every build.

Common causes

Ordered by hit rate, highest first.

1. datePublished hardcoded or set to build time

Template generates JSON-LD with datePublished: new Date() at build time, so every article shows today’s date. Or it’s hardcoded to a constant.

How to spot it: View source on multiple articles. If they all share the same datePublished (today or a fixed value), template is the source.

2. dateModified updated on every deploy

The most common variant: dateModified: new Date().toISOString() runs at build time. You deploy weekly. Every article looks like it was modified this week. Google detects this pattern and discounts the date signal.

How to spot it: Compare dateModified across articles you know weren’t edited. If they all match the last deploy date, this is the bug.

3. Timezone causes off-by-one day

Frontmatter: publishedAt: 2026-05-17. Template: new Date('2026-05-17').toISOString()"2026-05-16T16:00:00Z" for a UTC-8 user. Visible byline shows “May 17”; JSON-LD says “May 16.”

How to spot it: ISO date string’s day in UTC differs from the visible byline’s day in your timezone.

4. CMS has two date fields, template uses only one

CMS has firstPublished and lastEdited. Template uses only firstPublished for both datePublished and dateModified. Or vice versa.

How to spot it: Check the template’s JSON-LD generator. If datePublished and dateModified derive from the same field, you’ll never get “updated” dates correctly.

5. Migrated articles have wrong dates

After a CMS migration, all datePublished got set to the migration date. Original publish dates lost.

How to spot it: All articles share datePublished matching when you migrated. Cross-reference with git log or your old CMS.

6. Visible byline reads dateModified, JSON-LD reads datePublished (or reverse)

Byline says “Updated May 22.” JSON-LD datePublished says May 17. These reflect different fields but readers see only “May 22”; Google reads “May 17” as the publication date. Both correct individually, but inconsistent.

How to spot it: Inspect byline. Match it to which JSON-LD field it should correspond to. If byline says “Published,” it should match datePublished. If “Updated,” match dateModified.

Shortest path to fix

Step 1: Establish the data model

Each article needs two distinct dates in source:

---
publishedAt: 2024-03-08T00:00:00Z   # immutable; never changes
modifiedAt: 2026-05-22T00:00:00Z    # changes ONLY on meaningful edits
---

Use ISO 8601 with explicit timezone (Z = UTC). One single canonical timezone across CMS, template, byline, and JSON-LD.

Step 2: Fix the template’s JSON-LD generator

---
const { article } = Astro.props;
const published = new Date(article.publishedAt).toISOString();
const modified = article.modifiedAt
  ? new Date(article.modifiedAt).toISOString()
  : published;  // fallback to published; NOT to build time
---
<script type="application/ld+json" set:html={JSON.stringify({
  "@context": "https://schema.org",
  "@type": "Article",
  "datePublished": published,
  "dateModified": modified,
  // ...
})}></script>

Never new Date() at build time.

Step 3: Align the byline

<p class="byline">
  Published {formatDate(article.publishedAt)}
  {article.modifiedAt && article.modifiedAt > article.publishedAt && (
    <span>• Updated {formatDate(article.modifiedAt)}</span>
  )}
</p>

Show both. Make clear which is which.

Step 4: Establish a “meaningful edit” rule

Document team-wide:

  • Typos / minor wording: don’t bump modifiedAt.
  • Adding/removing a section, updating data, fixing factual error: bump modifiedAt.
  • Republishing as new article: change publishedAt AND keep modifiedAt the same.

This prevents modifiedAt from drifting.

Step 5: Backfill from git history if dates are wrong

# For each article, find the first commit that added the file
for f in src/content/articles/en/*.mdx; do
  first_commit=$(git log --diff-filter=A --follow --format=%aI -- "$f" | tail -1)
  echo -e "$f\t$first_commit"
done

Use the first-commit date as publishedAt for articles where the current value is wrong (e.g., a migration date).

Step 6: Audit dates in CI

// scripts/check-dates.mjs
import fs from 'node:fs';
import matter from 'gray-matter';

const errors = [];

for (const file of fs.readdirSync('src/content/articles/en')) {
  const { data } = matter(fs.readFileSync(`src/content/articles/en/${file}`, 'utf8'));
  const p = new Date(data.publishedAt);
  const m = data.modifiedAt ? new Date(data.modifiedAt) : null;

  if (isNaN(p)) errors.push(`Invalid publishedAt: ${file}`);
  if (m && isNaN(m)) errors.push(`Invalid modifiedAt: ${file}`);
  if (m && m < p) errors.push(`modifiedAt before publishedAt: ${file}`);
  if (m && Math.abs(m - p) < 60000) errors.push(`Suspicious: modifiedAt == publishedAt within minute: ${file}`);
}

if (errors.length) { console.error(errors.join('\n')); process.exit(1); }

Step 7: Re-request indexing

Use Search Console → URL Inspection on a few articles to trigger re-crawl. Watch for the Article rich result to reappear over 1-2 weeks.

When this is not on you

Google may derive a date from URL path, page content, or external signals when JSON-LD is contradictory. You can’t fully control which date Google shows in SERP — only what you provide.

Easy to misdiagnose as

Bumping dateModified on every deploy hoping for a freshness signal. Google can detect this pattern (all articles “modified” on the same day repeatedly) and discount the date entirely. Don’t game it.

Prevention

  • Never set dateModified from build time; only on real content edits.
  • Use UTC (or a single editorial timezone) consistently across CMS, template, byline, and JSON-LD.
  • CI check that asserts dateModified >= datePublished and both are valid ISO 8601.
  • Document the “meaningful edit” rule team-wide.
  • When backfilling old articles, use git history as the source of truth for original publish dates.

FAQ

  • Can dateModified be earlier than datePublished? No — that’s invalid and Google will warn.
  • Should I update dates when fixing typos? No — only for substantive edits that change information.

Tags: #SEO #Troubleshooting #Debug #Structured data #Schema date #JSON-LD