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"
# -------------^^------^^^^^^
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.
07/28/2017 11:56 PM This is my date and below is my current code:
Date.strptime("07/28/2017 11:56 PM", '%m/%d/%Y %H:%M %p').to_time
i'm getting o/p => 2017-07-28 00:00:00 +0530 but i want hours.What exactly should i do ?
The problem is that you're converting it into a Date object which doesn't have any time component and then converting it back into a Time object. Instead, you can use either
Time.strptime("07/28/2017 11:56 PM", '%m/%d/%Y %H:%M %p')
# => 2017-07-28 23:56:00 -0700
or
DateTime.strptime("07/28/2017 11:56 PM", '%m/%d/%Y %H:%M %p')
# => Fri, 28 Jul 2017 23:56:00 +0000
and you call call to_time on the DateTime if you'd like
DateTime.strptime("07/28/2017 11:56 PM", '%m/%d/%Y %H:%M %p').to_time
# => 2017-07-28 23:56:00 +0000
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)')
I want to convert one time from one timezone to another.
Example:
Image that I have a first Time, like
t=....
puts t
=> Sun Aug 12 00:00:00 +0200 2012
I want to have the exact same hour, but in a different time zone.
I can do this:
mytimezone.local(t.year,t.month,t.day,t.hour,t.min)
=> Sun, 12 Aug 2012 00:00:00 BRT -03:00
So is there a way to accomplish the same things in a better way?
Have you tried in_time_zone method?
> t = Time.parse("Sun Aug 12 00:00:00 +0200 2012")
=> 2012-08-12 02:00:00 +0400
> t.in_time_zone('Eastern Time (US & Canada)')
=> Sat, 11 Aug 2012 18:00:00 EDT -04:00
> mytimezone = ActiveSupport::TimeZone.new('Eastern Time (US & Canada)')
=> (GMT-05:00) Eastern Time (US & Canada)
> t.in_time_zone(mytimezone)
=> Sat, 11 Aug 2012 18:00:00 EDT -04:00
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 }