Rails 7 fixes timezone awareness for tsrange and tstzrange columns


Postgres has a fantastic collection of column types that makes it easy to store custom data. One such type are Ranges which allow you to store a range of values. For example, you can store a range of dates or a range of integers. When it comes to storing a range of timestamps, Postgres provides the tsrange and tstzrange column types which store a range of timestamps with and without timezones respectively.

Before

Let’s create a column in a Rails module to store a range of timestamps.

  # First we create a migration to add the column
  class AddTimestampRangeToEmployees < ActiveRecord::Migration[7.0]
    def change
      add_column :employees, :tenure, :tsrange, array: true
    end
  end

Now let’s store a range of timestamps in this column.

    rails c
  Loading development environment (Rails 7.0.2)
  irb(main):001:0> Employee.create!(tenure: 5.years.ago..DateTime.yesterday, name: "John")
  => #<Employee id: 1, tenure: ["2017-11-03 14:10:17.844978 UTC", "2022-11-03 14:10:17.844978 UTC"], name: "John">

This works as expected. However, if we set a timezone in the application config (and then make tsrange timezone aware), we get a TypeError.

  # config/application.rb
  config.time_zone = "Asia/Kolkata"
  ActiveRecord::Base.time_zone_aware_types += [:tsrange]

Now let’s try again!

    rails c
  Loading development environment (Rails 7.0.2)
  irb(main):001:0> Employee.create!(tenure: 5.years.ago..DateTime.yesterday, name: "John")
  /Users/swaathi/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/activesupport-7.0.2/lib/active_support/core_ext/range/each.rb:19:in `ensure_iteration_allowed': can't iterate from ActiveSupport::TimeWithZone (TypeError)
  /Users/swaathi/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/activesupport-7.0.2/lib/active_support/core_ext/range/each.rb:19:in `ensure_iteration_allowed': can't iterate from ActiveSupport::TimeWithZone (TypeError)

After

Thanks to this PR, the app timezone is now retained when storing a range of timestamps. Let’s try again!

    rails c
  Loading development environment (Rails 7.0.2)
  irb(main):001:0> Employee.create!(tenure: 5.years.ago..DateTime.yesterday, name: "John")
  => #<Employee id: 1, tenure: ["2017-11-03 13:36:14.419774000 IST +05:30", "2022-11-03 13:36:14.419774000 IST +05:30"], name: "John">

Join Our Newsletter