簡體   English   中英

Rails Upgrade 會更改默認時間格式。 如何恢復?

[英]Rails Upgrade makes default Time format change. How to revert?

我正在升級 Rails 應用程序

  • Rails 4.2 -> 5.2(后續升級到 Rails 6 待定)
  • 紅寶石 2.2 -> 2.5
  • Postgres 9.1 -> 10

在各個步驟中。 由於 Rails 升級需要 Postgres 升級,因此我無法以合理的方式將升級分開。

目前,我正在為 Rails 5.2 中處理“時間”對象的方式而苦苦掙扎。 AR 對象中的“時間”列現在作為ActiveSupport::TimeWithZone返回,即使數據庫列沒有時區。 以前它是一個普通的Time對象,它具有不同的默認 JSON 表示。

這使得許多 API 測試失敗,而這些測試以前都返回 UTC 時間。

用於PhoneNumber對象的 Rails 4.2、Ruby 2.2、PG 9.1 示例:

2.2.6 :002 > p.time_weekdays_from
  => 2000-01-01 07:00:00 UTC 
2.2.6 :003 > p.time_weekdays_from.class
  => Time 

Rails 5.2、Ruby 2.5、PG 10 的示例:

irb(main):016:0> p.time_weekdays_from
  => Sat, 01 Jan 2000 11:15:00 CET +01:00
irb(main):018:0> p.time_weekdays_from.class
  => ActiveSupport::TimeWithZone

我已經添加了一個初始化程序來暫時覆蓋它,這似乎工作正常,但我仍然想了解為什么進行了這種更改,以及為什么即使“沒有時區的時間”DB 列也被 Rails 視為如果他們有時區。

# This works, but why is it necessary?
module ActiveSupport
  class TimeWithZone
    def as_json(options = nil)
      self.utc.iso8601
    end
  end
end

PS:我並不總是想要 UTC,我只是想要它用於 API,因為這是我們的 API 客戶所期望的。

目前,我正在為 Rails 5.2 中處理“時間”對象的方式而苦苦掙扎。 AR 對象中的“時間”列現在作為 ActiveSupport::TimeWithZone 返回,即使數據庫列沒有時區。 以前它是一個普通的 Time 對象,它具有不同的默認 JSON 表示。

盡管如此,我還是想了解為什么進行了這種更改,以及為什么即使是“沒有時區的時間”DB 列也被 Rails 視為有時區。

進行此更改是因為 Ruby 的默認Time不了解時區。 ActiveSupport::TimeWithZone可以。 這在處理時間、時區和數據庫時解決了很多問題。

例如,假設您的應用程序的時區是America/Chicago 以前,您必須決定是否要存儲帶時區或不帶時區的時間。 如果您選擇沒有時區,您是將其存儲為 UTC 還是 America/Chicago? 如果您將其存儲為 UTC,您是否在加載或顯示時將其轉換為美國/紐約? 轉換意味着從Time添加和減去小時。 當您保存Time對象時,您必須小心記住Time被轉換為哪個時區並將其轉換回數據庫的時區。 協調所有這些會導致許多錯誤。

Rails 5 引入了ActiveSupport::TimeWithZone 這將時間存儲為 UTC 和所需的時區來表示它。現在處理時間更簡單:將其存儲為 UTC(即timestamp )並在加載時添加應用程序的時區。 不需要轉換。 Rails 會為您處理這個問題。

更改現在是timestamp列,默認情況下,將在應用程序的時區中格式化。 這需要一些時間來適應,但最終將使您對時間和時區的處理更加穩健。

> Time.zone.tzinfo.name
=> "America/Chicago"

> Time.zone.utc_offset
=> -21600

# Displayed in the application time zone
> Foo.last.created_at
=> Tue, 31 Dec 2019 17:16:14 CST -06:00

# Stored as UTC
> Foo.last.created_at.utc
=> 2019-12-31 23:16:14 UTC

如果您有手動執行時區轉換的代碼,請刪除它。 在 UTC 工作。 時區現在只是格式化。

越多越好...

  • 使用對象,而不是字符串。
  • 以 UTC 工作,時區用於格式化。
  • 如果您需要將時間轉換為字符串,請明確格式化。
    def get_api_time
      '2000-01-01 07:00:00 UTC'
    end

    # bad: downgrading to strings, implicit formatting
    expected_time = Time.utc(2000, 1, 1, 7)
    expect( get_api_time ).to eq expected_time

    # good: upgrading to objects, format is irrelevant
    expected_time = Time.zone.parse('2000-01-01 07:00:00 UTC')
    expect(
      Time.zone.parse(get_api_time)
    ).to eq expected_time

    # better: refactor method to return ActiveSupport::TimeWithZone
    def get_api_time
      Time.zone.parse('2000-01-01 07:00:00 UTC')
    end

    expected_time = Time.zone.parse('2000-01-01 07:00:00 UTC')
    expect( get_api_time ).to eq expected_time

我建議閱讀這些文章,它們可以解決問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM