Rails time zone from offset and dst - ruby-on-rails

I am looking for some help in how I can store the correct timezone in Rails, from data that provides me with a UTC offset, and DST.
The data comes from http://openflights.org/data.html
Timezone Hours offset from UTC. Fractional hours are expressed as decimals, eg. India is 5.5.
DST Daylight savings time. One of E (Europe), A (US/Canada), S (South America), O (Australia), Z (New Zealand), N (None) or U (Unknown).
I am wondering how I could use this data in Rails to store the timezone of these airports as a string in a Timezone column that Rails would recognise.

I don't think there there is a solution for this, unless you create a custom mapping of the DST and offset to a particular timezone.
I ended up using the geonames.org webservice, to find out the timezone based on latitude/longitude.

I needed the same functionality in the slightly simpler case of a database of USA zipcodes which includes a UTC hours offset and Y/N field indicating if it participates in daylight savings time, so West coast USA zipcodes in this database all have "-8" and "Y".
I could not find a built-in Rails method to do it, so I manually created a lookup hash where the key is the UTC & DST fields catenated, eg, "#{utc}#{dst}" and the value is the timezone name. This method could work for utc offsets such as "5.5" as well.
In the method that does the lookup is given the utc and dst values, I specify a default timezone (USA west coast for example) in the case where the hash lookup returns nil because an unexpected value such as "-5N" (since the east coast does not have any non-DST states that should never occur).
But the same method could be applied globally by creating a hash that represented all the possible timezone with both Y and N values for daylight savings time.
class MyZip
HOUR_DST_TO_TIMEZONE_NAME = {
"-5Y" => "Eastern Time (US & Canada)",
"-6Y" => "Central Time (US & Canada)",
"-7Y" => "Mountain Time (US & Canada)",
"-7N" => "Arizona",
"-8Y" => "Pacific Time (US & Canada)",
"-9Y" => "Alaska",
"-10N" => "Hawaii"
}
def self.timezone_name_from_zip_hour_dst(utc, dst)
key = "#{utc}#{dst}"
return HOUR_DST_TO_TIMEZONE_NAME[key] || MyZip.unknown_timezone_name
end
...

Related

Parsing a TimeZone name string that includes GMT offset hours

I did find this question but I am still stumbling around looking for a simple solution to the following:
An API call returns the following format which looks like they are using Time.zone.to_s
irb> ShopifyAPI::Shop.current.timezone
=> "(GMT-08:00) Pacific Time (US & Canada)"
I would like to parse the "(GMT-08:00) Pacific Time (US & Canada)" into a Ruby class and output the TimeZone name "Pacific Time (US & Canada)"
Alternately I could just strip the "(GMT-08:00)" offset and be left with a clean TimeZone name "Pacific Time (US & Canada)" but this seems like a messy string editing solution.
ShopifyAPI::Shop.current returns properties documented here. Yes, timezone is one of them, but it is intended to be a display name, not something you should parse.
Instead, use the iana_timezone property, which will give you the IANA time zone identifier, such as America/Los_Angeles. These are compatible with Rails, as well as Ruby's tzinfo gem, and also are used in many other platforms.
irb> ShopifyAPI::Shop.current.timezone
=> "(GMT-08:00) Pacific Time (US & Canada)"
irb> ShopifyAPI::Shop.current.iana_timezone
=> "America/Los_Angeles"
If you want to get a Rails time zone from there, you can use the MAPPING constant defined in ActiveSupport::TimeZone. Though I'd avoid it if possible, for the reasons mentioned in the timezone tag wiki in the "Rails Time Zone Identifiers" section at the bottom.
If you are confident about the API and string format you are going to receive, you can manipulate string as
string.partition(')').last.strip
# => Pacific Time (US & Canada)

How to set the timezone to EST throughout the rails app?

I want to use EST throughout the app. I have set the
config.time_zone = 'Eastern Time (US & Canada)'
config.active_record.default_timezone = 'Eastern Time (US & Canada)'
what I want is, I have to get all the time I have used like "DateTime.now" and it now returns time in my local timezone but I want it to return date time in EST. Also I have date time range picker and I get the start date and end date and parse the string to time using
(params[:start_date]).to_time
which also returns the date time in local zone and instead i want all those to return in EST. How can I do this? Any help is appreciated.
Thanks in advance.
DateTime.now and Time.now doesn't respect Time.zone and will always use the server time. If the machine you're using is set to a different timezone, these 2 will use that timezone.
to_time on the other hand defaults to local timezone as mentioned in apidock (although just tried this out and I get utc by default). To get these times in the timezone set, append in_time_zone
DateTime.now.in_time_zone
Time.now.in_time_zone
string.to_time.in_time_zone
There are some shortcuts for these though. The following code will return the timestamps in the current timezone
Time.zone.now
Time.zone.parse(string)
Normally, in database date-time would be saved in UTC format unless you have selected a specific TimeZone for Active Record and it would be easier if you have to meddle with multiple TimeZones. But, while you access the values you will get the values in you selected TimeZone.
config.time_zone = 'Eastern Time (US & Canada)
Now, suppose you have a datetime value in UTC from table as
datetime1 = "2014-08-25 10:25:57 +0000"
You can convert it to the selected timezone 'Eastern Time (US & Canada)' as below,
Time.zone.parse(datetime1) => Mon, 25 Aug 2014 06:25:57 EDT -04:00
Then, if you need it in a proper format, use method strftime.
Time.zone.parse("2014-08-25 10:25:57 +0000").strftime("%d-%m-%Y %H:%M:%S") => "25-08-2014 06:25:57"
Hoep it helps :)

Rails: How to parse date-time string into a specific time zone

I'm using Rails 3.2 and ruby 1.9.3 on Debian. I have an app that collects a date, time, and timezone in the form of strings via an HTML form. Something like this:
start_date: "04-15-2010",
start_time: "10:00:00",
timezone: "Central Time (US & Canada)"
What I'd like to do is parse these 3 elements into a single date that is saved into my database as UTC, which in this case would add 7 hours to the start time, once it's in the UTC time zone.
So the stored time would be 17:00 once it's in the DB as UTC instead of the received Central time.
I have tried something like this to parse the date:
ActiveSupport::TimeZone[timezone].at DateTime.strptime("{ 2012-04-09 20:00:00 }", "{ %Y-%m-%d %H:%M:%S }").to_i
However, I'm not able to incorporate the time zone into the resulting time with %Z. It either doesn't parse or the time is interpreted as UTC not Central time. So my question is, how to coerce a date string into a certain time zone without changing the value of the actual date/time stored. I'd like to be able to parse the string into a date/time object that includes the correct time zone with it at that time so that future time zone conversions are accurate. I've looked all over and can't find a way to do this. It's strange, since this seems like something common one does with dates inputted from HTML forms. Thank you for any help.
Try this:
zone = "Central Time (US & Canada)"
ActiveSupport::TimeZone[zone].parse("2013-04-03 17:47:00")
Use String#in_time_zone (Rails 4+)
I personally prefer using String#in_time_zone:
>> '22.09.1986 10:30'.in_time_zone('Central Time (US & Canada)')
# => Mon, 22 Sep 1986 10:30:00 CDT -05:00
This parses the date and time in the String into the time zone provided.
%Z is the correct way to specify a Time zone name. Have you tried the following ?
date_and_time = '%m-%d-%Y %H:%M:%S %Z'
DateTime.strptime("04-15-2010 10:00:00 Central Time (US & Canada)",date_and_time)
This is the method that I came up with. Not the prettiest, but it works. Allows parsing the string using a specified format, and then turning it into the format that I know Time.zone.parse requires.
class ActiveSupport::TimeZone
def strptime(time, format='%m/%d/%Y')
formatted = Time.strptime(time, format).strftime('%Y-%m-%d %T')
parse(formatted)
end
end
Then you can do something like what was mentioned in another question, but with a specified format:
zone = "Central Time (US & Canada)"
ActiveSupport::TimeZone[zone].strptime('2013-04-03', '%Y-%m-%d')
Or if you already have a time zone set:
Time.zone = "Central Time (US & Canada)"
Time.zone.strptime('01/13/2006')
I used a default format of %m/%d/%Y because that's what my user input is most of the time. You can customize this to your needs, or use the default format DateTime uses which is believe is iso8601 (%FT%T%z)
I've finally found the dirty, yet definitive way to do this.
First, parse the string using plain Ruby Time.strptime like this:
time = Time.strptime('12 : 00 : PM', '%I : %M : %p')
This way you get the parsed Time, but not yet in correct timezone. To fix that, let's convert the time to string form and parse it with the standard ActiveSupport::TimeZone#parse
Time.zone.parse(time.to_s)
The result is the ActiveSupport::TimeWithZone with our time parsed into the correct timezone.
The reason why we have to do it this way is that neither ActiveSupport::TimeZone nor ActiveSupport::TimeWithZone support the strptime method. So we have to parse the Time with core Ruby strptime that does not have timezone information, convert it to format acceptable in ActiveSupport objects and then parse it yet again.
To have DateTime take the date string and attach a timezone other than UTC without changing the values of the date string , use this, its easy , doesnt break on leap day :)
xx = DateTime.strptime("9/1/15 #{object.time_zone}", "%m/%d/%Y %Z")
Convert specific date format in UTC.
ActiveSupport::TimeZone['UTC'].parse(Time.strptime('01/24/2019T16:10:16', "%m/%d/%YT%H:%M:%S").asctime)

Ruby: convert "US/Eastern" time zone names to "Central Time (US & Canada)"

I've got an old database with time zone formats like:
US/Eastern
Australia/Melbourne
In my new Rails app, I'm saving them as:
Eastern Time (US & Canada)
Melbourne
How can I convert the old to the new? I've been messing around with ActiveSupport::TimeZone, but can't figure out the right combination to get from one to the other.
I was hoping I could create a new object, then return the newly formatted name, but it just returns the name I gave it. Example:
> tz = ActiveSupport::TimeZone.new("US/Eastern")
=> (GMT-05:00) US/Eastern
> tz.name
=> "US/Eastern"
Thanks in advance!
This is pretty ugly, but it's the only way I've found to do it:
city = TZInfo::Timezone.get('US/Eastern').instance_eval('#linked_timezone').name
ActiveSupport::TimeZone::MAPPING.invert[city]
Edit:
For this code to work with either city or zone, you can do this:
zone = TZInfo::Timezone.get(zone_name)
city = (zone.instance_eval('#linked_timezone') || zone).name
ActiveSupport::TimeZone::MAPPING.invert[city]

User friendly timezone names from tz database format

I have timezones in the following array format:
'America/New_York' => '(GMT-05:00) Eastern Time (US & Canada)',
'Europe/Lisbon' => '(GMT) Greenwich Mean Time : Lisbon',
etc.
How do I go about displaying a user-friendly summer/daylight savings time dependent timezone identifier to the user?
For example, displaying the time now in New York would append "(EDT)" to the time, which would make sense for local users. I want to avoid having to display ((GMT-05:00) Eastern Time (US & Canada)) or just (GMT-05:00), which isn't strictly accurate all year round.
Ideally then, is there a web service/database that can take a tz string in the format "America/New_York", and a timestamp as paramters and return the abbreviation in the formats here?
strftime's %Z format specifier gives you this abbreviation. You didn't say what programming language you are using, but most programming languages give you access to strftime in one way or another.
Python:
import pytz
from datetime import datetime
here = pytz.timezone('Asia/Tokyo')
print here.localize(datetime.utcnow()).strftime("%Z")
there = pytz.timezone('America/Montreal')
print there.localize(datetime.utcnow()).strftime("%Z")
PHP:
date_default_timezone_set("Asia/Tokyo");
echo strftime("%Z");
date_default_timezone_set("America/Montreal");
echo strftime("%Z");

Resources