1

これは、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_userTimeZone
4

1 に答える 1

3

表現は、現地時間の設定によって異なります。これ:

SET  timezone = 'EST';
SELECT TIMESTAMP WITH TIME ZONE '2012-04-29' AT TIME ZONE 'UTC';

これとは異なるタイムスタンプを返します:

SET  timezone = '-2';
SELECT TIMESTAMP WITH TIME ZONE '2012-04-29' AT TIME ZONE 'UTC';

明らかに、pgAdmin からの接続には、ARel からの接続とは異なる時間設定があります。次の方法で調べます。

SHOW timezone;

timezoneで定義されてpostgresql.confいますが、いつでも別の値に設定できます。次の方法でデフォルトにリセットできます。

RESET timezone;

この最近の回答 で、PostgreSQL のタイムスタンプとタイム ゾーンの処理について詳しく説明しましたタイムゾーンの
詳細については、マニュアルを参照してください。


「UTC の真夜中」が必要な場合は、代わりに次の表現を使用します。

SELECT TIMESTAMP '2012-04-29' AT TIME ZONE 'UTC';

または単に:

SELECT '2012-04-29 0:0 +0'::timestamptz;

これは、設定に関係なく同じ内部値を返しますtimezone。ただし、ローカルのタイムゾーンに従って表示されます。したがって、EST を使用すると、次のことがわかります。

2012-04-28 19:00:00-05

タイムゾーンなし

追加情報によると、テーブルにはタイプのデータが保持されますtimestamp without time zone-または単にtimestamp、デフォルトは ですwithout time zone。そして、これらのタイムスタンプは暗黙的に に基づいていESTます。

現在のタイムゾーンに関係なく、午前 0 時から午前 0 時までの 1 日のデータを取得する場合は、データ型timestamp with time zone(またはtimestamptz) をまったく使用しないでください。使うだけtimestampで、すべてがグルーヴィーです。サーバーは以下を取得する必要があります。

WHERE created_at >= timestamp '2012-04-28 0:0'
AND   created_at <  timestamp '2012-04-29 0:0'

または:

WHERE created_at >= '2012-04-28 0:0'::timestamp
AND   created_at <  '2012-04-29 0:0'::timestamp

または単に:

WHERE created_at >= '2012-04-28 0:0'
AND   created_at <  '2012-04-29 0:0'

現在のタイムゾーンに応じてデータの別のスライスが必要な場合、サーバーはこれを取得する必要があります。

WHERE created_at >= '2012-04-28 0:0'::timestamptz AT TIME ZONE 'EST'
AND   created_at <  '2012-04-29 0:0'::timestamptz AT TIME ZONE 'EST'

この用語'2012-04-28 0:0'::timestamptz AT TIME ZONE 'EST'は、ローカルの午前 0 時のニューヨークの時刻 (タイム ゾーンなし) を計算します。

または、それに応じてアプリのタイムスタンプを計算し、タイム ゾーンなしで上記のクエリを使用することもできます。

于 2012-05-02T02:13:55.870 に答える