Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
What's the best way to get the last occurrence of a date having the given month and day with Ruby/Rails. The Returned value should be a Date object.
i.e.:
prev_occuring(month, day)
today = Date.today # => Thu, 14 Dec 2019
today.prev_occurring(:june, 1) # => Sat, 1 Jun 2019
or
today = Date.today # => Thu, 9 Jan 2020
today.prev_occurring(:june, 1) # => Sat, 1 Jun 2019
There is more than one way to do it, of course.
But here's one approach:
$> Date.parse('28 dec').then { |date| date.advance(years: (date > Date.today ? -1 : 0)) }
=> Fri, 28 Dec 2018
$> Date.parse('3 feb').then { |date| date.advance(years: (date > Date.today ? -1 : 0)) }
=> Sun, 03 Feb 2019
Alternatively, you could opt to be more explicit (i.e. not rely on Date.parse magic)...
You could use Date::MONTHNAMES to fetch the month number, and then write a little conditional to check whether the given date falls after the current date, in a calendar year.
It would then probably be easiest to just use Date.new with the year/month/day values, rather than Date#advance.
I understand that, given a month expressed as a symbol and a day of that month, the requirement is to return a date object that represents the last date that has the given month and day that is no later than the current date. Here is a pure-Ruby solution.
require 'date'
def last_date(month, day)
date_this_year = Date.strptime("%s%s" % [month.to_s.capitalize, day], '%B%d')
date_this_year <= Date.today ? date_this_year : date_this_year << 12
end
See Date::strptime and Date#<<.
last_date(:june, 1)
#=> #<Date: 2019-06-01 ((2458636j,0s,0n),+0s,2299161j)>
last_date(:december, 23)
#=> #<Date: 2018-12-23 ((2458476j,0s,0n),+0s,2299161j)>
One could alternatively write:
def last_date(month, day)
today = Date.today
date_this_year = Date.new(today.year,
Date::MONTHNAMES.index(month.to_s.capitalize), day)
date_this_year <= today ? date_this_year : date_this_year << 12
end
See Date::new.
Related
I am having problem understanding datetime format in rails. I have stored a date which is called 'starts_at' and type is datetime using postman.
{
"reservation": {
"starts_at": "2020-1-7 7:00 pm",
"expires_at": "2020-1-7 9:00 pm"
}
}
I want to enter another date and want to validate the date I enter every time be in an interval of 120 minutes after.
So I tried plucking out 'starts_at' from my model 'reservations' and tried to loop through each 'starts_at' date and comparing their difference with my new entered dates' 'starts_at'.
# I have defined scope to pluck the 'starts_at' in reservation.rb
# scope :starts_at, -> { pluck(:starts_at) }
def create
Reservation.starts_at.each do |starts_at|
time_diffs = time_diff_in_minutes(date_time_conversion(starts_at) - params[:starts_at].to_datetime)
if time_diffs < 120
return
end
#reservation = Reservation.new(processed_params)
reservation.save
end
end
private
def date_time_conversion(starts_at)
starts_at.to_datetime
end
def time_diff_in_minutes(time_calc)
(time_calc * 24 * 60).to_s.to_i.abs
end
I have one value in database which has starts_at datetime = "2020-1-7 7:00 pm".
When I run debugger and checked the plucked value it has a format something like
Tue, 07 Jan 2020 18:00:00 +06 +06:00
And the format of the date I entered was something like
Tue, 07 Jan 2020 19:00:00 +0000
I want to understand what does +06 +6:00 means and how does postgresql database store datetime object and return format. Your help shall be highly appreciated. Thank you!
I'm trying to create a conditional where if #challenge.deadline (which is a Date) is within the last week (i.e. the last 7 days), then do x, else do y.
I tried:
if #challenge.deadline < 1.week.ago #2017-03-03 01:52:13 -0500
if #challenge.deadline < 7.days.ago.to_date #2017-03-03
if #challenge.deadline < Date.current-7.days #2017-03-03
# All these come up false since I guess it sees 06 as more than 03, but I want the conditional to be based on date such as 06 is less than 7 days ago and therefore should be true
In this example #challenge.deadline equals 2017-03-06
how can I trigger the conditional when #challenge.deadline is a date that has happened within the last 7 days?
"within the last 7 days" describes a range from:
Date.current - 7 #=> Fri, 03 Mar 2017
to:
Date.current #=> Fri, 10 Mar 2017
To check whether #challenge.deadline lies within these bounds, you can use between?:
today = Date.current
if #challenge.deadline.between?(today - 7, today)
# within last 7 days
else
# either before of after
end
Instead of today - 7, you can also use today - 7.days or today - 1.week.
Or, to use an actual range:
today = Date.current
last_week = (today - 7)..today
if last_week.cover?(#challenge.deadline)
# ...
else
# ...
end
If you need this often, you could also consider patching Date:
class Date
def within_last?(duration, date = Date.current)
between?(date - duration, date)
end
end
and check it via:
if #challenge.deadline.within_last?(1.week)
# ...
else
# ...
end
if #challenge.deadline.to_date < 1.week.ago.to_date
do X
else
do Y
end
I am trying to create an object which has both a date field and a time field. I'd like the time field to be generated based on the date in the date field. For example:
SampleObject.new(
date = Date.today #Sat, 28 Dec 2013
time = Date.today.at(5:00pm EST) #Sat, 28 Dec 2013 2013-12-28 17:00:00 -0500 Not valid ruby syntax)
)
How can I make time = line work as intended?
I'd do something like:
DateTime.parse Date.today.to_s + ' 5pm'
#<DateTime: 2013-12-28T17:00:00+00:00 ((2456655j,61200s,0n),+0s,2299161j)>
I am interpreting this question as "a way to set the time of the day on a given date to a given time".
One way you could do it is as follows:
def set_date_and_time(date = Date.today, time = Time.now)
this_day = [date.year, date.month, date.day]
this_time = [time.hour, time.min, time.sec]
Time.new(*this_day, *this_time)
end
Hope this helps.
I need a method that generates an array containing the month end date for each of the past 12 months. I've come up with the solution below. It works, however, there's probably a more elegant way to solve this problem. Any suggestions? Is there a more efficient way to generate this array? Any advice would be greatly appreciated.
require 'active_support/time'
...
def months
last_month_end = (Date.today - 1.month).end_of_month
months = [last_month_end]
11.times do
month_end = (last_month_end - 1.month).end_of_month
months << month_end
end
months
end
Usually when you want an array of things start thinking about map. While you're at it why not generalize such a method so you can get back any n months you wish:
def last_end_dates(count = 12)
count.times.map { |i| (Date.today - (i+1).month).end_of_month }
end
>> pp last_end_dates(5)
[Sun, 30 Jun 2013,
Fri, 31 May 2013,
Tue, 30 Apr 2013,
Sun, 31 Mar 2013,
Thu, 28 Feb 2013]
require 'active_support/time'
def months
(1..12).map{|i| (Date.today - i.month).end_of_month}
end
There isn't a specific method, this can be an option however:
(1..12).map { |i| (Date.today - i.month).end_of_month }
Nothing special, but does the job.
require 'active_support/time'
(1..12).map do |m|
m.months.ago.end_of_month
end
Note, if you want correct order of months, you should also call reverse
What is the best way to generate a random DateTime in Ruby/Rails? Trying to create a nice seeds.rb file. Going to use it like so:
Foo.create(name: Faker::Lorem.words, description: Faker::Lorem.sentence, start_date: Random.date)
Here is how to create a date in the last 10 years:
rand(10.years).ago
You can also get a date in the future:
rand(10.years).from_now
Update – Rails 4.1+
Rails 4.1 has deprecated the implicit conversion from Numeric => seconds when you call .ago, which the above code depends on. See Rails PR #12389 for more information about this. To avoid a deprecation warning in Rails 4.1 you need to do an explicit conversion to seconds, like so:
rand(10.years).seconds.ago
Here are set of methods for generating a random integer, amount, time/datetime within a range.
def rand_int(from, to)
rand_in_range(from, to).to_i
end
def rand_price(from, to)
rand_in_range(from, to).round(2)
end
def rand_time(from, to=Time.now)
Time.at(rand_in_range(from.to_f, to.to_f))
end
def rand_in_range(from, to)
rand * (to - from) + from
end
Now you can make the following calls.
rand_int(60, 75)
# => 61
rand_price(10, 100)
# => 43.84
rand_time(2.days.ago)
# => Mon Mar 08 21:11:56 -0800 2010
I prefer use (1..500).to_a.rand.days.ago
You are using Faker; why not use one of the methods provided by Faker::Date?
# Random date between dates
# Keyword arguments: from, to
Faker::Date.between(from: 2.days.ago, to: Date.today) #=> "Wed, 24 Sep 2014"
# Random date between dates except for certain date
# Keyword arguments: from, to, excepted
Faker::Date.between_except(from: 1.year.ago, to: 1.year.from_now, excepted: Date.today) #=> "Wed, 24 Sep 2014"
# Random date in the future (up to maximum of N days)
# Keyword arguments: days
Faker::Date.forward(days: 23) # => "Fri, 03 Oct 2014"
# Random date in the past (up to maximum of N days)
# Keyword arguments: days
Faker::Date.backward(days: 14) #=> "Fri, 19 Sep 2014"
# Random birthday date (maximum age between 18 and 65)
# Keyword arguments: min_age, max_age
Faker::Date.birthday(min_age: 18, max_age: 65) #=> "Mar, 28 Mar 1986"
# Random date in current year
Faker::Date.in_date_period #=> #<Date: 2019-09-01>
# Random date for range of year 2018 and month 2
# Keyword arguments: year, month
Faker::Date.in_date_period(year: 2018, month: 2) #=> #<Date: 2018-02-26>
# Random date for range of current year and month 2
# Keyword arguments: month
Faker::Date.in_date_period(month: 2) #=> #<Date: 2019-02-26>
current Faker version: 2.11.0
Here is how to create a date in this month:
day = 1.times.map{ 0+Random.rand(30) }.join.to_i
rand(day.days).ago
Another approach using DateTime's advance
def rand_date
# return a random date within 100 days of today in both past and future directions.
n = rand(-100..100)
Date.today.advance(days: n)
end
This is what I use:
# get random DateTime in last 3 weeks
DateTime.now - (rand * 21)
other way:
(10..20).to_a.sample.years.ago
I haven't tried this myself but you could create a random integer between two dates using the number of seconds since epoch. For example, to get a random date for the last week.
end = Time.now
start = (end - 1.week).to_i
random_date = Time.at(rand(end.to_i - start)) + start
Of course you end up with a Time object instead of a DateTime but I'm sure you can covert from here.
As I already mentioned in another question I think the following code-snippet is more consisent regarding the data-types of the parameters and of the value to be returned. Stackoverflow: How to generate a random date in Ruby?
Anyway this uses the rand() method's internal logic what is the random Date or random Time within a span. Maybe someone has a more efficient way to set the default-parameter to (Time.now.to_date) of the method random_date, so it doesn't need this typecasting.
def random_time from = Time.at(0.0), to = Time.now
rand(from..to)
end
# works quite similar to date :)
def random_date from = Date.new(1970), to = Time.now.to_date
rand(from..to)
end
Edit: this code won't work before ruby v1.9.3
You can pass Time Range to rand
rand(10.weeks.ago..1.day.ago)
Output Example:
=> Fri, 10 Jan 2020 10:28:52 WIB +07:00
Without user faker (cause I'm using an old version of ruby):
Time.zone.now - rand(16..35.years) - rand(1..31).days
My 'ish' gem provides a nice way of handling this:
# plus/minus 5 min of input date
Time.now.ish
# override that time range like this
Time.now.ish(:offset => 1.year)
https://github.com/spilliton/ish