Cucumber Date Ranges Ruby - ruby

I've seen some similar questions on here regarding date ranges yet none of the solutions seem to work for me. What i'm trying to do is have a date range of a month and confirm if today's date is within that range. Eventually, this will be put into a case method for every month of the year as the functionality I'm testing is date specific.
I tried to convert the dates to integer to make the calculation easier (or so I thought) then use between? to check the date range.
This is my code:
today = Time.now.to_i
month_start = Time.parse('1 Jan 2016').to_i
month_end = Time.parse('31 Jan 2016').to_i
if today.between?(month_start,month_end)
#do something
end
When having a puts on each variable, this is the output:
Today = 1468479863
month_start = 1451606400
month_end = 1454198400
As you can see, this should fail as today is not between the date range, it's far outside it. Yet, the tests are going green which would suggest my if statement containing the between? method isn't working.
Is there something blindingly obvious that I'm missing here as I can't see it.
The step in the feature is here:
Then(/^I can see that all results have a statement due this month$/) do
today = Time.now.to_i
month_start = Time.parse('1 Jan 2016').to_i
month_end = Time.parse('31 Jan 2016').to_i
results_table = all('table#clickable-rows tbody tr')
if today.between?(month_start,month_end)
results_table.each do |row|
within(row.all('td')[3]) do
statement_date = find('table#clickable-rows tbody tr td:nth-child(4) > span')
expect(statement_date).to have_text '1 Aug 2016'
end
end
end
end

Your tests pass, because if today.between?(month_start,month_end) return false, no expectations are run!
You might want to consider changing the if into an expectation in itself:
Then(/^I can see that all results have a statement due this month$/) do
today = Time.now.to_i
month_start = Time.parse('1 Jan 2016').to_i
month_end = Time.parse('31 Jan 2016').to_i
results_table = all('table#clickable-rows tbody tr')
expect(today).to be_between(month_start,month_end)
results_table.each do |row|
within(row.all('td')[3]) do
statement_date = find('table#clickable-rows tbody tr td:nth-child(4) > span')
expect(statement_date).to have_text '1 Aug 2016'
end
end
end

I think you mis-typed this:
month_end = Time.parse('1 Jan 2016').to_i
Shouldn't it be '31 Jan 2016' instead?

Your whole test seems a bit jumbled - I think from the description of the step what you actually want is
Then(/^I can see that all results have a statement due this month$/) do
expected_date = /\A#{Date.today.strftime('1 %b %Y')}\Z/ # rexex needed so it doesn't match 11 Jul 2016 or 21 Jul 2016
results_table = all('table#clickable-rows tbody tr')
results_table.each do |row|
expect(row.find('td:nth-child(4)')).to have_text(expected_date)
end
end
The results_table.each block could have also been written as
expect(results_table).to RSpec.all(have_selector('td:nth-child(4)', text: expected_date))

Related

How to avoid from storing date objects in array in Ruby?

Consider below code:
dates = ["20th OCT 1232", "6th JUN 2019", "23th AUG 2017", "9th JAN 2015"]
def reformateDate(dates)
ans = []
dates.length.times do |i|
ans << (DateTime.parse(dates[i], '%d %b %Y').to_date)
end
ans
end
This function return array in below format:
[#<Date: 1232-10-20 ((2171339j,0s,0n),+0s,2299161j)>, #<Date: 2019-06-06 ((2458641j,0s,0n),+0s,2299161j)>, #<Date: 2017-08-23 ((2457989j,0s,0n),+0s,2299161j)>, #<Date: 2015-01-09 ((2457032j,0s,0n),+0s,2299161j)>]
But i want it to return array in this format:
["1232-10-20","2019-06-06","2017-08-23","2015-01-09"]
So how can i do this?
dates.map { |e| Date.parse(e).strftime('%Y-%m-%d') }
#=> ["1232-10-20", "2019-06-06", "2017-08-23", "2015-01-09"]
Change the template '%Y-%m-%d' according to your needs, see this for reference: Date#strftime.
Picking up the wise suggestion from Cary Swoveland.
Instead of Date.parse(e) you can use Date.strptime(e, '%dth %b %Y'), which works more or less the reverse of strftime. See Date#strptime. It follows a template ('%dth %b %Y') to interpret the original string as a date. Adding th to the template after %d (day), it converts properly the current format to a date object:
Date.strptime("20th OCT 1232", '%dth %b %Y') #=> #<Date: 1232-10-20 ((2171339j,0s,0n),+0s,2299161j)>
But, what if the date is '1st OCT 2018' or '23rd OCT 2018'? The template does not match, because it expects to find th and not st or rd.
To be ordinal suffix agnostic, comes in hand the method String#sub:
"20th OCT 1232".sub(/(?<=\d)\p{Alpha}+/, '') #=> "20 OCT 1232"
So, mixing all together, the best solution to be safe should be:
dates.map { |e| Date.strptime(e.sub(/(?<=\d)\p{Alpha}+/, ''), '%d %b %Y').strftime('%Y-%m-%d') }
Well, you're actually storing Date objects when you write:
ans << (DateTime.parse(dates[i], '%d %b %Y').to_date)
There's a couple of problems with this: First, the parenthesis don't do anything, so you can remove them. Second, what you're doing is parsing a string into a DateTime object, and then converting it into a Date object. Not really sure why you would do that, but I believe it's a mistake. If you want to convert this to a string by temporally using DateTime objects, consider using strftime, which will take the DateTime object and turn it into a string with a specific format. It would look like this:
ans << DateTime.parse(dates[i], '%d %b %Y').strftime('%Y-%m-%d')
I would do something like this:
require 'date'
def reformat_date(dates)
dates.map { |date| Date.parse(date).to_s }
end
dates = ["20th OCT 1232", "6th JUN 2019", "23th AUG 2017", "9th JAN 2015"]
reformat_date(dates)
#=> ["1232-10-20", "2019-06-06", "2017-08-23", "2015-01-09"]

Does Ruby have an equivalent to TimeSpan in C#?

In C# there is a TimeSpan class. It represents a period of time and is returned from many date manipulation options. You can create one and add or subtract from a date etc.
In Ruby and specifically rails there seems to be lots of date and time classes but nothing that represents a span of time?
Ideally I'd like an object that I could use for outputting formatted dates easily enough using the standard date formatting options.
eg.
ts.to_format("%H%M")
Is there such a class?
Even better would be if I could do something like
ts = end_date - start_date
I am aware that subtracting of two dates results in the number of seconds separating said dates and that I could work it all out from that.
You can do something similar like this:
irb(main):001:0> require 'time' => true
irb(main):002:0> initial = Time.now => Tue Jun 19 08:19:56 -0400 2012
irb(main):003:0> later = Time.now => Tue Jun 19 08:20:05 -0400 2012
irb(main):004:0> span = later - initial => 8.393871
irb(main):005:0>
This just returns a time in seconds which isn't all that pretty to look at, you can use the strftime() function to make it look pretty:
irb(main):010:0> Time.at(span).gmtime.strftime("%H:%M:%S") => "00:00:08"
Something like this? https://github.com/abhidsm/time_diff
require 'time_diff'
time_diff_components = Time.diff(start_date_time, end_date_time)
No, it doesn't. You can just add seconds or use advance method.
end_date - start_date will have Float type
In the end I forked the suggestion in #tokland's answer. Not quite sure how to make it a proper gem but it's currently working for me:
Timespan fork of time_diff
Not yet #toxaq... but I've started something!
https://gist.github.com/thatandyrose/6180560
class TimeSpan
attr_accessor :milliseconds
def self.from_milliseconds(milliseconds)
me = TimeSpan.new
me.milliseconds = milliseconds
return me
end
def self.from_seconds(seconds)
TimeSpan.from_milliseconds(seconds.to_d * 1000)
end
def self.from_minutes(minutes)
TimeSpan.from_milliseconds(minutes.to_d * 60000)
end
def self.from_hours(hours)
TimeSpan.from_milliseconds(hours.to_d * 3600000)
end
def self.from_days(days)
TimeSpan.from_milliseconds(days.to_d * 86400000)
end
def self.from_years(years)
TimeSpan.from_days(years.to_d * 365.242)
end
def self.diff(start_date_time, end_date_time)
TimeSpan.from_seconds(end_date_time - start_date_time)
end
def seconds
self.milliseconds.to_d * 0.001
end
def minutes
self.seconds.to_d * 0.0166667
end
def hours
self.minutes.to_d * 0.0166667
end
def days
self.hours.to_d * 0.0416667
end
def years
self.days.to_d * 0.00273791
end
end

get next/previous month from a Time object

I have a Time object and would like to find the next/previous month. Adding subtracting days does not work as the days per month vary.
time = Time.parse('21-12-2008 10:51 UTC')
next_month = time + 31 * 24 * 60 * 60
Incrementing the month also falls down as one would have to take care of the rolling
time = Time.parse('21-12-2008 10:51 UTC')
next_month = Time.utc(time.year, time.month+1)
time = Time.parse('01-12-2008 10:51 UTC')
previous_month = Time.utc(time.year, time.month-1)
The only thing I found working was
time = Time.parse('21-12-2008 10:51 UTC')
d = Date.new(time.year, time.month, time.day)
d >>= 1
next_month = Time.utc(d.year, d.month, d.day, time.hour, time.min, time.sec, time.usec)
Is there a more elegant way of doing this that I am not seeing?
How would you do it?
Ruby on Rails
Note: This only works in Rails (Thanks Steve!) but I'm keeping it here in case others are using Rails and wish to use these more intuitive methods.
Super simple - thank you Ruby on Rails!
Time.now + 1.month
Time.now - 1.month
Or, another option if it's in relation to the current time (Rails 3+ only).
1.month.from_now
1.month.ago
Personally I prefer using:
Time.now.beginning_of_month - 1.day # previous month
Time.now.end_of_month + 1.day # next month
It always works and is independent from the number of days in a month.
Find more info in this API doc
you can use standard class DateTime
require 'date'
dt = Time.new().to_datetime
=> #<DateTime: 2010-04-23T22:31:39+03:00 (424277622199937/172800000,1/8,2299161)>
dt2 = dt >> 1
=> #<DateTime: 2010-05-23T22:31:39+03:00 (424282806199937/172800000,1/8,2299161)>
t = dt2.to_time
=> 2010-05-23 22:31:39 +0200
There are no built-in methods on Time to do what you want in Ruby. I suggest you write methods to do this work in a module and extend the Time class to make their use simple in the rest of your code.
You can use DateTime, but the methods (<< and >>) are not named in a way that makes their purpose obvious to someone that hasn't used them before.
If you do not want to load and rely on additional libraries you can use something like:
module MonthRotator
def current_month
self.month
end
def month_away
new_month, new_year = current_month == 12 ? [1, year+1] : [(current_month + 1), year]
Time.local(new_year, new_month, day, hour, sec)
end
def month_ago
new_month, new_year = current_month == 1 ? [12, year-1] : [(current_month - 1), year]
Time.local(new_year, new_month, day, hour, sec)
end
end
class Time
include MonthRotator
end
require 'minitest/autorun'
class MonthRotatorTest < MiniTest::Unit::TestCase
describe "A month rotator Time extension" do
it 'should return a next month' do
next_month_date = Time.local(2010, 12).month_away
assert_equal next_month_date.month, 1
assert_equal next_month_date.year, 2011
end
it 'should return previous month' do
previous_month_date = Time.local(2011, 1).month_ago
assert_equal previous_month_date.month, 12
assert_equal previous_month_date.year, 2010
end
end
end
below it works
previous month:
Time.now.months_since(-1)
next month:
Time.now.months_since(1)
I just want to add my plain ruby solution for completeness
replace the format in strftime to desired output
DateTime.now.prev_month.strftime("%Y-%m-%d")
DateTime.now.next_month.strftime("%Y-%m-%d")
You can get the previous month info by this code
require 'time'
time = Time.parse('2021-09-29 12:31 UTC')
time.prev_month.strftime("%b %Y")
You can try convert to datetime.
Time gives you current date, and DateTime allows you to operate with.
Look at this:
irb(main):041:0> Time.new.strftime("%d/%m/%Y")
=> "21/05/2015"
irb(main):040:0> Time.new.to_datetime.prev_month.strftime("%d/%m/%Y")
=> "21/04/2015"
Here is a solution on plain ruby without RoR, works on old ruby versions.
t=Time.local(2000,"jan",1,20,15,1,0);
curmon=t.mon;
prevmon=(Time.local(t.year,t.mon,1,0,0,0,0)-1).mon ;
puts "#{curmon} #{prevmon}"
Some of the solutions assume rails. But, in pure ruby you can do the following
require 'date'
d = Date.now
last_month = d<<1
last_month.strftime("%Y-%m-%d")
Im using the ActiveSupport::TimeZone for this example, but just in case you are using Rails or ActiveSupport it might come in handy.
If you want the previous month you can substract 1 month
time = Time.zone.parse('21-12-2008 10:51 UTC')
time.ago(1.month)
$ irb
irb(main):001:0> time = Time.now
=> 2016-11-21 10:16:31 -0800
irb(main):002:0> year = time.year
=> 2016
irb(main):003:0> month = time.month
=> 11
irb(main):004:0> last_month = month - 1
=> 10
irb(main):005:0> puts time
2016-11-21 10:16:31 -0800
=> nil
irb(main):006:0> puts year
2016
=> nil
irb(main):007:0> puts month
11
=> nil
irb(main):008:0> puts last_month
10
=> nil

does chronic have any options of date format it parses? (ruby)

I need to tell chronic that the format of date is day-month-year is that possible? The data I pass to chronic could also be words today/yesterday/2 days ago.
Currently chronic gives me 2 Dec 2010 instead of 12 Feb 2010 from 12-02-2010
The only solution I can think of is to swap day and month before passing the string to chronic.
require 'chronic'
puts "12-02-2010 = #{Chronic.parse('12-02-2010')}" #should be 12 Feb 2010
puts "yesteday = #{Chronic.parse('yesterday')}" #working ok
puts "Today = #{Chronic.parse('today')}" #working ok
I've found this question today, 20 months after it has been asked. It seems that there is a way to indicate to swap months and days. Just use the :endian_precedence option:
:endian_precedence (Array) — default: [:middle, :little] — By default,
Chronic will parse "03/04/2011" as the fourth day of the third month.
Alternatively you can tell Chronic to parse this as the third day of
the fourth month by altering the :endian_precedence to [:little,
:middle]
Example here:
Chronic.parse('12-02-2010').strftime('%d %b %Y') #=> 02 Dec 2010
Chronic.parse('12-02-2010', :endian_precedence => [:little, :median]).strftime('%d %b %Y') #=> 12 Feb 2010
Hope this helps!
Dorian
The output of chronic can be easily formatted. chronic.parse returns a time object. You can use strftime for formatting as described here.
puts Chronic.parse('today').strftime('%d %b %Y') #=> 23 Feb 2010
As far as the input is concerned, I cannot find anything in chronic that will do it automatically. Manipulating the input string is probably the way to go.
Edit: Chronic has an internal pre_normalize that you could over-ride..
require 'chronic'
puts Chronic.parse('12-02-2010').strftime('%d %b %Y') #=> 02 Dec 2010
module Chronic
class << self
alias chronic__pre_normalize pre_normalize
def pre_normalize(text)
text = text.split(/[^\d]/).reverse.join("-") if text =~ /^\d{1,2}[^\d]\d{1,2}[^\d]\d{4}$/
text = chronic__pre_normalize(text)
return text
end
end
end
puts Chronic.parse('12-02-2010').strftime('%d %b %Y') #=> 12 Feb 2010

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