How to Determine Leap Year in Ruby

Easy way to determine leap year in ruby?

Use Date#leap?.

now = DateTime.now 
flag = Date.leap?( now.year )

e.g.

Date.leap?( 2018 ) # => false

Date.leap?( 2016 ) # => true

Leap Years. Program in Ruby. Can someone explain?

Let's go through the code line-by-line. I'll both explain what's going on and show how it could be improved.

puts 'Input a starting year:'
start_year = gets.chomp

It will be more convenient to make start_year an integer so we don't have to keep converting between a string and an integer. We therefore should change the second line to

start_year = gets.to_i

If you examine the doc for String#to_i you will see that this is the same as

start_year = gets.chomp.to_i

Next,

puts 'Input an ending year:'
end_year = gets.to_i
puts ''

while start_year <= end_year

The last line requires that Ruby continue the while loop as long as start_year <= end_year is true. She exits the loop when this expression becomes false (when start_year == end_year + 1). Notice the simplification in the this line and in the lines below.

if start_year % 400 == 0
puts start_year

If the year is divisible by 400 it is a leap year.

elsif start_year % 100 == 0

If the year is divisible by 100, but not by 400, it is not a leap year, so we do nothing, go to the end of the if/elsif/end clause and next execute start_year += 1.

elsif start_year % 4 == 0
puts start_year
end

If the year is divisible by 4, but not by 100, is is a leap year. If start_year % 4 != 0 it is not a leap year, so we do not print it.

start_year += 1

which Ruby expands to start_year = start_year + 1.

end

Return to the top of the loop and repeat.

Better

Here's a more Ruby-like way of writing that, converting the code to a method, using a single boolean expression and printing the return value of the method (an array of leap years).

def leap_years(start_year, end_year)
(start_year..end_year).select { |yr|
yr % 400 == 0 || (yr % 100 != 0 && yr % 4 == 0) }
end

puts 'Input a starting year:'
start_year = gets.to_i
puts 'Input an ending year:'
end_year = gets.to_i

p leap_years(start_year, end_year)
#=> [1904, 1908, 1912, 1916, 1920]

first_year..last_year is a range (an instance of the class Range). Range includes the module Enumerable which provides it with the instance method Enumerable#select.

Best

The following would be easier way to obtain the leap years between two given years, by making use of the class method Date::leap?.

require 'date'

def leaps_between(first_year, last_year)
(first_year..last_year).select { |y| Date.leap?(y) }
end

leaps_between(2000, 2016)
#=> [2000, 2004, 2008, 2012, 2016]

leaps_between(1900, 1916)
#=> [1904, 1908, 1912, 1916]

Determine if a given year is a leap year

If you would like to determine if a given year is a leap year, that has already been implemented for you in the Date class as Date#leap?:

Date.leap?(2000)
#=> true

# Or, alternatively:

Date.gregorian_leap?(1900)
#=> false

Date.julian_leap?(1900)
#=> true

More info in the Ruby documentation: Date#leap?

If you would like to build it yourself regardless, this should work:

def leap_year?(year)
return false unless year % 4 == 0
return true unless year % 100 == 0
year % 400 == 0
end

leap_year?(2016)
#=> true

Implementing my own leap year class in ruby

The problem is Year.leap? should return true or false, not return a string, according to your tests.

class Year
def self.leap?(year)
(year % 400 == 0 && year % 100 == 0) || (year % 100 != 0 && year % 4 == 0)
end
end

Your tests will now pass.

The assert is looking for a truthy response, refute is looking for a falsy response.

We could've left the original code more intact, but saying...

if (condition_to_test) == true
return true
else
return false
end

is more succinctly rendered with just

condition_to_test

That will automatically return true or false as appropriate.

Rails Time.now.end_of_month not accounting for leap year

This is because the calculation computes the end of month for the current year and then adds 3 years.

(Time.now.beginning_of_month.to_date - 1.month) = 28,Feb 2013. Add 3 years and you get 28, Feb 2016.

Try this instead -

((Time.now.beginning_of_month.to_date - 1.month) + 3.years).end_of_month

BTW, subtracting 2 months takes me to January (its still 31st March here)



Related Topics



Leave a reply



Submit