Pergunta

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?

Foi útil?

Solução

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

Outras dicas

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?

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.

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

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.

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

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

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)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top