# Blog Post "Last Updated" Date System Review

**Last Updated:** 2026-02-15

**Review Date:** 2026-01-13

**Automation Status:** ✅ Fully automated via git pre-commit hooks (implemented 2026-01-13)

**Detection Method:** Content-based detection (updated 2026-01-13) - Dates update only when actual content changes (title, HTML, excerpt, meta, featured image), not for metadata or formatting changes.

Comprehensive review of how "last updated" dates are handled across blog posts, cards, schema markup, and meta tags.

## Executive Summary

The blog post date system is **fully automated** via git pre-commit hooks. Dates are automatically synced and updated when blog post JSON files are committed.

### Current Status

✅ **Working Correctly:**

- Date storage format (ISO 8601)
- Schema markup (`dateModified` in Article and WebPage)
- Meta tags (`article:modified_time`)
- UI display logic (7+ days threshold)
- Date validation scripts
- **Automated date sync via git hooks**
- **Automated content change detection**

✅ **Automated:**

- Dates sync automatically on commit via git pre-commit hook
- Content changes detected automatically via git pre-commit hook (including FAQ changes)
- No manual intervention required for date updates

## How It Works

### 1. Date Storage

**Location:** `v2/data/blog/posts/{category}/{slug}.json`

**Fields:**

- `publication_date`: Original publication date (never changes)
- `modified_date`: Last significant content update date

**Format:** ISO 8601 (`YYYY-MM-DDTHH:i:s+00:00`). Both `Z` and `+00:00` are supported by `parse_iso8601_date()`; new posts use `+00:00` per `create-new-blog-post.php`.

**Example:**

```json
{
  "publication_date": "2023-06-18T11:04:00+00:00",
  "modified_date": "2026-01-10T21:26:17+00:00"
}
```

### 2. UI Display

**PostHeader Component** (`v2/components/blog/PostHeader.php`):

- Shows updated date if `modified_date` is **7+ days** after `publication_date`
- Format: "Zuletzt aktualisiert: 10. Januar 2026"
- Uses `should_show_updated_date()` helper function

**PostCard Component** (`v2/components/blog/PostCard.php`):

- Shows updated date if `modified_date` is **7+ days** after `publication_date`
- Only for default variant (not compact/related posts)
- Same format as PostHeader

**Logic:**

```php
function should_show_updated_date($publication_date, $modified_date) {
    // Calculate difference in days
    $diff = $mod_date->diff($pub_date);
    $days_diff = $diff->days;

    // Show if modified date is 7+ days after publication date
    return $days_diff >= 7;
}
```

### 3. Schema.org Markup

**Location:** `v2/config/blog-schema-generator.php`

**Article Schema:**

```php
$article = [
    '@type' => 'Article',
    'datePublished' => $publication_date,
    'dateModified' => $modified_date,  // Uses modified_date from JSON
    // ...
];
```

**WebPage Schema:**

```php
$webpage = [
    '@type' => 'WebPage',
    'dateModified' => $modified_date,  // Uses modified_date from JSON
    // ...
];
```

**Fallback:** If `modified_date` is missing, falls back to `publication_date`:

```php
$modified_date = $data['modified_date'] ?? $publication_date;
```

### 4. Meta Tags

**Location:** `v2/config/blog-meta-generator.php`

**Article Meta Tags:**

```php
if (!empty($data['modified_date'])) {
    $metaTags[] = [
        'tag' => 'meta',
        'property' => 'article:modified_time',
        'content' => $data['modified_date']
    ];
}
```

**Published Time:**

```php
if (!empty($data['publication_date'])) {
    $metaTags[] = [
        'tag' => 'meta',
        'property' => 'article:published_time',
        'content' => $data['publication_date']
    ];
}
```

## What "Updating a Post" Means

### Definition

**"Updating a post"** means making **significant content changes** that warrant updating the `modified_date`:

✅ **Update `modified_date` When:**

- Post content HTML is modified significantly
- Title is changed (not just capitalization)
- Excerpt is rewritten
- Meta description is updated
- Featured image is changed
- FAQs are added, removed, or modified (questions or answers)
- Content structure changes (new sections, major rewrites)

❌ **Do NOT Update `modified_date` When:**

- Related posts are added/removed
- Internal links are updated
- Topics/clusters are reassigned
- Reading time is recalculated
- Minor formatting changes (spacing, punctuation)
- Metadata-only changes (author, category)

### How Dates Are Determined

**Current System (Automated - Content-Based Detection):**

1. **Git Pre-Commit Hook**: Automatically runs when blog JSON files are staged and committed
2. **Content Change Detection Hook** (`scripts/blog/pre-commit-detect-changes.sh`):
   - Detects staged blog JSON files
   - Compares content hash (title, HTML, excerpt, meta, featured image) with stored `_content_hash`
   - Updates `modified_date` and `_content_hash` **only if content changed**
   - Stages updated files automatically
3. **Manual Scripts** (for bulk operations or manual updates):
   - `detect-content-changes.php --update` - Manual content change detection
   - `sync-dates-from-filemtime.php` - Manual date sync (for bulk operations only)
   - `validate-post-dates.php` - Date validation

**Automatic (Content-Based):**

- ✅ Dates automatically update when committing **content changes** (title, HTML, excerpt, meta, featured image)
- ✅ Dates do NOT update for metadata changes (related_posts, topics, category)
- ✅ Dates do NOT update for formatting changes (JSON whitespace)
- ✅ No manual steps required for normal content edits

**What Triggers Date Updates:**

- ✅ Title changes
- ✅ Content HTML changes
- ✅ Excerpt changes
- ✅ Meta description changes
- ✅ Featured image changes
- ✅ FAQ changes (questions, answers – affects FAQPage schema and user display)

**What Does NOT Trigger Date Updates:**

- ❌ Related posts changes
- ❌ Topics changes
- ❌ Category changes
- ❌ Formatting changes
- ❌ Field additions

## Date Update Scripts

### 1. Detect Content Changes

**Script:** `v2/scripts/blog/detect-content-changes.php`

**Purpose:** Detect when content has changed and needs date update

**Usage:**

```bash
# Dry run (detect only)
php v2/scripts/blog/detect-content-changes.php --verbose

# Update dates for changed posts
php v2/scripts/blog/detect-content-changes.php --update
```

**How it works:**

- Creates hash from: title, content HTML, excerpt, meta description, featured image src
- Compares current hash with stored `_content_hash` in JSON
- If hash changed, updates `modified_date` to file modification time
- Stores new hash in `_content_hash` field

**Limitations:**

- Requires `_content_hash` field to be present (first run initializes it)
- Only detects changes to tracked fields (title, content, excerpt, meta, image)
- Does NOT detect changes to other fields (topics, links, etc.)

### 2. Sync Dates from File Modification Time

**Script:** `v2/scripts/blog/sync-dates-from-filemtime.php`

**Purpose:** Sync `modified_date` with actual file modification time

**Usage:**

```bash
# Dry run (show what would change)
php v2/scripts/blog/sync-dates-from-filemtime.php --dry-run --verbose

# Sync dates (only if > 1 day difference)
php v2/scripts/blog/sync-dates-from-filemtime.php

# Force sync all dates
php v2/scripts/blog/sync-dates-from-filemtime.php --force
```

**How it works:**

- Gets file modification time using `filemtime()`
- Converts to ISO 8601 format
- Updates `modified_date` if:
  - Date is missing, OR
  - Difference is > 1 day (unless `--force` is used)
- Ensures `modified_date` >= `publication_date`

**Limitations:**

- Only updates if difference is significant (> 1 day)
- May miss recent edits if run immediately after editing
- File modification time reflects JSON file changes, not content changes

### 3. Fix Date Formats

**Script:** `v2/scripts/blog/fix-post-dates.php`

**Purpose:** Normalize date formats across all posts

**Usage:**

```bash
php v2/scripts/blog/fix-post-dates.php [--dry-run] [--verbose]
```

**What it fixes:**

- Removes microseconds from dates
- Ensures UTC timezone (`+00:00`)
- Sets missing `modified_date` to file modification time
- Ensures `modified_date` >= `publication_date`

### 4. Validate Dates

**Script:** `v2/scripts/blog/validate-post-dates.php`

**Purpose:** Validate all dates meet requirements

**Usage:**

```bash
php v2/scripts/blog/validate-post-dates.php [--verbose]
```

**What it validates:**

- All posts have `modified_date` set
- All dates are valid ISO 8601 format
- `modified_date` >= `publication_date`

## Current Status & Automation

### ✅ Issue 1: Dates Not Automatically Synced - RESOLVED

**Status:** **RESOLVED** - Git pre-commit hook automatically detects content changes and updates dates

**Solution Implemented (Updated 2026-01-13):**

1. **Git Pre-Commit Hook**: Automatically detects content changes when committing blog post changes
2. **Content Change Detection** (`scripts/blog/pre-commit-detect-changes.sh`):
   - Runs automatically on commit
   - Compares content hash (title, HTML, excerpt, meta, featured image) with stored hash
   - Updates `modified_date` and `_content_hash` **only if content changed**
   - Stages updated files automatically

**How It Works:**

- When you stage and commit blog JSON files, the git hook:
  1. Detects staged blog files
  2. Compares content hash with stored hash
  3. Updates dates/hashes **only if content changed** (not for metadata/formatting changes)
  4. Stages updated files automatically
  5. Continues with commit

**Content-Based Detection:**

- ✅ Dates update for content changes (title, HTML, excerpt, meta, featured image)
- ❌ Dates do NOT update for metadata changes (related_posts, topics, category)
- ❌ Dates do NOT update for formatting changes (JSON whitespace)

**No manual steps required** - dates update automatically for content changes only!

### ✅ Issue 2: Content Hash Detection Limitations - BY DESIGN

**Status:** **BY DESIGN** - Content hash detection intentionally tracks only content fields

**Current Behavior:**

- `detect-content-changes.php` tracks: title, content HTML, excerpt, meta description, featured image, FAQs
- Changes to metadata fields (topics, related_posts, etc.) do NOT trigger date updates
- This is intentional - dates should reflect content changes, not metadata changes

**Impact:**

- ✅ Dates accurately reflect when post content changed
- ✅ Dates do NOT update for metadata-only changes (correct behavior)
- ✅ Dates do NOT update for formatting changes (correct behavior)

**Recommendation:**

- ✅ Current behavior is correct - dates should reflect content changes only
- ✅ Documented which fields trigger automatic updates
- ✅ Metadata changes intentionally excluded from date updates

### Issue 3: 7-Day Display Threshold

**Problem:** Updated dates only show if 7+ days after publication.

**Impact:**

- Recent updates (< 7 days) won't show "last updated" badge
- Users may not see that content was recently updated

**Recommendation:**

- Current behavior is intentional (avoids clutter)
- Consider reducing threshold to 3-5 days if needed
- Document threshold in team guidelines

## Verification Checklist

### For Each Blog Post:

- [ ] `modified_date` is set in JSON file
- [ ] `modified_date` is valid ISO 8601 format (`YYYY-MM-DDTHH:i:s+00:00`)
- [ ] `modified_date` >= `publication_date`
- [ ] Schema markup includes `dateModified` field
- [ ] Meta tags include `article:modified_time`
- [ ] UI shows "last updated" if 7+ days after publication
- [ ] Date matches actual last content update

### Testing Commands:

```bash
# Validate all dates
php v2/scripts/blog/validate-post-dates.php

# Test specific post
php v2/scripts/blog/test-date-implementation.php --post=your-slug

# Check for content changes
php v2/scripts/blog/detect-content-changes.php --verbose

# Sync dates from file modification time
php v2/scripts/blog/sync-dates-from-filemtime.php --dry-run
```

## Best Practices

### When Editing Blog Posts:

**Automated (Recommended):**

1. **Make content changes** to JSON file
2. **Stage and commit** - git hook automatically:
   - Syncs dates from file modification times
   - Detects content changes and updates dates/hashes
   - Stages updated files
3. **Commit completes** - dates are automatically up-to-date!

**Manual (If needed):**

If you need to update dates manually (e.g., bypassing git hook):

```bash
# Sync dates manually
php v2/scripts/blog/sync-dates-from-filemtime.php

# Verify dates
php v2/scripts/blog/validate-post-dates.php
```

### Regular Maintenance:

**Weekly:**

```bash
# Validate all dates
php v2/scripts/blog/validate-post-dates.php

# Detect content changes
php v2/scripts/blog/detect-content-changes.php --update
```

**Monthly:**

```bash
# Full audit
php v2/scripts/blog/audit-post-dates.php --verbose

# Sync dates if needed
php v2/scripts/blog/sync-dates-from-filemtime.php --dry-run
```

## Git Hook Automation

### Hook Files

- **Main hook**: `.git/hooks/pre-commit` - Calls blog content detection script
- **Content detection script**: `scripts/blog/pre-commit-detect-changes.sh` - Detects content changes
- **Manual sync script**: `scripts/blog/pre-commit-sync-dates.sh` - For manual bulk operations only

### How It Works

1. **Developer edits blog JSON file**
2. **Stages file**: `git add v2/data/blog/posts/...`
3. **Commits**: `git commit -m "..."`
4. **Pre-commit hook runs**:
   - Detects staged blog JSON files
   - Compares content hash (title, HTML, excerpt, meta, featured image) with stored hash
   - Updates `modified_date` and `_content_hash` **only if content changed**
   - Stages updated files if dates changed
5. **Commit proceeds** with updated dates (only if content changed)

**Important:** Dates update based on **content changes only**, not file modification times or metadata changes.

### Bypassing Hooks

If you need to bypass the hook (not recommended):

```bash
git commit --no-verify -m "Your commit message"
```

**Warning**: Dates will not be automatically synced if you bypass the hook.

### Troubleshooting

**Hook not running:**

- Check hook is executable: `ls -l .git/hooks/pre-commit`
- Make executable: `chmod +x .git/hooks/pre-commit scripts/blog/pre-commit-*.sh`

**Hook fails:**

- Check error output in terminal
- Review log files: `/tmp/blog-sync-dates.log`, `/tmp/blog-detect-changes.log`

## Related Documentation

- [Date Management Guide](guides/DATE_MANAGEMENT_GUIDE.md) - Complete guide with git hook details
- [Date Implementation Summary](DATE_IMPLEMENTATION_SUMMARY.md) - Implementation details
- [Blog Template Best Practices](BLOG_TEMPLATE_BEST_PRACTICES.md) - Best practices

## Scripts Reference

All scripts are located in `v2/scripts/blog/`:

- `audit-post-dates.php` - Audit all dates
- `fix-post-dates.php` - Fix date formats
- `sync-dates-from-filemtime.php` - Sync dates from file mtime
- `validate-post-dates.php` - Validate dates
- `detect-content-changes.php` - Detect content changes
- `test-date-implementation.php` - Test implementation

**Git Hook Scripts** (located in `scripts/blog/`):

- `pre-commit-detect-changes.sh` - Content change detection hook script (runs automatically)
- `pre-commit-sync-dates.sh` - Date sync script (manual use only, for bulk operations)
