dan
dan

Reputation: 45622

Yearless Ruby dates?

Is there a way to represent dates like 12/25 without year information? I'm thinking of just using an array of [month, year] unless there is a better way.

Upvotes: 2

Views: 1651

Answers (5)

Dan L
Dan L

Reputation: 4439

For anyone wondering why you would want a date without a year, here's a real-world use case. One of my projects is a contact management system and the customer wants to send notes/emails/gifts to their clients on their birthday. They usually don't get the year for their client's birthdays, so they have "June 15", "December 8", etc...

The way I handled this was not trying to force it into a date/time object, because it would technically be invalid, but just store the month/day/year as separate integer columns on the record and display them properly on the page when needed, and I make the year value optional.

Upvotes: 0

svoop
svoop

Reputation: 3454

While @Phrogz answer makes perfect sense, it has a downside:

YearlessDate = Struct.new(:month,:day)
yearless_date = YearlessDate.new(5, 8)

This interface is prone to MM, DD versus DD, MM confusion.

You might want to use Date instead and consider the year 0 as "yearless date" (provided you're not a historian dealing with real dates around bc/ad of course).

The year 0 is a leap year and therefore accommodates every possible day/month duple:

Date.parse("0000-02-29").leap?   #=> true

If you want to make this convention air tight, just define your own class around it, here's a minimalistic example:

class YearlessDate < Date
  private :year
end

Upvotes: 0

sgrif
sgrif

Reputation: 3822

The most "correct" way to represent a date without a year is as a Fixnum between 001 and 365. You can do comparisons on them without having to turn it into a date, and can easily create a date for a given year as needed using Date.ordinal

Upvotes: -2

Phrogz
Phrogz

Reputation: 303168

You could use the Date class and hard set the year to a leap year (so that you could represent 2/29 if you wanted). This would be convenient if you needed to perform 'distance' calculations between two dates (assuming that you didn't need to wrap across year boundaries and that you didn't care about the off-by-one day answers you'd get when crossing 2/29 incorrectly for some years).

It might also be convenient because you could use #strftime to display the date as (for example) "Mar-3" if you wanted.

Depending on the usage, though, I think I would probably represent them explicitly, either in a paired array or something like YearlessDate = Struct.new(:month,:day). That way you're not tempted to make mistakes like those mentioned above.

However, I've never had a date that wasn't actually associated with a year. Assuming this is the case for you, then @SeanHill's answer is best: keep the year info but don't display it to the user when it's not appropriate.

Upvotes: 4

Sean Hill
Sean Hill

Reputation: 15056

You would use the strftime function from the Time class.

time = Time.now
time.strftime("%m/%d")

Upvotes: 1

Related Questions