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
Related
Why won't Ruby's strptime convert this to a DateTime object:
DateTime.strptime('Monday 10:20:20', '%A %H:%M:%S')
# => ArgumentError: invalid date
While these work?
DateTime.strptime('Wednesday', '%A')
# => #<DateTime: 2015-11-18T00:00:00+00:00 ((2457345j,0s,0n),+0s,2299161j)>
DateTime.strptime('10:20:20', '%H:%M:%S')
# => #<DateTime: 2015-11-18T10:20:20+00:00 ((2457345j,37220s,0n),+0s,2299161j)>
This looks like a bug - minitech's comment is spot on. For now, though, a workaround (because you probably want this to work now):
You can split it on the space, get the date from the weekday, then get the time component from the other string (using the _strptime method minitech mentioned). Then you can set the time on the first date to the time component from the second string:
def datetime_from_weekday_time_string(string)
components = string.split(" ")
date = DateTime.strptime(components[0], '%A')
time = Date._strptime(components[1], '%H:%M:%S') # returns a hash like {:hour=>10, :min=>20, :sec=>20}
return date.change(time)
end
2.2.2 :021 > datetime_from_weekday_time_string("Monday 10:20:20")
=> Mon, 16 Nov 2015 10:20:20 +0000
2.2.2 :022 > datetime_from_weekday_time_string("Saturday 11:45:21")
=> Sat, 21 Nov 2015 11:45:21 +0000
2.2.2 :023 > datetime_from_weekday_time_string("Thursday 23:59:59")
=> Thu, 19 Nov 2015 23:59:59 +0000
I would like recover a list of entries for the current month with Sequel.
I tried:
Entry.where(:date >= Date.month).sum(:duration)
or
Entry.where(:date.like('%/06/2013')).sum(:duration)
and other ways, but none of them seemed to work.
If you want all entries the current month and the current year, it's probably easiest to use a range:
d = Date.today
Entry.where(:date=> Date.new(d.year, d.month)...(Date.new(d.year, d.month) >> 1)).sum(:duration)
If you want the current month in any year, Sequel has built in support for this:
Entry.where(Sequel.extract(:month, :date) => Date.today.month).sum(:duration)
You'll need to think in terms of how a database thinks, and how Sequel turns Ruby ranges into SQL:
require 'date'
today = Date.today # => #<Date: 2013-07-03 ((2456477j,0s,0n),+0s,2299161j)>
first_of_month = (today - today.day) + 1
first_of_month # => #<Date: 2013-07-01 ((2456475j,0s,0n),+0s,2299161j)>
next_month = today + 31 # => #<Date: 2013-08-03 ((2456508j,0s,0n),+0s,2299161j)>
last_of_month = next_month - next_month.day # => #<Date: 2013-07-31 ((2456505j,0s,0n),+0s,2299161j)>
last_of_month # => #<Date: 2013-07-31 ((2456505j,0s,0n),+0s,2299161j)>
Entry.where(:date => [first_of_month .. last_of_month]).sum(:duration)
I'd show you the SQL output, but I don't know the database type you're using, and, well, I'm lazy.
Often, you can play tricks inside the DB by truncating "now" to remove the day, then finding all timestamps whose truncated date matches it. That's a lot more specific to the DBM than using Sequel, which already knows how to deal with ranges when converting them to a "between"-type statement.
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.
With 1.9.2p0, Date#parse assumes an UE format. Check out format.rb, line: 1042 if you don't believe me.
Anyways, how can I make it assume a US format, so that:
> Date.parse("10/4/2010")
=> Mon, 04 Oct 2010
Instead of April 10th.
I've tried this:
class Date
def _parse_eu(str,e)
_parse_us(str,e)
end
end
but no luck. Any other ideas?
Date.strptime is what you want but unfortunately it doesn't look like the documentation has the date formatting strings. I got the following to work based on Googling for the format strings:
1.9.2 > d = Date.strptime("10/4/2010", "%m/%d/%Y")
=> #<Date: 2010-10-04 (4910947/2,0,2299161)>
1.9.2 > d.day
=> 4
1.9.2 > d = Date.strptime("10/4/2010", "%d/%m/%Y")
=> #<Date: 2010-04-10 (4910593/2,0,2299161)>
1.9.2 > d.day
=> 10
You might want to check out strptime instead.
If I have d = DateTime.now, how do I convert 'd' into UTC (with the appropriate date)?
DateTime.now.new_offset(0)
will work in standard Ruby (i.e. without ActiveSupport).
d = DateTime.now.utc
Oops!
That seems to work in Rails, but not vanilla Ruby (and of course that is what the question is asking)
d = Time.now.utc
Does work however.
Is there any reason you need to use DateTime and not Time? Time should include everything you need:
irb(main):016:0> Time.now
=> Thu Apr 16 12:40:44 +0100 2009
Unfortunately, the DateTime class doesn't have the convenience methods available in the Time class to do this. You can convert any DateTime object into UTC like this:
d = DateTime.now
d.new_offset(Rational(0, 24))
You can switch back from UTC to localtime using:
d.new_offset(DateTime.now.offset)
where d is a DateTime object in UTC time. If you'd like these as convenience methods, then you can create them like this:
class DateTime
def localtime
new_offset(DateTime.now.offset)
end
def utc
new_offset(Rational(0, 24))
end
end
You can see this in action in the following irb session:
d = DateTime.now.new_offset(Rational(-4, 24))
=> #<DateTime: 106105391484260677/43200000000,-1/6,2299161>
1.8.7 :185 > d.to_s
=> "2012-08-03T15:42:48-04:00"
1.8.7 :186 > d.localtime.to_s
=> "2012-08-03T12:42:48-07:00"
1.8.7 :187 > d.utc.to_s
=> "2012-08-03T19:42:48+00:00"
As you can see above, the initial DateTime object has a -04:00 offset (Eastern Time). I'm in Pacific Time with a -07:00 offset. Calling localtime as described previously properly converts the DateTime object into local time. Calling utc on the object properly converts it to a UTC offset.
Try this, works in Ruby:
DateTime.now.to_time.utc
You can set an ENV if you want your Time.now and DateTime.now to respond in UTC time.
require 'date'
Time.now #=> 2015-11-30 11:37:14 -0800
DateTime.now.to_s #=> "2015-11-30T11:37:25-08:00"
ENV['TZ'] = 'UTC'
Time.now #=> 2015-11-30 19:37:38 +0000
DateTime.now.to_s #=> "2015-11-30T19:37:36+00:00"
In irb:
>>d = DateTime.now
=> #<DateTime: 11783702280454271/4800000000,5/12,2299161>
>> "#{d.hour.to_i - d.zone.to_i}:#{d.min}:#{d.sec}"
=> "11:16:41"
will convert the time to the utc. But as posted if it is just Time you can use:
Time.now.utc
and get it straight away.