これは、PostgreSQL9.1とRails3.0.7で遭遇した最も不可解な問題です。同じUbuntu10.04vboxゲストでPostgresとRails3を実行しています。
"PostgreSQL 9.1.3 on i686-pc-linux-gnu, compiled by gcc-4.4.real (Ubuntu 4.4.3-4ubuntu5) 4.4.3, 32-bit"
アプリはcreated_atタイムスタンプに基づいて単純なクエリを実行する必要がありますが、サーバーのタイムゾーンに基づいて1日になります。私の場合、東部標準時です。「タイムゾーンあり」と「タイムゾーンあり」のさまざまなバリエーションを試し、pgAdminで正常に動作するようにしましたが、RailsWebサイトでは正常に動作しませんでした。
これがARelクエリを使用したクラスメソッドです。
def self.transactions2(business_id, begin_date, end_date )
trans = LogVouchers.select( %{
created_at
} ).
where( %{
created_at >= ( TIMESTAMP WITH TIME ZONE ? at time zone 'utc')::timestamp
and created_at < (TIMESTAMP WITH TIME ZONE ? at time zone 'utc')::timestamp
},
begin_date,
end_date )
end
コントローラコードは次のとおりです。
def voucher_transactions
default_date = Time.zone.now.beginning_of_day
@end_date = params[:end_date].blank? ? \
Date.new(default_date.year, default_date.month, default_date.day ) : \
Date.new(params[:end_date][:year].to_i, params[:end_date][:month].to_i, \
params[:end_date][:day].to_i)
@begin_date = params[:begin_date].blank? ? \
Date.new(default_date.year, default_date.month, default_date.day ) : \
@begin_date = Date.new(params[:begin_date][:year].to_i, \
params[:begin_date][:month].to_i, params[:begin_date][:day].to_i)
@data2 = LogVouchers.transactions2(business_id, @begin_date, @end_date + 1.day)
...
end
Railsログファイルの実際のSQLコードとデバッグ検査は次のとおりです。
LogVouchers Load (0.7ms) SELECT
created_at
FROM "log_vouchers" WHERE (
created_at >= ( TIMESTAMP WITH TIME ZONE '2012-04-28' at time zone 'utc')::timestamp
and created_at < (TIMESTAMP WITH TIME ZONE '2012-04-29' at time zone 'utc')::timestamp
)
### 2012-05-01 21:18:04 -0400 voucher_transactions() @data2 =[
#<LogVouchers created_at: "2012-04-28 03:14:29">
, #<LogVouchers created_at: "2012-04-28 03:15:24">
, #<LogVouchers created_at: "2012-04-28 03:18:19">
, #<LogVouchers created_at: "2012-04-28 03:38:35">
, #<LogVouchers created_at: "2012-04-28 03:58:08">
, #<LogVouchers created_at: "2012-04-28 15:44:46">
, #<LogVouchers created_at: "2012-04-28 17:12:20">
, #<LogVouchers created_at: "2012-04-28 18:46:45">
, #<LogVouchers created_at: "2012-04-28 18:55:47">
, #<LogVouchers created_at: "2012-04-28 18:57:52">
, #<LogVouchers created_at: "2012-04-28 19:02:15">
, #<LogVouchers created_at: "2012-04-28 19:02:50">
, #<LogVouchers created_at: "2012-04-28 19:46:36">
, #<LogVouchers created_at: "2012-04-28 19:47:17">
, #<LogVouchers created_at: "2012-04-28 19:50:22">
, #<LogVouchers created_at: "2012-04-28 19:50:56">]
ただし、pgAdminのクエリウィンドウでSQLコードを直接実行すると、正しい結果が得られました。
"2012-04-28 15:44:46.641305"
"2012-04-28 17:12:20.615641"
"2012-04-28 18:46:45.277561"
"2012-04-28 18:55:47.40109"
"2012-04-28 18:57:52.616501"
"2012-04-28 19:02:15.542964"
"2012-04-28 19:02:50.888847"
"2012-04-28 19:46:36.16556"
"2012-04-28 19:47:17.084047"
"2012-04-28 19:50:22.672805"
"2012-04-28 19:50:56.376571"
UTCで「2012-04-2803:14:29」の周りにcreated_atがある5つのレコードは、正しい結果セットに含まれていないことに注意してください。
ここで最も不可解な質問:
Railsはどのようにして同じSQLステートメントをPostgresデータベースに送信し、異なる結果を得ることができますか?
私は明らかにここで何かが欠けています。教祖は助けてくれますか?
追記2012-5-2
背景:Rails 3.0は、デフォルトで、Postgresのすべてのdbテーブルに「タイムゾーンのないタイムスタンプ」としてcreated_at列を生成しました。サーバーのタイムゾーン「アメリカ/ニューヨーク」に基づいて日次レポートを実行する必要があります。関係するテーブルの数が多いため、ARel selectメソッドを使用して複数のテーブルを結合し、begin_dateおよびend_dateタイムスタンプを渡しました。正しい結果セットを返すのが難しい。UTCタイムスタンプ付きのレコードを返す必要があります。
2012-04-28 04:00:00 to 2012-04-29 04:00:00
ただし、UTCタイムスタンプのレコードが返されました。
2012-04-28 00:00:00 to 2012-04-29 00:00:00
私が理解できるのは、クラスメソッドを変更することです。
def self.transactions2(business_id, begin_date, end_date )
st_tz_offset = Time.zone.now.formatted_offset(true)
st_begin_date_tz = "#{begin_date} 00:00:00#{st_tz_offset}"
st_end_date_tz = "#{end_date} 00:00:00#{st_tz_offset}"
trans = LogVouchers.select( %{
created_at
} ).
where( %{
created_at >= ( TIMESTAMP WITH TIME ZONE ? at time zone 'utc')
and created_at < (TIMESTAMP WITH TIME ZONE ? at time zone 'utc')
},
st_begin_date_tz ,
st_end_date_tz )
end
実際、これはPostgresに比較する前にそれtimestamp with time zone
を変換するように指示します。timestamp without time zone
そしてそれは私のために働いた。これは複雑すぎることを私は知っています。これを達成するためのより良い方法を誰かが指摘してくれれば幸いです。
追記2
- 可能であれば、Ubuntu / Linuxサーバー、Postgresサーバーのデフォルト設定を変更しないようにします。これは、他の副作用を引き起こす傾向があるためです。
- 将来的には、これらの毎日のカットオフタイムスタンプは、サーバーの代わりにの
TimeZone
設定に基づいています。同じWebアプリのユーザーは複数のタイムゾーンにまたがることができ、日次レポートはそれぞれのログインユーザーにとって意味のあるものでなければならないためです。current_user
TimeZone