From 9c9cf3e82bd436c71d1ee322482b9abaed8b7e26 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Wed, 8 Dec 2021 11:35:42 +0000 Subject: [PATCH] Support both tzinfo v1 and v2 alongwith non-half hour offsets. (#8880) Merge pull request 8880 --- Gemfile | 2 +- appveyor.yml | 12 ++++++ docs/_docs/installation/windows.md | 20 +++------ features/site_configuration.feature | 22 ++++++++++ lib/jekyll/commands/new.rb | 2 +- lib/jekyll/utils/win_tz.rb | 65 ++++++++--------------------- test/test_win_tz.rb | 31 ++++++++++++++ 7 files changed, 91 insertions(+), 63 deletions(-) create mode 100644 test/test_win_tz.rb diff --git a/Gemfile b/Gemfile index 70c1c553..a6190c93 100644 --- a/Gemfile +++ b/Gemfile @@ -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 diff --git a/appveyor.yml b/appveyor.yml index 24ea983c..db721048 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -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" @@ -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" diff --git a/docs/_docs/installation/windows.md b/docs/_docs/installation/windows.md index eacd92c6..c7d77ed0 100644 --- a/docs/_docs/installation/windows.md +++ b/docs/_docs/installation/windows.md @@ -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 ``` -
-
TZInfo 2.0 incompatibility
-

- 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. -

-

- We therefore recommend that you lock the Timezone library to version 1.2 and above by listing - gem 'tzinfo', '~> 1.2' in your Gemfile. -

-
- ## Auto Regeneration Jekyll uses the `listen` gem to watch for changes when the `--watch` switch is specified during a build or serve. diff --git a/features/site_configuration.feature b/features/site_configuration.feature index e9afc9b6..53d5b21f 100644 --- a/features/site_configuration.feature +++ b/features/site_configuration.feature @@ -291,6 +291,28 @@ Feature: Site configuration And I should see "Post Layout:

content for entry1.

\n built at 2013-04-09T09:22:00-10:00" in "_site/2013/04/09/entry1.html" And I should see "Post Layout:

content for entry2.

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

content for entry1.

\n built at 2013-04-10T04:07:00\+08:45" in "_site/2013/04/10/entry1.html" + And I should see "Post Layout:

content for entry2.

\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: diff --git a/lib/jekyll/commands/new.rb b/lib/jekyll/commands/new.rb index 150b4aa7..f85026ba 100644 --- a/lib/jekyll/commands/new.rb +++ b/lib/jekyll/commands/new.rb @@ -92,7 +92,7 @@ module Jekyll # 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 diff --git a/lib/jekyll/utils/win_tz.rb b/lib/jekyll/utils/win_tz.rb index 9c55c42e..d6ffad43 100644 --- a/lib/jekyll/utils/win_tz.rb +++ b/lib/jekyll/utils/win_tz.rb @@ -11,64 +11,35 @@ module Jekyll # 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)) - # - # Format the hour as a two-digit number. - # Establish the minutes based on modulo expression. - hh = format("%02d", :hour => absolute_hour(difference).ceil) - mm = modulo.zero? ? "00" : "30" + sign = offset.positive? ? "-" : "+" - Jekyll.logger.debug "Timezone:", "#{timezone} #{offset}#{hh}:#{mm}" + rational_hours = offset.abs.to_r / 3600 + hours = rational_hours.to_i + minutes = ((rational_hours - hours) * 60).to_i + + # + # Format the hours and minutes as two-digit numbers. + time = format("%02d:%02d", :hours => hours, :minutes => minutes) + + 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 diff --git a/test/test_win_tz.rb b/test/test_win_tz.rb new file mode 100644 index 00000000..5e7eb896 --- /dev/null +++ b/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 |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