解析 timestamp 以考虑时区

parsing timestamptz to consider a timezone

提问人:Jerome 提问时间:9/17/2023 最后编辑:Jerome 更新时间:9/18/2023 访问量:53

问:

一个 rails 7 应用程序定义

    config.time_zone = 'Vienna' 
    config.active_record.default_timezone = :local

模型有两个属性

date, :timestamptz  # date is just short for actual column name
date_tz,   :string

表单通过time_zone_select和datetime_select提交数据

"time_zone"=>"Paris","date_f(3i)"=>"12", "date_f(2i)"=>"1", "date_f(1i)"=>"1999", "date_f(4i)"=>"00", "date_f(5i)"=>"00"

目标是以适当的偏移量将数据保存在 postgresql 中。

控制器可以解析日期时间,但滑点部分正在设置值local

local = ??
datetime = DateTime.civil_from_format(local, params[:date_m(1i)], params[:date_m(2i)], params[:date_m(3i)], params[:date_m(4i)], params[:date_m(5i)], 0)

有许多问题在起作用:

  • 可能对“本地”的理解有些混淆......local 是否打算用作表示偏移量的字符串?documentation states 返回具有本地偏移量的 DateTime没有指出一个实际的例子,想到的第一个假设是它期望类似的东西-0400
  • 这并不一定考虑 DST:用户正在输入时区,但尚未使用日期进行解析以获得适当的偏移量。它可能是或在夏令时...-04000300
  • ActiveSupport::TimeZone对偏移量一无所知(除了第一个),因为所有元素的所有基础值都有......但也许这是在 ?@utc_offset=nilapplication.rb
> ActiveSupport::TimeZone.all
[#<ActiveSupport::TimeZone:0x00000001143339b0
  @name="International Date Line West",
  @tzinfo=#<TZInfo::DataTimezone: Etc/GMT+12>,
  @utc_offset=nil>,
[...]
#<ActiveSupport::TimeZone:0x000000011597f4d0
  @name="Arizona",
  @tzinfo=#<TZInfo::DataTimezone: America/Phoenix>,
  @utc_offset=nil>,
[...]

如何为 postgresql 设置适当的偏移量?

Ruby-on-Rails PostgreSQL 日期时间

评论


答:

1赞 max 9/18/2023 #1

参数应为符号或 。您可以通过该方法的实现方式看到这一点:utc_or_local:local:utc

  # Returns DateTime with local offset for given year if format is local else
  # offset is zero.
  #
  #   DateTime.civil_from_format :local, 2012
  #   # => Sun, 01 Jan 2012 00:00:00 +0300
  #   DateTime.civil_from_format :local, 2012, 12, 17
  #   # => Mon, 17 Dec 2012 00:00:00 +0000
  def self.civil_from_format(utc_or_local, year, month = 1, day = 1, hour = 0, min = 0, sec = 0)
    if utc_or_local.to_sym == :local
      offset = ::Time.local(year, month, day).utc_offset.to_r / 86400
    else
      offset = 0
    end
    civil(year, month, day, hour, min, sec, offset)
  end

如果你想使用特定用户提供的时区,你想要的是:Time.use_zone

Time.use_zone(the_user_provided_timezone) do
  DateTime.civil_from_format(:local, params[:date_m(1i)], params[:date_m(2i)], params[:date_m(3i)], params[:date_m(4i)], params[:date_m(5i)], 0)
end