Rails API vs NSDate - 比较日期时的精度问题
Rails API vs NSDate - Precision issues when comparing dates
我的 iOS 应用程序使用 rails API 输出 JSON 格式的日期,精度为 3 位微秒,如下例所示。这是为对应实体解析保存的,NSDate如预期一样保留了3位精度:
{ "updated_at": "2015-02-24T22:37:57.683Z" }
当我将这个日期发送回 API 以查询记录时,我在数据库中的 updated_at
字段似乎有(至少)6 位精度:
>> my_model.update_at.strftime('%Y-%m-%d %H:%M:%S.%N')
>> 2015-02-24 22:37:57.683977000
这意味着每次我查询
>> ios_date = json_hash["updated_at"]
>> Model.where("updated_at > ?", ios_date)
我得到一条已存在于我的应用程序数据库中的记录。
我知道我可以在我的 ios_date
上加半秒来解决这个问题,但这显然是一个 hack。
任何人都可以推荐在这种情况下最合适的行动方案吗?
TIA
在这种情况下,我认为最好的方法是将存储在数据库中并输出为 JSON 完全 相同的东西,以避免任何转换的需要或类似的四舍五入问题。正如我假设您想要保留 3 位微秒精度日期一样,我只是将您的数据库配置为也以该精度存储日期。
您可以使用迁移来修改 :created_at 和 :updated_at 字段的精度,如下所示:
class MicrosecondsMigration < ActiveRecord::Migration
def up
change_column :your_table, :created_at, :datetime, precision: 3
change_column :your_table, :updated_at, :datetime, precision: 3
end
def down
change_column :your_table, :created_at, :datetime
change_column :your_table, :updated_at, :datetime
end
end
我相信这将适用于主要数据库。 Check this commit at Rails where support was added for Postgresql. If you are on MySQL, please note that only MySQL 5.6.4 and above 支持比秒更精确的 DATETIME。那不是你的问题,你已经说过你的数据库存储了 6 位精度的日期时间,但我认为这里值得注意。
我还可以想到其他方法,尽管 none 看起来比上述方法更好,但它们也可能值得注意:
- 使用一些 SQL 更改您的查询以从您的日期中删除多余的数字。我不建议走那条路:您需要在需要执行的每个查询中考虑到这一点。越野车,违背了 AR 的目的,看起来很丑。
- 手动给你的
ios_date
添加几毫秒,感觉更丑。
- 当然,您可以随时更改 JSON API 以支持数据库处理的完整 6 位精度。正如我之前所说,我认为那不是你的意图。
尽管更改 created_at 和 updated_at 的精度限制的选项是一个有效选项,但我发现的最干净/最 "Railsy" 的解决方案是获得 to_json
的输出匹配数据库精度。
一行代码即可实现:
ActiveSupport::JSON::Encoding.time_precision = 6
实际上这并没有真正解决问题,因为时间戳的底层存储类型是浮点数,所以一些时间比较仍然失败。
根据本页@dgilperez 的回答,最终降低精度是我的选择。
我的 iOS 应用程序使用 rails API 输出 JSON 格式的日期,精度为 3 位微秒,如下例所示。这是为对应实体解析保存的,NSDate如预期一样保留了3位精度:
{ "updated_at": "2015-02-24T22:37:57.683Z" }
当我将这个日期发送回 API 以查询记录时,我在数据库中的 updated_at
字段似乎有(至少)6 位精度:
>> my_model.update_at.strftime('%Y-%m-%d %H:%M:%S.%N')
>> 2015-02-24 22:37:57.683977000
这意味着每次我查询
>> ios_date = json_hash["updated_at"]
>> Model.where("updated_at > ?", ios_date)
我得到一条已存在于我的应用程序数据库中的记录。
我知道我可以在我的 ios_date
上加半秒来解决这个问题,但这显然是一个 hack。
任何人都可以推荐在这种情况下最合适的行动方案吗?
TIA
在这种情况下,我认为最好的方法是将存储在数据库中并输出为 JSON 完全 相同的东西,以避免任何转换的需要或类似的四舍五入问题。正如我假设您想要保留 3 位微秒精度日期一样,我只是将您的数据库配置为也以该精度存储日期。
您可以使用迁移来修改 :created_at 和 :updated_at 字段的精度,如下所示:
class MicrosecondsMigration < ActiveRecord::Migration
def up
change_column :your_table, :created_at, :datetime, precision: 3
change_column :your_table, :updated_at, :datetime, precision: 3
end
def down
change_column :your_table, :created_at, :datetime
change_column :your_table, :updated_at, :datetime
end
end
我相信这将适用于主要数据库。 Check this commit at Rails where support was added for Postgresql. If you are on MySQL, please note that only MySQL 5.6.4 and above 支持比秒更精确的 DATETIME。那不是你的问题,你已经说过你的数据库存储了 6 位精度的日期时间,但我认为这里值得注意。
我还可以想到其他方法,尽管 none 看起来比上述方法更好,但它们也可能值得注意:
- 使用一些 SQL 更改您的查询以从您的日期中删除多余的数字。我不建议走那条路:您需要在需要执行的每个查询中考虑到这一点。越野车,违背了 AR 的目的,看起来很丑。
- 手动给你的
ios_date
添加几毫秒,感觉更丑。 - 当然,您可以随时更改 JSON API 以支持数据库处理的完整 6 位精度。正如我之前所说,我认为那不是你的意图。
尽管更改 created_at 和 updated_at 的精度限制的选项是一个有效选项,但我发现的最干净/最 "Railsy" 的解决方案是获得 to_json
的输出匹配数据库精度。
一行代码即可实现:
ActiveSupport::JSON::Encoding.time_precision = 6
实际上这并没有真正解决问题,因为时间戳的底层存储类型是浮点数,所以一些时间比较仍然失败。
根据本页@dgilperez 的回答,最终降低精度是我的选择。