A person was born in 1988 Feb 29 a leap year. How to define this date - 1 day(Feb 28) when the year is not leap?
def valid_date
d = DateTime.parse("Feb. 29")
rescue ArgumentError
d = "Invalid Date"
end
date = 'Feb. 29'
begin
DateTime.parse(date)
rescue ArgumentError
DateTime.parse('Feb. 28') if date == 'Feb. 29'
end
I have users with a birth_date and now I want to query all users who have their birthday within the next 10 days. I can't just order on birth_date because then I will get this:
01-01-1960
18-12-1975
16-12-1998
Instead of the desired result:
16-12-1998
18-12-1975
01-01-1960
So how can I only order on the day and month, but not the year?
This works in postgreSQL...
start_month = Date.today.month
start_day = Date.today.day
end_month = (Date.today + 10).month
end_day = (Date.today + 10).day
if start_month == end_month
#users = User.where("DATE_PART('month', birth_date) = ? AND DATE_PART('day', birth_date) >= ? AND DATE_PART('day', birth_date) <= ?", start_month, start_day, end_day)
else
#users = User.where("(DATE_PART('month', birth_date) = ? AND DATE_PART('day', birth_date) >= ?) OR (DATE_PART('month', birth_date) = ? AND DATE_PART('day', birth_date) <= ?)", start_month, start_day, end_month, end_day)
end
#users.order("DATE_PART('month', birth_date), DATE_PART('day', birth_date)")
This selects all records with birthdays within the next 10 days and sorts them.
If ten days in the future is still the same month (say on December 14) it selects December between 14 and 24... if it's in a future month (say on December 25) it selects December to end of month and January from beginning to the 4th.
A ruby implementation. Note that it may not be as performant as the DB version...
ids_of_next_10_days = User.all.pluck(:id, :birth_date).map do |user_info|
next_birthday = user_info[1].change(year: Time.now.year)
next_birthday = next_birthday.change(year: Time.now.year + 1) if next_birthday < Date.today
next_birthday.between?(Date.today, Date.today + 10) ? user_info[0] : nil
end.compact
User.where(id: ids_of_next_10_days)
On the Ruby side you can select needed users at first(Postgresql to_char is used):
today = Date.current
dates = (today ... today + 10.days).map { |d| d.strftime('%m%d') }
dates << '0229' if dates.include?('0228') # update by SteveTurczyn
users = User.where("to_char(birth_date, 'MMDD') in (?)", dates).to_a
then sort them:
users.sort_by! do |user|
user_yday = user.birth_date.yday
user_yday >= today.yday ? user_yday : user_yday + 366
end
I am building a rails 5 app.
I need to be able to get the dates that is from a current quarter. With that I mean the user will provide me with a selected quarter (1 to 4) and I will convert that number to a start and end date for that selected quarter. How can I do that?
This is how I tried it but it is good?
def quarter_date(quarter, year)
if quarter == 1
where(date_at: Time.parse("01-01-#{year}")..Time.parse("01-03-#{year}"))
elsif quarter == 2
where(date_at: Time.parse("01-04-#{year}")..Time.parse("01-06-#{year}"))
elsif quarter == 3
where(date_at: Time.parse("01-07-#{year}")..Time.parse("01-09-#{year}"))
elsif quarter == 4
where(date_at: Time.parse("01-10-#{year}")..Time.parse("01-12-#{year}"))
end
end
You mean something like this?
require 'date'
today = Date.today
=> #<Date: 2018-07-02 ((2458302j,0s,0n),+0s,2299161j)>
year = today.year
=> 2018
input = 3
start_date = Date.new(2018, input * 3 - 2, 1)
=> #<Date: 2018-07-01 ((2458301j,0s,0n),+0s,2299161j)>
end_date = Date.new(2018, input * 3 + 1, 1) - 1
=> #<Date: 2018-09-30 ((2458392j,0s,0n),+0s,2299161j)>
It returns the start and end dates for the given quarter of current year.
Update
Updated with method from your attempt:
def quarter_date_range(quarter, year)
start_date = Time.parse("#{year}-#{quarter * 3 - 2}-1")
end_date = (start_date + 2.months).end_of_month
where(date_at: start_date..end_date)
end
I just wonder how the date period can be written in Ruby?
date_a = Time.at() # <= new
date_b = Time.at() # <= old
I'd love to have something like:
September 1 - 30, 2016
Also it needs to be considered the year.
(Ex. if it's in January,
It should be December 25 2016 - January 25 2017)
You could do something like this
def format_dates(*dates)
date1, date2 = dates.sort
return "#{date1.strftime("%B %d %Y")} if date1 == date2
if date1.year == date2.year
if date1.month == date2.month
"#{date1.strftime("%B")} #{date1.day} - #{date2.day}, #{date1.year}"
else
"#{date1.strftime("%B %d")} - #{date2.strftime("%B %d")}, #{date1.year}"
end
else
"#{date1.strftime("%B %d %Y")} - #{date2.strftime("%B %d %Y")}"
end
end
p format_dates(Date.parse('25/12/2016'), Date.parse('25/01/2017'))
# => "December 25 2016 - January 25 2017"
p format_dates(Date.parse('25/12/2016'), Date.parse('25/01/2016'))
# => "January 25 - December 25, 2016"
I can't seem to find an elegant way to do this...
Given a date how can I find the next Tuesday that is either the 2nd or the 4th Tuesday of the calendar month?
For example:
Given 2012-10-19 then return 2012-10-23
or
Given 2012-10-31 then return 2012-11-13
October November
Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
1 2 3 4 5 6 1 2 3
7 8 9 10 11 12 13 4 5 6 7 8 9 10
14 15 16 17 18 19 20 11 12 13 14 15 16 17
21 22 23 24 25 26 27 18 19 20 21 22 23 24
28 29 30 31 25 26 27 28 29 30
Scroll to the bottom if you just want to see what the end result can look like..
Using code snippets from some date processing work I've done recently in ruby 1.9.3.
Some upgrades to DateTime:
require 'date'
class DateTime
ALL_DAYS = [ 'sunday', 'monday', 'tuesday',
'wednesday', 'thursday', 'friday', 'saturday' ]
def next_week
self + (7 - self.wday)
end
def next_wday (n)
n > self.wday ? self + (n - self.wday) : self.next_week.next_day(n)
end
def nth_wday (n, i)
current = self.next_wday(n)
while (i > 0)
current = current.next_wday(n)
i = i - 1
end
current
end
def first_of_month
self - self.mday + 1
end
def last_of_month
self.first_of_month.next_month - 1
end
end
method_missing Tricks:
I have also supplemented the class with some method missing tricks to map calls from next_tuesday to next_wday(2) andnth_tuesday(2)tonth_wday(2, 2)`, which makes the next snippet easier on the eyes.
class DateTime
# ...
def method_missing (sym, *args, &block)
day = sym.to_s.gsub(/^(next|nth)_(?<day>[a-zA-Z]+)$/i, '\k<day>')
dindex = ALL_DAYS.include?(day) ? ALL_DAYS.index(day.downcase) : nil
if (sym =~ /^next_[a-zA-Z]+$/i) && dindex
self.send(:next_wday, dindex)
elsif (sym =~ /^nth_[a-zA-Z]+$/i) && dindex
self.send(:nth_wday, dindex, args[0])
else
super(sym, *args, &block)
end
end
def respond_to? (sym)
day = sym.to_s.gsub(/^(next|nth)_(?<day>[a-zA-Z]+)$/i, '\k<day>')
(((sym =~ /^next_[a-zA-Z]+$/i) || (sym =~ /^nth_[a-zA-Z]+$/i)) && ALL_DAYS.include?(day)) || super(sym)
end
end
Example:
Given a date:
today = DateTime.now
second_tuesday = (today.first_of_month - 1).nth_tuesday(2)
fourth_tuesday = (today.first_of_month - 1).nth_tuesday(4)
if today == second_tuesday
puts "Today is the second tuesday of this month!"
elsif today == fourth_tuesday
puts "Today is the fourth tuesday of this month!"
else
puts "Today is not interesting."
end
You could also edit method_missing to handle calls such as :second_tuesday_of_this_month, :fourth_tuesday_of_this_month, etc. I'll post the code here if I decide to write it at a later date.
Take a look at Chronic or Tickle, both are gems for parsing complex times and dates. Tickle in particular will parse recurring times (I think it uses Chronic as well).
Check out this gem, you might be able to figure out your answer
https://github.com/mojombo/chronic/
Since you already use Rails, you don't need the includes, but this works in pure Ruby as well for reference.
require 'rubygems'
require 'active_support/core_ext'
d = DateTime.parse('2012-10-19')
result = nil
valid_weeks = [d.beginning_of_month.cweek + 1, d.beginning_of_month.cweek + 3]
if valid_weeks.include?(d.next_week(:tuesday).cweek)
result = d.next_week(:tuesday)
else
result = d.next_week.next_week(:tuesday)
end
puts result
I think you should probably use a library if you're needing to branch out into more interesting logic, but if what you've described is all you need, the code below should help
SECONDS_PER_DAY = 60 * 60 * 24
def find_tuesday_datenight(now)
tuesdays = [*-31..62].map { |i| now + (SECONDS_PER_DAY * i) }
.select { |d| d.tuesday? }
.group_by { |d| d.month }
[tuesdays[now.month][1], tuesdays[now.month][-2], tuesdays[(now.month + 1) % 12][1]]
.find {|d| d.yday > now.yday }
end
Loop through the last month and next month, grab the tuesdays, group by month, take the 2nd and the 2nd last tuesday of the current month (If you actually do want the 4th tuesday, just change -2 to 3) and the 2nd tuesday of the next month and then choose the first one after the provided date.
Here's some tests, 4 tuesdays in month, 5 tuesdays in month, random, and your examples:
[[2013, 5, 1], [2013, 12, 1], [2012, 10, 1], [2012, 10, 19], [2012, 10, 31]].each do |t|
puts "#{t} => #{find_tuesday_datenight(Time.new *t)}"
end
which produces
[2013, 5, 1] => 2013-05-14 00:00:00 +0800
[2013, 12, 1] => 2013-12-10 00:00:00 +0800
[2012, 10, 1] => 2012-10-09 00:00:00 +0800
[2012, 10, 19] => 2012-10-23 00:00:00 +0800
[2012, 10, 31] => 2012-11-13 00:00:00 +0800
I'm sure it could be simplified, and I'd love to hear some suggestions :) (way too late &tired to even bother figuring out what the actual range should be for valid dates i.e. smaller than -31..62)
so here is the code that will resolve a weekday for a given week in a month (what you asked for with little sugar). You should not have problems if you are running inside rails framework. Otherwise make sure you have active_support gem installed. Method name is stupid so feel free to change it :)
usage: get_next_day_of_week(some_date, :friday, 1)
require 'rubygems'
require 'active_support/core_ext'
def get_next_day_of_week(date, day_name, count)
next_date = date + (-date.days_to_week_start(day_name.to_sym) % 7)
while (next_date.mday / 7) != count - 1 do
next_date = next_date + 7
end
next_date
end
I use the following to calculate Microsoft's patch Tuesday date. It was adapted from some C# code.
require 'date'
#find nth iteration of given day (day specified in 'weekday' variable)
findnthday = 2
#Ruby wday number (days are numbered 0-7 beginning with Sunday)
weekday = 2
today = Time.now
todayM = today.month
todayY = today.year
StrtMonth = DateTime.new(todayY,todayM ,1)
while StrtMonth.wday != weekday do
StrtMonth = StrtMonth + 1;
end
PatchTuesday = StrtMonth + (7 * (findnthday - 1))