Ruby:Change UTC time zone to UTC+ or UTC - zone - ruby-on-rails

I want to convert the utc time to 'utc+1' or 'utc+something' time zone and convert back that to utc time zone.
Its something like that what i want to do. I am asking user to choose there UTC time zone ex: utc+4 .and i am getting current UTC time using Time.now.utc .Now i wwant to convert this utc time to 'utc+4'.
And after displaying that time to 'utc+4' i want to convert back that time equivalent utc time zone.
How this can be done?

If you are working with Ruby On Rails and you want to change time zone per request and reset it back after finishing the request. You can use Time.use_zone to set the time zone for the user (document: http://api.rubyonrails.org/classes/Time.html#method-i-use_zone)
The following is what I have tested in Rails 4.1.
First, It is recommended to set the sane default for config.time_zone (in config/application.rb), I set to "Mumbai" (UTC+5.30) for instance. (to list time zones, you can use command bundle exec rake time:zones:all)
module MyApp
class Application < Rails::Application
config.time_zone = 'Mumbai'
end
end
In your project, run rails g model User name:string time_zone:string
And bundle exec rake db:migrate
Then, create some test users via rails console, run rails c
Loading development environment (Rails 4.1.4)
irb(main):001:0> first_user = User.create!(name: 'zdk', time_zone: 'Bangkok')
(0.1ms) begin transaction
SQL (0.4ms) INSERT INTO "users" ("created_at", "name", "time_zone", "updated_at") VALUES (?, ?, ?, ?) [["created_at", "2014-07-31 09:21:13.750710"], ["name", "zdk"], ["time_zone", "Bangkok"], ["updated_at", "2014-07-31 09:21:13.750710"]]
(0.6ms) commit transaction
=> #<User id: 1, name: "zdk", time_zone: "Bangkok", created_at: "2014-07-31 09:21:13", updated_at: "2014-07-31 09:21:13">
irb(main):002:0> second_user = User.create!(name: 'joe', time_zone: 'London')
(0.1ms) begin transaction
SQL (0.8ms) INSERT INTO "users" ("created_at", "name", "time_zone", "updated_at") VALUES (?, ?, ?, ?) [["created_at", "2014-07-31 09:21:31.299606"], ["name", "joe"], ["time_zone", "London"], ["updated_at", "2014-07-31 09:21:31.299606"]]
(1.9ms) commit transaction
=> #<User id: 2, name: "joe", time_zone: "London", created_at: "2014-07-31 09:21:31", updated_at: "2014-07-31 09:21:31">
irb(main):003:0>
Try to query what we just created, you can see that it uses time zone that you have set in Application config (config.time_zone).
The output:
irb(main):003:0> first_user.created_at
=> Thu, 31 Jul 2014 14:51:13 IST +05:30
irb(main):005:0> second_user.created_at
=> Thu, 31 Jul 2014 14:51:31 IST +05:30
And how to handle per request basis time zone using Time.zone.
Go to your ApplicationController (app/controllers/application_controller.rb file).
Create a method that set time zone called by around_filter ( More details: http://www.elabs.se/blog/36-working-with-time-zones-in-ruby-on-rails ). I also create hello action will be routed from root url. Like so:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
around_filter :set_time_zone
def set_time_zone
if true #if user loggin ?
#user = User.first #Change to User.last to see the result.
Time.use_zone(#user.time_zone) { yield }
else
yield
end
end
def hello
render plain: "Hello, user: #{#user.name}. Created: #{#user.created_at}"
end
end
Routing application uri to your controller method by editing your config routes (config/routes.rb) to have following this
Rails.application.routes.draw do
root 'application#hello'
end
If you set everything correctly. You should have the output in this format
for the first user: Hello, user: zdk. Created: <Date> <Time> +0700
for the second user: Hello, user: joe. Created: <Date> <time> +0100
In summary, the flow is something like:
+------------+
| APP |
+------------+
+ Use the Time.zone value instead if it's set
| ^
WRITE |
v | READ
+
Convert to UTC Convert from UTC to config.time_zone
+ ^
| |
WRITE | READ
| |
v +
+--------------------------------+
| |
| DB |
| |
| UTC |
+--------------------------------+
Hope this helps.

You can use ActiveSupport::TimeWithZone
Example :
#Define a TimeZone
Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
#Define a utc time
t = Time.utc(2007, 2, 10, 20, 30, 45) # => '2007-02-10 20:30:45 UTC
#same time with gmt -5
t.in_time_zone # => Sat, 10 Feb 2007 15:30:45 EST -5:00

Related

Issue Formatting DateTime in Rails

So my rails application supports different time zones and sets the time zone at each request like so:
def set_current_time_zone
Time.zone = current_user.time_zone
end
All of my formatting and reports site wide work except for a jQuery date time selector.
The form submits a start time in the format of 04/20/2018 5:23 AM. When I parse the time with the code below I get the following output.
# this is the value of params[:job][:start] 04/20/2018 5:23 AM
job.start = DateTime.strptime(params[:job][:start],"%m/%d/%Y %H:%M %p")
#printing job.start outputs this: 2018-04-20 00:23:00 -0500
As you'll notice the time zone has been applied to the time and is now the wrong time. How do I correct this?
Thanks in advance.
Keep in mind DateTime.strptime('04/20/2018 5:23 AM',"%m/%d/%Y %H:%M %p") will return the value in UTC =>Fri, 20 Apr 2018 05:23:00 +0000
However if you store this value say to a database field, in an active record object, it will output the timezone value to whatever value you've set to Time.zone
This may not be a complete answer but could lead you in the right direction.
Add some more details to my last comment above.
I did this in rails console, my model is Shift and has start_time: datetime.
2.1.3 :022 > time = "04/02/2018 5:23 AM"
=> "04/02/2018 5:23 AM"
2.1.3 :023 > Time.zone = "Singapore"
=> "Singapore"
2.1.3 :024 > shift.start_at = time
=> "04/02/2018 5:23 AM"
2.1.3 :025 > shift.save
(0.1ms) begin transaction
SQL (0.5ms) UPDATE "shifts" SET "start_at" = ?, "updated_at" = ? WHERE "shifts"."id" = 2 [["st
art_at", "2018-02-03 21:23:00.000000"], ["updated_at", "2018-05-02 05:43:38.527338"]]
(1.4ms) commit transaction
=> true
2.1.3 :026 > Time.zone = "Dublin"
=> "Dublin"
2.1.3 :027 > shift.start_at = time
=> "04/02/2018 5:23 AM"
2.1.3 :028 > shift.save
(0.2ms) begin transaction
SQL (0.4ms) UPDATE "shifts" SET "start_at" = ?, "updated_at" = ? WHERE "shifts"."id" = 2 [["st
art_at", "2018-02-04 05:23:00.000000"], ["updated_at", "2018-05-02 05:45:44.638291"]]
(1.4ms) commit transaction
=> true
See the data inside the SQL.

How to set timezone for rails and sqlite?

The test app:
$ rails new ra1
$ cd ra1
$ ./bin/rails g model m1 dt1:datetime
$ ./bin/rake db:migrate
Then I add config.time_zone = 'Europe/Kiev' into config/application.rb, run console:
irb(main):001:0> M1.create dt1: Time.now
(0.1ms) begin transaction
SQL (0.3ms) INSERT INTO "m1s" ("dt1", "created_at", "updated_at") VALUES (?, ?, ?) [["dt1", "2015-03-30 11:11:43.346991"], ["created_at", "2015-03-30 11:11:43.360987"], ["updated_at", "2015-03-30 11:11:43.360987"]]
(33.0ms) commit transaction
=> #<M1 id: 3, dt1: "2015-03-30 11:11:43", created_at: "2015-03-30 11:11:43", updated_at: "2015-03-30 11:11:43">
irb(main):002:0> Time.now
=> 2015-03-30 14:12:27 +0300
irb(main):003:0> Rails.configuration.time_zone
=> "Europe/Kiev"
What am I doing wrong?
Values in the database are always stored in UTC, no matter the time_zone.
The time zone configuration affects only the Ruby environment. The data is fetched from the database and the dates are converted into the selected timezone. The same applies to new time instances, as you noticed using Time.now.
The main reasons the time is normalized in the database is that it allows to easily convert the same value to multiple timezones, or change the timezone after the initial phase of the project without the need to reconvert all the dates. It's a good practice to use UTC in the database.

Time.new subtracting 6 hours AND tacking on CST -6 on Heroku?

I'm sure there's some conversion thing I'm looking over here.
On Heroku's console,
irb(main):052:0> Time.new(2014, 1, 21)
=> 2014-01-21 00:00:00 +0000
However, setting a column to that:
irb(main):042:0> PressRelease.first.update_attribute :published_on, Time.new(2014, 1, 21)
PressRelease Load (1.9ms) SELECT "press_releases".* FROM "press_releases" ORDER BY created_at DESC LIMIT 1
(1.0ms) BEGIN
FriendlyId::Slug Load (0.8ms) SELECT "friendly_id_slugs".* FROM "friendly_id_slugs" WHERE "friendly_id_slugs"."sluggable_id" = 1 AND "friendly_id_slugs"."sluggable_type" = 'PressRelease' ORDER BY "friendly_id_slugs".id DESC LIMIT 1
(0.6ms) COMMIT
=> true
Gives this date:
=> Mon, 20 Jan 2014 18:00:00 CST -06:00
To clarify, application.rb does indeed have the time zone set:
config.time_zone = 'Central Time (US & Canada)'
And when I check on their console:
irb(main):054:0> Time.zone
=> (GMT-06:00) Central Time (US & Canada)
However, doing this locally works fine:
1.9.3-p448 :011 > Time.new(2014, 1, 21)
=> 2014-01-21 00:00:00 -0600
So, it looks like Heroku is subtracting -6 (since our Time Zone is set to CST -6), then tacking on the timezone of CST -6 as well. Why? This is, as you can see, messing up date-specific items.
If you want Rails to use the time_zone setting, you need to use the wrappers provided by ActiveSupport::TimeZone. See docs: http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html
Here's an example:
Time.zone.parse("2014-1-21")
# Outputs Tue, 21 Jan 2014 00:00:00 MST -07:00

Rails Postgresql Time Column

I've got 2 Time Without Time Zone columns in my table (start_time and end_time). When an event occurs, I need to see which of these columns has a start_time < Time.now < end_time. The problem seems to be that Rails is not saving the Time in UTC and is instead saving the time exactly as I enter it in my field. My local timezone is UTC -5, so if I put in an end_time of 09:00:00, I would expect that to get saved to the database as 14:00:00, but it's getting saved as 09:00:00, so when I later run my start_time < Time.now < end_time query, I'm getting the wrong results (I'm off by those 5 hours).
Here's an example (the model is called TimeParameter):
1.9.3-p392 :001 > TimeParameter.create(start_time: "00:30:00", end_time: "09:00:00")
(7.0ms) SELECT * FROM geometry_columns WHERE f_table_name='time_parameters'
(0.2ms) BEGIN
SQL (9.8ms) INSERT INTO "time_parameters" ("created_at", "day_of_week", "end_time", "start_time", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["created_at", Tue, 23 Jul 2013 14:29:06 UTC +00:00], ["day_of_week", nil], ["end_time", 2000-01-01 09:00:00 UTC], ["start_time", 2000-01-01 00:30:00 UTC], ["updated_at", Tue, 23 Jul 2013 14:29:06 UTC +00:00]]
(308.9ms) COMMIT
=> #<TimeParameter id: 24, start_time: "2000-01-01 00:30:00", end_time: "2000-01-01 09:00:00", day_of_week: nil, created_at: "2013-07-23 14:29:06", updated_at: "2013-07-23 14:29:06">
Local time is ~09:30, UTC is ~14:30
Notice that the updated_at and created_at reflect the 14:30 UTC time but my start_time and end_time are not converted to UTC before being saved.
Ruby version: 1.9.3-p392
Rails version: 3.2.13

Rails config.time_zone and datetime attribute update

In my application I have: config.time_zone = 'Warsaw'
A strange issue I have, is that it seems like Rails are having problems with comparision of datetime fields.
If I change the datetime 1 hour back (and Warsaw is currently in timezone +0100), Rails won't update the database, even if the field has changed. However, if I change the field once again, then the update will go to the database.
Example:
(Rails 3.1.0, ruby-1.9.2-p290, fresh rails app):
$ rails g model User starts_at:datetime
$ rake db:migrate
$ rails c
Loading development environment (Rails 3.1.0)
ruby-1.9.2-p290 :001 > u = User.create({:starts_at => "2011-01-01 10:00"})
SQL (21.3ms) INSERT INTO "users" ("created_at", "starts_at", "updated_at") VALUES (?, ?, ?) [["created_at", Tue, 13 Dec 2011 11:32:50 CET +01:00], ["starts_at", Sat, 01 Jan 2011 10:00:00 CET +01:00], ["updated_at", Tue, 13 Dec 2011 11:32:50 CET +01:00]]
=> #<User id: 1, starts_at: "2011-01-01 09:00:00", created_at: "2011-12-13 10:32:50", updated_at: "2011-12-13 10:32:50">
ruby-1.9.2-p290 :002 > u.starts_at
=> Sat, 01 Jan 2011 10:00:00 CET +01:00 # datetime created
ruby-1.9.2-p290 :003 > u.starts_at = "2011-01-01 09:00:00" # new datetime with one hour back
=> "2011-01-01 09:00:00"
ruby-1.9.2-p290 :004 > u.starts_at
=> Sat, 01 Jan 2011 09:00:00 CET +01:00 # changed datetime
ruby-1.9.2-p290 :005 > u.save
=> true
ruby-1.9.2-p290 :006 > u.starts_at = "2011-01-01 09:00:00"
=> "2011-01-01 09:00:00"
ruby-1.9.2-p290 :007 > u.save
(0.3ms) UPDATE "users" SET "starts_at" = '2011-01-01 08:00:00.000000', "updated_at" = '2011-12-13 10:33:17.919092' WHERE "users"."id" = 1
=> true
I've tested it in this fresh app, because I have a problem with this in larger application. What is going on? I've tried to browse the Rails code, tried to re-copy the relevant code 'by-hand' in console (like update, assign_attributes, even checked time_zone_conversion) and it worked, but not in 'real world'..
looks like you stumbled on a similar issue.
The problem appears to be here:
https://github.com/rails/rails/blob/3-1-stable/activerecord/lib/active_record/attribute_methods/dirty.rb#L62
When rails it testing if the value was changed it compares old & new:
old = From cache (which is Time in your current timezone)
new = Time in UTC (+00:00) as saved in the database
If the difference in time is the UTC offset, the above erroneously succeeds (luckly the new cached value holds the intended change).
The next save/update compares with the new (and correct) cached value and marks the field as changed.
EDIT:
Done some tests, this works well for me:
https://github.com/rails/rails/blob/3-1-stable/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb#L50
Change
write_attribute(:#{attr_name}, original_time)
to
write_attribute(:#{attr_name}, time.in_time_zone('UTC').to_s)
Boris

Resources