Rails: How to parse date-time string into a specific time zone - ruby-on-rails

I'm using Rails 3.2 and ruby 1.9.3 on Debian. I have an app that collects a date, time, and timezone in the form of strings via an HTML form. Something like this:
start_date: "04-15-2010",
start_time: "10:00:00",
timezone: "Central Time (US & Canada)"
What I'd like to do is parse these 3 elements into a single date that is saved into my database as UTC, which in this case would add 7 hours to the start time, once it's in the UTC time zone.
So the stored time would be 17:00 once it's in the DB as UTC instead of the received Central time.
I have tried something like this to parse the date:
ActiveSupport::TimeZone[timezone].at DateTime.strptime("{ 2012-04-09 20:00:00 }", "{ %Y-%m-%d %H:%M:%S }").to_i
However, I'm not able to incorporate the time zone into the resulting time with %Z. It either doesn't parse or the time is interpreted as UTC not Central time. So my question is, how to coerce a date string into a certain time zone without changing the value of the actual date/time stored. I'd like to be able to parse the string into a date/time object that includes the correct time zone with it at that time so that future time zone conversions are accurate. I've looked all over and can't find a way to do this. It's strange, since this seems like something common one does with dates inputted from HTML forms. Thank you for any help.

Try this:
zone = "Central Time (US & Canada)"
ActiveSupport::TimeZone[zone].parse("2013-04-03 17:47:00")

Use String#in_time_zone (Rails 4+)
I personally prefer using String#in_time_zone:
>> '22.09.1986 10:30'.in_time_zone('Central Time (US & Canada)')
# => Mon, 22 Sep 1986 10:30:00 CDT -05:00
This parses the date and time in the String into the time zone provided.

%Z is the correct way to specify a Time zone name. Have you tried the following ?
date_and_time = '%m-%d-%Y %H:%M:%S %Z'
DateTime.strptime("04-15-2010 10:00:00 Central Time (US & Canada)",date_and_time)

This is the method that I came up with. Not the prettiest, but it works. Allows parsing the string using a specified format, and then turning it into the format that I know Time.zone.parse requires.
class ActiveSupport::TimeZone
def strptime(time, format='%m/%d/%Y')
formatted = Time.strptime(time, format).strftime('%Y-%m-%d %T')
parse(formatted)
end
end
Then you can do something like what was mentioned in another question, but with a specified format:
zone = "Central Time (US & Canada)"
ActiveSupport::TimeZone[zone].strptime('2013-04-03', '%Y-%m-%d')
Or if you already have a time zone set:
Time.zone = "Central Time (US & Canada)"
Time.zone.strptime('01/13/2006')
I used a default format of %m/%d/%Y because that's what my user input is most of the time. You can customize this to your needs, or use the default format DateTime uses which is believe is iso8601 (%FT%T%z)

I've finally found the dirty, yet definitive way to do this.
First, parse the string using plain Ruby Time.strptime like this:
time = Time.strptime('12 : 00 : PM', '%I : %M : %p')
This way you get the parsed Time, but not yet in correct timezone. To fix that, let's convert the time to string form and parse it with the standard ActiveSupport::TimeZone#parse
Time.zone.parse(time.to_s)
The result is the ActiveSupport::TimeWithZone with our time parsed into the correct timezone.
The reason why we have to do it this way is that neither ActiveSupport::TimeZone nor ActiveSupport::TimeWithZone support the strptime method. So we have to parse the Time with core Ruby strptime that does not have timezone information, convert it to format acceptable in ActiveSupport objects and then parse it yet again.

To have DateTime take the date string and attach a timezone other than UTC without changing the values of the date string , use this, its easy , doesnt break on leap day :)
xx = DateTime.strptime("9/1/15 #{object.time_zone}", "%m/%d/%Y %Z")

Convert specific date format in UTC.
ActiveSupport::TimeZone['UTC'].parse(Time.strptime('01/24/2019T16:10:16', "%m/%d/%YT%H:%M:%S").asctime)

Related

Rails: Converting created_at to PST

I want to convert the created_at to this format January 12, 2:00 PM PST.
The first step I tried was to convert the created_at field to Pacific Standard Time (PST).
However, I'm stuck - I can't even get past this step.
I've tried these, but neither worked:
Time.parse(self.created_at).in_time_zone('Pacific Time (US & Canada)')
time = Time.parse(self.created_at)
time.in_time_zone('Pacific Time (US & Canada)')
I receive no implicit conversion of ActiveSupport::TimeWithZone into String when I do this.
I based it on these questions:
How do you convert the following time from UTC to EST in Ruby (without Rails)?
How to convert time from UTC to PST in rails
Since it is already a datetime, you should be able to just do this to convert it:
self.created_at.in_time_zone('Pacific Time (US & Canada)')
Don't forget to save the new time zone to the record in the database.
Time.parse() is meant to convert strings into datetime(https://apidock.com/ruby/v2_5_5/Time/parse/class).

Convert particular timezone to UTC timezone

For example my local time_zone is Canada and i am getting date in Chennai timezone. How can I convert the Chennai timezone to UTC?
Using server time
You can call ActiveSupport::TimeWithZone#utc on the result.
Example:
Time.zone.now.utc
Keep in mind that .now is using the server time zone when config.time_zone is set.
When you receive a time
If you are getting the datetime in Chennai timezone it will probably look like this: 2019-11-26T12:20:00.655+05:30
To convert it in different time zone, use:
time = Time.parse("2019-11-26T12:20:00.655+05:30")
pacific_time = time.in_time_zone("Pacific Time (US & Canada)")
# and then UTC if you want
pacific_time.utc

Converting a date string into a time zone specific format

Rails 5.2.3
I have a date:
"Apr-03-2013 17:47:00"
I have a date zone:
"America/Los_Angeles"
I am trying to turn it into a string:
"Apr-03-2013 17:47:00 Pacific Daylight Time (GMT-07)"
The best I can come up with is:
time_obj = ActiveSupport::TimeZone["America/Los_Angeles"].parse("2013-04-03 17:47:00")
Time.at(time_obj).strftime("%b-%d-%Y %H:%M:%S %Z")
Which gives me:
"Apr-03-2013 17:47:00 PDT"
Any ideas?
I believe you need custom logic and/or you own database of timezones to get it exactly like that.
Using %Z with strftime is going to give you what ever your OS likes and there are a few disclaimers in the ruby docs.
One idea that you might get some mileage out of: If you are starting with a time zone identifier like "America/Los_Angeles" then you can use ActiveSupport::TimeZone::MAPPING to get a friendlier name, or at least a Rails time zone name.
eg:
ActiveSupport::TimeZone::MAPPING.key("America/Los_Angeles")
=> "Pacific Time (US & Canada)"
But that won't work for every identifier:
ActiveSupport::TimeZone::MAPPING.key("America/Detroit")
=> nil
You can see which ones will map like this:
TZInfo::Country.get('US').zone_identifiers.map {|ident| [ident, ActiveSupport::TimeZone::MAPPING.key(ident)] }
So in that case you need to fall back to the identifier you have, or perhaps this approach might work.
Then you'd need to deal with the daylight savings part, here you can use dst?
ActiveSupport::TimeZone["America/Los_Angeles"].parse("2013-04-03 17:47:00").dst?
Then you'd need to splice all that together! ... and add the offset as well.

Why can't I parse a date string saved to a variable in Ruby?

I need to run the following code in my Rails app:
ActiveSupport::TimeZone["Central Time (US & Canada)"].parse(game.date).utc.to_date.strftime("%_m/%d")[1..-1]
Where game is #games.each do |game|
But this doesn't work, I get the error, TypeError: no implicit conversion of ActiveSupport::TimeWithZone into String.
However, I can run:
ActiveSupport::TimeZone["Central Time (US & Canada)"].parse("2014-04-11 12am").utc.to_date.strftime("%_m/%d")[1..-1]
which returns "4/11"
How can I use the above code with `game.date' instead of the hard coded string?
EDIT
a Game object looks like the following (from db/seeds.rb):
Game.create(id: 9, date: "2014-04-11 12am", time: "705PM", opponent: "Jacksonville", away: false, event: "friday night fireworks")
EDIT 2
In the rails console when I do game.date it returns:
Fri, 11 Apr 2014 00:00:00 UTC +00:00
so it seems its not a string.
To make what you're trying to do work, you need to convert your date to a string with to_s:
ActiveSupport::TimeZone["Central Time (US & Canada)"].parse(game.date.to_s).utc.to_date.strftime("%_m/%d")[1..-1]
However, you should consider whether this is really what you want to do. As it stands now, this code is taking a date, converting it to a string, parsing the string to get back to the date, then converting it to a string a second time. Are you sure you couldn't get by with something like this?
game.date.strftime(%_m/%d")[1..-1]
ActiveSupport::TimeZone.parse needs a string and not a Date object example below:
ActiveSupport::TimeZone["Central Time (US & Canada)"].parse(Date.current.to_s).utc.to_date.strftime("%_m/%d")[1..-1]
#=> "4/11"
so change:
ActiveSupport::TimeZone["Central Time (US & Canada)"].parse(game.date).utc.to_date.strftime("%_m/%d")[1..-1]
to:
ActiveSupport::TimeZone["Central Time (US & Canada)"].parse(game.date.to_s).utc.to_date.strftime("%_m/%d")[1..-1]
You can use below string:
As refereed to documentation http://rubyinrails.com/2013/09/strftime-format-time-in-ruby/
game.date.strftime("%Y-%m-%d %I:%M%P")
#output=> "2014-04-11 12am"
So in your loop you can use:
ActiveSupport::TimeZone["Central Time (US & Canada)"].parse(game.date.strftime("%Y-%m-%d %I:%M%P")).utc.to_date.strftime("%_m/%d")[1..-1]

How to parse date such that it is in EST

I am using Rails 3.1. Here is my configuration
config.time_zone = "Eastern Time (US & Canada)"
Now user enters Nov 30, 2011 at 7:00 PM. How do I parse this text so that after parsing I get the result in EST?
You can do so by calling time = ActiveSupport::TimeZone.new('EST').parse('Nov 30, 2011 at 7:00 PM'). Also you can parse the time string as being in user-specific or default timezone by calling Time.zone.parse. You can convert the result time into any timezone thanm i.e. Time.zone.now.in_time_zone('Asia/Yekaterinburg')
Also there is no need in do any manual conversions of timezones before storing the time to database as Rails does is automatically.
You should store all values in UTC and render them in user's local time. "Eastern Time" is a loose concept at best and changes on a fairly regular basis as politicians decide to extend or contract Daylight Saving Time.
Generally you can do this with the ActiveSupport::TimeZone methods local_to_utc and utc_to_local conversion methods.

Resources