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
Related
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'm trying to extract the time component from a DateTime object (which is represented as "at" in my example). How do I do this, I am absolutely stumped? (I don't want to parse it to a string with strftime as i did here):
#session_date.at.strftime("%H:%M")
I would really like to return the hours and minutes as a Time object.
Is there a specific reason you want a Time object?
Just so we're clear, the Time class in Ruby isn't just "DateTime without the date." As "What's the difference between DateTime and Time in Ruby?" explains, "Time is a wrapper around POSIX-standard time_t, or seconds since January 1, 1970." Like DateTime, a Time object still has year, month, and day, so you don't really gain anything by using Time instead. There's not really a way to represent just hour and minute using either Time or DateTime.
The best you could do with Time, I think, would be this:
date_time = DateTime.now
seconds = date_time.hour * 60 * 60 + date_time.minute * 60
time = Time.at(seconds)
# => 1970-01-01 09:58
...but then you still have to call time.hour and time.min to get at the hour and minute.
If you're just looking for a lightweight data structure to represent an hour and minute pair, though, you might as well just roll your own:
HourAndMinute = Struct.new(:hour, :minute) do
def self.from_datetime(date_time)
new(date_time.hour, date_time.minute)
end
end
hm = HourAndMinute.from_datetime(DateTime.now)
# => #<struct HourAndMinute hour=15, minute=58>
hm.to_h
# => { :hour => 15, :minute => 58 }
hm.to_a
# => [ 15, 58 ]
Edit re:
I have a variable that stores an appointment -- this variable is a DateTime object. I have two table fields that store the start and end times of a location. I need to check if the time scheduled for that appointment lies between the start and end times.
Ah, it seems you had a bit of a XY problem. This makes a lot more sense now.
Absent any more information, I'm going to assume your "fields that store the start and end times of a location" are MySQL TIME columns called start_time and end_time. Given MySQL TIME columns, Rails casts the values to Time objects with the date component set to 1/1/2000. So if your database has the values start_time = '09:00' and end_time = '17:00', Rails will give you Time objects like this:
start_time = Time.new(2000, 1, 1, 9, 0) # => 2000-01-01 09:00:00 ...
end_time = Time.new(2000, 1, 1, 17, 0) # => 2000-01-01 17:00:00 ...
Now you say your appointment time is a DateTime, so let's call it appointment_datetime and suppose it's at 10:30am tomorrow:
appointment_datetime = DateTime.new(2014, 11, 18, 10, 30) # => 2014-11-18 10:30:00 ...
So now to rephrase your question: How do we tell if the time part of appointment_datetime is between the time part of start_time and end_time. The answer is, we need to either change the date part of start_time and end_time to match the date part of appointment_datetime, or the other way around. Since it's easier to change one thing than two, let's do it the other way around and change appointment_datetime to match start_time and end_time (and, since those two are Time objects, we'll create a Time object):
appointment_time = DateTime.new(2000, 1, 1, appointment_datetime.hour, appointment_datetime.minute)
# => 2000-01-01 10:30:00 ...
Now we can compare them directly:
if appointment_time >= start_time && appointment_time <= end_time
puts "Appointment time is good!"
end
# Or, more succinctly:
if (start_time..end_time).cover?(appointment_time)
puts "Appointment time is good!"
end
You would, of course, want to wrap all of this up in a method, perhaps in your Location model (which, again, I'm assuming has start_time and end_time attributes):
class Location < ActiveRecord::Base
# ...
def appointment_time_good?(appointment_datetime)
appointment_time = DateTime.new(2000, 1, 1,
appointment_datetime.hour, appointment_datetime.minute)
(start_time..end_time).cover?(appointment_time)
end
end
location = Location.find(12) # => #<Location id: 12, ...>
location.appointment_time_good?(appointment_time) # => true
I hope that's helpful!
P.S. Another way to implement this would be to ditch the date/time objects entirely and do a straight numeric comparison:
def appointment_time_good?(appointment_datetime)
appointment_hour_min = [ appointment_datetime.hour, appointment_datetime.minute ]
appointment_hour_min >= [ start_time.hour, start_time.min ]
&& appointment_hour_min <= [ end_time.hour, end_time.min ]
end
If you have a DateTime object:
date_time = DateTime.now
date_time.hour
# => 16
date_time.minute
# => 1
If you are looking for the Time since now in words (which is common in Rails apps), then this may be a good read: http://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html#method-i-time_ago_in_words
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.
How would I get the most recently occurring Wednesday, using Ruby (and Rails, if there's a pertinent helper method)?
Ultimately need the actual date (5/1/2013).
time = Time.now
days_to_go_back = (time.wday + 4) % 7
last_wed = days_to_go_back.days.ago
This works in Ruby:
require 'date'
def last_wednesday(date)
date - (date.wday - 3) % 7
end
last_wednesday(Date.today)
# => #<Date: 2013-05-01 ((2456414j,0s,0n),+0s,2299161j)>
In Rails there's beginning_of_week:
Date.today.beginning_of_week(:wednesday)
# => Wed, 01 May 2013
If you are okay with using another gem, I recommend Chronic.
With it, you can get last Wednesday by doing:
Chronic.parse('last Wednesday')
The simplest way (to me) is:
require 'date'
date = Date.today
date -= 1 until date.wednesday?
Pretty straightforward with Date:
require 'date'
today = DateTime.now.to_date
last_wednesday = today.downto(today - 6).select { |d| d.wednesday? }
You can even get the last weekday of your choice like this (here without error handling):
def last_weekday(weekday)
today = Time.now.to_date
today.downto(today-6).select do |d|
d.send((weekday.to_s + "?").to_sym)
end
end
I'm implementing the Timezone support in Rails 2.1+, and I've run into an apparent bug in the way the data is pulled from the db. Let me try to set it up.
The "deals" model contains an "offer_date" datetime field. Let's say I have two records with these offer_dates:
Deal 1: 2009-12-29 23:59:59 -0500
Deal 2: 2009-12-28 23:59:59 -0500
This is correct: dates should mark the last second of its given day.
Now, when it comes time to find the deal for today, I have the following AR query:
#deal = Deal.first(:conditions=>['offer_date > ?', Time.now.beginning_of_day], :order=>"offer_date ASC")
In this case, although it's the 29th today, it returns the record ostensibly for the 28th. Why? Here's what happens in the console:
>> #deal = Deal.first(:conditions=>['offer_date > ?', Time.now.beginning_of_day], :order=>"offer_date ASC")
=> #<Deal id: 2, <blah blah blah...>, offer_date: "2009-12-29 04:59:00">
It's shifting the time forward by 5 hours, putting yesterday's day into the next. But when I do this:
>> #deal.offer_date
=> Mon, 28 Dec 2009 23:59:00 EST -05:00
I get the right time. What the heck??? Suffice to say, I need that date to work properly, but I can't figure out where my issue is. Any assistance you can provide would be greatly appreciated!
See my prior rsponse on Time vs Time.zone for AR date queries.
Instead of using Time.now, try using Time.zone.now
Of course you have to set this and everything. This tutorial seems helpful.
the Time class doesn't care about the time zone in the implementation of #to_s, therefore you have to use
Time.zone.now # or any other TimeWithZone object
in your finders/named_scopes/find calls. Or you could read through http://marklunds.com/articles/one/402 and then put
module ActiveRecord
module ConnectionAdapters # :nodoc:
module Quoting
# Convert dates and times to UTC so that the following two will be equivalent:
# Event.all(:conditions => ["start_time > ?", Time.zone.now])
# Event.all(:conditions => ["start_time > ?", Time.now])
def quoted_date(value)
value.respond_to?(:utc) ? value.utc.to_s(:db) : value.to_s(:db)
end
end
end
end
into your environment.rb or into an initializer.