Chr*_*aus 13 ruby time activesupport alpine-linux
我不知道我是不是在做蠢事,所以请耐心等待。
tl; dr Rails ActiveSupport 时间和时区在 Alpine Linux 上似乎有错误。当它应该使用冬季时间时,它使用我的时区的 DST 变体(夏令时)。
重现步骤:
$ docker run -it --rm ruby:2.7.1-alpine sh
Run Code Online (Sandbox Code Playgroud)
以下所有步骤都发生在正在运行的 docker 容器内。
$ 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
Run Code Online (Sandbox Code Playgroud)
$ ruby -e 'puts Time.now.inspect'
2020-10-28 20:34:24.4817918 +0000
Run Code Online (Sandbox Code Playgroud)
看起来挺好的。时间以 UTC 打印。
Time类只能处理本地时间。这取决于本地系统时区,它可以使用操作系统配置机制进行配置,或者可以简单地通过TZenv var传递给 Ruby 。让我们尝试使用我的时区“欧洲/柏林”:$ TZ="Europe/Berlin" ruby -e 'puts Time.now.inspect'
2020-10-28 21:39:22.7037648 +0100
Run Code Online (Sandbox Code Playgroud)
看起来挺好的。柏林时区在冬季(标准)是 UTC+01,在夏季(DST)是 UTC+02。在撰写本文时,我们有冬天,所以+0100很好。
$ 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
Run Code Online (Sandbox Code Playgroud)
Time.current,所以让我们试试这个:$ ruby -e 'require "active_support/all"; puts Time.current.inspect'
2020-10-28 20:43:51.1098842 +0000
Run Code Online (Sandbox Code Playgroud)
这看起来与第 3 步的输出没有什么不同。原因是它的Time.current行为与Time.now配置时区时的行为不同。
Time.zone=(timezone_identifier)或为 ActiveSupport 配置时区Time.use_zone(timezone_identifier) { "inside this block the timezone is used" }。让我们尝试第一个变体:$ ruby -e 'require "active_support/all"; Time.zone = "UTC"; puts Time.current.inspect'
Wed, 28 Oct 2020 20:50:55 UTC +00:00
Run Code Online (Sandbox Code Playgroud)
我们仍然使用 UTC,但输出看起来与以前不同。由此我们知道我们得到了一个ActiveSupport::TimeWithZone对象。这很好。
$ ruby -e 'require "active_support/all"; Time.zone = "Europe/Berlin"; puts Time.current.inspect'
Wed, 28 Oct 2020 22:52:21 CEST +02:00
Run Code Online (Sandbox Code Playgroud)
乍一看,这看起来不错,但请仔细将其与第 4 步的输出进行比较。在“欧洲/柏林”时区,我们目前有冬季时间 UTC+01,也称为“CET”(中欧时间)。但是这次在输出中时间戳被标记为“CEST”(中欧夏令时),即 UTC+02。
这是错误的。
错误在哪里?它在 Alpine Linux 中吗?它是标准的 Ruby 吗?但是第 4 步中的输出是正确的。还是 ActiveSupport 中的错误或其与时区数据的连接?难道我做错了什么?
odl*_*dlp 12
事实证明这是一个影响tzinfogem (versions < 1.2.8/ < 2.0.3) 的问题 - 它与 timezone-data 2020b (onwards?)不兼容,并且最近版本的Alpine 随附 2020c。文件格式从“fat”更改为“slim”,这与tzinfogem 尚不兼容。
现在已经tzinfo在发行版的gem 中添加了对“slim”格式 zoneinfo 文件的支持:
您可以使用tzinfo-datagem 而不是依赖系统 tzinfo:
gem install activesupport tzinfo-data
ruby -e 'require "active_support/all"; Time.zone = "Europe/Berlin"; puts TZInfo::DataSource.get; puts Time.current.inspect'
# Ruby DataSource
# Thu, 29 Oct 2020 16:25:08 CET +01:00
Run Code Online (Sandbox Code Playgroud)
您可以以 'fat' 格式(改编自评论)重建时区数据包:
FROM ruby:2.7.2-alpine
# Install tzdata because we need the zic binary
RUN apk add --no-cache tzdata
# Fix incompatibility with slim tzdata from 2020b onwards
RUN wget https://data.iana.org/time-zones/tzdb/tzdata.zi -O /usr/share/zoneinfo/tzdata.zi && \
/usr/sbin/zic -b fat /usr/share/zoneinfo/tzdata.zi
Run Code Online (Sandbox Code Playgroud)
然后测试时间Europe/Berlin:
FROM ruby:2.7.2-alpine
# Install tzdata because we need the zic binary
RUN apk add --no-cache tzdata
# Fix incompatibility with slim tzdata from 2020b onwards
RUN wget https://data.iana.org/time-zones/tzdb/tzdata.zi -O /usr/share/zoneinfo/tzdata.zi && \
/usr/sbin/zic -b fat /usr/share/zoneinfo/tzdata.zi
Run Code Online (Sandbox Code Playgroud)