Site search

Search IndexLane

Type at least two characters to search.

    Technical note

    Keep dates in structured data accurate

    Use real publication, update, offer, and event dates in your structured data. Find expired values and stop templates from inventing a new date on every request.

    By NikoPublished October 1, 2026Read 5 min

    A structured-data validator can accept a date that is wrong for the page. Check the actual offer, article, or event as well as the markup: a sale may have expired, an event may have moved, or a template may be claiming that an unchanged article was updated today.

    Google's structured-data guidelines require information that is current and matches the page. Use the properties supported by the relevant Search feature; a valid Schema.org field is not automatically used in every rich result.

    Check the dates that matter for each page

    Use these checks when reviewing the markup:

    Content typeRelevant propertyAccuracy check
    Product offervalidFrom and priceValidUntil or validThrough, when applicableThe sale price and visible offer are actually active for that period
    ReviewdatePublishedThe date matches the visible review publication date
    ArticledatePublished, dateModifiedDates match substantive publication and update history
    Local businessopeningHoursSpecificationHours and validity periods match what customers can use
    EventstartDate, endDateTime, timezone, rescheduling, and cancellation state are current

    Find offer dates that need review

    In a Laravel e-commerce application, the priceValidUntil property is often set once when the product is created and never updated. A scheduled command can flag products where this date is approaching or has passed:

    namespace App\Console\Commands;
    
    use App\Models\Product;
    use Illuminate\Console\Command;
    
    class CheckPriceValidity extends Command
    {
        protected $signature = 'seo:check-price-validity';
    
        public function handle()
        {
            $expiring = Product::where('price_valid_until', '<', now()->addDays(7))
                ->where('price_valid_until', '>', now())
                ->get();
    
            $expired = Product::where('price_valid_until', '<', now())->get();
    
            $this->info("Expiring soon: {$expiring->count()} products");
            $this->info("Expired: {$expired->count()} products");
    
            return self::SUCCESS;
        }
    }
    

    Keeping article dates accurate in Go

    Use the article's actual publication and content-update dates. Page views, routine deployments, and unrelated database changes should not advance its modification date. In this example, ContentUpdatedAt is a nullable timestamp maintained when the article itself changes:

    type ArticleSchema struct {
        Context       string `json:"@context"`
        Type          string `json:"@type"`
        Headline      string `json:"headline"`
        DatePublished string `json:"datePublished"`
        DateModified  string `json:"dateModified,omitempty"`
    }
    
    func BuildArticleSchema(article Article) ArticleSchema {
        schema := ArticleSchema{
            Context:       "https://schema.org",
            Type:          "Article",
            Headline:      article.Title,
            DatePublished: article.PublishedAt.Format(time.RFC3339),
        }
        if article.ContentUpdatedAt != nil && article.ContentUpdatedAt.After(article.PublishedAt) {
            schema.DateModified = article.ContentUpdatedAt.Format(time.RFC3339)
        }
        return schema
    }
    

    Google's Article documentation recommends ISO 8601 values with timezone information. If the article changes substantively and the page shows an updated date, dateModified should reflect that same event.

    Article date accuracy in Next.js

    Next.js blog templates can serialise the article's dates from the CMS into JSON-LD. Keep the source fields explicit rather than deriving them during rendering:

    const articleJsonLd = {
      '@context': 'https://schema.org',
      '@type': 'Article',
      headline: post.title,
      datePublished: post.publishedAt,
      ...(post.substantiveUpdatedAt
        ? { dateModified: post.substantiveUpdatedAt }
        : {}),
    };
    

    An ORM's generic updatedAt can advance for workflow flags, imports, or relation changes. If that timestamp does not mean "visible article changed," add a separate substantive-content timestamp rather than exposing it directly:

    // Prisma schema
    model Post {
      id          String   @id @default(cuid())
      title       String
      body        String
      createdAt   DateTime @default(now())
      updatedAt   DateTime @updatedAt
      substantiveUpdatedAt DateTime?
      publishedAt DateTime @default(now())
    }
    

    Find inconsistent dates in a URL sample

    A crawler can extract date fields and produce review candidates. It cannot decide whether a change was substantive without visible content and CMS history. An old article without dateModified is not automatically wrong:

    import json, re, requests
    from datetime import datetime
    
    def parse_iso8601(value):
        return datetime.fromisoformat(value.replace('Z', '+00:00'))
    
    def audit_schema_dates(sitemap_url):
        response = requests.get(sitemap_url, timeout=10)
        response.raise_for_status()
        urls = re.findall(r'<loc>(.*?)</loc>', response.text)
        stale_pages = []
    
        for url in urls[:200]:  # sample
            page = requests.get(url, timeout=10)
            page.raise_for_status()
            blocks = re.findall(
                r'<script type="application/ld\+json">(.*?)</script>',
                page.text, re.DOTALL
            )
            for block in blocks:
                data = json.loads(block)
                nodes = data.get('@graph', [data]) if isinstance(data, dict) else data
                for node in nodes:
                    if not isinstance(node, dict):
                        continue
                    published = node.get('datePublished')
                    modified = node.get('dateModified')
                    if published and modified and parse_iso8601(modified) < parse_iso8601(published):
                        stale_pages.append((url, 'dateModified precedes datePublished'))
    
        return stale_pages
    

    Stop using the request time as an update date

    Many CMS templates generate schema with new Date().toISOString() or Carbon::now() at render time, meaning every page claims to have changed on every request. That contradicts the visible content history and makes the metadata unreliable.

    Read the date from content history. Keep the real modification date for an unchanged article, or omit an optional modification field when no reliable value exists. After deployment, compare a few live pages with their CMS records and visible dates.

    Related notes

    How to verify Googlebot requests in your logs

    Check the source IP of a request claiming to be Googlebot. Use Google’s published ranges or reverse-and-forward DNS before trusting crawler totals.

    Read article →

    Use stable @id values in Schema.org markup

    Give an author, publisher, or article one consistent identifier so your JSON-LD blocks describe the same entities across pages.

    Read article →