LoginSignup
1
0

More than 3 years have passed since last update.

TimeWithZoneで時間操作に苦戦したこと

Posted at

はじめに

Railsアプリ作成時にgem 'simple_calendar', '~> 2.0'
を使ったので、その際に困ったTimeWithZoneで時間操作する点について記載して行きたいと思います。

バージョン

Rails 6.0.3
Ruby 2.6.6

参考資料

simple_calendar公式ドキュメント
https://github.com/excid3/simple_calendar
TimeWithZone 参考サイトです
https://api.rubyonrails.org/v6.0.2.1/classes/ActiveSupport/TimeWithZone.html
https://www.javadrive.jp/ruby/time_class/index2.html

作業内容

カレンダー作成は割愛させて頂きます!
最初のコードはこんな感じになります。
基本的なcrud処理

app/controllers/events_controller.rb
class EventsController < ApplicationController

  def index
    @events = Event.all
  end

  def new
    @event = Event.new
  end

  def show
    @event = event.find(params[:id])
  end

  def create
    Event.create(event_params)
    redirect_to events_path
  end

  def destroy
    @event = event.find(params[:id])
    @event.destroy
    redirect_to events_path, notice:"削除しました"
  end

  def edit
    @event = Event.find(params[:id])
  end

  def update
    @event = Event.find(params[:id])
    if @event.update(event_params)
      redirect_to events_path, notice: "編集しました"
    else
      render 'edit'
    end
  end

  private

  def event_params
    params.require(:event).permit(:title, :content, :start_time)
  end

end
app/views/events/new.html.erb
<%= form_with(model: @blog, local: true) do |form| %>

  <div class="title">
    <%= form.label :title %>
    <%= form.text_field :title %>
  </div>

    <div class="time">
    <%= form.label :start_time %>
    <%= form.datetime_select :start_time %>
  </div>

  <div class="content">
    <%= form.label :content %>
    <%= form.text_field :content %>
  </div>


  <div class="submit">
    <%= form.submit %>
  </div>

<% end %>

viewに関してはform内がタイトル・開始時刻・内容と言う感じで
僕がしたい内容とは少し違っていました。

イベントカレンダーを作りたかったので、日付+開始+終了時刻を設けたいと考えました。

カラム追加
 end_time(終了時刻)

start_day(開始日時)も追加しようとしていたのですが、
データとして保持する際にstart_timeに日付の情報が入ってくるので省きます。

その代わりモデルに

event.rb
attr_accessor :start_day ⇦記載

パラメーターとしてこの形で受け取れる
[2] pry(#<EventsController>)> @event.start_day
=> {1=>2021, 2=>7, 3=>22}

viewファイルも変更

app/views/events/new.html.erb
.
.
.
   <div class="form-group">↓追加
      <%= form.label :start_day, "時刻" %>
      <%= form.date_select :start_day, {discard_year: true}, class: "form-control" %><br>
    </div> 

    <div class="form-group">
      <%= form.time_select :start_time,  class: "form-control" %>
      ~             ↓追加
      <%= form.time_select :end_time, class: "form-control" %>
    </div>
.
.

コントローラーでの処理も変更

events_controller.rb

@event = Event.create(event_params)
このままだと対応できないので、

下記の処理に変更

@event = Event.new(event_params)
@event.start_time = ~~~~
@event.end_time = ~~~~
@event.save

@event.start_time開始時刻+作成日が作れます
同じく@event.end_time終了時刻+作成日が作成される。

作成日はタイムスタンプとして自動的に入ってしまう。

なので、入力欄に2月9日として投稿したとしても、、
2月3日(作成日)に追加されてしまう。


pry(#<EventsController>)> @event.start_time
=> Wed, 03 Feb 2021 18:44:00 JST +09:00

そこで@event.start_dayから[年][月][日]取得し上書きしてあげる。

pry(#<EventsController>)> @event.start_day
=> {1=>2021, 2=>2, 3=>9}
    []   []  []

なので、シンプルに記述

@event.start_time = @event.start_day
結果
1=>2021, 2=>2, 3=>9, 4=>0, 5=>0
   []   []  []    []    []

うん!今のままだと当然[時][分]が取得出来てない 笑

@event.start_dayからは「年」「月」「日」が取得。
@event.start_timeからは「時間」「分」が取得。
これを採用した新しいTimeWithZoneのインスタンスを作成して代入すれば解決します。

なので、
一旦この形にまとめます!

Time.zone.local(2007, 2, 10, 15, 30, 45)        # => Sat, 10 Feb 2007 15:30:45 EST -05:00

安易な変数に変えておきます。

    d = @event.start_day.values ⇦ 値のみを取得したいのでvaluesメソッドを使っています!
    s = @event.start_time
    e = @event.end_time
pry(#<EventsController>)> Time.zone.local(d[0], d[1], d[2], s.hour, s.min)
=> Tue, 09 Feb 2021 18:44:00 JST +09:00

うまく出来たのであとはこれを代入してあげれば完成!

@event.start_time = Time.zone.local(d[0], d[1], d[2], s.hour, s.min)
@event.end_time = Time.zone.local(d[0], d[1], d[2], e.hour, e.min)
app/controllers/events_controller.rb

class EventsController < ApplicationController

  def index
    @events = Event.all
  end

  def new
    @event = Event.new
  end

  def show
    @event = Event.find(params[:id])
  end

  def create
    @event = Event.new(event_params)
    d = @event.start_day.values
    s = @event.start_time
    e = @event.end_time
    @event.start_time = Time.zone.local(d[0], d[1], d[2], s.hour, s.min)
    @event.end_time = Time.zone.local(d[0], d[1], d[2], e.hour, e.min)
    if @event.save
      redirect_to events_path, notice: 'リストを作成しました'
    else
      flash.now[:alert] = "#{@event.errors.messages.length}つ空欄です。"
      render :new
    end
  end

  def destroy
    @event = Event.find(params[:id])
    @event.destroy
    redirect_to events_path, notice:"削除しました"
  end

  def edit
    @event = Event.find(params[:id])

  end

  def update
    @event = Event.find(params[:id])
    if @event.update(event_params)
      redirect_to events_path, notice: "編集しました"
    else
      render 'edit'
    end
  end

  private

  def event_params
    params.require(:event).permit(:title, :content, :start_time, :end_time, :start_day)
  end
end

おわりに

@event.start_dayから値を取り出すことにかなり苦戦しました^^;
いろんな変換メソッドを試したり、formヘルパーを触ってみたり
なかなか答えに辿りつきませんでした。
中身はハッシュで,欲しいのは「値」。「ハッシュ」から「値」に変換するだけの話でした。

何をしないといけないかをしっかり考えることが重要だと今回で改めて気付きました。^^

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0