Ossie
Ossie

Reputation: 1113

Rails partial dates

My table has a release_date column that saves in the format

2002-02-01

In the model I have

validates_date :release_date, :allow_blank => true

In my new view I have the following

<%= f.label :release_date, class: "control-label" %>
              <%= f.date_select :release_date, :start_year => Date.current.year, :end_year => 1970, :include_blank => true %>

With gives three separate drop downs for selecting a date.

What I want is for a partial date to be selectable when a user only knows the year, or the month and the year. I thought this might work with the appropriate allow_blank tags in but it doesn't Currently if you select and month and year but leave the day blank it doesn't save the date to the db at all.

How can I allow for partial dates? Preferably saving with the same format as currently used so that my existing data isn't invalid.

In my show view I call release_date with this

<%= @miniature.release_date.strftime("%d %b %Y") unless @miniature.release_date.blank? %>

I would like to be able to display the whole date when available and if not have it appear as

Feb 2002

or just

2002

where that is all that's available.

Upvotes: 0

Views: 976

Answers (2)

Sibevin Wang
Sibevin Wang

Reputation: 4538

I guess your release_date should be a date column, it means that a full-date is required for creating record even only the year or year+month are given.

You may need an integer column, date_mask for example, to tell which parts(year, month, day) are available and modify the create action in your controller to store date_mask information.

class MyController < ApplicationController
  def create
    #...
    if params[:my_model][:"release_date(2i)"] == ''
      # no month is given, insert fake month and day
      params[:my_model][:"release_date(2i)"] = '1'
      params[:my_model][:"release_date(3i)"] = '1'
      mask = 4 # 100
    elsif params[:my_model][:"release_date(3i)"] == ''
      # no day is given, insert a fake day
      params[:my_model][:"release_date(3i)"] = '1'
      mask = 6 # 110
    else
      # full-date
      mask = 7 # 111
    end
    # ...
    MyModel.create(my_model_params.merge(date_mask: mask))
    # ...
  end

  private

  def my_model_params
    params.require(:my_model).permit(:release_date, :date_mask)
  end
end

In your model, create a method release_date_display to show the date according release_date and date_mask

class MyModel < ActiveRecord::Base
  #...
  def release_date_display
    if self.date_mask == 4
      return self.release_date.strftime('%Y')
    elsif self.date_mask == 6
      return self.release_date.strftime('%b %Y')
    else
      return self.release_date.strftime('%F')
    end
  end
  #...
end

For existing data, just insert 7 in your data_mask column.

UPDATE:

An example to handle partial date: https://github.com/sibevin/partial-date-app

Upvotes: 4

tirdadc
tirdadc

Reputation: 4703

There are some gems out there to deal with this use case:

https://github.com/58bits/partial-date (this one uses a single column)

The other ones tend to use multiple columns:

https://github.com/gnapse/incomplete_date

https://github.com/alexreisner/flex_date

You'll also find more information in this SO thread: How would I store a date that can be partial (i.e. just the year, maybe the month too) and output it later with the same specifity?

Upvotes: 1

Related Questions