Skip to content

Instantly share code, notes, and snippets.

@Fedcomp
Last active March 25, 2019 20:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Fedcomp/f63dc6cdb5ca204c2f02302553457771 to your computer and use it in GitHub Desktop.
Save Fedcomp/f63dc6cdb5ca204c2f02302553457771 to your computer and use it in GitHub Desktop.
Rails timestamptz migration
docker-compose --run --rm ruby test.rb
version: '3'
services:
ruby:
image: ruby:2.3
depends_on:
- db
working_dir: /opt/project
environment:
- TZ=Europe/Moscow
volumes:
- .:/opt/project
- ./volumes/ruby/bundle:/usr/local/bundle
command: ruby test.rb
db:
image: postgres:9.3
begin
require 'bundler/inline'
rescue LoadError => e
$stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler'
raise e
end
gemfile(true) do
source 'https://rubygems.org'
gem 'rails', '~> 5.0', '>= 5.0.6'
gem 'pg'
gem 'rspec'
gem 'pry-byebug'
end
require 'logger'
require 'pry-byebug'
require 'rails/all'
require 'active_record'
require 'rspec'
require 'rspec/autorun'
DATABASE_TIMEZONE = 'UTC' # Works without migrations this way
# DATABASE_TIMEZONE = 'Europe/Moscow'
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Base.establish_connection(
adapter: 'postgresql',
user: 'postgres',
database: 'postgres',
host: 'db',
pool: 1
)
binding.pry
# Do it before the fixing migration
ActiveRecord::Base.connection.execute("SET TIME ZONE '#{DATABASE_TIMEZONE}'")
ActiveRecord::Schema.define do
create_table :assignments, force: true do |t|
t.column :assigned_at, 'timestamp with time zone'
end
end
class Assignment < ActiveRecord::Base
end
OLD_ASSIGNMENT_CURRENT_TIME = Time.parse('2000-06-11 10:05:05 +0300').utc
NEW_ASSIGNMENT_CURRENT_TIME = Time.parse('2019-03-11 10:05:05 +0300').utc
TEST_OLD_ASSIGNMENT_ID = 100001
TEST_NEW_ASSIGNMENT_ID = 100000
# Assigment created with time in daylight saving
Assignment.create(id: TEST_OLD_ASSIGNMENT_ID, assigned_at: OLD_ASSIGNMENT_CURRENT_TIME)
# Assignment created with time without daylight saving
Assignment.create(id: TEST_NEW_ASSIGNMENT_ID, assigned_at: NEW_ASSIGNMENT_CURRENT_TIME)
class MigrateAssignmentTimestamptz < ActiveRecord::Migration[5.2]
def up
change_column :assignments, :assigned_at, :timestamp
end
end
MigrateAssignmentTimestamptz.migrate(:up)
RSpec.describe Assignment do
let(:current_time) { Time.parse('2019-03-11 10:05:05 +0300').utc }
around do |example|
# Database reset
Assignment.transaction do
example.call
raise ActiveRecord::Rollback
end
end
before do
db_timezone = ActiveRecord::Base.connection.execute('SHOW TIMEZONE').first.dig('TimeZone')
expect(db_timezone).to eq(DATABASE_TIMEZONE)
end
specify 'migration should fix existing old rows to have correct time' do
assignment = Assignment.find(TEST_OLD_ASSIGNMENT_ID)
expect(assignment.reload.assigned_at).to eq(OLD_ASSIGNMENT_CURRENT_TIME)
end
specify 'migration should fix existing fresh rows to have correct time' do
assignment = Assignment.find(TEST_NEW_ASSIGNMENT_ID)
expect(assignment.reload.assigned_at).to eq(NEW_ASSIGNMENT_CURRENT_TIME)
end
it 'keeps timezone between saving and reading' do
assignment = Assignment.create(assigned_at: current_time)
expect(assignment.reload.assigned_at).to eq(current_time)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment