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">