How to get a Date from date_select or select_date in Rails?

Using select_date gives me back a params[:my_date] with year, month and day attributes. How do get a Date object easily? I'm hoping for something like params[:my_date].to_date.

I'm happy to use date_select instead as well.


Solution 1:

Using date_select gives you 3 separate key/value pairs for the day, month, and year respectively. So you can pass them into Date.new as parameters to create a new Date object.

An example date_select returned params for an Event model:

"event"=>
 {"name"=>"Birthday",
  "date(1i)"=>"2012",
  "date(2i)"=>"11",
  "date(3i)"=>"28"},

Then to create the new Date object:

event = params[:event]
date = Date.new event["date(1i)"].to_i, event["date(2i)"].to_i, event["date(3i)"].to_i

You may instead decide to wrap this logic in a method:

def flatten_date_array hash
  %w(1 2 3).map { |e| hash["date(#{e}i)"].to_i }
end

And then call it as date = Date.new *flatten_date_array params[:event]. But this is not logic that truly belongs in a controller, so you may decide to move it elsewhere. You could even extend this onto the Date class, and call it as date = Date.new_from_hash params[:event].

Solution 2:

Here is another one:

# view
<%= date_select('event', 'date') %>

# controller
date = Date.civil(*params[:event].sort.map(&:last).map(&:to_i))

Found at http://kevinlochner.com/use-rails-dateselect-without-an-activerecord

Solution 3:

I use the following method, which has the following benefits:

  1. it doesn't have to explicitly name param keys xxx(1i) through xxx(3i) (and thus could be modified to capture hour and minute simply by changing Date to DateTime); and
  2. it extracts a date from a set of params even when those params are populated with many other key-value pairs.

params is a hash of the format { xxx(1i): '2017', xxx(2i): 12, xxx(3i): 31, ... }; date_key is the common substring xxx of the target date parameters.

def date_from_params(params, date_key)
  date_keys = params.keys.select { |k| k.to_s.match?(date_key.to_s) }.sort
  date_array = params.values_at(*date_keys).map(&:to_i)
  Date.civil(*date_array)
end

I chose to place this as a class method of ApplicationRecord, rather than as an instance helper method of ApplicationController. My reasoning is that similar logic exists within the ActiveRecord instantiator (i.e., Model.new) to parse dates passed in from Rails forms.

Solution 4:

Here is the another one

Date.civil(params[:event]["date(1i)"].to_i,params[:event]["date(2i)"].to_i,params[:event]["date(3i)"].to_i)

Solution 5:

Here is another one for rails 5:

module Convert
  extend ActiveSupport::Concern

  included  do
    before_action :convert_date
  end

  protected

  def convert_date
    self.params = ActionController::Parameters.new(build_date(params.to_unsafe_h))
  end

  def build_date(params)
    return params.map{|e| build_date(e)} if  params.is_a? Array

    return params unless params.is_a? Hash

    params.reduce({}) do |hash, (key, value)|
      if result = (/(.*)\(\di\)\z/).match(key)
        params_name = result[1]
        date_params = (1..3).map do |index|
          params.delete("#{params_name}(#{index}i)").to_i
        end
        hash[params_name] =  Date.civil(*date_params)
      else
        hash[key] = build_date(value)
      end

      hash
    end
  end
end

You need to include it to your controller or application_controller.rb:

class ApplicationController < ActionController::Base
  include Convert
end