In my rails4 app, I have date given in two strings, one gives timestamp, while other gives which timezone this is in:
a = "04/23/2014 04:00"
b = "Eastern Time (US & Canada)"
I want to convert this date to utc, so that I can save it in UTC
"2014-04-23 08:00:00 UTC"
What is the best way to do it?
Using the strptime method on the Rails DateTime class, you can parse a DateTime object from a string containing both the time and timezone (timezone is passed via the %z directive). From there, you can convert the time to UTC:
a = "04/23/2014 04:00"
b = "Eastern Time (US & Canada)"
datetime_with_tz = DateTime.strptime([a, b].join(' '), "%m/%d/%Y %H:%M %z")
#=> Wed, 23 Apr 2014 04:00:00 -0500
datetime_with_tz.utc
#=> Wed, 23 Apr 2014 09:00:00 +0000
Use the in_time_zone method of the DateTime class
Loading development environment (Rails 2.3.2)
>> now = DateTime.now.utc
=> Sun, 06 Sep 2009 22:27:45 +0000
>> now.in_time_zone('Eastern Time (US & Canada)')
=> Sun, 06 Sep 2009 18:27:45 EDT -04:00
>> quit
So for your particular example
a.in_time_zone('Eastern Time (US & Canada)')
Related
I am trying to covert the UTC time "2018-04-02T14:30:00Z" to EST in pure Ruby. I noticed the following discrepancy. If I parse the time in UTC using Rails and then add the EST zone_offset I get a different time than using the "in_time_zone" helper. 9:30 versus 10:30.
2.2.4 :001 > t = Time.parse "2018-04-02T14:30:00Z"
=> 2018-04-02 14:30:00 UTC
2.2.4 :002 > t + Time.zone_offset("EST")
=> 2018-04-02 09:30:00 UTC
2.2.4 :003 > t.in_time_zone('Eastern Time (US & Canada)')
=> Mon, 02 Apr 2018 10:30:00 EDT -04:00
Rails does time zones really well, and you're going to have a difficult time replicating the results of ActiveSupport::TimeWithZone#in_time_zone without just using it.
For example, as you've pointed out:
>> t = Time.parse "2018-04-02T14:30:00Z"
>> t.in_time_zone('Eastern Time (US & Canada)')
Mon, 02 Apr 2018 10:30:00 EDT -04:00
But consider that you can likewise do:
>> t = Time.parse "2018-01-02T14:30:00Z"
>> t.in_time_zone('Eastern Time (US & Canada)')
Tue, 02 Jan 2018 09:30:00 EST -05:00
In other words, ActiveSupport is handling not only your time zones, but also your Standard/Daylight Savings challenges, all for free. Not even moment.js will do that (or if it will, I haven't figured out how).
Have you considered using require 'active_support', which would give you this functionality without using all of Rails?
you can try the localtime method, something like Time.parse("2018-04-02T14:30:00Z").localtime("-05:00").strftime("%m/%d/%Y %I:%M %p") to pretty print to get the time object remove the strftime
I believe that "Eastern Time (US & Canada)" is actually EDT and not EST. That would explain your hour difference.
irb(main):014:0> t = Time.parse "2018-04-02T14:30:00Z"
=> 2018-04-02 14:30:00 UTC
irb(main):015:0> t.in_time_zone('EST')
=> Mon, 02 Apr 2018 09:30:00 EST -05:00
irb(main):016:0> t.in_time_zone('Eastern Time (US & Canada)')
=> Mon, 02 Apr 2018 10:30:00 EDT -04:00
irb(main):017:0>
See also: https://time.is/EST
and https://time.is/EDT
Also, timezones are a huge pain and routinely break my brain.
This issue has been eating away at me for a while.
I take a date time string:
[14] pry(#<EventsController>)> params[:event][:ended_at]
=> "05/31/2017 2:00 PM"
I convert it to a DateTime object:
pry(#<EventsController>)> to_datetime = DateTime.strptime(params[:event][:ended_at], "%m/%d/%Y %H:%M %p")
=> Wed, 31 May 2017 14:00:00 +0000
If I run the in_time_zone method on the to_datetime object it outputs the wrong time for pacific timezone:
[16] pry(#<EventsController>)> to_datetime.in_time_zone("Pacific Time (US & Canada)")
=> Wed, 31 May 2017 07:00:00 PDT -07:00
It should read "2:00PM" the same as how it is entered.
If I go to google and check to see if "14:00:00 +0000" is the correct entry for PDT time it verifies as correct:
http://imgur.com/a/ZJ80F
Any clues about why it's not converting correctly?
the error comes that the ended_at is been assumed by the system configuration that in this case is '+0000' you need to include the original timezone the ended_at is.
irb(main):001:0> date = "05/31/2017 2:00 PM"
=> "05/31/2017 2:00 PM"
irb(main):002:0> to_datetime = DateTime.strptime(date, "%m/%d/%Y %H:%M %p")
=> Wed, 31 May 2017 14:00:00 +0000
Note that this one has already set as UTC since was assumed by the system the timezone
irb(main):001:0> date = "05/31/2017 2:00 PM -0700"
=> "05/31/2017 2:00 PM -0700"
irb(main):002:0> to_datetime = DateTime.strptime(date, "%m/%d/%Y %H:%M %p %z")
=> Wed, 31 May 2017 14:00:00 -0700
irb(main):003:0> new_to_datetime = to_datetime.utc
=> Wed, 31 May 2017 21:00:00 +0000
irb(main):004:0> new_to_datetime.in_time_zone("Pacific Time (US & Canada)")
=> Wed, 31 May 2017 14:00:00 PDT -07:00
Update
#antonio's comment mention he was 1 hour off
irb(main):046:0> time = DateTime.strptime(date + " Pacific Time (US & Canada)", "%m/%d/%Y %H:%M %p %Z").class
=> DateTime
irb(main):047:0> time = DateTime.strptime(date + " Pacific Time (US & Canada)", "%m/%d/%Y %H:%M %p %Z")
=> Wed, 31 May 2017 14:00:00 -0800
irb(main):048:0> time.utc.class
=> Time
As you can see these are different classes and that's sign of problems what you can do is use Time instead of DateTime
irb(main):049:0> time = Time.strptime(date + " Pacific Time (US & Canada)", "%m/%d/%Y %H:%M %p %Z")
=> 2017-05-31 14:00:00 -0700
irb(main):050:0> time.class
=> Time
If we look at your first attempt:
pry(#<EventsController>)> to_datetime = DateTime.strptime(params[:event][:ended_at], "%m/%d/%Y %H:%M %p")
=> Wed, 31 May 2017 14:00:00 +0000
we see that "05/31/2017 2:00 PM" is being parsed correctly in UTC, hence the +0000 offset.
Then, if you convert that UTC time to PDT, you apply a -7 hour adjustment:
=> Wed, 31 May 2017 07:00:00 PDT -07:00
# -------------------------------^^^^^^
and the 14:00:00 becomes 07:00:00 because 14 - 7 == 7.
If you want to_datetime to be in PDT then it would be easiest to start that way by telling DateTime.strptime that the timestamp string is PDT:
no_tz = '05/31/2017 2:00 PM'
to_datetime = DateTime.strptime("#{no_tz} PDT", '%m/%d/%Y %H:%M %p %z')
# ------------------------------^^^^^^^^^^^^^^^---------------------^^
Then you'll get the 14:00:00 PDT you're looking for:
> to_datetime.iso8601
=> "2017-05-31T14:00:00-07:00"
# -------------^^------^^^^^^
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...
Is there any way in rails to convert Mountain Time (US & Canada) to EDT. And are there other names for other worldwide timezones?
To anwer your question:
2.0.0p353 :001 > now = DateTime.now.in_time_zone('Mountain Time (US & Canada)')
=> Mon, 06 Jan 2014 06:11:53 MST -07:00
2.0.0p353 :002 > now.in_time_zone('Eastern Time (US & Canada)')
=> Mon, 06 Jan 2014 08:11:53 EST -05:00
Please notice DST is being handled automatically:
2.0.0p353 :009 > now = (DateTime.now+6.months).in_time_zone('Mountain Time (US & Canada)')
=> Sun, 06 Jul 2014 07:14:30 MDT -06:00
2.0.0p353 :010 > now.in_time_zone('Eastern Time (US & Canada)') => Sun, 06 Jul 2014 09:14:30 EDT -04:00
And of course look at resource linked by #Micheal Moulsdale, you will find there all TimeZones names.
A good place to start would be the Active support Time Zone, and then on to the TZInfo gem
http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html
after you convert the date to the right zone, try date.zone() to get the short zone name.
zone = ActiveSupport::TimeZone.new(current_user.time_zone)
date.in_time_zone zone
date.zone
I need to convert a Date object into a TimeWithZone object representing the beginning of that day in a given time zone.
The following approach works, but seems too convoluted as it requires me to convert the date to a string:
?> date = Date.parse("2010-02-17")
=> Wed, 17 Feb 2010
>> ActiveSupport::TimeZone['Eastern Time (US & Canada)'].parse(date.to_s)
=> Wed, 17 Feb 2010 00:00:00 EST -05:00
>> ActiveSupport::TimeZone['UTC'].parse(date.to_s)
=> Wed, 17 Feb 2010 00:00:00 UTC 00:00
Is there a better way I'm missing?
Edit:
People are suggesting variations of:
?> date.to_datetime.in_time_zone('Eastern Time (US & Canada)').beginning_of_day
=> Tue, 16 Feb 2010 00:00:00 EST -05:00
As you can see, this isn't an equivalent conversion since it leaves me at the start of Feb. 16th EST, instead of the start of Feb. 17th EST.
I'm late to the party, but this is still a great question. ActiveSupport's in_time_zone was introduced since the O.P., but it does exactly what you want without parsing a string (slow) or setting Time.zone (risky):
>> date = Date.parse("2010-02-17")
=> Wed, 17 Feb 2010
>> date.in_time_zone('Eastern Time (US & Canada)')
=> Wed, 17 Feb 2010 00:00:00 EST -05:00
Of course if you want the beginning of day expressed at utc, you can do this:
>> date.in_time_zone('Eastern Time (US & Canada)').utc
=> 2010-02-17 05:00:00 UTC
If you have Time.zone set in Rails then you can call Date#at_beginning_of_day (see http://api.rubyonrails.org/classes/Date.html#method-i-at_beginning_of_day). Contrast this with Date#to_datetime:
Time.zone
=> #<ActiveSupport::TimeZone:0x10cf10858 #tzinfo=#<TZInfo::TimezoneProxy: Etc/UTC>, #utc_offset=nil, #current_period=nil, #name="UTC">
date = Date.today
=> Thu, 31 May 2012
date.to_datetime
=> Thu, 31 May 2012 00:00:00 +0000
date.at_beginning_of_day
=> Thu, 31 May 2012 00:00:00 UTC +00:00
Time.zone = 'America/Chicago'
=> "America/Chicago"
date.to_datetime
=> Thu, 31 May 2012 00:00:00 +0000
date.at_beginning_of_day
=> Thu, 31 May 2012 00:00:00 CDT -05:00
I strongly recommend against any solution that converts the date to a time using to_datetime or to_time because those methods are unaware of the zone, and tacking in_time_zone onto the result, as some answers suggest, won't retroactively fix the mistake. Also, don't try to build your own daylight saving time math using UTC offsets. You're bound to get it wrong, and you're doing work unnecessarily.
Use the TimeZone itself which has this logic built in.
Given a zone and a date, you can get a TimeWithZone for the beginning of the day like this:
time = zone.local(date.year, date.month, date.day)
If you want a specific time of day other than the beginning, you can pass the hour, minute, and second as the 4th, 5th, and 6th arguments to #local.
If zone is actually your system's local time zone (Time.zone), then ActiveSupport will let you shorten the above to this:
time = date.to_time_in_current_zone
All of the above handle daylight saving time correctly. Let's verify that by looking at the UTC offsets for two times, one that's outside DST and one that's within DST:
irb(main):009:0> zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
=> (GMT-05:00) Eastern Time (US & Canada)
irb(main):010:0> t1 = zone.local(2013, 1, 1)
=> Tue, 01 Jan 2013 00:00:00 EST -05:00
irb(main):011:0> t2 = zone.local(2013, 5, 1)
=> Wed, 01 May 2013 00:00:00 EDT -04:00
irb(main):012:0> t1.utc_offset
=> -18000
irb(main):013:0> t2.utc_offset
=> -14400
Would something like this work for you?
'2010-04-01'.to_time.in_time_zone('Eastern Time (US & Canada)').beginning_of_day
Subtract utc_offset:
d = Date.today
Time.zone.class.all.map(&:name).map { |tz| dt = d.to_datetime.in_time_zone(tz); dt -= dt.utc_offset }
Using ActiveSupport::TimeZone[tz] doesn't take daylight savings time into account.
Time.zone.class.all.map(&:name).map { |tz| o = d.to_datetime.in_time_zone(tz).utc_offset - ActiveSupport::TimeZone[tz].utc_offset }