In Rails 3 How do I parse an ISO8601 date string to get an instance of TimeWithZone?

Here is my solution. Is it more compact?

> time_from_client = "2001-03-30T19:00:00-05:00" => "2001-03-30T19:00:00-05:00" > time_from_client.to_datetime => Fri, 30 Mar 2001 19:00:00 -0500 > timezone_offset = time_from_client.to_datetime.offset.numerator => -5 > tz = ActiveSupport::TimeZone[timezone_offset] => (GMT-05:00) America/New_York > tz.class => ActiveSupport::TimeZone 
+4
source share
2 answers

ActiveSupport::TimeWithZone documentation for ActiveSupport::TimeWithZone . Short answer: use Time.parse(time_string).in_time_zone :

 [9] pry(main)> Time.parse("2001-03-30T19:00:00-05:00") => 2001-03-30 19:00:00 -0500 [10] pry(main)> Time.parse("2001-03-30T19:00:00-05:00").class => Time [11] pry(main)> Time.parse("2001-03-30T19:00:00-05:00").in_time_zone => Sat, 31 Mar 2001 00:00:00 UTC +00:00 [12] pry(main)> Time.parse("2001-03-30T19:00:00-05:00").in_time_zone.class => ActiveSupport::TimeWithZone 

If you want it in a different time zone:

 [13] pry(main)> Time.parse("2001-03-30T19:00:00-05:00").in_time_zone("America/Los_Angeles") => Fri, 30 Mar 2001 16:00:00 PST -08:00 
+2
source

Unfortunately, this is not possible, at least without special skill.

To understand why you should distinguish between time zone and UTC offset:

Consider mapping from a zone to an offset: you also need to know the time in question, as well as the zone, in order to decide whether to apply the standard or daylight offset.

Going the other way is much more difficult. Once again, just shifting is not enough, because we donโ€™t know whether this shift refers to standard or daylight. But this time we have a problem with the chicken / egg: even if we have time, we need to know the zone to see if this time was standard or daytime. But we have no zone.


Here is an example that you use, you use Fri, 30 Mar 2001 19:00:00 , which is standard time (EST), so it looks good on the first pass:

 > time_from_client = "2001-03-30T19:00:00-05:00" => "2001-03-30T19:00:00-05:00" > time_from_client.to_datetime => Fri, 30 Mar 2001 19:00:00 -0500 > timezone_offset = time_from_client.to_datetime.offset.numerator => -5 > tz = ActiveSupport::TimeZone[timezone_offset] => (GMT-05:00) America/New_York 

We have America/New_York .


But let's see what happens if we switch to daylight saving time, say, 30 Jun 2001 19:00:00 . Now the offset component of your time_from_client will be -04:00 , which is the New York Daylight Offset (EDT).

 > time_from_client = "2001-03-30T19:00:00-4:00" => "2001-06-30T19:00:00-05:00" > time_from_client.to_datetime => Fri, 30 Jun 2001 19:00:00 -0400 

Disclaimer: the next step does not actually work, because the numerator rounds 4/24 to 1/6 and you get the wrong timezone_offset of 1 . So I changed your implementation and used utc_offset .

 > timezone_offset = time_from_client.to_datetime.utc_offset => -14400 > tz = ActiveSupport::TimeZone[timezone_offset] => (GMT-04:00) Atlantic Time (Canada) 

Now you can see the problem, instead of getting America/New_York we get Atlantic Time (Canada) . The latter is one of the zone names for the standard offset -04:00 , because the ActiveSupport::TimeZone[] implementation can only find the standard utc_offset , and does not know about daylight.

If you follow this until its completion, you will get the following counterintuitive parse :

 > tz.parse "2001-06-30T19:00:00-04:00" => Sat, 30 Jun 2001 20:00:00 ADT -03:00 

What I suppose is happening here, this TimeWithZone sees that it is June, and therefore tunes in to the Atlantic daylight shift, -03:00 .


It is worth noting that even if you could take into account daylight and get the standard offset for switching to ActiveSupport::TimeZone[] , you still would not have the correct zone, because the offset to the zone display is not one-way โ€” it is.

As shown here:

 ActiveSupport::TimeZone.all.select { |z| z.utc_offset == -14400 } => [(GMT-04:00) Atlantic Time (Canada), (GMT-04:00) Georgetown, (GMT-04:00) La Paz, (GMT-04:00) Santiago] 

This is my reason to think that this is not possible unless you also have location information for the source string of ISO 8601.

By the way, if you follow this approach, I recommend the tzwhere Node.js library, which can use the geometry of the zones, makes the location in the search zone up.

+2
source

All Articles