8
\$\begingroup\$

Given a date, need to find the middle day of that day's month, or if there are an even number of days (with no exact middle day), the day should be the closest day to the middle of the month on the second half of the month.

For example, if the date given is August 2 (a month with 31 days), the middle day returned should be Aug 16. If the date given is September 28 (a month with 30 days), the middle date returned should be 16 (15 would be closest day to middle in first half of month, 16 would be closest day to middle in second half of month).

Here's the code. Any better way to accomplish this with only version 2.4 and higher standard library and core?

#ruby 2.4.2
#require 'date'
def middleOfMonth(d)
  advancedMonth = d.next_month
  lastDayOfPreviousMonth = (Date.new(advancedMonth.year, advancedMonth.month, 1) - 1).day
  exactMiddle = lastDayOfPreviousMonth.to_f / 2
  actualMiddle = exactMiddle.floor + 1
  actualMiddle
end
\$\endgroup\$
  • 3
    \$\begingroup\$ Should this only work for the Western Gregorian Calendar, with 12 months ranging from 28 to 31 days? Because there are other calendar systems with other date ranges. \$\endgroup\$ – Nzall Oct 5 '17 at 8:30
  • \$\begingroup\$ @Nzall yes. Sorry, you've brought up a good point, globilization should always be in the field of view with these things, but for this particular question I meant only the Western Gregorian Calendar. \$\endgroup\$ – Aaron Thomas Oct 9 '17 at 21:09
31
\$\begingroup\$

Since February is the only month where the middle is 15 you could just write

d.month == 2 ? 15 : 16
\$\endgroup\$
  • 5
    \$\begingroup\$ I'd add that you should provide a comment as well, as this may not be self explanatory at first glance. \$\endgroup\$ – Alex Oct 5 '17 at 12:39
9
\$\begingroup\$

snake_case is more conventional for Ruby variables and functions. lastDayOfPreviousMonth feels misleading: I consider it to be the last day of this month.

Given the last day of the month, you want to map… $$ 28 \rightarrow 15 \\ 29 \rightarrow 15 \\ 30 \rightarrow 16 \\ 31 \rightarrow 16 $$ … to obtain the answer. You should devise a formula that takes advantage of the truncation that naturally occurs with integer division.

Here's how I would write it:

def middle_of_month(d)
  next_month = d.next_month
  end_of_month = Date.new(next_month.year, next_month.month, 1).prev_day
  end_of_month.day / 2 + 1
end

Since your function accepts a Date object, I would consider it more logical for it to also return a Date object:

def middle_of_month(d)
  next_month = d.next_month
  end_of_month = Date.new(next_month.year, next_month.month, 1).prev_day
  end_of_month.prev_day((end_of_month.day - 1) / 2)
end
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.