59

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) ?

Netorica
  • 18,523
  • 17
  • 73
  • 108
sanny Sin
  • 1,555
  • 3
  • 17
  • 27

7 Answers7

110

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
Gerry
  • 10,337
  • 3
  • 31
  • 40
Netorica
  • 18,523
  • 17
  • 73
  • 108
  • 4
    thanks, i used this way `18] pry(main)> Date.new(2012,11,-1) => Fri, 30 Nov 2012` – sanny Sin Jan 02 '13 at 10:49
  • one more question, is there any way to get amount of days in month? – sanny Sin Jan 02 '13 at 11:03
  • 1
    Yes you can use `Date.new(year, month, -1).day` well I know you can understand the parameters already =) – Netorica Jan 02 '13 at 11:11
  • 1
    For future problem-solvers: I could not massage `Date.civil` to create a "End of month *in timezone*". Instead, the ActiveSupport `end_of_month` helper from another answer can be used like `Time.now.in_time_zone('America/Sao_Paulo').end_of_month.to_date` – joshfindit Nov 02 '22 at 13:16
81

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.

Thomas Klemm
  • 10,678
  • 1
  • 51
  • 54
  • `require 'active_support/core_ext` perfect, methods `days`, `months` also works like rails, thanks much. – overallduka Jan 21 '15 at 13:55
  • Can not find it in active support 4.2.4. Looks like it's outdated. – Zeke Fast Sep 29 '15 at 10:27
  • 1
    @ZekeFast I think `end_of_month` works in rails 4 without requiring anything – jtmarmon Jan 06 '16 at 16:28
  • According to the docs, you must do a `require 'active_support'` first. http://edgeguides.rubyonrails.org/active_support_core_extensions.html – tokland May 01 '17 at 10:16
  • P.s. You only need `active_support/core_ext/time` for `Time.now.end_of_month`, `Date.today.end_of_month`, or even `Time.now.in_time_zone('America/Sao_Paulo').end_of_month`. You have to add `active_support/core_ext/date_time` if you want `DateTime.now.in_time_zone('America/Sao_Paulo')` – joshfindit Nov 02 '22 at 13:23
22

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

Isaac G Sivaa
  • 1,289
  • 4
  • 15
  • 32
Jumpers
  • 329
  • 2
  • 3
  • Also just to clarify, I'm using just Ruby... not rails so i can't use any helper functions from there. – Jumpers Oct 10 '14 at 21:31
  • It's not working. `Date.new(2016, 3, 4).next_month.prev_day = Sun, 03 Apr 2016` – RandyTek Mar 25 '16 at 20:29
  • @RandyTek The return value you're seeing is correct for the input values you are using in your comment. The format is Date.new(year,month,day). You're starting with 3/4/2016. 'next_month' method moves that to 4/4/2016. 'prev_day' method returns 4/3/2016. What results are you trying to achieve? – Jumpers Jun 21 '16 at 19:44
  • I think the question is how to get the last day of a month. Not previous day. So if I am starting with 3/4/2016 (MM/DD/YYYY), the expected result should be 3/31/2016 instead of 4/3/2016. You could probably refer to @Thomas Klemm 's answer for that. – RandyTek Jun 21 '16 at 22:58
  • @RandyTek Right, the goal was to get the last day of the month. So the method call would be Date.new(2016,3,1).next_month.prev_day using my example, not Date.new(2016,3,4) like you mention in your comment from March 25. Note that you need to change the day value when instantiating the new Date object. Something like this: [link](https://gist.github.com/anonymous/b981403d3d8da4e0232df952df514dd5). Note, when I originally posted my solution, I mention in my first comment that I wasnt using rails. If you're using rails, Mr. Klemm's answer is easier since it's already implemented – Jumpers Jun 23 '16 at 20:20
  • But your solution only works when the input is the first day of a month. The answer does not mention that point. – RandyTek Jun 23 '16 at 22:42
  • @Jumpers I see your point and the beauty of this logic, thank you. This utilizes all pwoer of the Date class (including leap years) without being complicated. Just for the record (in case you got confused by Randy): Next month's first day's previous day is always current month's last day. Just change your input's day to 1 – hotfire42 May 15 '19 at 14:16
0

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.

akostadinov
  • 17,364
  • 6
  • 77
  • 85
  • @shashwat, interesting, I assume I have tested this. But now I checked with latest ruby and is failing. I updated the answer to get it working with latest ruby. – akostadinov Mar 14 '20 at 14:08
0
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

Ali Akbar
  • 370
  • 3
  • 9
0

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?
0

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 
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 26 '23 at 09:48