does chronic have any options of date format it parses? (ruby) - ruby

I need to tell chronic that the format of date is day-month-year is that possible? The data I pass to chronic could also be words today/yesterday/2 days ago.
Currently chronic gives me 2 Dec 2010 instead of 12 Feb 2010 from 12-02-2010
The only solution I can think of is to swap day and month before passing the string to chronic.
require 'chronic'
puts "12-02-2010 = #{Chronic.parse('12-02-2010')}" #should be 12 Feb 2010
puts "yesteday = #{Chronic.parse('yesterday')}" #working ok
puts "Today = #{Chronic.parse('today')}" #working ok

I've found this question today, 20 months after it has been asked. It seems that there is a way to indicate to swap months and days. Just use the :endian_precedence option:
:endian_precedence (Array) — default: [:middle, :little] — By default,
Chronic will parse "03/04/2011" as the fourth day of the third month.
Alternatively you can tell Chronic to parse this as the third day of
the fourth month by altering the :endian_precedence to [:little,
:middle]
Example here:
Chronic.parse('12-02-2010').strftime('%d %b %Y') #=> 02 Dec 2010
Chronic.parse('12-02-2010', :endian_precedence => [:little, :median]).strftime('%d %b %Y') #=> 12 Feb 2010
Hope this helps!
Dorian

The output of chronic can be easily formatted. chronic.parse returns a time object. You can use strftime for formatting as described here.
puts Chronic.parse('today').strftime('%d %b %Y') #=> 23 Feb 2010
As far as the input is concerned, I cannot find anything in chronic that will do it automatically. Manipulating the input string is probably the way to go.
Edit: Chronic has an internal pre_normalize that you could over-ride..
require 'chronic'
puts Chronic.parse('12-02-2010').strftime('%d %b %Y') #=> 02 Dec 2010
module Chronic
class << self
alias chronic__pre_normalize pre_normalize
def pre_normalize(text)
text = text.split(/[^\d]/).reverse.join("-") if text =~ /^\d{1,2}[^\d]\d{1,2}[^\d]\d{4}$/
text = chronic__pre_normalize(text)
return text
end
end
end
puts Chronic.parse('12-02-2010').strftime('%d %b %Y') #=> 12 Feb 2010

Related

How to avoid from storing date objects in array in Ruby?

Consider below code:
dates = ["20th OCT 1232", "6th JUN 2019", "23th AUG 2017", "9th JAN 2015"]
def reformateDate(dates)
ans = []
dates.length.times do |i|
ans << (DateTime.parse(dates[i], '%d %b %Y').to_date)
end
ans
end
This function return array in below format:
[#<Date: 1232-10-20 ((2171339j,0s,0n),+0s,2299161j)>, #<Date: 2019-06-06 ((2458641j,0s,0n),+0s,2299161j)>, #<Date: 2017-08-23 ((2457989j,0s,0n),+0s,2299161j)>, #<Date: 2015-01-09 ((2457032j,0s,0n),+0s,2299161j)>]
But i want it to return array in this format:
["1232-10-20","2019-06-06","2017-08-23","2015-01-09"]
So how can i do this?
dates.map { |e| Date.parse(e).strftime('%Y-%m-%d') }
#=> ["1232-10-20", "2019-06-06", "2017-08-23", "2015-01-09"]
Change the template '%Y-%m-%d' according to your needs, see this for reference: Date#strftime.
Picking up the wise suggestion from Cary Swoveland.
Instead of Date.parse(e) you can use Date.strptime(e, '%dth %b %Y'), which works more or less the reverse of strftime. See Date#strptime. It follows a template ('%dth %b %Y') to interpret the original string as a date. Adding th to the template after %d (day), it converts properly the current format to a date object:
Date.strptime("20th OCT 1232", '%dth %b %Y') #=> #<Date: 1232-10-20 ((2171339j,0s,0n),+0s,2299161j)>
But, what if the date is '1st OCT 2018' or '23rd OCT 2018'? The template does not match, because it expects to find th and not st or rd.
To be ordinal suffix agnostic, comes in hand the method String#sub:
"20th OCT 1232".sub(/(?<=\d)\p{Alpha}+/, '') #=> "20 OCT 1232"
So, mixing all together, the best solution to be safe should be:
dates.map { |e| Date.strptime(e.sub(/(?<=\d)\p{Alpha}+/, ''), '%d %b %Y').strftime('%Y-%m-%d') }
Well, you're actually storing Date objects when you write:
ans << (DateTime.parse(dates[i], '%d %b %Y').to_date)
There's a couple of problems with this: First, the parenthesis don't do anything, so you can remove them. Second, what you're doing is parsing a string into a DateTime object, and then converting it into a Date object. Not really sure why you would do that, but I believe it's a mistake. If you want to convert this to a string by temporally using DateTime objects, consider using strftime, which will take the DateTime object and turn it into a string with a specific format. It would look like this:
ans << DateTime.parse(dates[i], '%d %b %Y').strftime('%Y-%m-%d')
I would do something like this:
require 'date'
def reformat_date(dates)
dates.map { |date| Date.parse(date).to_s }
end
dates = ["20th OCT 1232", "6th JUN 2019", "23th AUG 2017", "9th JAN 2015"]
reformat_date(dates)
#=> ["1232-10-20", "2019-06-06", "2017-08-23", "2015-01-09"]

Adding "st,nd,rd,th" to a date in Ruby [duplicate]

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

Add st, nd, rd, or th to date (ordinalize) [duplicate]

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

How can I find the dates for the same week a year ago?

This is an example of a week, from Sunday to Saturday:
11/21/2010 - 11/27/2010
I would like to find the dates for the same week Sunday-Saturday, only for last year.
require 'date' # Included in Ruby's standard library, no gem needed
now = Date.today
before = Date.civil( now.year-1, now.month, now.day )
sunday = Date.commercial( before.year, before.cweek, 1 ) - 1 # Day 1 is Monday
this_week_last_year = sunday..(sunday+6)
Edit: Though Date.commercial is cool, it's not needed. Here's a Simpler way to find the Sunday starting the week:
require 'date'
now = Date.today
before = Date.civil( now.year-1, now.month, now.day )
sunday = before - before.wday
>> 1.year.ago.beginning_of_week.to_date
Mon, 30 Nov 2009
>> 1.year.ago.end_of_week.to_date
Sun, 06 Dec 2009
require 'chronic'
Chronic.parse '1 year ago'
# => 2009-12-01 14:05:39 -0800
Chronic is a pretty sweet rubygem which can handle a range of things, including being adapted for your particular request.
Try this:
require 'active_support/all'
today = Time.now #=> 2010-12-01 14:58:36 -0700
sunday = (today - today.wday.days).beginning_of_day #=> 2010-11-28 00:00:00 -0700
saturday = sunday + 6.days #=> 2010-12-04 00:00:00 -0700
sunday.wday #=> 0
saturday.wday #=> 6
sunday - 1.year #=> 2009-11-28 00:00:00 -0700
sunday.prev_year #=> 2009-11-28 00:00:00 -0700
saturday - 1.year #=> 2009-12-04 00:00:00 -0700
saturday.prev_year #=> 2009-12-04 00:00:00 -0700
You could also figure out the week of the year for one of the two days, then subtract 365.days
ActiveSupport was actually split into finer granularity with Rails 3, so you don't have to load in the entire suite if you don't want. I did it for simplicity. More info is on the ActiveSupport Core Extensions page.
Or, you could get jiggy with string parsing:
require 'chronic'
Chronic.parse('1 year ago last sunday') #=> 2009-11-28 12:00:00 -0700
Chronic.parse('1 year ago next saturday') #=> 2009-12-04 12:00:00 -0700
I like Chronic, and, for this sort of parsing I think it's a OK solution because the string is something you'd create, and not a user, so there's less chance of your code blowing up with an unparsable string. I'm not sure if there's a speed hit because of the parsing though so some benchmarks might be in order if the parsing was going to be in a loop.

Forcing "+0000" timezone for RFC2822 times in Ruby

How can I force the Time.rfc2822 function to spit out +0000?
Ruby lets me parse RFC2822 formatted times pretty easily:
require 'time'
time = Time.parse('14 Aug 2009 09:28:32 +0000')
puts time
=> "2009-08-14 05:28:32 -0400"
But what about displaying times? Notice that the time it parsed is a local time. No worries, I can convert it back to a UTC time with gmtime:
puts time.gmtime
=> "2009-08-14 09:28:32 UTC"
I can then put it back into RFC2822 format:
puts time.gmtime.rfc2822
=> "Fri, 14 Aug 2009 09:28:32 -0000"
Unfortunately, this is not quite what I want. Notice that the +0000 is now -0000. According to RFC2822, this is because:
The form "+0000" SHOULD be used to indicate a time zone at
Universal Time. Though "-0000" also indicates Universal Time, it is
used to indicate that the time was generated on a system that may be
in a local time zone other than Universal Time and therefore
indicates that the date-time contains no information about the local
time zone.
Great - so how can I force +0000 other than monkey-patching the rfc2822 function?
Here's my monkeypatch solution:
class Time
alias_method :old_rfc2822, :rfc2822
def rfc2822
t = old_rfc2822
t.gsub!("-0000", "+0000") if utc?
t
end
end
If you have a non-monkeypatch solution, I would love to see it!
simplest way if you don't need to use on multiple places
Time.now.gmtime.rfc2822.sub(/(-)(0+)$/, '+\2')
=> "Fri, 31 Mar 2017 08:39:04 +0000"
or as a static (singleton) method version
require 'time'
module MyCustomTimeRFC
def custom_rfc2822(time)
time.gmtime.rfc2822.sub(/(-)(0+)$/, '+\2')
end
module_function :custom_rfc2822
end
t = Time.now
p MyCustomTimeRFC.custom_rfc2822(t)
#=> "Fri, 31 Mar 2017 08:43:15 +0000"
or as module extension if you like the oop style with ruby flexibility.
require 'time'
module MyCustomTimeRFC
def custom_rfc2822
gmtime.rfc2822.sub(/(-)(0+)$/, '+\2')
end
end
t = Time.now
t.extend(MyCustomTimeRFC)
p t.custom_rfc2822
#=> "Fri, 31 Mar 2017 08:43:15 +0000"

Resources