25

I have a String which I parse with DateTime.strptime. The Timezone of the Date in the String is CET but Ruby creates an UTC DateTime object which of course has an offset of 2hrs.

Currently I'm working around the issue with DateTime.strptime().change(:offset => "+0020") but I'm pretty sure this is not the way it's meant to work.

Can someone enlighten me on the correct way to do this?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Nicolas
  • 1,828
  • 6
  • 23
  • 34
  • if you are in a country with CEST/CET timezone a fixed offset won't work. Also interested in this, I don't see how make strptime parse for the current timezone (but not the value right now, the right one depending on the parsed string). – tokland Jan 26 '12 at 11:24

9 Answers9

14

I use it the following way:

ruby-1.9.2-head :001 > require 'date'
 => true 
ruby-1.9.2-head :002 > fmt = "%m-%d-%Y %H:%M:%S %Z"
 => "%m-%d-%Y %H:%M:%S %Z" 
ruby-1.9.2-head :003 > DateTime.strptime "10-26-2011 10:16:29 CET", fmt
 => #<DateTime: 2011-10-26T10:16:29+01:00 (212186380589/86400,1/24,2299161)> 
ruby-1.9.2-head :004 > DateTime.strptime "10-26-2011 10:16:29 UTC", fmt
 => #<DateTime: 2011-10-26T10:16:29+00:00 (212186384189/86400,0/1,2299161)> 
ruby-1.9.2-head :005 > DateTime.strptime "10-26-2011 10:16:29 PST", fmt
 => #<DateTime: 2011-10-26T10:16:29-08:00 (212186412989/86400,-1/3,2299161)> 

Is it what you mean?

WarHog
  • 8,622
  • 2
  • 29
  • 23
  • 2
    i dont have a string determining the timezone in my parsed string. its 10-26-2011 10:16:29 instead of "10-26-2011 10:16:29 CET" – Nicolas Oct 26 '11 at 22:05
  • 1
    You solved my problem, I can just `"09-19-2012 11:08:44" + " CST"`. I think yours is the correct answer. – mwoods79 Sep 19 '12 at 16:09
  • 1
    What about when the you move into daylight savings time? Shouldn't this + "CST" be more informed? As per the comment above, it'd be nice if we could pick that based on the time that just got formatted... i'm learning to hate timezones. – mr rogers Jan 24 '13 at 22:28
9

This works in Rails 5:

Time.zone.strptime("2017-05-20 18:20:10", "%Y-%m-%d %H:%M:%S")

Returns the time in the specified time zone.

avergin
  • 91
  • 2
  • 5
5

At least as of Rails 4.2 (maybe earlier, haven't tested) as long as you use Time#strptime instead of DateTime.strptime, the current time zone is automatically applied:

> DateTime.strptime('12/28/2016 5:00 PM', '%m/%d/%Y %H:%M %p')
=> Wed, 28 Dec 2016 17:00:00 +0000

> Time.strptime('12/28/2016 5:00 PM', '%m/%d/%Y %H:%M %p')
=> 2016-12-28 17:00:00 -0800
Jared
  • 2,885
  • 2
  • 28
  • 31
  • 1
    This uses the timezone of the machine, not the timezone set in Rails, btw. – Sean Sep 11 '18 at 23:54
  • @Sean Yes, that's true. The workaround is to use Time.zone.strptime for Rails 5+ or Time.zone.local_to_utc(DateTime.strptime('12/28/2016 5:00 PM', '%m/%d/%Y %H:%M %p')) for older versions of Rails – RealMan Jan 29 '21 at 09:22
1

I was "strptiming" this string:

19/05/2014 8:13:26 a.m.

A local timestamp, which is in my case is Auckland NZ without a timezone stamp in the string as can be seen.

As best I can tell Time.strptime uses the server timezone as the base.

In my situation, and general good practice, my servers run in UTC timezone so every parse of the string ended up creating a time object, the time to +0000 (UTC):

2014-05-19 08:13:26 +0000

Then converting to in_time_zone(Time.zone) gave:

Mon, 19 May 2014 20:13:26 NZST +12:00

Which as can be seen is 12 hours (the UTC offset) later than the actual time I wanted.

I tried to use the +' Auckland', '...%Z' trick as per above without any change. I then used the + '+1200', '...%Z' trick as per above which worked correctly.

However I was concerned about summer time, then the parsing would be out by an hour, so this is what I've finshed with:

Time.strptime(call_data[:datetime].gsub(/\./, "").gsub(/am/, "AM").gsub(/pm/, "PM") + (Time.zone.now.time_zone.utc_offset/3600).to_s.ljust(4,'0').rjust(6,' +'), '%d/%m/%Y %I:%M:%S %p %Z').in_time_zone(Time.zone)

Result:

Mon, 19 May 2014 08:13:26 NZST +12:00

It's not particularly elegant but it works.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Gordon
  • 56
  • 2
0

You can convert date to timezone with to_time_in_current_zone. For example:

Date.strptime('04/07/1988', '%m/%d/%Y').to_time_in_current_zone
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Mikhail K.
  • 567
  • 1
  • 6
  • 15
0

I have been battling this same issue trying to parse a date string from a form and save it independently of the servers time. The process I have followed is as such. I assure the RoR and Ruby timezones are set to UTC. It makes it easier if the server is on UTC time as well.

I am storing the users locale using the ActiveSupport:TimeZone format like US/Eastern I have an interpret date function which can account for a date string that doesn't natively contain a timezone or offset

# date_str looks something like this `12/31/2014 6:22 PM`
# tz is a string containing an ActiveSupport::TimeZone name like `US/Eastern`
def interpret_date(date_str, tz)
  Time.use_zone tz do
    parsed_dt = DateTime.strptime(date_str, '%m/%d/%Y %H:%M %p')
    offset = parsed_dt.in_time_zone(tz).utc_offset
    parsed_dt.advance(seconds: -(offset)).in_time_zone(tz)
  end
end

This of course doesn't feel like a perfect solution but it works. The steps to assure the correct timezone are:

  1. Parse the date string according to our format
  2. Extract the current utc offset based upon the time in that timezone (We have to do this if the date you are parsing happens to be in dst while your local time is not dst for eastern time this will assure that the dst is local to the parsed time)
  3. Convert the time to our target locale and allow the time to shift with the correct offset added
PaulSCoder
  • 593
  • 5
  • 19
0

For pre Rails 5, the following will work:

t0 = Time.strptime("2018-05-01", "%Y-%m-%d") # Or whatever
t1 = Time.zone.now.change(year: t0.year, month: t0.month, day: t0.day, hour: t0.hour, min: t0.min, sec: t0.sec, usec: t0.usec)
troelskn
  • 115,121
  • 27
  • 131
  • 155
0

try this

(date + Rational(5,24)).new_offset(-Rational(5,24))

what u are doing here: suppose that u want to change to timezone 5

date + Rational(5,24)

i'll add 5 hour to your datetime, maintain the time zone.

after, how adjust the offset to recover the original data and get the timezone that u want.

-3

I can't delete an accepted answer

As @LeeJarvis pointed out in the comment section below, Chronic.parse does not accept a :time_class option. So this answer is buggy and while it looks like it works, it doesn't (unless Chronic allows passing a :time_class option soon.)


The Chronic gem is really powerful.

I use it like this:

Chronic.parse("next 5:00 pm", 
:time_class => ActiveSupport::TimeZone.new(User.first.time_zone)).utc

In my example above, User.first.time_zone is "Pacific Time (US & Canada)".

Chronic supports a lot of formats so check it out at https://github.com/mojombo/chronic

Make sure to pass the :time_class option and to convert to UTC (in the documentation, Chronic sets :time_class before calling parse. I avoid this approach because it might cause other parts across the application not to work)

TimeZone's documentation is at http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html

Abdo
  • 13,549
  • 10
  • 79
  • 98
  • 1
    This answer is not correct. `parse()` does not take a `time_class` option. It's set on the `Chronic` class directly. This will eventually change in future versions of Chronic, though – Lee Jarvis Jun 25 '14 at 12:58
  • My bad... I'll remove until this option is available =) – Abdo Jun 25 '14 at 13:39
  • Erm... I can't delete an accepted answer. I'll comment above. – Abdo Jun 25 '14 at 13:39