読者です 読者をやめる 読者になる 読者になる

Railsで動的日付プルダウンを作成

tips_01

Railsで動的日付プルダウンを作成

困ったこと

Railsのselect_datetimeヘルパーがイケてない

  • デフォルトで「月」が月名表示
  • 各プルダウン間のセパレータを「年・月・日・時・分」にしたい
  • 大の月小の月およびうるう年を考慮がされていない

前提

  • Rails 3.2.13
  • 専用のgemは使わない

解決策

views/hoges/_form.html.erb

<div class="field" >
    <%= f.label '開始日' %><br />
    <%== sprintf( 
      f.datetime_select(
        :begin_at,
          {:use_month_numbers => true, 
          :date_separator => '%s',
          :time_separator =>'時',
          :datetime_separator =>'日'},
          :onchange => "generateDay(this)"
        ), '年 ', '月'
      )
    %>分
</div>

js

<script>
// うるう年を考慮したプルダウンの日付を動的に生成する
// 参考
// http://d.hatena.ne.jp/sakana_eie/20110427/1303833963
// 使い方
// <select id="hoge_begin_at_1i" onchange="generateDay(this)">  // 年のプルダウン
// <select id="hoge_begin_at_2i" onchange="generateDay(this)">  // 年のプルダウン
// <select id="hoge_begin_at_3i" onchange="generateDay(this)">  // 年のプルダウン
function generateDay(obj) {

  // プルダウンのidの末尾を取得する
  var obj_ids = obj.id.split("_")
  var obj_ids_last = obj_ids[obj_ids.length-1]

  // 年および月のプルダウンならば処理を続行する
  if(obj_ids_last == '1i' || obj_ids_last == '2i'){
    // プルダウンのidから末尾を除外する
    var id = obj_ids[0]
    for(i=1; i<obj_ids.length-1; i++){
      id = id + "_" + obj_ids[i]
    }

    // 選択中の年・月・日
    var y = document.getElementById(id + '_1i').options[document.getElementById(id + '_1i').selectedIndex].text;
    var m = document.getElementById(id + '_2i').options[document.getElementById(id + '_2i').selectedIndex].text;
    var d = document.getElementById(id + '_3i').options[document.getElementById(id + '_3i').selectedIndex].text;

    // 閏年判定
    if (2 == m && (0 == y % 400 || (0 == y % 4 && 0 != y % 100))) {
      var last = 29;
    } else {
      var last = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)[m - 1];
    }

    // 要素取得と初期化
    target = document.getElementById(id + '_3i');
    target.length = 0;

    // 日の要素生成
    for (var i = 0; i < last; i++) {
      target.options[target.length++] = new Option(i + 1, i + 1);
    }

    // 日を元に戻す
    if(d <= last){
      target.value = d
    }
  }
}
</script>

画面

f:id:naotooncajon:20130817234653p:image

感想

  • <%= =>のなかでsprintfを使っているあたりがなかなかキモい。
  • 大の月小の月および閏年対応は標準でしてもらいたい。