gojekyll icon indicating copy to clipboard operation
gojekyll copied to clipboard

Investigate time zone handling in permalink date formatting

Open osteele opened this issue 7 months ago • 1 comments

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

  1. Determine how Jekyll handles time zones in permalinks. Does it use UTC consistently, or does it also use the local time zone?
  2. 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

  1. If Jekyll uses UTC: Modify permalinks.go to use date = p.PostDate().UTC() instead of date = p.PostDate().In(time.Local)
  2. If Jekyll uses local time: Keep the current implementation but document this behavior clearly
  3. Make the time zone configurable in the site configuration

Related Files

  • pages/permalinks.go - Contains the current implementation
  • pages/permalinks_test.go - Contains tests that were updated to accommodate local time zone

osteele avatar May 09 '25 11:05 osteele

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: UTCCritical 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:

  1. Reads timezone from config
  2. Sets TZ environment variable, affecting all Ruby time operations
  3. Parses post dates and converts to configured timezone using Time.parse(input).localtime
  4. Uses converted date for permalink placeholders (:year, :month, :day)

How gojekyll currently handles timezones:

  1. Has setTimeZone() in site/build.go:50 that sets TZ environment variable from timezone config
  2. However, this is only called in Write() (site/write.go:17), which happens after permalinks are computed
  3. For permalinks, pages/permalinks.go:52-61 uses PermalinkTimezone config (from permalink_timezone YAML key)
  4. If permalink_timezone is not set, falls back to time.Local (whatever timezone was in effect when the process started)
  5. The timezone config 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

  1. Jekyll incompatibility: Jekyll users expect timezone: config to affect permalinks
  2. Inconsistent behavior: The same site generates different URLs depending on build machine's timezone
  3. Documentation gap: The permalink_timezone config option isn't mentioned in Jekyll docs (because it doesn't exist in Jekyll)
  4. Surprising behavior: Users who set timezone: UTC for consistency will be surprised that permalinks still use their local timezone

Recommendation

Option 1 (Preferred): Match Jekyll behavior

  • Modify pages/permalinks.go to check s.site.Config().Timezone first, then fall back to PermalinkTimezone, then time.Local
  • This makes gojekyll Jekyll-compatible while preserving the permalink_timezone escape 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

osteele avatar Nov 14 '25 17:11 osteele