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
Related
I'm new into HAML, and I have ( I hope easy ) question regarding date/ time. Is it possible to get start and end date of current week in HAML in not very very commplicated way? I want to use those dates for navigation in my calendar.
For month and year is rather easy:
- year = Date.today.year
- month = Date.today.month
- day = Date.today
- monthEnd = year.to_s + "-" + month.to_s + "-" + Integer(Date.new(year, month, -1).strftime("%d")).to_s
- monthStart = Date.new(year, month, 1)
- yearStart = year.to_s + "-01-01";
- yearEnd = year.to_s + "-12-31";
If it is not possible, I will do it in Javascript, but I would like to have this consistent.
In short, these are the methods you're looking for:
Date.today.beginning_of_week
Date.today.end_of_week
If you want to set the beginning of the week to sunday, you can do so in an initializer. Remember to restart your server.
initializers/set_beginning_of_week.rb
Date.today.beginning_of_week = :sunday
Further Improvements
The HAML syntax - is equivalent to ERB <% %>. So this is used for coding Ruby code in your views.
In your views you should not set a long list of variables or perform complex functions. You should try to limit these to if, else, each & other basic operations.
Additionally, for these type of functions you can call helper methods.
I'd setup some basic helpers like:
helpers/application_helper.rb
def current_date
Date.today # or use Time.zone.today as Rob suggested
end
def current_month
current_date.month
end
def current_year
current_date.year
end
Resulting in easy calls
current_year
current_month
current_date
current_date.beginning_of_week
current_date.end_of_week
current_month.beginning_of_month
current_month.end_of_month
current_year.beginning_of_year
current_year.end_of_year
This can then be implemented in any view.
views/foo/show.html.haml
.calendar_header
- current_year
%p Beginning of month is
- current_month.beginning_of_month
Assuming the week starts on Sunday and ends on Saturday, and you want to use Ruby's standard library:
- weekStart = Date.today.prev_day(Date.today.cwday)
- weekEnd = Date.today.next_day(6-Date.today.cwday)
BTW, I would probably take advantage of Time.zone.now instead of Date.today if you're using Rails. Same idea for your other ones. More on that in this blog.
#TheChamp:
Rails has convenience methods for doing just this:
weekStart = Time.zone.today.beginning_of_week
weekEnd = Time.zone.today.end_of_week
This isn't really a HAML issue, it's more a Ruby and/or Rails issue. Have a look at the rails date and time helpers:
http://api.rubyonrails.org/classes/DateAndTime/Calculations.html
They allow you to do:
week_start = Time.now.beginning_of_week
I'm sure there is a already a solution for what I need, but I guess I don't know what to search for. Any pointings into the right direction?
I'm thinking of something like Rails' distance_of_time_in_words_to_now.
Thank you.
I believe you could use a helper like this.
def custom_format(date)
if date == Date.today
"Today"
elsif date == Date.yesterday
"Yesterday"
elsif (date > Date.today - 7) && (date < Date.yesterday)
date.strftime("%A")
else
date.strftime("%B %-d")
end
end
Didn't test the code, it's just a pointer to your problem.
Create a file .../config/initializers/time_format.rb and put this code in it:
Time::DATE_FORMATS[:humanized_ago] = ->(time) do
st = Time.now.beginning_of_day
nd = Time.now.end_of_day
case
when time.between?(st + 1.day, nd + 1.day)
"Tomorrow #{time.strftime('%H:%M')}"
when time.between?(st, nd)
"Today #{time.strftime('%H:%M')}"
when time.between?(st - 1.day, nd - 1.day)
"Yesterday #{time.strftime('%H:%M')}"
when time.between?(st - 6.day, nd - 2.day)
time.strftime('%a %H:%M')
else
time.strftime('%y-%b-%d %H:%M')
end
end
On a Rails Time object, call function time.to_s(:humanized_ago). If you don't like the symbol ":humanized_ago", change it it whatever you want in the first line of time_format.rb." If you want other formatting, you can figure it out.
I wrote the comparisons the way I did for a reason. I couldn't find a way to use the Ruby built-in ranges to test Time, and you need to be able to test Time intervals excluding the end point.
Try this
<%= time_ago_in_words(time) %> ago
I have a model in my Rails 3 application which has a date field:
class CreateJobs < ActiveRecord::Migration
def self.up
create_table :jobs do |t|
t.date "job_date", :null => false
...
t.timestamps
end
end
...
end
I would like to prepopulate my database with random date values.
What is the easiest way to generate a random date ?
Here's a slight expansion on Chris' answer, with optional from and to parameters:
def time_rand from = 0.0, to = Time.now
Time.at(from + rand * (to.to_f - from.to_f))
end
> time_rand
=> 1977-11-02 04:42:02 0100
> time_rand Time.local(2010, 1, 1)
=> 2010-07-17 00:22:42 0200
> time_rand Time.local(2010, 1, 1), Time.local(2010, 7, 1)
=> 2010-06-28 06:44:27 0200
Generate a random time between epoch, the beginning of 1970, and now:
Time.at(rand * Time.now.to_i)
Keeping simple..
Date.today-rand(10000) #for previous dates
Date.today+rand(10000) #for future dates
PS. Increasing/Decreasing the '10000' parameter, changes the range of dates available.
rand(Date.civil(1990, 1, 1)..Date.civil(2050, 12, 31))
my favorite method
def random_date_in_year(year)
return rand(Date.civil(year.min, 1, 1)..Date.civil(year.max, 12, 31)) if year.kind_of?(Range)
rand(Date.civil(year, 1, 1)..Date.civil(year, 12, 31))
end
then use like
random_date = random_date_in_year(2000..2020)
For recent versions of Ruby/Rails, You can use rand on a Time range ❤️ !!
min_date = Time.now - 8.years
max_date = Time.now - 1.year
rand(min_date..max_date)
# => "2009-12-21T15:15:17.162+01:00" (Time)
Feel free to add to_date, to_datetime, etc. to convert to your favourite class
Tested on Rails 5.0.3 and Ruby 2.3.3, but apparently available from Ruby 1.9+ and Rails 3+
The prettiest solution to me is:
rand(1.year.ago..50.weeks.from_now).to_date
The following returns a random date-time in the past 3 weeks in Ruby (sans Rails).
DateTime.now - (rand * 21)
Here's also a more (in my oppinion) improved version of Mladen's code-snippet. Luckily Ruby's rand() function can also handle Time-Objects. Regarding the Date-Object is defined when including Rails, the rand() method gets overwritten so it can handle also Date-Objects. e.g.:
# works even with basic ruby
def random_time from = Time.at(0.0), to = Time.now
rand(from..to)
end
# works only with rails. syntax is quite similar to time method above :)
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
Here's my one liner to generate a random date in the last 30 days (for example):
Time.now - (0..30).to_a.sample.days - (0..24).to_a.sample.hours
Works great for my lorem ipsum. Obviously minutes and seconds would be fixed.
Since you're using Rails, you can install the faker gem and make use of the Faker::Date module.
e.g. The following generates a random date in the year of 2018:
Faker::Date.between(Date.parse('01/01/2018'), Date.parse('31/12/2018'))
Mladen's answer is a little difficult to understand from just a single look. Here's my take on this.
def time_rand from=0, to= Time.now
Time.at(rand(from.to_i..to.to_i))
end
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
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.