簡単に書くと、こういうことです。
mysql> show create table items \G
*************************** 1. row ***************************
Table: items
Create Table: CREATE TABLE `items` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
irb(main):002:0> Item.new(name: 'foo').save
(0.2ms) BEGIN
SQL (0.4ms) INSERT INTO `items` (`name`, `created_at`, `updated_at`) VALUES ('foo', '2016-11-24 15:14:34', '2016-11-24 15:14:34')
(0.8ms) COMMIT
=> true
irb(main):003:0> require 'mathn'
=> true
irb(main):004:0> Item.new(name: 'foo').save
(0.3ms) BEGIN
SQL (0.3ms) INSERT INTO `items` (`name`, `created_at`, `updated_at`) VALUES ('foo', '2016-11-24 15:14:55.147015', '2016-11-24 15:14:55.147015')
(0.6ms) COMMIT
=> true
なにが起きたかおわかりだろうか?
require 'mathn'
した後にcreated_at
へのINSERT
には小数点以下がついています。
何が起きたか?
MySQL5.6以降、datetimeでミリ秒がサポートされるようになったが、何も考えずにTime.new
のミリ秒以下までINSERT文に含めると問題が多数発生するため、精度を見て小数点以下を付けたり付けなかったりするようになった。
精度を見て、小数点以下を削除するコードは以下の通り。
def apply_seconds_precision(value)
return value unless precision && value.respond_to?(:usec)
number_of_insignificant_digits = 6 - precision
round_power = 10**number_of_insignificant_digits
value.change(usec: value.usec / round_power * round_power)
end
さて、通常Rubyでは123456 / 1000000 / 1000000
の結果は以下のようになる:
irb(main):002:0> 123456 / 1000000 / 1000000
=> 0
これがmathn
をrequire
するとどうなるかというと
irb(main):004:0> 123456 / 1000000 / 1000000
=> (1929/15625000000)
こうなります。
なんでこれがdatetimeの精度に影響するかというと、
value.change(usec: value.usec / round_power * round_power)
この部分ですね。
割り算で小数点以下を消しているのですが、mathn
をrequire
すると、正確な計算をしようとするため、小数点以下が消えないのです。
なんでこうなるの?
class Fixnum
remove_method :/
##
# +/+ defines the Rational division for Fixnum.
#
# 1/3 # => (1/3)
alias / quo
end
恐ろしい、恐ろしい。
筋トレと野菜が必要ですね。