6

I have one question regarding to time zone

I have done the following yet, my activerecord is not still matching with my timezone when I do following query

def stock_by_date(date)
   UserStock.where("date(created_at) = ? and user_id = ? ", date , current_user.id)
end

I did the following in side my application.rb

config.active_record.default_timezone = :utc
config.active_record.time_zone_aware_attributes = true
config.beginning_of_week = :sunday
config.active_record.time_zone_aware_types = [:datetime, :time]

I added timezone field to my sign up method, and it shows the current day of timezone correctly, yet when I added a stock for the current day timezone, it shows up in either the previous day or next day of current daytime zone and when I added my stock, the timestamp is for a day before when I look at the rails console

My schema.rb

ActiveRecord::Schema.define(version: 2018_11_28_183416) do

  create_table "friendships", force: :cascade do |t|
    t.integer "user_id"
    t.integer "friend_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["friend_id"], name: "index_friendships_on_friend_id"
    t.index ["user_id"], name: "index_friendships_on_user_id"
  end

  create_table "notes", force: :cascade do |t|
    t.string "content"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer "user_id"
    t.index ["user_id"], name: "index_notes_on_user_id"
  end

  create_table "stocks", force: :cascade do |t|
    t.string "ticker"
    t.string "name"
    t.decimal "last_price"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "user_stocks", force: :cascade do |t|
    t.integer "user_id"
    t.integer "stock_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["stock_id"], name: "index_user_stocks_on_stock_id"
    t.index ["user_id"], name: "index_user_stocks_on_user_id"
  end

  create_table "users", force: :cascade do |t|
    t.string "email", default: "", null: false
    t.string "encrypted_password", default: "", null: false
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "first_name"
    t.string "last_name"
    t.string "time_zone", default: "UTC"
    t.index ["email"], name: "index_users_on_email", unique: true
    t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
  end

end

for example, enter image description here enter image description here

How can I fix this issue?

Note: If more info is needed, please let me know

Note: for the matter of being concise this is my repo

Note: my configuration does not show a reliable behavior at all and I am very confused :/ and I have the same issue for note of a current day

----update-------------------

In my rails console

enter image description here

Kick Buttowski
  • 6,709
  • 13
  • 37
  • 58
  • Based on the pics, I think the issue is that Javascript/FE is stripping or not using the timezone when displaying the event – Raj Dec 04 '18 at 02:54
  • in my rails console , each time, I get different result which is very confusing :/, I even shared my repo , and I am open to try anything to get something reliable – Kick Buttowski Dec 04 '18 at 02:56
  • you can set timezone at config/application.rb https://stackoverflow.com/questions/6118779/how-to-change-default-timezone-for-active-record-in-rails – MorboRe' Dec 04 '18 at 03:45
  • the whole point of having zone field inside signup form is avoiding to set up any specific local zone – Kick Buttowski Dec 04 '18 at 04:23
  • Are you storing time zone value in your db for every user? – Abhilash Reddy Dec 04 '18 at 07:23
  • I mean for user1 timezone will be ''UTC" and for another user it can be something else like that? – Abhilash Reddy Dec 04 '18 at 07:24
  • ActiveRecord persists timestamps in UTC, so `2018-12-01 01:56:53 +0900` will be persisted as `2018-11-30 16:56:53`, and taking `date(timestamp)` would return `2018-11-30` – Stefan Rendevski Dec 04 '18 at 09:08
  • @AbhilashReddy all the users gonna have different timezones – Kick Buttowski Dec 04 '18 at 17:05
  • @StefanRendevski that is a good point but my db either shows different behavior or your point is right? Idk it is very confusing to me – Kick Buttowski Dec 04 '18 at 17:07
  • I am not sure if I understand your problem, but if I understand good, have you tried changing the default value of created_at from a migration with change_column_default (so it's not generated on database level), and then specify it when you create a new `UserStock` with the specific timezone, something like `UserStock.create(created_at: Time.now.in_time_zone('London').to_time)`. then you can make the query like that too, changing the `date` to `date.in_time_zone...`. maybe it's no perfect for your case, but might lead you to the complete solution using `in_time_zone` when needed – xploshioOn Dec 04 '18 at 22:04
  • @xploshioon thanks for the response. I posted my repo as well if it helps – Kick Buttowski Dec 04 '18 at 22:08
  • Time is time. When you display it is up to you. If you want to o query based on the time convert all queries to the server's time zone to ensure accuracy then display based on the User's timezone – engineersmnky Dec 05 '18 at 01:59
  • yes please if you have any suggestion – Kick Buttowski Dec 05 '18 at 02:52

2 Answers2

1

Did you try the query by specifying the range?

Because you're trying to find the user_stocks that are created on some date. But notice that created_at is a datetime object and I guess your date variable might be just a date object.

def stock_by_date(date)
  UserStock.where(created_at: date.midnight..date.end_of_day, user_id: current_user.id)
end
Abhilash Reddy
  • 1,499
  • 1
  • 12
  • 24
1

create a migration with this content to remove the default value to created_at, so it's not filled by default on database level.

change_column_default(:user_stocks, :created_at, nil)

after that, when you create a new UserStock, you need to specify the created_at value, and there you can specify the created_at with the date of the user, taking care of the timezone.

UserStock.create(created_at: Time.now.in_time_zone(user.time_zone).to_time...)

or you can just maybe add it to a callback on the model so it's automatic everytime you create a UserStock, it change the value

class UserStock < ActiveRecord::Base
  before_create :set_created_at

  private
    def set_created_at
      self.created_at = time.now.in_time_zone(self.user.time_zone).to_time
    end
end

so everytime you create one, the value is correct. with that it may work as expected.

Another option if you don't need it just for that specific model and use them for all the user interaction, you can create a before filter on the application controller to set the time zone dinamically, something like

class ApplicationController < ActionController::Base
  before_filter :set_time_zone

  def set_time_zone(&block)
    time_zone = current_user.try(:time_zone) || 'UTC'
    Time.use_zone(time_zone, &block)
  end
end
xploshioOn
  • 4,035
  • 2
  • 28
  • 37