Date validation in Ruby - ruby-on-rails

How to check if the date is valid date in ruby. Many of the methods are check only the range. But, I need to check the date with day of
week to check whether the date is valid or not. For Ex:
20 Jul 2016 Wed --> Valid
20 Jul 2016 Mon --> Not-Valid
How to do this in ruby ?

I'm not pretend on the best solution ever, but this should work.
def valid_date?(date)
Date.parse(date).strftime("%d %b %Y %a") == date
end
[55] pry(main)> valid_date?("20 Jul 2016 Wed")
=> true
[56] pry(main)> valid_date?("20 Jul 2016 Mon")
=> false
[57] pry(main)>
If you have many formats you may pass format as a second argument
def valid_date?(date, fmt)
Date.parse(date).strftime(fmt) == date
end
=> :valid_date?
[59] pry(main)> valid_date?("20 Jul 2016 Wed", "%d %b %Y %a")
=> true
Hope this will help.
UPDATE
As I mentioned in comment that method name overlaps with existing method valid_date?
So, you may just rename the custom method
def date_valid?(date, fmt)
Date.parse(date).strftime(fmt) == date
end
[2] pry(main)> date_valid?("20 Jul 2016 Wed", "%d %b %Y %a")
=> true

Just out of curiosity:
dates = ['20 Jul 2016 Wed', '20 Jul 2016 Mon']
dates.map do |date|
Date.parse(date).public_send(
Date.instance_methods.detect do |m|
m.to_s =~ /\A#{date[-3..-1].downcase}.*day\?\z/
end)
end
#⇒ [ true, false ]

require 'date'
dates = ['20 Jul 2016 Wed', '20 Jul 2016 Mon']
dates.select do |s|
d = Date.strptime(s[0,11], "%d %b %Y") rescue nil
d.nil? ? false : (Date::ABBR_DAYNAMES[d.wday] == s[-3,3])
end
#=> ["20 Jul 2016 Wed"]
This reads, "select strings 'dd mmm yyyy' that represent valid dates and whose day-of-week matches the day-of-week given by the last three characters of the string".

Related

Group by month and week

I would like to know what is the best way to group my hash of
{date->value} in month and week.
{Sat, 23 Apr 2016=>6.0, Mon, 06 Mar 2017=>9.0, Tue, 04 Apr 2017=>13.0, Tue, 11 Apr 2017=>25.0}
I would like a result like:
{Apr 2016=>6, Mar 2017=>9, Apr 2017=>38}
I tried with Groupdate/Chartkick but I am not getting a good result.
Thanks
Given the desired result for the OP's example, I've assumed that the OP means "month and year" rather than by "week and month".
I also assumed that the keys of the input hash are intended to be strings (rather than date objects) and the keys of the hash returned are intended to be strings. In any event, I would use Hash::new with a default value of zero (a counting hash). See the doc for details.
dates = {"Sat, 23 Apr 2016"=>6.0, "Mon, 06 Mar 2017"=>9.0,
"Tue, 04 Apr 2017"=>13.0, "Tue, 11 Apr 2017"=>25.0}
dates.each_with_object(Hash.new(0)) { |(k,v),h| h[k[-8..-1]] += v.to_i }
#=> {"Apr 2016"=>6, "Mar 2017"=>9, "Apr 2017"=>38}
If the dates of the input hash are date objects, we can do the following. First create the date objects.
require 'date'
dates = {"Sat, 23 Apr 2016"=>6.0, "Mon, 06 Mar 2017"=>9.0,
"Tue, 04 Apr 2017"=>13.0, "Tue, 11 Apr 2017"=>25.0}.
map { |str, v| [Date.parse(str), v] }.to_h
#=> {#<Date: 2016-04-23 ((2457502j,0s,0n),+0s,2299161j)>=>6.0,
# #<Date: 2017-03-06 ((2457819j,0s,0n),+0s,2299161j)>=>9.0,
# #<Date: 2017-04-04 ((2457848j,0s,0n),+0s,2299161j)>=>13.0,
# #<Date: 2017-04-11 ((2457855j,0s,0n),+0s,2299161j)>=>25.0}
Then we can write
dates.each_with_object(Hash.new(0)) { |(k,v),h| h[k.strftime("%b %Y")] += v.to_i }
#=> {"Apr 2016"=>6, "Mar 2017"=>9, "Apr 2017"=>38}
See DateTime#strftime.
This is a pure-Ruby solution.
If you have the hash of data (as a hash) already an option would be to use reduce
for the month it would be
data.reduce({}) do |memo, (date, value)|
new_date = date.beginning_of_month.strftime("%B %Y")
memo[new_date] ||= 0
memo[new_date] += value
memo
end
for the week it would be
data.reduce({}) do |memo, (date, value)|
new_date = date.beginning_of_week.strftime("%B %e %Y")
memo[new_date] ||= 0
memo[new_date] += value
memo
end
You can use this document to modify the strftime format however you want it.
Even easier: data.transform_keys! { |k| k.strftime("%B %Y") }

How do I push dates into an array by week?

I am writing this code that maps dates by day and pushes them into an array:
rtn_ary = []
(2.weeks.ago.to_date..Date.today).map do |date|
rtn_ary << {period: date}
end
How can I write the code if my expected result is based on weeks instead of days like this?
[[{:period=>Mon, 28 Sep 2015}, {:period=>Tue, 05 Oct 2015}, {:period=>Tue, 12 Oct 2015}]
A simple solution could be to iterate by 7 days instead of 1 day:
rtn_ary = (2.weeks.ago.to_date..Date.today).step(7).map do |date|
{period: date}
end
=> [{:period=>Mon, 28 Sep 2015}, {:period=>Mon, 05 Oct 2015}]
More info about step: http://ruby-doc.org/core-1.9.3/Range.html#method-i-step
To get what you want you'll need an additional step:
(2.weeks.ago.to_date..Date.today).map do |date|
rtn_ary << {period: date.to_formatted_s(:long)}
end
The to_formatted_s() method convert the date into a formatted string and takes a parameter which will define how the date will be formatted into a string.
date.to_formatted_s(:short) # => "10 Nov"
date.to_formatted_s(:number) # => "20071110"
date.to_formatted_s(:long) # => "November 10, 2007"
date.to_formatted_s(:long_ordinal) # => "November 10th, 2007"
date.to_formatted_s(:rfc822) # => "10 Nov 2007"
date.to_formatted_s(:iso8601) # => "2007-11-10"
Take a look at the rails Documentation

How to convert date format in Ruby

Here are some outputs:
Date.today => Mon, 25 Jun 2012
Date.today.to_formatted_s(:long_ordinal) => "June 25th, 2012"
Date.today.strftime('%A %d, %B') => "Monday 25, June"
Now I need output in the format ie:
Monday 25th, June or Thrusday, 1st, October
Problem is to_formatted_s and strftime apply only on date and both or them return string. How can I get the output in the way I need?
You can use Date::DATE_FORMATS to add a new customized format, and Integer.ordinalize to get the day ordinal:
Date::DATE_FORMATS[:month_ordinal] = lambda { |date|
date.strftime("%A #{date.day.ordinalize}, %B")
}
>> Date.today.to_formatted_s(:month_ordinal)
=> "Monday 25th, June"

Transforming Datetime into month, day and year?

I can't seem to find this and I feel like it should be easy. In Ruby on Rails, how do I take:
2010-06-14 19:01:00 UTC
and turn it into
June 14th, 2010
Can I not just use a helper in the view?
I don't know for
June 14th, 2010
But if you want
June 14, 2010
Ref how do i get name of the month in ruby on Rails? or this
Just do
#date = Time.now
#date.strftime("%B %d, %Y")
And for suffix use following
#date.strftime("%B #{#date.day.ordinalize}, %Y") # >>> Gives `June 18th, 2010`
Time and date formats in rails:
Date
====
db:‘%Y-%m-%d’ 2008-08-20
long_ordinal:‘&proc’ August 20th, 2008
long:‘%B %e, %Y’ August 20, 2008
rfc822:‘%e %b %Y’ 20 Aug 2008
number:‘%Y%m%d’ 20080820
short:‘%e %b’ 20 Aug
DateTime
====
db:‘%Y-%m-%d’ 2008-08-20 16:56:21
long_ordinal:‘&proc’ August 20th, 2008 16:56
long:‘%B %e, %Y’ August 20, 2008 16:56
rfc822:‘%e %b %Y’ Wed, 20 Aug 2008 16:56:21 -0600
number:‘%Y%m%d’ 20080820165621
short:‘%e %b’ 20 Aug 16:56
Time
====
db:‘%Y-%m-%d %H:%M:%S’ 2008-08-20 16:56:21
long_ordinal:‘&proc’ August 20th, 2008 16:56
long:‘%B %d, %Y %H:%M’ August 20, 2008 16:56
rfc822:‘%a, %d %b %Y %H:%M:%S %z’ Wed, 20 Aug 2008 16:56:21 -0600
short:‘%d %b %H:%M’ 20 Aug 16:56
number:‘%Y%m%d%H%M%S’ 20080820165621
time:‘%H:%M’ 16:56
for example:
<%= news.created_at.strftime("%B %d, %Y %H:%M") %>
Thanks http://onrails.org/2008/08/20/what-are-all-the-rails-date-formats.html
For future reference: Rails date time formats
You don't need to save it in a variable.
Time.now.strftime("%Y-%m-%d") # 2013-01-08
Needs the Time module for Time.parse and ActiveSupport for Integer#ordinalize:
require 'time'
require 'active_support'
input = '2010-06-14 19:01:00 UTC'
t = Time.parse(input)
date = "%s %s, %d" % [t.strftime("%B"), t.day.ordinalize, t.year]
# => "June 14th, 2010"
Just the other day there was a similar question. In my answer how do I get name of the month in ruby on Rails? I showed how you can add a custom to_s definition in your config/environment.rb file.
ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(
:my_own_long_date_format => "%B %d, %Y")
Now you can call Time.now.to_s(:my_own_long_date_format) from any view to get:
June 15, 2010
Update that is working in Rails 5 :
<%= l #user.created_at, format: :short %>
Internationalize :
<%= I18n.l( #user.created_at, format: :short) %>
You can use :long instead of :short

In Ruby on Rails, how do I format a date with the "th" suffix, as in, "Sun Oct 5th"?

I want to display dates in the format: short day of week, short month, day of month without leading zero but including "th", "st", "nd", or "rd" suffix.
For example, the day this question was asked would display "Thu Oct 2nd".
I'm using Ruby 1.8.7, and Time.strftime just doesn't seem to do this. I'd prefer a standard library if one exists.
Use the ordinalize method from 'active_support'.
>> time = Time.new
=> Fri Oct 03 01:24:48 +0100 2008
>> time.strftime("%a %b #{time.day.ordinalize}")
=> "Fri Oct 3rd"
Note, if you are using IRB with Ruby 2.0, you must first run:
require 'active_support/core_ext/integer/inflections'
You can use active_support's ordinalize helper method on numbers.
>> 3.ordinalize
=> "3rd"
>> 2.ordinalize
=> "2nd"
>> 1.ordinalize
=> "1st"
Taking Patrick McKenzie's answer just a bit further, you could create a new file in your config/initializers directory called date_format.rb (or whatever you want) and put this in it:
Time::DATE_FORMATS.merge!(
my_date: lambda { |time| time.strftime("%a, %b #{time.day.ordinalize}") }
)
Then in your view code you can format any date simply by assigning it your new date format:
My Date: <%= h some_date.to_s(:my_date) %>
It's simple, it works, and is easy to build on. Just add more format lines in the date_format.rb file for each of your different date formats. Here is a more fleshed out example.
Time::DATE_FORMATS.merge!(
datetime_military: '%Y-%m-%d %H:%M',
datetime: '%Y-%m-%d %I:%M%P',
time: '%I:%M%P',
time_military: '%H:%M%P',
datetime_short: '%m/%d %I:%M',
due_date: lambda { |time| time.strftime("%a, %b #{time.day.ordinalize}") }
)
>> require 'activesupport'
=> []
>> t = Time.now
=> Thu Oct 02 17:28:37 -0700 2008
>> formatted = "#{t.strftime("%a %b")} #{t.day.ordinalize}"
=> "Thu Oct 2nd"
Although Jonathan Tran did say he was looking for the abbreviated day of the week first followed by the abbreviated month, I think it might be useful for people who end up here to know that Rails has out-of-the-box support for the more commonly usable long month, ordinalized day integer, followed by the year, as in June 1st, 2018.
It can be easily achieved with:
Time.current.to_date.to_s(:long_ordinal)
=> "January 26th, 2019"
Or:
Date.current.to_s(:long_ordinal)
=> "January 26th, 2019"
You can stick to a time instance if you wish as well:
Time.current.to_s(:long_ordinal)
=> "January 26th, 2019 04:21"
You can find more formats and context on how to create a custom one in the Rails API docs.
Create your own %o format.
Initializer
config/initializers/srtftime.rb
module StrftimeOrdinal
def self.included( base )
base.class_eval do
alias_method :old_strftime, :strftime
def strftime( format )
old_strftime format.gsub( "%o", day.ordinalize )
end
end
end
end
[ Time, Date, DateTime ].each{ |c| c.send :include, StrftimeOrdinal }
Usage
Time.new( 2018, 10, 2 ).strftime( "%a %b %o" )
=> "Tue Oct 2nd"
You can use this with Date and DateTime as well:
DateTime.new( 2018, 10, 2 ).strftime( "%a %b %o" )
=> "Tue Oct 2nd"
Date.new( 2018, 10, 2 ).strftime( "%a %b %o" )
=> "Tue Oct 2nd"
I like Bartosz's answer, but hey, since this is Rails we're talking about, let's take it one step up in devious. (Edit: Although I was going to just monkeypatch the following method, turns out there is a cleaner way.)
DateTime instances have a to_formatted_s method supplied by ActiveSupport, which takes a single symbol as a parameter and, if that symbol is recognized as a valid predefined format, returns a String with the appropriate formatting.
Those symbols are defined by Time::DATE_FORMATS, which is a hash of symbols to either strings for the standard formatting function... or procs. Bwahaha.
d = DateTime.now #Examples were executed on October 3rd 2008
Time::DATE_FORMATS[:weekday_month_ordinal] =
lambda { |time| time.strftime("%a %b #{time.day.ordinalize}") }
d.to_formatted_s :weekday_month_ordinal #Fri Oct 3rd
But hey, if you can't resist the opportunity to monkeypatch, you could always give that a cleaner interface:
class DateTime
Time::DATE_FORMATS[:weekday_month_ordinal] =
lambda { |time| time.strftime("%a %b #{time.day.ordinalize}") }
def to_my_special_s
to_formatted_s :weekday_month_ordinal
end
end
DateTime.now.to_my_special_s #Fri Oct 3rd

Resources