Investigate time zone handling in permalink date formatting
Description
There's a potential inconsistency in how time zones are handled in permalink date formatting. The current implementation uses the local time zone when formatting dates in permalinks:
date = p.PostDate().In(time.Local)
This means that the same content will generate different permalinks depending on the local time zone of the machine running the code. For example, a post with a UTC date of February 3 will have a permalink with "02/03" in UTC time zones, but "02/04" in UTC+9 (Japan) time zones.
Investigation Needed
- Determine how Jekyll handles time zones in permalinks. Does it use UTC consistently, or does it also use the local time zone?
- Decide whether gojekyll should match Jekyll's behavior or adopt a more consistent approach (e.g., always using UTC).
Current Workaround
The tests have been updated to expect dates in the local time zone, but this is just a temporary fix. The real solution depends on what behavior we want to standardize on.
Potential Solutions
- If Jekyll uses UTC: Modify
permalinks.goto usedate = p.PostDate().UTC()instead ofdate = p.PostDate().In(time.Local) - If Jekyll uses local time: Keep the current implementation but document this behavior clearly
- Make the time zone configurable in the site configuration
Related Files
pages/permalinks.go- Contains the current implementationpages/permalinks_test.go- Contains tests that were updated to accommodate local time zone
Investigation Results: Timezone Handling in Permalinks
I've conducted empirical testing to compare how Jekyll and gojekyll handle timezones in permalink generation. TL;DR: Gojekyll does not currently honor the timezone config setting for permalinks, while Jekyll does.
Test Setup
Created two test posts with timezone-aware dates that fall near midnight:
- Morning post:
2023-02-03 02:00:00 +0000(2 AM UTC on Feb 3) - Evening post:
2023-02-03 23:00:00 +0000(11 PM UTC on Feb 3)
These dates shift to different calendar days when converted to different timezones, making discrepancies obvious.
Test Results
System timezone: CST (UTC+8)
Test 1: No timezone config (system default)
| Post | Date (UTC) | Jekyll | Gojekyll | Match? |
|---|---|---|---|---|
| Morning | 02:00 UTC | 2023/02/03 |
2023/02/03 |
✅ |
| Evening | 23:00 UTC | 2023/02/04 |
2023/02/04 |
✅ |
Both systems convert to system timezone (CST/UTC+8) and match.
Test 2: timezone: UTC ← Critical Test
| Post | Date (UTC) | Jekyll | Gojekyll | Match? |
|---|---|---|---|---|
| Morning | 02:00 UTC | 2023/02/03 |
2023/02/03 |
✅ |
| Evening | 23:00 UTC | 2023/02/03 |
2023/02/04 |
❌ |
Jekyll correctly keeps both posts on Feb 3 (since they're already in UTC). Gojekyll incorrectly converts the evening post to Feb 4 (using system CST timezone instead of configured UTC).
Test 3: timezone: America/New_York (UTC-5) ← Critical Test
| Post | Date (UTC) | Jekyll | Gojekyll | Match? |
|---|---|---|---|---|
| Morning | 02:00 UTC | 2023/02/02 |
2023/02/03 |
❌ |
| Evening | 23:00 UTC | 2023/02/03 |
2023/02/04 |
❌ |
Jekyll correctly converts to EST:
- 02:00 UTC = Feb 2, 9:00 PM EST →
2023/02/02✓ - 23:00 UTC = Feb 3, 6:00 PM EST →
2023/02/03✓
Gojekyll incorrectly ignores the config and uses system CST timezone instead.
Test 4: timezone: Asia/Tokyo (UTC+9)
| Post | Date (UTC) | Jekyll | Gojekyll | Match? |
|---|---|---|---|---|
| Morning | 02:00 UTC | 2023/02/03 |
2023/02/03 |
✅ |
| Evening | 23:00 UTC | 2023/02/04 |
2023/02/04 |
✅ |
Both match, but this is coincidental (CST is UTC+8, close to JST UTC+9).
Root Cause Analysis
How Jekyll handles timezones:
- Reads
timezonefrom config - Sets
TZenvironment variable, affecting all Ruby time operations - Parses post dates and converts to configured timezone using
Time.parse(input).localtime - Uses converted date for permalink placeholders (
:year,:month,:day)
How gojekyll currently handles timezones:
- Has
setTimeZone()insite/build.go:50that setsTZenvironment variable fromtimezoneconfig - However, this is only called in
Write()(site/write.go:17), which happens after permalinks are computed - For permalinks,
pages/permalinks.go:52-61usesPermalinkTimezoneconfig (frompermalink_timezoneYAML key) - If
permalink_timezoneis not set, falls back totime.Local(whatever timezone was in effect when the process started) - The
timezoneconfig is essentially ignored for permalink generation
Code evidence from pages/permalinks.go:52-62:
loc := time.Local // Uses process start timezone
if tzName := p.site.Config().PermalinkTimezone; tzName != "" { // Only checks permalink_timezone
l, err := time.LoadLocation(tzName)
if err != nil {
log.Warn("Could not load timezone %q for permalink: %s. Using local time zone instead.", tzName, err)
} else {
loc = l
}
}
date := p.PostDate().In(loc) // Convert date to chosen timezone
The code looks for PermalinkTimezone (from permalink_timezone: in config), not Timezone (from timezone: in config).
Why the Current Implementation is Problematic
- Jekyll incompatibility: Jekyll users expect
timezone:config to affect permalinks - Inconsistent behavior: The same site generates different URLs depending on build machine's timezone
- Documentation gap: The
permalink_timezoneconfig option isn't mentioned in Jekyll docs (because it doesn't exist in Jekyll) - Surprising behavior: Users who set
timezone: UTCfor consistency will be surprised that permalinks still use their local timezone
Recommendation
Option 1 (Preferred): Match Jekyll behavior
- Modify
pages/permalinks.goto checks.site.Config().Timezonefirst, then fall back toPermalinkTimezone, thentime.Local - This makes gojekyll Jekyll-compatible while preserving the
permalink_timezoneescape hatch for users who want different behavior
Option 2: Keep current behavior but document it
- Document that gojekyll requires explicit
permalink_timezone:config - Note the incompatibility with Jekyll in README
- This is less user-friendly but simpler to implement
I recommend Option 1 to maximize Jekyll compatibility and match user expectations.
Test Script
The full test script is available and can be run to reproduce these results:
# Script location: /tmp/test-timezone-permalinks.sh
# Creates minimal test sites and builds them with both Jekyll and gojekyll
# Shows side-by-side comparison of generated permalinks