如何获取带有 DST 偏移量的 time_zone_options_for_select ?

发布于 2024-11-14 11:34:33 字数 318 浏览 3 评论 0原文

ActionView::Helpers::FormOptionsHelper 提供 time_zone_options_for_select 来获取选项列表用于包含所有时区及其 UTC 偏移量的选择控件。我遇到的问题是如何让它在夏令时生效时显示正确的偏移量?

例如,美国山区时间通常为 -7 UTC,但在夏季实际上为 -6 UTC。有没有办法让该列表正确反映这一点?

The ActionView::Helpers::FormOptionsHelper provides the time_zone_options_for_select to get a list of options for a select control that includes all of the timezones with their UTC offset. The problem I'm having is how to get it to display the correct offset for when daylight savings time is in effect?

For instance U.S. Mountain time is usually -7 UTC but during the summer it's effectively -6 UTC. Is there a way to have that list correctly reflect that?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

贩梦商人 2024-11-21 11:34:33

TL;DR

您收到的时区数据是“正确的”,因为 ActiveSupport::TimeZoneTZInfo::Timezone 实例不会对当前日期做出假设,因此就这些对象的责任而言,对它们应用 DST 没有“意义”。

注意到这个问题,因为time_zone_options_for_selectActiveSupport::TimeZone,不幸返回调用#to_s时的偏移字符串,如果该位置当前正在遵守夏令时,则该偏移字符串将不正确。无法修复选项中生成的字符串值,甚至无法删除其中的偏移量。

如果这是不可接受的,您需要跳过使用 time_zone_options_for_select,并使用 options_for_select 而是自己生成选项。

一些调查

time_zone_options_for_select 使用 ::ActiveSupport::TimeZone 作为默认 model 参数,因此手动传递它不会改变您的结果。为了构造选择框的选项,该方法将以 [time_zone.to_s, time_zone.name] 格式构造一个元组数组,以便将其传递给更通用的 <代码>options_for_select 方法。在本例中,time_zone::ActiveSupport::TimeZone 的实例。

这里的重要因素是,这个时区实例对象在概念上与“当前日期”的概念完全无关/分离。时区的定义(严格来说)与当前日期无关。我们可以这样确认这个“不使用 DST”问题:

::ActiveSupport::TimeZone.all.find { |tz| tz.name == "Adelaide" }.utc_offset
=> 34200 # 9 hours and 30 minutes, in seconds

阿德莱德的非 DST 时区是 ACST(澳大利亚中部标准时间),即 GMT+9.5。目前(截至撰写本文时),阿德莱德采用 DST,这意味着他们采用 ACDT(澳大利亚中部夏令时),即 GMT+10.5。

::ActiveSupport::TimeZone.all.find { |tz| tz.name == "Adelaide" }.now.utc_offset
=> 37800 # 10 hours and 30 minutes, in seconds

这里的关键区别本质上就是我上面概述的 - ActiveSupport::TimeZone 实例只是关心当前日期。该类本身是一个围绕 TZInfo::DataTimezone 实例的便捷包装器,它对当前日期有类似的看法 - 没有。

如果您注意到,在上面的第二个代码片段中,我们在调用 #utc_offset 之前对时区对象调用了 #now。这将返回一个 ActiveSupport::TimeWithZone 实例,其中包含有关时区的信息以及当前日期 - 因此我们得到一个偏移量,该偏移量反映了当前日期应包含 DST 偏移量这一事实。

因此,这里唯一的问题是,在 ActiveSupport::TimeZone 实例上的 #to_s 返回值中包含 UTC 偏移字符串在这种情况下有点误导。包含它是因为它是该时区的“基本”UTC 偏移量。

资源:

TL;DR

The time zone data you are receiving is "correct" because ActiveSupport::TimeZone and TZInfo::Timezone instances do not make assumptions about the current date, and therefore applying DST to them doesn't make "sense" in the context of the responsibility of those objects.

You notice the problem because the default model for time_zone_options_for_select, ActiveSupport::TimeZone, unfortunately returns the offset string when calling #to_s, which, if the location is currently is observing DST, will be incorrect. There is no way to fix the string values generated in the options, or even remove the offset from them.

If this isn't acceptable you'll need to skip on using time_zone_options_for_select, and use options_for_select instead, and generate the options yourself.

Some investigation

time_zone_options_for_select uses ::ActiveSupport::TimeZone as the default model parameter, so passing it in manually will not change your results. In order to construct options for a select box, that method will construct an array of tuples in the format of [time_zone.to_s, time_zone.name], for the purpose of passing it to the more generic options_for_select method. time_zone, in this case, is an instance of ::ActiveSupport::TimeZone.

The important factor here is that this time zone instance object is, conceptually, completely unrelated/divorced from the idea of "the current date". The definition of a time zone (strictly speaking) has nothing to do with the current date. We can confirm this "not using DST" issue like so:

::ActiveSupport::TimeZone.all.find { |tz| tz.name == "Adelaide" }.utc_offset
=> 34200 # 9 hours and 30 minutes, in seconds

Adelaide's non-DST time zone is ACST (Australian Central Standard Time) which is GMT+9.5. Currently (as in the time of writing), Adelaide is in DST which means they are on ACDT (Australian Central Daylight Time), which is GMT+10.5.

::ActiveSupport::TimeZone.all.find { |tz| tz.name == "Adelaide" }.now.utc_offset
=> 37800 # 10 hours and 30 minutes, in seconds

The crucial difference here is essentially what I've outlined above - the ActiveSupport::TimeZone instance is just not concerned with the current date. The class itself is a convenience wrapper around a TZInfo::DataTimezone instance, which has similar opinions on the current date - none.

If you noticed, in the second code snippet above, we called #now on the time zone object before calling #utc_offset. This returns an ActiveSupport::TimeWithZone instance, which includes information about the time zone, as well as the current date - and therefore we get an offset which reflects the fact that the current date should include the DST offset.

So, the only problem here is that including the UTC offset string in the return value of #to_s on ActiveSupport::TimeZone instances is sort of misleading in this instance. It's included because it's the "base" UTC offset for that time zone.

Resources:

栀子花开つ 2024-11-21 11:34:33

我遇到了类似的问题,但最终使用了这个,

time_zone_select('time_zone', TZInfo::Timezone.us_zones, 
                                     :default => "America/Los_Angeles", 
                                     :model => TZInfo::Timezone

你找到更好的解决方案了吗?

I had similar problem but ended up using this

time_zone_select('time_zone', TZInfo::Timezone.us_zones, 
                                     :default => "America/Los_Angeles", 
                                     :model => TZInfo::Timezone

Did you find a better solution?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文