Rails - A better way to parse the time with a timezone? - ruby-on-rails

Time.parse returns a Time object that does not have a timezone. I would like to keep the timezone information. Is there a better way to do this then the following code?
def parse_with_timezone( string_input)
/(.*)([+-]\d\d):?(\d\d)$/.match( string_input) do |match|
tz = ActiveSupport::TimeZone[match[2].to_i.hours + match[3].to_i.minutes]
tz.parse( match[1])
end
end
The input is a string like this "2012-12-25T00:00:00+09:00". This function outputs a TimeWithZone object.

Were you looking for a specific timezone of the current local one?
# Current zone
1.9.3p194> Time.zone.parse('2012-12-25T00:00:00+09:00')
=> Mon, 24 Dec 2012 15:00:00 UTC +00:00
Console was set at UTC for above but will work for whatever you have configured
# Specific timezone
1.9.3p194> Time.find_zone('Wellington').parse('2012-12-25T00:00:00+09:00')
=> Tue, 25 Dec 2012 04:00:00 NZDT +13:00
I notice you're trying to pass +9 so as an example
1.9.3p194> Time.zone = 'Tokyo'
=> "Tokyo"
1.9.3p194> Time.zone.parse('2012-12-25T00:00:00+09:00')
=> Tue, 25 Dec 2012 00:00:00 JST +09:00
Gives you the right result.

What about the Rails Timezone API: http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html

I prefer to use Chronic for all my date/time parsing needs.

Related

Converting time zone of Rails ActiveRecord timestamps

I extended the Time class in my Rails projects so I can easily get the time in NYC:
/lib/extensions.rb .
class Time
# Get NYC time:
def nyc
self.in_time_zone('Eastern Time (US & Canada)')
end
end
Testing it out, looks good:
time_a = Time.now.utc.nyc
=> Sun, 21 Apr 2019 18:42:12 EDT -04:00
The problem is when I pull timestamps from the DB:
time_b = object.created_at.in_time_zone('Eastern Time (US & Canada)')
=> Sun, 21 Apr 2019 17:22:04 EDT -04:00
time_c = object.created_at.nyc
=> Sun, 21 Apr 2019 17:22:04 UTC +00:00
Super confused. Converting the timestamp to EDT works when I use in_time_zone in the console, but not when I use the extension? Even though my extension method works on Time objects I create in console? What's happening here?
(Note: Time instances in Rails are in fact instances of ActiveSupport::TimeWithZone. "TimeWithZone instances implement the same API as Ruby Time instances, so that Time and TimeWithZone instances are interchangeable." - ActiveSupportTimeWithZone)
you would need to patch ActiveSupport::TimeWithZone instead of Time, e.g.
class ActiveSupport::TimeWithZone
def nyc
in_time_zone('Eastern Time (US & Canada)')
end
end
Time.zone.now.nyc # => Mon, 22 Apr 2019 06:44:41 EDT -04:00
User.last.created_at.nyc # => Sun, 21 Apr 2019 13:34:45 EDT -04:00
https://api.rubyonrails.org/classes/ActiveSupport/TimeWithZone.html
(edit: I previously said "DateTime" instead of "ActiveSupport::TimeWithZone")

Convert Active Support timezone original format into a string

I'm trying to convert Active Support timezone original format into a string. I want to store it in an array of characters then parse each needed data individually.
Time.zone = current_user.timezone
date_and_time = Time.zone.now
Now
date_and_time = Thu, 21 Apr 2016 20:58:04 PDT -07:00
Ruby method ( to_s ) does not convert it. I found other ways to convert it to but all of them will change the format to numbers only, I want the day to stay the same because I will store it in a variable then use it in a different method.
You can use .to_formatted_s(DATE_FORMAT) for this.
time = Time.now # => Thu Jan 18 06:10:17 CST 2007
time.to_formatted_s(:db) # => "2007-01-18 06:10:17"
time.to_formatted_s(:long) # => "January 18, 2007 06:10"
time.to_formatted_s(:long_ordinal) # => "January 18th, 2007 06:10"
time.to_formatted_s(:rfc822) # => "Thu, 18 Jan 2007 06:10:17 -0600"
time.to_formatted_s(:iso8601) # => "2007-01-18T06:10:17-06:00"
A list of all DATE_FORMATS and more information can be found here:
http://api.rubyonrails.org/classes/Time.html#method-i-to_formatted_s
You can try this
date_and_time.strftime("%a %d %b %Y")
Also You can check this guide, to get format you want
You should get what you want using this :
date_and_time.strftime("%a %d %b %Y %H:%M:%S UTC %:z")
Please see strftime Docs for more info
Explanation
Reason for hardcoding UTC is so that according to the docs
%z - Time zone as hour and minute offset from UTC
So i believe it should be UTC all the time.

Ruby String to_time broken?

I would expect Time and to_time to be reflective.
UPDATE
This is the answer, if you add the parameter :local, it doesn't ignore the offset. I find it wierd that it ignores data (the offset) by default, though...
Time.zone.now.to_s.to_time(:local) returns 2012-03-20 14:39:03 +0100
/UPDATE
> Time.zone.now.to_s
=> "2012-03-20 12:50:05 +0100"
> Time.zone.now.to_s.to_time.to_s
=> "2012-03-20 12:50:05 UTC"
# PROBLEM:
# UTC is +0000, meaning that there is 1 hour diff in the above
> Time.zone.now
=> Tue, 20 Mar 2012 12:51:32 CET +01:00
> Time.zone.now.to_time
=> Tue, 20 Mar 2012 12:51:32 CET +01:00
> Time.zone.now.to_json
=> "\"2012-03-20T12:50:36+01:00\""
> Time.zone.now.to_json.to_time
=> 2012-03-20 12:50:36 UTC
I have this problem with JSON messages. Anyway, I wouldn't expect to_time to actually change the time. OK to translate to UTC, probably, but it should adjust time accordingly. Anybody know if this is a known issue or "by design"?
This method, String#to_time, defined in ActiveSupport takes one parameter, form, which can be either :utc (default) or :local. So by default, it always returns a Time in UTC.
To get a Time with timezone:
Time.zone.parse("2012-03-20T12:50:36+01:00")

ROR + TodayDate in Different Format

If I have #today = Date.today.to_s, how do I convert #today into UTC (with the appropriate date only)? But the format should be like this : 2011-03-08 00:00:00
Acutally I am looking for Yesterday date also ??
This worked for me to get yesterday at time 00hr
Date.yesterday.to_time.utc.at_beginning_of_day
=> Sun Mar 06 00:00:00 UTC 2011
I don't know if its the correct way to do it but it works
ruby-1.9.2-p0 > Date.today.to_time.utc
=> 2011-03-07 18:15:00 UTC
and for yesterday, you can subtract a day form today
(Date.today-1.day).to_time.utc
=> 2011-03-06 18:15:00 UTC
For Yesterday Date.
#date = Date.yesterday
=> Mon, 07 Mar 2011
#date.to_time.utc.strftime("%Y-%m-%d %H:%M:%S")
=> "2011-03-06 18:30:00"
This will do(%F - %Y-%m-%d and %T - %H:%M:%S)
Date.today.strftime("%F %T")
Date.yesterday.strftime("%F %T")
If you are particular about UTC time, you should do like this
(Time.now.utc).to_date.strftime("%F %T")
(Time.now.utc - 1.day).to_date.strftime("%F %T")

Rails ActiveSupport Time Parsing?

Rails' ActiveSupport module extends the builtin ruby Time class with a number of methods.
Notably, there is the to_formatted_s method, which lets you write Time.now.to_formatted_s(:db) to get a string in Database format, rather than having to write ugly strftime format-strings everywhere.
My question is, is there a way to go backwards?
Something like Time.parse_formatted_s(:db) which would parse a string in Database format, returning a new Time object. This seems like something that rails should be providing, but if it is, I can't find it.
Am I just not able to find it, or do I need to write it myself?
Thanks
It looks like ActiveSupport does provide the parsing methods you are looking for (and I was looking for too), after all! — at least if the string you are trying to parse is a standard, ISO-8601-formatted (:db format) date.
If the date you're trying to parse is already in your local time zone, it's really easy!
> Time.zone.parse('2009-09-24 08:28:43')
=> Thu, 24 Sep 2009 08:28:43 PDT -07:00
> Time.zone.parse('2009-09-24 08:28:43').class
=> ActiveSupport::TimeWithZone
and that time-zone-aware time can then easily be converted to UTC
> Time.zone.parse('2009-09-24 08:28:43').utc
=> 2009-09-24 15:28:43 UTC
or to other time zones:
> ActiveSupport::TimeZone.us_zones.map(&:name)
=> ["Hawaii", "Alaska", "Pacific Time (US & Canada)", "Arizona", "Mountain Time (US & Canada)", "Central Time (US & Canada)", "Eastern Time (US & Canada)", "Indiana (East)"]
> Time.zone.parse('2009-09-24 08:28:43').utc.in_time_zone('Eastern Time (US & Canada)')
=> Thu, 24 Sep 2009 11:28:43 EDT -04:00
If the date string you're trying to parse is in UTC, on the other hand, it doesn't look like there's any method to parse it directly into a TimeWithZone, but I was able to work around that be first using DateTime.strptime...
If the date you're trying to parse is in UTC and you want it to stay as UTC, you can use:
> DateTime.strptime('2009-09-24 08:28:43', '%Y-%m-%d %H:%M:%S').to_time
=> 2009-09-24 08:28:43 UTC
If the date you're trying to parse is in UTC and you want it converted to your default time zone, you can use:
> DateTime.strptime('2009-09-24 08:28:43', '%Y-%m-%d %H:%M:%S').to_time.in_time_zone
=> Thu, 24 Sep 2009 01:28:43 PDT -07:00
It looks like it can even parse other formats, such as the strange format that Time#to_s produces:
irb -> Time.zone.parse('Wed, 23 Sep 2009 02:18:08').to_s(:db)
=> "2009-09-23 09:18:08"
irb -> Time.zone.parse('Wed, 23 Sep 2009 02:18:08 EDT').to_s(:db)
=> "2009-09-23 06:18:08"
I'm quite impressed.
Here are some more examples from [http://api.rubyonrails.org/classes/ActiveSupport/TimeWithZone.html][1]:
Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
Time.zone.local(2007, 2, 10, 15, 30, 45) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
Time.zone.parse('2007-02-10 15:30:45') # => Sat, 10 Feb 2007 15:30:45 EST -05:00
Time.zone.at(1170361845) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
Time.zone.now # => Sun, 18 May 2008 13:07:55 EDT -04:00
Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone # => Sat, 10 Feb 2007 15:30:45 EST -05:00
More documentation links for reference:
http://api.rubyonrails.org/classes/ActiveSupport/TimeWithZone.html
http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html
ActiveSupport::TimeZone.new('UTC').parse('2009-09-23 09:18:08')
=> Wed, 23 Sep 2009 09:18:08 UTC +00:00
Rails 5 finally provides strptime!
value = '1999-12-31 14:00:00'
format = '%Y-%m-%d %H:%M:%S'
Time.zone.strptime(value, format)
# => Fri, 31 Dec 1999 14:00:00 HST -10:00
ActiveSupport::TimeZone.all.sample.strptime(value, format)
# => Fri, 31 Dec 1999 14:00:00 GST +04:00
I just ran into this as well and none of the above answers were satisfactory to me. Ideally one could use ActiveSupport::TimeZone just like Time and call .strptime on it with any arbitrary format and get back the correct TimeZone object. ActiveSupport::TimeZone.strptime doesn't exist so I created this monkeypatch:
class ActiveSupport::TimeZone
def strptime(str, fmt, now = self.now)
date_parts = Date._strptime(str, fmt)
return if date_parts.blank?
time = Time.strptime(str, fmt, now) rescue DateTime.strptime(str, fmt, now)
if date_parts[:offset].nil?
ActiveSupport::TimeWithZone.new(nil, self, time)
else
time.in_time_zone(self)
end
end
end
>> "2009-09-24".to_date
=> Thu, 24 Sep 2009
>> "9/24/2009".to_date
=> Thu, 24 Sep 2009
Works great unless your date is in some weird format.

Resources