【问题标题】:Change default_timezone of active record更改活动记录的 default_timezone
【发布时间】:2013-02-14 02:17:32
【问题描述】:

在 Rails 中,我首先有以下用于 activerecord 的配置。

config.active_record.default_timezone = :utc

现在,我想使用本地时区,所以我将其更改为:

config.active_record.default_timezone = :local

问题是,我需要将日期/日期时间列中的所有现有数据转移到本地时区。 实现这一目标的最佳方法是什么?


为什么我必须这样做是因为我必须在本地时区上进行聚合,例如 :group => 'DATE(created_at)', GROUP BY DATE(created_at) 将基于 UTC,但我想聚合当地时区一天。

我知道如何编写迁移文件来迁移某个日期时间列。但是这样的专栏有很多,所以我正在寻找更好的解决方案。

【问题讨论】:

  • 您是否存储了任何指示给定用户所在时区的信息?如下所述,除非另有说明,否则切换到本地时区会将每个值设置为服务器的时区。
  • @Chris 用户被限制在与服务器相同的时区。

标签: ruby-on-rails ruby ruby-on-rails-3 migration


【解决方案1】:

您必须更改数据库中的数据吗?您可以改为显示本地时区的日期吗?这有帮助吗:Convert UTC to local time in Rails 3

【讨论】:

  • 我必须在本地时区进行聚合,例如:group => 'DATE(created_at)'GROUP BY DATE(created_at) 将基于UTC,但我想在本地时区中聚合一天。
  • 您使用的是什么数据库? Postgres 有一系列时区函数(postgresql.org/docs/9.2/static/functions-datetime.html,参见章节:9.9.3. AT TIME ZONE)。这将让您在执行查询时指定时区。
【解决方案2】:

我的第一个建议是强烈建议您不要这样做。你正在向一个受伤的世界敞开大门。也就是说,这就是你想要的:

class ShootMyFutureSelfInTheFootMigration
  def up
    Walrus.find_each do |walrus|
      married_at_utc = walrus.married_at
      walrus.update_column(:married_at, married_at_utc.in_time_zone)
    end
  end

  def down
    Walrus.find_each do |walrus|
      married_at_local = walrus.married_at
      walrus.update_column(:married_at, married_at_local.utc)
    end
  end
end

您可以将首选时区传入 DateTime#in_time_zone,如下所示:

central_time_zone = ActiveSupport::TimeZone.new("Central Time (US & Canada)")
walrus.update_column(:married_at, married_at_utc.in_time_zone(central_time_zone))

或者你可以离开它,Rails 将使用你当前的时区。请注意,这不是您所在的位置,而是您的服务器所在的位置。因此,如果您的用户在冰岛和上海,但您的服务器在加利福尼亚,则每个“本地”时区都是美国太平洋时间。

【讨论】:

  • 感谢您的回复。我在问题中添加了一些信息。
  • 如果您想在 Rails 迁移中执行此操作,update_column 与您将获得的一样高效。您可以通过仅选择要更改的字段来减少内存使用,但由于您要运行一次,所以时间不是一个大问题。
  • 但是您必须列出很多列,我正在寻找其他可以检测所有需要迁移的数据时间列的解决方案。
【解决方案3】:

就像许多其他人所说的那样,您可能不想这样做。

您可以在分组之前将时间转换为不同的区域,所有这些都在数据库中。例如,使用 postgres,转换为山区标准时间:

SELECT COUNT(id), DATE(created_at AT TIME ZONE 'MST') AS created_at_in_mst
FROM users GROUP BY created_at_in_mst;

【讨论】:

    【解决方案4】:

    这很危险,但这是我在迁移中要做的:

    class MigrateDateTimesFromUTCToLocal < ActiveRecord::Migration
    
      def self.up
        # Eager load the application, in order to find all the models
        # Check your application.rb's load_paths is minimal and doesn't do anything adverse
        Rails.application.eager_load!
    
        # Now all the models are loaded. Let's loop through them
        # But first, Rails can have multiple models inheriting the same table
        # Let's get the unique tables
        uniq_models = ActiveRecord::Base.models.uniq_by{ |model| model.table_name }
    
        begin
          # Now let's loop
          uniq_models.each do |model|
            # Since we may be in the middle of many migrations,
            # Let's refresh the latest schema for that model
            model.reset_column_information
    
            # Filter only the date/time columns
            datetime_columns = model.columns.select{ |column| [ :datetime, :date, :time].include? column.type }
    
            # Process them
            # Remember *not* to loop through model.all.each, or something like that
            # Use plain SQL, since the migrations for many columns in that model may not have run yet
            datetime_columns.each do |column|
              execute <<-SQL
                UPDATE #{model.table_name} SET #{column.name} = /* DB-specific date/time conversion */
              SQL
            end
    
          rescue
            # Probably time to think about your rescue strategy
            # If you have tested the code properly in Test/Staging environments
            # Then it should run fine in Production
            # So if an exception happens, better re-throw it and handle it manually
          end
    
        end
      end
    end
    

    【讨论】:

    • 谢谢,这个看起来不错。
    猜你喜欢
    • 2022-06-23
    • 1970-01-01
    • 1970-01-01
    • 2013-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-07
    • 2021-07-26
    相关资源
    最近更新 更多