I don't know if I am doing something stupid, so bear with me.
tl;dr Rails ActiveSupport time and timezone seems to have an error on Alpine Linux. It uses the DST variant (summer time) of my timezone when it should use the winter time.
Steps to reproduce:
- Start a shell in a Docker Alpine Linux Ruby image:
$ docker run -it --rm ruby:2.7.1-alpine sh
All following steps happen inside the running docker container.
- Install timezone data:
$ apk add --no-cache --update tzdata
fetch http://dl-cdn.alpinelinux.org/alpine/v3.11/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.11/community/x86_64/APKINDEX.tar.gz
(1/1) Installing tzdata (2020c-r0)
Executing busybox-1.31.1-r9.trigger
OK: 23 MiB in 37 packages
- Print standard ruby current time:
$ ruby -e 'puts Time.now.inspect'
2020-10-28 20:34:24.4817918 +0000
Looks good. Time is printed in UTC.
- Besides UTC, standard ruby
Timeclass can only handle local time. This is dependent on the local system timezone, which can be configured using operating system configuration mechanisms, or which can simply be passed to Ruby with theTZenv var. Let's try to use my timezone "Europe/Berlin":
$ TZ="Europe/Berlin" ruby -e 'puts Time.now.inspect'
2020-10-28 21:39:22.7037648 +0100
Looks good. Berlin timezone is UTC+01 in winter (standard) or UTC+02 in summer (DST). At the time of this writing, we have winter time, so +0100 is fine.
- Now let's move to ActiveSupport:
$ gem install activesupport
Fetching tzinfo-1.2.7.gem
Fetching i18n-1.8.5.gem
Fetching activesupport-6.0.3.4.gem
# #### many more lines of output ####
Successfully installed activesupport-6.0.3.4
6 gems installed
- In contrast to standard ruby, ActiveSupport supports all possible timezones, rather than only two. Getting the current time is
Time.current, so let's try this:
$ ruby -e 'require "active_support/all"; puts Time.current.inspect'
2020-10-28 20:43:51.1098842 +0000
This looks not different from the output of step 3. The reason for this is that Time.current only behaves differently than Time.now when a timezone is configured.
- We can configure a timezone for ActiveSupport with
Time.zone=(timezone_identifier)or withTime.use_zone(timezone_identifier) { "inside this block the timezone is used" }. Let's try the first variant:
$ ruby -e 'require "active_support/all"; Time.zone = "UTC"; puts Time.current.inspect'
Wed, 28 Oct 2020 20:50:55 UTC +00:00
We are still on UTC, but the output looks different than before. From this we know that we got a ActiveSupport::TimeWithZone object. This is good.
- Now I want the same for my timezone:
$ ruby -e 'require "active_support/all"; Time.zone = "Europe/Berlin"; puts Time.current.inspect'
Wed, 28 Oct 2020 22:52:21 CEST +02:00
On a first glance this looks good, but compare it carefully with the output of step 4. In "Europe/Berlin" timezone we currently have winter time, UTC+01, also called "CET" (central european time). But this time in the output the timestamp is labeled as being "CEST" (central european summer time), which is UTC+02.
And this is wrong.
Where is the error? Is it in Alpine Linux? Is it in standard Ruby? But the output is correct in step 4. Or is the error in ActiveSupport or its connection to the timezone data? Am I doing something wrong?