I have an array of Dates. I need to check if it follows a month sequence, e.g.:
[Mar 2010, Apr 2010, May 2010, Jun 2010, ..., Jan 2012]
Since a Date object should have day, month and year, I want to ignore the day, and just worry about month and year.
I want to get true if there are no months "missing" on the sequence. In other words, after April or the vector ends, or I have a May; after a May either the vector ends or there is a June.
I want to get false if the months are not ordered correctly (from older to newer) or if there are months missing.
I can easily check if the dates are ordered by using the "<" operator. But I'm not sure how to check if there are missing months. How can I do that?
Here's one way
require 'date'
>> dates
=> ["Nov 2010", "Dec 2010", "Jan 2011"]
>> date_objs = dates.map{|d| Date.parse d }
=> [#<Date: 2010-03-01 ((2455257j,0s,0n),+0s,2299161j)...]
>> date_objs.each_cons(2).all?{|d1, d2| d1.next_month == d2 }
=> true
This handles missing months as well:
>> dates = ["Nov 2010", "Dec 2010", "Feb 2011"]
>> date_objs = dates.map{|d| Date.parse(d) }
>> date_objs.each_cons(2).all?{|d1, d2| d1.next_month == d2 }
=> false
require 'date'
ar =["Mar 2010","Apr 2010", "May 2010", "Jun 2010"]
p ar.map{|d| Date.parse(d)}.each_cons(2).all?{|(d1,d2)| (d1 >> 1) == d2} #=> true
Related
I am trying to use the Date.parse method in Ruby but it is returning odd results when I try to parse early historical dates. For example:
Date.parse("16 January 27 BC")
=> #<Date: -0026-01-16 >
This returns the correct year (although it is -26 instead of -27 but that is normal I think).
But when I try:
Date.parse("19 august 1 BC")
=> #<Date: 2000-08-19 >
Here it assumes the year 2000 as a start date. Same thing for later years:
Date.parse("19 August 14")
=> #<Date: 2014-08-19 >
yearninetynine = Date.parse("19 august AD 99")
=> #<Date: 1999-08-19 >
(I have also tried to replace AD with "CE" but that makes no difference)
The year 100 is where it starts to work properly again:
Date.parse("19 august AD 100")
=> #<Date: 0100-08-19 >
To summarize: up to the year 100 it doesn't parse properly and treats it as years occurring in the 1990s and 2000s, negative years are one less than you would intuitively expect, negative years close to 0 are also not parsed properly.
I tried a few other methods like start=Date::ITALY, but these seem to make no difference. Is there a way to make it parse these years properly?
According to the docs that is exactly what the 2nd argument is for.
Date.parse("19 august 99") #=> Thu, 19 Aug 1999
Date.parse("19 august 99", false) #=> Mon, 19 Aug 0099
Ruby correctly parses the first date but the second one is incorrect. Tested with ruby 1.9.3 and 2.1.2.
Any idea how to get it to work consistently? (We are getting in birth dates as 2 digit years)
Date.strptime("10/11/89","%d/%m/%y")
=> Fri, 10 Nov 1989
Date.strptime("15/10/63","%d/%m/%y")
=> Mon, 15 Oct 2063
The strptime method is parsing the text "63" to the year 2063, not 1963 as you want.
This is because the method decides the century by using the POSIX standard.
The chronic gem has a similar issue because it decides the century, though differently.
A solution is to adjust the date:
d = Date.strptime("15/10/63","%d/%m/%y")
if d > Date.today
d = Date.new(d.year - 100, d.month, d.mday)
end
In the comments of this post, Stefan suggests a good one liner:
d = d.prev_year(100) if d > Date.today
If you need speed, you can try optimizing like this:
d <= Date.today || d = d << 1200
When using %y in strptime, the code assumes that values under 68 are considered in the 21st century, as descirbed here:
The year within century (0-99). When a century is not otherwise specified (with a value for %C), values in the range 69-99 refer to years in the twentieth century (1969-1999); values in the range 00-68 refer to years in the twenty-first century (2000-2068).
In the chronic gem, incidentally, the cut-off year is 64:
Chronic.parse('15/10/64')
# => 1964-10-15 12:00:00 +0200
Chronic.parse('15/10/63')
# => 2063-10-15 12:00:00 +0300
Chronic gem has added an extended support parsing 2 digit years with ambiguous_year_future_bias option:
irb(main):029:0> Chronic.parse('15/10/99', ambiguous_year_future_bias: 10)
=> 2099-10-15 12:00:00 +0300
irb(main):030:0> Chronic.parse('15/10/99', ambiguous_year_future_bias: 50)
=> 1999-10-15 12:00:00 +0300
Add the Chronic gem to your Gemfile
gem 'chronic'
Then just parse it:
Chronic.parse("15/10/68")
=> 1968-10-15 12:00:00 -0700
I want to check if a day is the last day of the month and if it is, for a function to return true, otherwise return false.
For example, if I pass in an argument of "Sun, 30 Jun 2013", the function is to return true, because it is the last day of the month, however if I pass in the argument "Mon, 03 Jun 2013" the function is to return false.
How can this be accomplished using Ruby.
If you're using Rails, you can always do this as well:
date == date.end_of_month
or to check the end of this month:
date == Date.today.end_of_month
I would do something like this
def is_last_day(mydate)
mydate.month != mydate.next_day.month
end
Parse the date with DateTime.parse. DateTime.parse has built-in support for many date formats (including those in your example), but you can always use DateTime.strptime for more complex formats.
See if the next day is 1 (first day of next month) by using Date#+.
require 'date'
def last_day?(date_string)
date = DateTime.parse(date_string)
(date + 1).day == 1
end
puts last_day?('Sun, 30 Jun 2013') # true
puts last_day?('Mon, 03 Jun 2013') # false
I made new object Date.new with args (year, month). After create ruby added 01 number of day to this object by default. Is there any way to add not first day, but last day of month that i passed as arg(e.g. 28 if it will be 02month or 31 if it will be 01month) ?
use Date.civil
With Date.civil(y, m, d) or its alias .new(y, m, d), you can create a new Date object. The values for day (d) and month (m) can be negative in which case they count backwards from the end of the year and the end of the month respectively.
=> Date.civil(2010, 02, -1)
=> Sun, 28 Feb 2010
>> Date.civil(2010, -1, -5)
=> Mon, 27 Dec 2010
To get the end of the month you can also use ActiveSupport's helper end_of_month.
# Require extensions explicitly if you are not in a Rails environment
require 'active_support/core_ext'
p Time.now.utc.end_of_month # => 2013-01-31 23:59:59 UTC
p Date.today.end_of_month # => Thu, 31 Jan 2013
You can find out more on end_of_month in the Rails API Docs.
So I was searching in Google for the same thing here...
I wasn't happy with above so my solution after reading documentation
in RUBY-DOC was:
Example to get 10/31/2014
Date.new(2014,10,1).next_month.prev_day
require "date"
def find_last_day_of_month(_date)
if(_date.instance_of? String)
#end_of_the_month = Date.parse(_date.next_month.strftime("%Y-%m-01")) - 1
else if(_date.instance_of? Date)
#end_of_the_month = _date.next_month.strftime("%Y-%m-01") - 1
end
return #end_of_the_month
end
find_last_day_of_month("2018-01-01")
This is another way to find
You can do something like that:
def last_day_of_month?
(Time.zone.now.month + 1.day) > Time.zone.now.month
end
Time.zone.now.day if last_day-of_month?
This is my Time based solution. I have a personal preference to it compared to Date although the Date solutions proposed above read somehow better.
reference_time ||= Time.now
return (Time.new(reference_time.year, (reference_time.month % 12) + 1) - 1).day
btw for December you can see that year is not flipped. But this is irrelevant for the question because december always has 31 day. And for February year does not need flipping. So if you have another use case that needs year to be correct, then make sure to also change year.
Here is taking the first and third answers to find the last day of the previous month.
today_c = Date.civil(Date.today.prev_month.year, -1, -1)
p today_c
I would like to know how to get the current week number from Rails and how do I manipulate it:
Translate the week number into date.
Make an interval based on week number.
Thanks.
Use strftime:
%U - Week number of the year. The week starts with Sunday. (00..53)
%W - Week number of the year. The week starts with Monday. (00..53)
Time.now.strftime("%U").to_i # 43
# Or...
Date.today.strftime("%U").to_i # 43
If you want to add 43 weeks (or days,years,minutes, etc...) to a date, you can use 43.weeks, provided by ActiveSupport:
irb(main):001:0> 43.weeks
=> 301 days
irb(main):002:0> Date.today + 43.weeks
=> Thu, 22 Aug 2013
irb(main):003:0> Date.today + 10.days
=> Sun, 04 Nov 2012
irb(main):004:0> Date.today + 1.years # or 1.year
=> Fri, 25 Oct 2013
irb(main):005:0> Date.today + 5.months
=> Mon, 25 Mar 2013
You are going to want to stay away from strftime("%U") and "%W".
Instead, use Date.cweek.
The problem is, if you ever want to take a week number and convert it to a date, strftime won't give you a value that you can pass back to Date.commercial.
Date.commercial expects a range of values that are 1 based.
Date.strftime("%U|%W") returns a value that is 0 based. You would think you could just +1 it and it would be fine. The problem will hit you at the end of a year when there are 53 weeks. (Like what just happened...)
For example, let's look at the end of Dec 2015 and the results from your two options for getting a week number:
Date.parse("2015-12-31").strftime("%W") = 52
Date.parse("2015-12-31").cweek = 53
Now, let's look at converting that week number to a date...
Date.commercial(2015, 52, 1) = Mon, 21 Dec 2015
Date.commercial(2015, 53, 1) = Mon, 28 Dec 2015
If you blindly just +1 the value you pass to Date.commercial, you'll end up with an invalid date in other situations:
For example, December 2014:
Date.commercial(2014, 53, 1) = ArgumentError: invalid date
If you ever have to convert that week number back to a date, the only surefire way is to use Date.cweek.
date.commercial([cwyear=-4712[, cweek=1[, cwday=1[, start=Date::ITALY]]]]) → date
Creates a date object denoting the given week date.
The week and the day of week should be a negative
or a positive number (as a relative week/day from the end of year/week when negative).
They should not be zero.
For the interval
require 'date'
def week_dates( week_num )
year = Time.now.year
week_start = Date.commercial( year, week_num, 1 )
week_end = Date.commercial( year, week_num, 7 )
week_start.strftime( "%m/%d/%y" ) + ' - ' + week_end.strftime("%m/%d/%y" )
end
puts week_dates(22)
EG: Input (Week Number): 22
Output: 06/12/08 - 06/19/08
credit: Siep Korteling http://www.ruby-forum.com/topic/125140
Date#cweek seems to get the ISO-8601 week number (a Monday-based week) like %V in strftime (mentioned by #Robban in a comment).
For example, the Monday and the Sunday of the week I'm writing this:
[ Date.new(2015, 7, 13), Date.new(2015, 7, 19) ].map { |date|
date.strftime("U: %U - W: %W - V: %V - cweek: #{date.cweek}")
}
# => ["U: 28 - W: 28 - V: 29 - cweek: 29", "U: 29 - W: 28 - V: 29 - cweek: 29"]