Skip to content

Commit

Permalink
Support both tzinfo v1 and v2 alongwith non-half hour offsets. (#8880)
Browse files Browse the repository at this point in the history
Merge pull request 8880
  • Loading branch information
philr committed Dec 8, 2021
1 parent 956701f commit 9c9cf3e
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 62 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ group :jekyll_optional_dependencies do
# 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", ENV["TZINFO_VERSION"] if ENV["TZINFO_VERSION"]
gem "tzinfo-data"
end
end
Expand Down
12 changes: 12 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ build: off
environment:
BUNDLE_WITHOUT: "benchmark:development"
matrix:
- RUBY_FOLDER_VER: "26"
TZINFO_VERSION: "~> 1.2"
TEST_SUITE: "test"
- RUBY_FOLDER_VER: "26"
TZINFO_VERSION: "~> 2.0"
TEST_SUITE: "test"
- RUBY_FOLDER_VER: "26"
TEST_SUITE: "test"
- RUBY_FOLDER_VER: "26"
Expand All @@ -21,6 +27,12 @@ environment:
TEST_SUITE: "profile-docs"
- RUBY_FOLDER_VER: "26"
TEST_SUITE: "memprof"
- RUBY_FOLDER_VER: "26"
TZINFO_VERSION: "~> 1.2"
TEST_SUITE: "cucumber"
- RUBY_FOLDER_VER: "26"
TZINFO_VERSION: "~> 2.0"
TEST_SUITE: "cucumber"
- RUBY_FOLDER_VER: "26"
TEST_SUITE: "cucumber"

Expand Down
20 changes: 6 additions & 14 deletions docs/_docs/installation/windows.md
Original file line number Diff line number Diff line change
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
22 changes: 22 additions & 0 deletions features/site_configuration.feature
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,28 @@ Feature: Site configuration
And I should see "Post Layout: <p>content for entry1.</p>\n built at 2013-04-09T09:22:00-10:00" in "_site/2013/04/09/entry1.html"
And I should see "Post Layout: <p>content for entry2.</p>\n built at 2013-04-09T13:14:00-10:00" in "_site/2013/04/09/entry2.html"

Scenario: Generate proper dates with explicitly set timezone (using non-half hour offset )
Given I have a _layouts directory
And I have a page layout that contains "Page Layout: {{ site.posts.size }}"
And I have a post layout that contains "Post Layout: {{ content }} built at {{ page.date | date_to_xmlschema }}"
And I have an "index.html" page with layout "page" that contains "site index page"
And I have a configuration file with:
| key | value |
| timezone | Australia/Eucla |
And I have a _posts directory
And I have the following posts:
| title | date | layout | content |
| entry1 | 2013-04-09 23:22 +0400 | post | content for entry1. |
| entry2 | 2013-04-10 03:14 +0400 | post | content for entry2. |
When I run jekyll build
Then I should get a zero exit status
And the _site directory should exist
And I should see "Page Layout: 2" in "_site/index.html"
And the "_site/2013/04/10/entry1.html" file should exist
And the "_site/2013/04/10/entry2.html" file should exist
And I should see "Post Layout: <p>content for entry1.</p>\n built at 2013-04-10T04:07:00\+08:45" in "_site/2013/04/10/entry1.html"
And I should see "Post Layout: <p>content for entry2.</p>\n built at 2013-04-10T07:59:00\+08:45" in "_site/2013/04/10/entry2.html"

Scenario: Limit the number of posts generated by most recent date
Given I have a _posts directory
And I have a configuration file with:
Expand Down
2 changes: 1 addition & 1 deletion lib/jekyll/commands/new.rb
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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 |tz, expected|
should "use base offset in winter for #{tz}" do
result = Jekyll::Utils::WinTZ.calculate(tz, Time.utc(2021, 1, 1))
assert_equal expected, result
end
end

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

[["Australia/Eucla", "WTZ-08:45"], ["Pacific/Marquesas", "WTZ+09:30"]].each do |tz, expected|
should "handle non zero minutes for #{tz}" do
result = Jekyll::Utils::WinTZ.calculate(tz, 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 9c9cf3e

Please sign in to comment.