Skip to content

Commit

Permalink
Support both tzinfo v1 and v2. Support non-half hour offsets.
Browse files Browse the repository at this point in the history
Use the defined offset (in a v1 and v2 compatible way) instead of
determining the offset from the difference between local time and UTC.

Change the minutes calculation to allow for time zones that don't use
hour or half hour offsets (e.g. Australia/Eucla's UTC+08:45).

Resolves jekyll#8516.
  • Loading branch information
philr committed Nov 14, 2021
1 parent d81cc40 commit 7781342
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 68 deletions.
9 changes: 2 additions & 7 deletions Gemfile
Expand Up @@ -32,6 +32,8 @@ group :test do
gem "test-theme", :path => File.expand_path("test/fixtures/test-theme", __dir__)
gem "test-theme-skinny", :path => File.expand_path("test/fixtures/test-theme-skinny", __dir__)
gem "test-theme-symlink", :path => File.expand_path("test/fixtures/test-theme-symlink", __dir__)
gem "tzinfo", ">= 1", "< 3"
gem "tzinfo-data"

if RUBY_ENGINE == "jruby"
gem "http_parser.rb", "~> 0.6.0"
Expand Down Expand Up @@ -81,13 +83,6 @@ group :jekyll_optional_dependencies do
gem "liquid-c", "~> 4.0"
gem "yajl-ruby", "~> 1.4"
end

# Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem
# and associated library
platforms :jruby, :mswin, :mingw, :x64_mingw do
gem "tzinfo", "~> 1.2"
gem "tzinfo-data"
end
end

#
Expand Down
20 changes: 6 additions & 14 deletions docs/_docs/installation/windows.md
Expand Up @@ -121,22 +121,14 @@ While 'new' blogs created with Jekyll v3.4 and greater, will have the following
sites *will* have to update their `Gemfile` (and installed gems) to enable development on Windows:

```ruby
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
# Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem
# and associated library.
platforms :mingw, :x64_mingw, :mswin, :jruby do
gem "tzinfo", ">= 1", "< 3"
gem "tzinfo-data"
end
```

<div class="note warning">
<h5>TZInfo 2.0 incompatibility</h5>
<p>
Version 2.0 of the TZInfo library has introduced a change in how timezone offsets are calculated.
This will result in incorrect date and time for your posts when the site is built with Jekyll 3.x on Windows.
</p>
<p>
We therefore recommend that you lock the Timezone library to version 1.2 and above by listing
<code>gem 'tzinfo', '~> 1.2'</code> in your <code>Gemfile</code>.
</p>
</div>

## Auto Regeneration

Jekyll uses the `listen` gem to watch for changes when the `--watch` switch is specified during a build or serve.
Expand Down
2 changes: 1 addition & 1 deletion lib/jekyll/commands/new.rb
Expand Up @@ -92,7 +92,7 @@ def gemfile_contents
# Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem
# and associated library.
platforms :mingw, :x64_mingw, :mswin, :jruby do
gem "tzinfo", "~> 1.2"
gem "tzinfo", ">= 1", "< 3"
gem "tzinfo-data"
end
Expand Down
63 changes: 17 additions & 46 deletions lib/jekyll/utils/win_tz.rb
Expand Up @@ -11,64 +11,35 @@ module WinTZ
# timezone - the IANA Time Zone specified in "_config.yml"
#
# Returns a string that ultimately re-defines ENV["TZ"] in Windows
def calculate(timezone)
def calculate(timezone, now = Time.now)
External.require_with_graceful_fail("tzinfo") unless defined?(TZInfo)
tz = TZInfo::Timezone.get(timezone)
difference = Time.now.to_i - tz.now.to_i

#
# Use period_for_utc and utc_total_offset instead of
# period_for and observed_utc_offset for compatibility with tzinfo v1.
offset = tz.period_for_utc(now.getutc).utc_total_offset

#
# POSIX style definition reverses the offset sign.
# e.g. Eastern Standard Time (EST) that is 5Hrs. to the 'west' of Prime Meridian
# is denoted as:
# EST+5 (or) EST+05:00
# Reference: http://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html
sign = difference.negative? ? "-" : "+"
offset = sign == "-" ? "+" : "-" unless difference.zero?
#
# convert the difference (in seconds) to hours, as a rational number, and perform
# a modulo operation on it.
modulo = modulo_of(rational_hour(difference))
sign = offset.positive? ? "-" : "+"

rational_hours = offset.abs.to_r / 3600
hours = rational_hours.to_i
minutes = ((rational_hours - hours) * 60).to_i

#
# Format the hour as a two-digit number.
# Establish the minutes based on modulo expression.
hh = format("%<hour>02d", :hour => absolute_hour(difference).ceil)
mm = modulo.zero? ? "00" : "30"
# Format the hours and minutes as two-digit numbers.
time = format("%<hours>02d:%<minutes>02d", hours: hours, minutes: minutes)

Jekyll.logger.debug "Timezone:", "#{timezone} #{offset}#{hh}:#{mm}"
Jekyll.logger.debug "Timezone:", "#{timezone} #{sign}#{time}"
#
# Note: The 3-letter-word below doesn't have a particular significance.
"WTZ#{sign}#{hh}:#{mm}"
end

private

# Private: Convert given seconds to an hour as a rational number.
#
# seconds - supplied as an integer, it is converted to a rational number.
# 3600 - no. of seconds in an hour.
#
# Returns a rational number.
def rational_hour(seconds)
seconds.to_r / 3600
end

# Private: Convert given seconds to an hour as an absolute number.
#
# seconds - supplied as an integer, it is converted to its absolute.
# 3600 - no. of seconds in an hour.
#
# Returns an integer.
def absolute_hour(seconds)
seconds.abs / 3600
end

# Private: Perform a modulo operation on a given fraction.
#
# fraction - supplied as a rational number, its numerator is divided
# by its denominator and the remainder returned.
#
# Returns an integer.
def modulo_of(fraction)
fraction.numerator % fraction.denominator
"WTZ#{sign}#{time}"
end
end
end
Expand Down
31 changes: 31 additions & 0 deletions test/test_win_tz.rb
@@ -0,0 +1,31 @@
# frozen_string_literal: true

require "helper"

class TestWinTz < JekyllUnitTest
[["America/New_York", "WTZ+05:00"], ["Europe/Paris", "WTZ-01:00"]].each do |timezone, expected|
should "use base offset in winter for #{timezone}" do
result = Jekyll::Utils::WinTZ.calculate(timezone, Time.utc(2021, 1, 1))
assert_equal expected, result
end
end

[["America/New_York", "WTZ+04:00"], ["Europe/Paris", "WTZ-02:00"]].each do |timezone, expected|
should "apply DST in summer for #{timezone}" do
result = Jekyll::Utils::WinTZ.calculate(timezone, Time.utc(2021, 7, 1))
assert_equal expected, result
end
end

[["Australia/Eucla", "WTZ-08:45"], ["Pacific/Marquesas", "WTZ+09:30"]].each do |timezone, expected|
should "handle non zero minutes for #{timezone}" do
result = Jekyll::Utils::WinTZ.calculate(timezone, Time.utc(2021, 1, 1))
assert_equal expected, result
end
end

should "return zero for UTC" do
result = Jekyll::Utils::WinTZ.calculate("UTC")
assert_equal "WTZ+00:00", result
end
end

0 comments on commit 7781342

Please sign in to comment.