Ruby Time.zone.now.zone bug - ruby

What I mean is that there are different timezones with the same name, like CST (-06:00 in US and +09:30 in Australia). Accordingly conversion to utc gives wrong results for Adelaide.
Any elegant way to solve this?

an easy way would be that you use the other nomenclature of naming time zones..
e.g. for UTC+9:30 intstead of using CST, you can use "Australia/Adelaide"
tz = TZInfo::Timezone.get("Australia/Adelaide")
t = Time.now # you could also get the UTC time here with Time.now.utc
t.zone
=> "PDT"
t.utc_offset
=> -25200
t.in_time_zone(tz) # this is a Rails extension of the Time class
=> Sat, 01 Oct 2011 14:31:05 CST +09:30
t.in_time_zone(tz).zone
=> "CST"
t.in_time_zone(tz).utc_offset
=> 34200
See:
http://api.rubyonrails.org/classes/Time.html
http://tzinfo.rubyforge.org/doc/
http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
do a "gem install activesupport", and in your script:
require 'rubygems'
require 'tzinfo'
require 'active_support'

Related

ruby DateTime parsing from 'mm/dd/yyyy' format

I am using ruby 1.9.3 and want to get Date or Time object from 'mm/dd/yyyy' date format string
Time.zone.parse("12/22/2011")
this is giving me *** ArgumentError Exception: argument out of range
require 'date'
my_date = Date.strptime("12/22/2011", "%m/%d/%Y")
As above, use the strptime method, but note the differences below
Date.strptime("12/22/2011", "%m/%d/%Y") => Thu, 22 Dec 2011
DateTime.strptime("12/22/2011", "%m/%d/%Y") => Thu, 22 Dec 2011 00:00:00 +0000
Time.strptime("12/22/2011", "%m/%d/%Y") => 2011-12-22 00:00:00 +0000
(the +0000 is the timezone info, and I'm now in GMT - hence +0000. Last week, before the clocks went back, I was in BST +0100. My application.rb contains the line config.time_zone = 'London')
Try Time.strptime("12/22/2011", "%m/%d/%Y")
Would it be an option for you to use Time.strptime("01/28/2012", "%m/%d/%Y") in place of Time.parse? That way you have better control over how Ruby is going to parse the date.
If not there are gems: (e.g. ruby-american_date) to make the Ruby 1.9 Time.parse behave like Ruby 1.8.7, but only use it if it's absolutely necessary.
1.9.3-p0 :002 > Time.parse '01/28/2012'
ArgumentError: argument out of range
1.9.3-p0 :003 > require 'american_date'
1.9.3-p0 :004 > Time.parse '01/28/2012'
=> 2012-01-28 00:00:00 +0000

Ruby DateTime.Parse to local time

I have a date string 20101129220021, so I will use
require 'date'
d = DateTime.parse('20101129220021')
This part works fine, and I get a date, which is in UTC.
My question is, how can I convert this into my local time? I tried many methods like extracting the time part using d.to_time and manipulate the result, but it didn't work. As far as I know, DateTime object is immutable. Can I please get some help?
irb(main):001:0> require "date"
=> true
irb(main):002:0> d = DateTime.parse('20101129220021')
=> #<DateTime: 2010-11-29T22:00:21+00:00 (70719276007/28800,0/1,2299161)>
irb(main):003:0> d.to_time
=> 2010-11-30 00:00:21 +0200
ruby 1.9.2p180 (2011-02-18)
You can add a rational fraction based on the timezone to get the local time.
require 'date'
# Make this whatever your zone is. Using UTC +0300 here.
ZONE = 3
d = DateTime.parse('20101129220021') + Rational(ZONE,24)
d.to_s # => "2010-11-30T01:00:21+00:00"

Ruby Date Subtraction (e.g. 90 days Ago)

I've been a bit spoiled by the joda-time API of:
DateTime now = new DateTime();
DateTime ninetyDaysAgo = now.minusDays(90);
I'm trying to do a similar thing in Ruby, but I'm
now = Time.now
ninetyDaysAgo = now - (90*24)
However, the math is off here (I'm really working with dates at midnight).
Is there friendly API for date subtraction?
require 'date'
now = Date.today
ninety_days_ago = (now - 90)
Running this thru the IRB console I get:
>>require 'date'
now = Date.today
ninety_days_ago = (now - 90)
require 'date'
=> false
now = Date.today
=> #<Date: 2011-03-02 (4911245/2,0,2299161)>
ninety_days_ago = (now - 90)
=> #<Date: 2010-12-02 (4911065/2,0,2299161)>
If you need the time you could just say now = DateTime.now
For those using Rails, check out the following:
DateTime.now - 10.days
=> Sat, 04 May 2013 12:12:07 +0300
20.days.ago - 10.days
=> Sun, 14 Apr 2013 09:12:13 UTC +00:00
If you're using Rails or don't mind including ActiveSupport, you can use the Numeric#days DSL like this:
ruby-1.9.2-p136 :002 > Date.today
=> Wed, 02 Mar 2011
ruby-1.9.2-p136 :003 > Date.today - 90.days
=> Thu, 02 Dec 2010
Since you are working with dates instead of times, you should also either start with Date instances, or convert your DateTime intances with #to_date. When adding/subtracting numbers from date instances, the numbers are implicitly days.
ruby-1.9.2-p136 :016 > DateTime.now.to_date
=> #<Date: 2011-03-02 (4911245/2,0,2299161)>
ruby-1.9.2-p136 :017 > DateTime.now.to_date - 90
=> #<Date: 2010-12-02 (4911065/2,0,2299161)>
Ruby supports date arithmetic in the Date and DateTime classes, which are part of Ruby's standard library. Both those classes expose #+ and #- methods, which add and subtract days from a date or a time.
$ irb
> require 'date'
=> true
> (DateTime.new(2015,4,1) - 90).to_s # Apr 1, 2015 - 90 days
=> "2015-01-01T00:00:00+00:00"
> (DateTime.new(2015,4,1) - 1).to_s # Apr 1, 2015 - 1 day
=> "2015-03-31T00:00:00+00:00"
Use the #<< and #>> methods to operate on months instead of days. Arithmetic on months is a little different than arithmetic on days. Using Date instead of DateTime makes the effect more obvious.
> (Date.new(2015, 5, 31) << 3).to_s # May 31 - 3 months; 92 days diff
=> "2015-02-28"
Following your joda-time example, you might write something like this in Ruby.
now = DateTime.now
ninety_days_ago = now - 90
or maybe just
ninety_days_ago = DateTime.now - 90
use the number of seconds:
Time.now - 90*24*60*60
This is a super old post, but if you wanted to keep with a Time object, like was originally asked, rather than switching to a Date object you might want to consider using Ruby Facets.
Ruby Facets is a standardized library of extensions for core Ruby classes.
http://rubyworks.github.io/facets/
By requiring Facets you can then do the following with Time objects.
Time.now.less(90, :days)
Simple solution using Rails Active Support:
days90_ago = 90.days.ago.to_date.to_s
OUTPUT:
puts 90_days_ago
=> "2019-10-09" # considering cur_date: 2020-01-07

Rails 2.3.2, wrong date, system date is correct

ruby-1.8.6-p399 :005 > Date.today
=> Wed, 24393 Dec 2135
ruby-1.8.6-p399 :006 > DateTime.now
=> Wed, 24393 Dec 2135 17:07:09 +0100
wopi#wopi-desktop:~/work/trunk$ date
Mi 2. Feb 17:08:46 CET 2011
What's wrong ?
https://gist.github.com/807916
What do you get with: Time.at(0) ?
Should be 1969...
What do you get with Time.now?
The date objects come from the date class, which inherits from the time class:
http://corelib.rubyonrails.org/classes/Time.html
Other helpful methods to test could be there. Try doing straight ruby to see if it's a problem with your rails setup or with ruby. You should be able to trace the specific place where this goes wrong.

Forcing "+0000" timezone for RFC2822 times in Ruby

How can I force the Time.rfc2822 function to spit out +0000?
Ruby lets me parse RFC2822 formatted times pretty easily:
require 'time'
time = Time.parse('14 Aug 2009 09:28:32 +0000')
puts time
=> "2009-08-14 05:28:32 -0400"
But what about displaying times? Notice that the time it parsed is a local time. No worries, I can convert it back to a UTC time with gmtime:
puts time.gmtime
=> "2009-08-14 09:28:32 UTC"
I can then put it back into RFC2822 format:
puts time.gmtime.rfc2822
=> "Fri, 14 Aug 2009 09:28:32 -0000"
Unfortunately, this is not quite what I want. Notice that the +0000 is now -0000. According to RFC2822, this is because:
The form "+0000" SHOULD be used to indicate a time zone at
Universal Time. Though "-0000" also indicates Universal Time, it is
used to indicate that the time was generated on a system that may be
in a local time zone other than Universal Time and therefore
indicates that the date-time contains no information about the local
time zone.
Great - so how can I force +0000 other than monkey-patching the rfc2822 function?
Here's my monkeypatch solution:
class Time
alias_method :old_rfc2822, :rfc2822
def rfc2822
t = old_rfc2822
t.gsub!("-0000", "+0000") if utc?
t
end
end
If you have a non-monkeypatch solution, I would love to see it!
simplest way if you don't need to use on multiple places
Time.now.gmtime.rfc2822.sub(/(-)(0+)$/, '+\2')
=> "Fri, 31 Mar 2017 08:39:04 +0000"
or as a static (singleton) method version
require 'time'
module MyCustomTimeRFC
def custom_rfc2822(time)
time.gmtime.rfc2822.sub(/(-)(0+)$/, '+\2')
end
module_function :custom_rfc2822
end
t = Time.now
p MyCustomTimeRFC.custom_rfc2822(t)
#=> "Fri, 31 Mar 2017 08:43:15 +0000"
or as module extension if you like the oop style with ruby flexibility.
require 'time'
module MyCustomTimeRFC
def custom_rfc2822
gmtime.rfc2822.sub(/(-)(0+)$/, '+\2')
end
end
t = Time.now
t.extend(MyCustomTimeRFC)
p t.custom_rfc2822
#=> "Fri, 31 Mar 2017 08:43:15 +0000"

Resources