Rails6 のちょい足しな新機能を試す65（timestamps precision編）

# はじめに

Rails 6 に追加されそうな新機能を試す第65段。 今回は、 `timestamps precision` 編です。
Rails 6 では、 `timestamps` にデフォルトで、 precision が設定されるようになりました。

Ruby 2.6.3, Rails 6.0.0.rc1, Rails 5.2.3, MySQL 8.0.16 で確認しました。Rails 6.0.0.rc1 は `gem install rails --prerelease` でインストールできます。

```shell
$ rails --version
Rails 6.0.0.rc1
```

今回は、 `created_at` を検索条件にして検索してみます。

# プロジェクトを作る

```shell
rails new rails6_0_0rc1
cd rails6_0_0rc1
```

# model を作る

User モデルを作ります。

```shell
bin/rails g model User name
```

# seed データを作る

1件だけですが、 seed データを作成します。

```ruby:db/seeds.rb
User.create(name: 'Taro')
```

# User モデルを修正する

User モデルに scope を1つ作ります。

1. 1件目のデータの created_at カラムを取得します。
2. created_at カラムから 秒まで指定した Time オブジェクトを作る。
3. Timeオブジェクトを条件にして、 created_at で検索する。

```ruby:app/models/user.rb
class User < ApplicationRecord
  scope :find_first, -> {
    created_at = first.created_at
    t = Time.new(created_at.year, created_at.month, created_at.day, created_at.hour, created_at.min, created_at.sec)
    where(created_at: t)
  }
end
```

# seed データを登録します。


```shell
$ bin/rails db:create db:migrate db:seed
```

# rails console で確認する

`rails console` で確認してみます。

```irb
irb(main):001:0> User.find_first
   (0.4ms)  SET NAMES utf8mb4,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
  User Load (0.4ms)  SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
  User Load (0.3ms)  SELECT `users`.* FROM `users` WHERE `users`.`created_at` = '2019-07-26 06:52:38' LIMIT 11
=> #<ActiveRecord::Relation []>
```

データは見つかりませんでした。

# mysql で検索してみる

mysql で検索してみます。

```
mysql> select * from users;
+----+------+----------------------------+----------------------------+
| id | name | created_at                 | updated_at                 |
+----+------+----------------------------+----------------------------+
|  1 | Taro | 2019-07-26 06:52:38.606642 | 2019-07-26 06:52:38.606642 |
+----+------+----------------------------+----------------------------+
1 row in set (0.00 sec)
```

`created_at` と `updated_at` の秒に6桁の小数部分が存在していることが確認できます。

# schema.rb を確認する

`schema.rb` を確認すると `precision: 6` が指定されていることがわかります。

```ruby:db/schema.rb
  ...
  create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end
  ...
```

# Rails 5 では

Rails 5.2.3 では、データが検索できます。

```irb
irb(main):001:0> User.find_first
   (0.7ms)  SET NAMES utf8mb4,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
  User Load (0.3ms)  SELECT  `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
  User Load (0.4ms)  SELECT  `users`.* FROM `users` WHERE `users`.`created_at` = '2019-07-26 06:42:34' LIMIT 11
=> #<ActiveRecord::Relation [#<User id: 1, name: "Taro", created_at: "2019-07-26 06:42:34", updated_at: "2019-07-26 06:42:34">]>
```

MySQL で確認すると、秒に小数部分がありません。

```
mysql> select * from users;
+----+------+---------------------+---------------------+
| id | name | created_at          | updated_at          |
+----+------+---------------------+---------------------+
|  1 | Taro | 2019-07-26 06:42:34 | 2019-07-26 06:42:34 |
+----+------+---------------------+---------------------+
1 row in set (0.00 sec)
```

`schema.rb` にも `precision: 6` がありません。

```ruby:db/schema.rb
  ...
  create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end
  ...
```

# Rails 6 でデータを見つけるには

十分な検証はしてませんが、 `created_at.subsec` を考慮してやれば良さそうです。

```ruby:app/models/user.rb
class User < ApplicationRecord
  scope :find_first, -> {
    created_at = first.created_at
    # t = Time.new(created_at.year, created_at.month, created_at.day, created_at.hour, created_at.min, created_at.sec)
    t = Time.new(created_at.year, created_at.month, created_at.day, created_at.hour, created_at.min, created_at.sec) + created_at.subsec
    where(created_at: t)
  }
end
```

# 試したソース

試したソースは以下にあります。
https://github.com/suketa/rails6_0_0rc1/tree/try065_mysql_precision


# 参考情報

* [What is new in Rails 6.0](https://bogdanvlviv.com/posts/ruby/rails/what-is-new-in-rails-6_0.html)
* [Make `t.timestamps` with precision by default](https://github.com/rails/rails/pull/34970)