Convert string to datetime in specific timezone - ruby-on-rails

I'm using datetimepicker and need to save the string datetime obtained from params to a datetime in a specific, user dependent time zone. It will allow me to save proper UTC datetime to database.
params[:notify_at] #=> "2014-07-05 14:30:00"
user.time_zone #=> #<ActiveSupport::TimeZone:0x00000007535ac8 #name="Warsaw", #utc_offset=nil, #tzinfo=#<TZInfo::TimezoneProxy: Europe/Warsaw>, #current_period=nil>
And I would like to do something like:
date = params[:notify_at].to_datetime(user.time_zone) #=> Sat, 05 Jul 2014 12:30:00 +0000
(its 14:30 in user's localtime but 12:30 in UTC)

You can use the in_time_zone method. For example:
DateTime.current.in_time_zone("Alaska")
# => Fri, 23 May 2014 07:21:30 AKDT -08:00
So for your use case:
params[:notify_at].to_datetime.in_time_zone(user.time_zone)
Pro Tip: If using Rails v4+ you can actually do this directly on the string:
"2014-07-05 14:30:00".in_time_zone("Alaska")
# => Sat, 05 Jul 2014 14:30:00 AKDT -08:00
UPDATE
You can parse a string directly into a time zone (where the String should already be IN that time zone) like this:
Time.zone.parse("2014-07-05 14:30:00")
# => Sat, 05 Jul 2014 14:30:00 CEST +02:00
So for your use case do:
user.time_zone.parse(params[:notify_at])

Try this:-
date = params[:notify_at].to_datetime.in_time_zone(user.time_zone)
Rails console output:-
1.9.3p385 :004 > d= "2014-07-05 14:30:00"
=> "2014-07-05 14:30:00"
1.9.3p385 :010 > d.to_datetime.in_time_zone("Pacific Time (US & Canada)")
=> Sat, 05 Jul 2014 07:30:00 PDT -07:00
1.9.3p385 :011 > d.to_datetime.in_time_zone("Alaska")
=> Sat, 05 Jul 2014 06:30:00 AKDT -08:00
1.9.3p385 :012 > to_datetime.in_time_zone

Related

strftime(%Z) returns wrong result

I have this date
date = Mon, 15 Aug 2016 13:00:00 UTC +00:00
which is ActiveSupport::TimeWithZone class
Then, I need to get the time in time zone "Fiji"
start_in_time_zone = date.in_time_zone("Fiji")
This returns Tue, 16 Aug 2016 01:00:00 +12 +12:00
Then, I need to present the date with the name of the time zone, so
time_zone_abbr = start_in_time_zone.strftime("%Z")
It should return "FJT"
but returns "+12"
Any idea why?
I am using ruby 2.3.7 and rails 4.2.7
UPDATE
If I do
start_in_time_zone = date.in_time_zone("Madrid")
it returns
"CEST"
UPDATE 2
I have tried to see where the problem is by setting different time.
date=Time.utc(2018, 07, 25, 20, 30, 45)
date.class #=> Time
date.in_time_zone("Madrid") #=> Wed, 25 Jul 2018 22:30:45 CEST +02:00
date.in_time_zone("Fiji") #=> Thu, 26 Jul 2018 08:30:45 +12 +12:00
date.in_time_zone("EST") #=> Wed, 25 Jul 2018 15:30:45 EST -05:00
Sadly, it seems there is no 'FJT' abbreviation assigned to 'Fiji' in timezone data used by Rails. Also, support for those abbreviations seems patchy regarding Pacific timezones.
irb(main):002:0> DateTime.now.in_time_zone('Samoa').strftime('%Z')
=> "+13"
irb(main):003:0> DateTime.now.in_time_zone('Midway Island').strftime('%Z')
=> "SST"
irb(main):004:0> DateTime.now.in_time_zone('Samoa').strftime('%Z')
=> "+13"
irb(main):005:0> DateTime.now.in_time_zone('Tokelau Is.').strftime('%Z')
=> "+13"
irb(main):006:0> DateTime.now.in_time_zone('Wellington').strftime('%Z')
=> "NZST"
UTC offset is displayed as fallback. If it's any help, remember that full name and additional information can be retrieved with .time_zone.tzinfo on ActiveSupport::TimeWithZone objects. 'FJ' code is recognized by TZInfo::Country.
irb(main):056:0> TZInfo::Country.get('FJ')
=> #<TZInfo::Country: FJ>
irb(main):057:0> TZInfo::Country.get('FJ').zone_info
=> [#<TZInfo::CountryTimezone: Pacific/Fiji>]

Support to convert time zone to IST from any other time zone in Ruby

I,m using following versions of Ruby and Rails
Ruby : 2.0.0p481 &
Rails : 4.1.1
Could not find any method in DateTime class to convert time to IST.
Tried DateTime.in_time_zone in IRB, could convert IST to EST but not vise versa.
2.0.0-p481 :003 > date = "Thu Jan 07 2016 16:20:00 GMT+0530 (India Standard Time)"
2.0.0-p481 :003 > date = date.in_time_zone('Eastern Time (US & Canada)')
2.0.0-p481 :003 > date
=> Thu, 07 Jan 2016 05:50:00 EST -05:00
Wanted to know how can I convert other tine zones to IST.
Please let me know if there is any way from which I can achive this.
ActiveSupport::TimeZone provide the names of all timezones. You can list out all timezones by doing
ActiveSupport::TimeZone.all.map(&:name)
For just US timezones
ActiveSupport::TimeZone.us_zones.map(&:name)
So change your time with the available timezones. Like this
irb> date = DateTime.now
=> Thu, 31 Dec 2015 22:15:59 +0530
# convert to EST
irb> date_est = date.in_time_zone("Eastern Time (US & Canada)")
=> Thu, 31 Dec 2015 11:45:59 EST -05:00
# convert to IST
irb> date_ist = date_est.in_time_zone("Chennai")
=> Thu, 31 Dec 2015 22:15:59 IST +05:30
Happy coding...

Rails: DateTime of different time zones

I have a project model with a datetime attribute to define the deadline. The deadlines are of different time zones, and I receive them in a string format like below:
Jan 1st 2013 00:00:00 EST
Feb 9th 2013 23:59:00 PST
I want to store these values in the default UTC format in the database. I've seen that there are many options to parse the time like Time.zone.parse and Time.parse. My question is: what's the best practice to parse the datetime of different time zones? I'm using Rails 3.2.9.
You need not worry about that at all, as long as you set correct timezone in config/application.rb:
config.time_zone = 'UTC'
You just assign the time strings to the attributes, ActiveRecord will convert it correctly.
1.9.3p125 :002 > project.deadline = "Jan 1st 2013 00:00:00 EST"
=> "Jan 1st 2013 00:00:00 EST"
1.9.3p125 :003 > project.deadline
=> Tue, 01 Jan 2013 05:00:00 UTC +00:00
1.9.3p125 :004 > project.deadline = "Feb 9th 2013 23:59:00 PST"
=> "Feb 9th 2013 23:59:00 PST"
1.9.3p125 :005 > project.deadline
=> Sun, 10 Feb 2013 07:59:00 UTC +00:00
ActiveRecord uses Time.zone.parse to parse the strings internally.
When you run Time.parse it will convert the timestamp to your configured timezone in rails. For example, my rails app runs in EST.
[5] pry(main)> Time.parse('Jan 1st 2013 00:00:00 EST')
=> 2013-01-01 00:00:00 -0500
[6] pry(main)> Time.parse('Feb 9th 2013 23:59:00 PST')
=> 2013-02-10 02:59:00 -0500
Notice the +3:00hrs for the PST timestamp used to get the result into my EST timezone.
To get the UTC version of each timestamp, just call utc
[7] pry(main)> Time.parse('Jan 1st 2013 00:00:00 EST').utc
=> 2013-01-01 05:00:00 UTC
[8] pry(main)> Time.parse('Feb 9th 2013 23:59:00 PST').utc
=> 2013-02-10 07:59:00 UTC

Is Rails time broken for midnight?

Let's look at the date:
1.9.2p320 :008 > Date.today
=> Wed, 03 Oct 2012
1.9.2p320 :009 > Time.now
=> 2012-10-03 22:32:55 -0400
Now, given that when is midnight?
1.9.2p320 :005 > Date.today.midnight
=> Wed, 03 Oct 2012 00:00:00 UTC +00:00
Makes sense. But what about yesterday?
1.9.2p320 :006 > Date.yesterday.midnight
=> Wed, 03 Oct 2012 00:00:00 UTC +00:00
Uh, that doesn't quite make sense. Midnight today is the same as midnight yesterday? You can't be serious!
1.9.2p320 :026 > Date.today.midnight == Date.yesterday.midnight
=> true
1.9.2p320 :033 > 1.day.ago.midnight == Date.yesterday.midnight
=> true
1.9.2p320 :034 > 1.day.ago.midnight == Date.today.midnight
=> true
Oh, you are serious. What about tomorrow?
1.9.2p320 :007 > Date.tomorrow.midnight
=> Fri, 05 Oct 2012 00:00:00 UTC +00:00
Wait, if midnight today is 00:00 on the 3rd, and midnight yesterday is 00:00 on the 3th, but midnight tomorrow is 00:00 on the 5th, where's 00:00 on the 4th?
Here it is:
1.9.2p320 :010 > 0.days.ago
=> Thu, 04 Oct 2012 02:34:58 UTC +00:00
1.9.2p320 :011 > 0.days.ago.midnight
=> Thu, 04 Oct 2012 00:00:00 UTC +00:00
but isn't zero days ago today? Apparently not.
Is it me, or is this not at all internally consistent? It seem to me that Date.today should be the same as 0.days.ago.
I understand that days.ago is actually using the Time object, and that this is a time zone issue:
1.9.2p320 :030 > Date.today
=> Wed, 03 Oct 2012
1.9.2p320 :021 > Time.now
=> 2012-10-03 22:40:09 -0400
1.9.2p320 :023 > 0.days.ago
=> Thu, 04 Oct 2012 02:40:22 UTC +00:00
1.9.2p320 :022 > Time.zone.now
=> Thu, 04 Oct 2012 02:40:14 UTC +00:00
But it seems as though, given that these are convenience functions, it's kind of mean to throw a timezone assumption into one convenience function and not throw it into another convenience function, both of which, by all accounts, mean the same thing.
Even setting that aside, it doesn't seem to explain the fact that Date.today.midnight == Date.yesterday.midnight, which is– quite simply– barking mad.
Since I know that I can't be the first to have been bitten by this, I ask what am I missing?
Rails will base relative date calculations such as yesterday, tomorrow, and midnight off of Date.current which will attempt to use the configured Time.zone: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/date/calculations.rb#L46
Since your Time.zone is set to UTC, you won't get the same results as calculations based off Date.today, which will use your computer's clock time, unless you're actually sitting in UTC time.
So, if the time difference between you and UTC is greater than the time to midnight, Date.yesterday and Date.today actually return the same date!
Try setting your Rails time zone with Time.zone = 'Eastern Time (US & Canada)' or whatever time zone you're in and retry your examples.
rossta identified the culprit. You may have better luck with Time.now.to_date and specifying the time zone if necessary:
> Time.now.in_time_zone("Asia/Tokyo")
=> Thu, 04 Oct 2012 12:54:43 JST +09:00
> Time.now.in_time_zone("Asia/Tokyo").to_date.midnight
=> Thu, 04 Oct 2012 00:00:00 JST +09:00
Time.now.in_time_zone("Asia/Tokyo").to_date.yesterday.midnight
=> Wed, 03 Oct 2012 00:00:00 JST +09:00
> Time.zone = "America/Los_Angeles"
=> "America/Los_Angeles"
> Time.now.in_time_zone
=> Wed, 03 Oct 2012 20:55:35 PDT -07:00
> Time.now.to_date # using the system time
=> Wed, 03 Oct 2012
> Time.now.to_date.midnight
=> Wed, 03 Oct 2012 00:00:00 PDT -07:00
> Time.now.to_date.yesterday.midnight
=> Tue, 02 Oct 2012 00:00:00 PDT -07:00

In Rails, what's the nicest way to create a specific time (not now) in a particular time zone?

Here's one way, but can you think of a more idiomatic way?
>> Time.use_zone('Sydney'){ Time.zone.parse('2011-04-12 2pm') }
=> Tue, 12 Apr 2011 14:00:00 EST +10:00
I think you're looking for
Time.find_zone('Alaska').local(2011,1,1)
=> Sat, 01 Jan 2011 00:00:00 AKST -09:00
Time.find_zone('Amsterdam').local(2011,1,1)
=> Sat, 01 Jan 2011 00:00:00 CET +01:00
Time.find_zone('Sydney').local(2011,1,1)
=> Sat, 01 Jan 2011 00:00:00 EST +11:00
Time.find_zone('Wellington').local(2011,1,1)
=> Sat, 01 Jan 2011 00:00:00 NZDT +13:00
This also works with parse
Time.find_zone('Sydney').parse('2011-04-12 2pm')
=> Tue, 12 Apr 2011 14:00:00 EST +10:00
For parsing a date within a specific time zone, you can use ActiveSupport::TimeZone
> ActiveSupport::TimeZone["Sydney"].parse("2011-04-12 2pm")
=> Tue, 12 Apr 2011 14:00:00 EST 10:00
TimeZone API documentation is here:
http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html#method-c-5B-5D
This is what I use:
Time.zone.local(2011, 4, 12, 14, 0)
As said above, to create a time in a specific timezone (e.g., 4/10/2014 1:30pm New York):
#event.start_time = Time.find_zone('Eastern Time (US & Canada)').local(2014,4,10,13,30)
=> Thu, 10 Apr 2014 13:30:00 EDT -04:00
#event.start_time.utc
=> 2014-04-10 17:30:00 UTC
When it is saved to your db, it will be converted to UTC (in Postgres at least if using a timestamp type in your migration), and on future access it will be displayed relative to the application timezone set in config/application.rb
To properly display the local time, we also store the timezone name (e.g., 'Eastern Time (US & Canada)' ) in our database. So, when we want to print the time in our views, we do...
#event.start_time.in_time_zone(#event.timezone)
=> Thu, 10 Apr 2014 13:30:00 EDT -04:00
To get the abbreviated timezone (e.g., EST)
#event.start_time.in_time_zone(#event.timezone).zone
=> "EDT"
How about using the *in_time_zone* helper..
Time.now.in_time_zone('Sydney')

Resources