Android - 如何在SQLite中“正确”存储日期和时间?

时间:2016-09-17 11:57:05

标签: android sqlite date time

我已阅读其他一些有关此问题的帖子。大多数答案建议将日期/时间数据转换为特定格式(UTC)并将其存储在字符串dataType中。

以整数格式存储它们是否“合适”,每个数据(年,月,日,小时,分钟)存储1列?或者这是不可接受的?因为这会比较当前日期/时间更容易。我打算做的是行的数据是否超过当前日期。

Java代码:

compareTo

2 个答案:

答案 0 :(得分:5)

TL;博士

使用:

  • 严格定义的ISO 8601字符串格式
  • 调整为UTC
  • 中间的
  • T(无空格字符)
  • 最后
  • Z

YYYY-MM-DDTHH:MM:SS.S…Z

  

2016-09-17T18:28:27Z

严格的ISO 8601格式

Answer by GVillani82是正确的,应该被接受。唯一的问题是引用错误地声明的SQLite文档:

  

TEXT as ISO8601字符串(" YYYY-MM-DD HH:MM:SS.SSS")

该陈述中有两点错误:中间的SPACE和小数的三位数。

中间

T

严格来说,中间有空格的格式不符合ISO 8601。 ISO 8601标准中间需要T而不是SPACE。引用总结ISO 8601标准的the Wikipedia page

  

...连接一个完整的日期表达式,字母T作为分隔符,以及一个有效的时间表达式。例如," 2007-04-05T14:30"。

只有在数据的发件人和收件人的特殊安排下,T才可以替换为SPACE。

进一步令人困惑的是,中间中的SPACE格式符合SQL标准的日期时间格式定义。 SQL规范早于ISO 8601.ISO 8601正在迅速成为首选格式,因为它继续在全球范围内被商业世界以及现代计算机协议采用。

小数分数

ISO 8601标准允许以秒为单位的小数位数的任意位数。引用的文件意味着需要三个数字,但实际上允许任何数字包括零(根本没有数字,整数秒)。

三位数代表milliseconds,六位是microseconds,九位是nanoseconds。这些都是主流面向商业的应用程序中常用的。任何更精细的分数只能在科学/工程类型的应用程序中看到。

java.time类使用nanosecond分辨率,而旧的日期时间Java类仅限于毫秒。

实用性

这两个问题不仅仅是学术问题,而是提出了实际问题。

在Java环境中,我们可能会从SQLite中提取此字符串,然后将其作为java.time对象进行削减。 java.time类是处理日期时间编程的现代方法。这个框架的灵感来自Joda-Time(同一个人领导的努力,Stephen Colbourne),由JSR 310定义,内置于Java 8及更高版本,正式取代了麻烦的旧日期时间类,后端移植到Java 6& ThreeTen-Backport项目中的7,并进一步适应Android项目中的ThreeTenABP

在解析/生成表示日期时间值的字符串时,java.time类默认使用标准ISO 8601格式。但是java.time只使用严格的定义。这意味着在日期时间中间T

Instant instant = Instant.parse( "2016-09-17T18:28:27Z" );

问题是,解析由SQLite doc错误标记为ISO 8601的格式,中间是SPACE将无法通过java.time解析。另一方面,默认情况下java.time生成的字符串中间会有T而不是空格。

解决方案很简单:只需使用严格的ISO 8601定义,中间有T

如果您在SQLite documentation page on date-time functions内阅读得足够深,则会看到它提到:

  

..." T"是按照ISO-8601

的要求分隔日期和时间的文字字符

SQLite及其日期时间函数支持T格式。此外,该文件还指出,Z UTC Zulu的时区指标也受支持。

  

...可以选择性地跟随形式的时区指示符" [+ - ] HH:MM"或只是" Z"。日期和时间功能使用UTC或" zulu"内部时间,所以" Z"后缀是无操作。

所以我建议在SQLite中存储片刻的最明智的方法是调整为UTC并使用严格的ISO 8601格式,包括最后的Z2016-09-17T18:28:27Z

对于小数秒,SQLite文档说虽然只有前三个小数位数对其SQLite函数有意义,但实际上可以存储任意数量的数字。因此,java.time中允许的纳秒数的最大9位数显然可以成功存储,但正好比较2016-09-17T18:28:27.123456789Z

如果此比较精度是您的应用程序的问题,您可以使用可处理纳秒的java.time类在应用程序的Java端进行比较工作,或者您可以考虑将日期时间值截断为毫秒解析度。 java.time类提供了一种方便的截断方法。

其他选项

选项怎么样?

  • 真实的朱利安日数字。
  • INTEGER as Unix Time,自1970-01-01 00:00:00 UTC以来的秒数。

Floating point technology用于实数,例如float / Float& Java中的double / Double,故意以准确性换取执行速度。这意味着无关数字通常出现在分数的右侧。虽然这在某些科学/工程/游戏领域是可以容忍的,但在金钱问题和日期时间值的某些用途方面是不可接受的。所以,在我看来,这不是一个好主意。

来自epoch的计数,例如自UTC 1970年以来的秒数,这不是一个好主意。虽然java.time类在封面下使用这种方法来实际跟踪时间,但我们有这些封面是有原因的。这样的整数对于人类读者没有意义,没有日期时间值可以辨别,因此可能无法检测到错误。数据类型本身是模糊的,因为我们必须猜测一些计算机系统使用整秒作为计数的预期粒度,其他计算机系统使用毫秒或微秒或纳秒或其他粒度。至于时代,这也是模棱两可的,因为在各种计算系统中至少使用了couple dozen epoch points

其他数据库

请注意,SQLite从未打算成为一个严肃的重型数据库。因此,SQLite中的liteinventor has explained他希望SQLite成为编写纯文本文件的升级替代方案。他从不打算让SQLite成为更严肃的数据库的竞争者。

因此,如果您遇到SQLite限制的痛点,您的需求可能超出了SQLite的设计参数。如果您需要更严肃的数据库,请使用更严肃的数据库。

在Android环境中,我首先考虑的是H2 Database Engine。这种纯Java,开源,免费的数据库产品正在积极开发并越来越受欢迎。参见:

答案 1 :(得分:4)

根据Sqlite documentation (section 2.2),您应该通过以下方式之一存储日期:

  • TEXT as ISO8601 strings(" YYYY-MM-DD HH:MM:SS.SSS")。
  • 真实的朱利安日数字。
  • INTEGER as Unix Time,自1970-01-01 00:00:00 UTC以来的秒数。

比较问题可以使用strftime函数来解决,该函数允许您主持其存储类型(TEXT,REAL或INTEGER)的日期。

让我们说你想要获得数据库中所有具有日期Jenuary的行。你可以这样做:

SELECT * FROM table
WHERE strftime('%m', your_date_column) == '01'

或许你想要时间09:40获取所有行:

SELECT * FROM table
WHERE strftime('%H:%M', your_date_column) == '09:40'

或者:

SELECT * FROM table
WHERE strftime('%s', your_date_column) < strftime('%s','now')

将返回当前日期(现在)之前的日期的所有行。

摘要:函数strftime抽象出数据库存储模式,因此它实际上并未满足您使用的模式。