feat: Convert country timezones to JSONB format

- Changed `timezones` attribute from text to JSONB in `Country`.
- Updated related model methods to handle JSONB data.
- Added a migration to convert existing timezone data.
- Used safe navigation operators to prevent errors.

This change improves the storage and management of timezone
information by using the JSONB data type.  It includes data
migration to handle existing timezone data.
This commit is contained in:
songtianlun 2025-02-15 16:51:36 +08:00
parent 983564d534
commit ca3691004f
6 changed files with 50 additions and 6 deletions

View File

@ -218,7 +218,7 @@ class City < ApplicationRecord
def formatted_current_time(type = :date, use_local_timezone = true)
# 获取时区
timezone_info = self.country&.timezones.present? ?
eval(self.country.timezones).first :
country&.timezones&.first :
{ "zoneName" => "UTC", "gmtOffsetName" => "UTC+00:00" }
# 设置时区对象

View File

@ -11,6 +11,8 @@ class Country < ApplicationRecord
validates :code, presence: true, uniqueness: true
validates :iso2, uniqueness: true, allow_blank: true
attribute :timezones, :jsonb
def to_s
name
end

View File

@ -71,7 +71,7 @@ class WeatherArt < ApplicationRecord
def formatted_time(type = :date, use_local_timezone = false)
# 获取时区
timezone_info = self.city&.country&.timezones.present? ?
eval(self.city.country.timezones).first :
self.city.country&.timezones&.first :
{ "zoneName" => "UTC", "gmtOffsetName" => "UTC+00:00" }
# 设置时区对象

View File

@ -62,7 +62,7 @@ class BatchGenerateWeatherArtsWorker
def get_local_time(city)
return Time.current unless city
timezone_info = eval(city.country.timezones).first
timezone_info = city.country&.timezones&.first
return Time.current unless timezone_info
timezone = ActiveSupport::TimeZone[timezone_info["zoneName"]] ||

View File

@ -0,0 +1,42 @@
class ConvertCountryTimezonesToJson < ActiveRecord::Migration[8.0]
def up
# 1. 首先添加一个临时列
add_column :countries, :timezones_json, :jsonb
# 2. 转换现有数据
Country.find_each do |country|
if country.timezones.present?
begin
# 尝试解析现有的 timezones 字符串
parsed_timezones = eval(country.timezones) # 这里使用 eval 是为了处理历史数据
country.update_column(:timezones_json, parsed_timezones)
rescue => e
puts "Error converting timezones for country #{country.id}: #{e.message}"
# 设置默认值
country.update_column(:timezones_json, [{"zoneName" => "UTC", "gmtOffsetName" => "UTC+00:00"}])
end
else
# 设置默认值
country.update_column(:timezones_json, [{"zoneName" => "UTC", "gmtOffsetName" => "UTC+00:00"}])
end
end
# 3. 删除旧列并重命名新列
remove_column :countries, :timezones
rename_column :countries, :timezones_json, :timezones
end
def down
# 回滚操作
add_column :countries, :timezones_string, :text
Country.find_each do |country|
if country.timezones.present?
country.update_column(:timezones_string, country.timezones.to_s)
end
end
remove_column :countries, :timezones
rename_column :countries, :timezones_string, :timezones
end
end

6
db/schema.rb generated
View File

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2025_02_11_035423) do
ActiveRecord::Schema[8.0].define(version: 2025_02_15_084540) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"
@ -120,7 +120,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_11_035423) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "slug"
t.integer "country_id", null: false
t.integer "country_id"
t.string "state_code"
t.string "country_code"
t.boolean "flag", default: true
@ -150,7 +150,6 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_11_035423) do
t.string "native"
t.string "subregion"
t.string "nationality"
t.text "timezones"
t.text "translations"
t.decimal "latitude", precision: 10, scale: 8
t.decimal "longitude", precision: 11, scale: 8
@ -159,6 +158,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_11_035423) do
t.boolean "flag", default: true
t.string "wiki_data_id"
t.bigint "subregion_id"
t.jsonb "timezones"
t.index ["code"], name: "index_countries_on_code", unique: true
t.index ["region_id"], name: "index_countries_on_region_id"
t.index ["slug"], name: "index_countries_on_slug", unique: true