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

nan*_*zen 3 sqlite time android date

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

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

Java代码:

public void saveExam() {
    date = (DatePicker)findViewById(R.id.examDate);
    Integer day = date.getDayOfMonth();
    Integer month = date.getMonth();
    Integer year = date.getYear();
    StringBuilder temp = new StringBuilder();
    temp.append(day.toString()).append("/").append(month.toString()).append("/").append(year.toString());
    String dateStr = temp.toString();

    time = (TimePicker)findViewById(R.id.examTime);
    Integer hour, minutes;
    if (Build.VERSION.SDK_INT >= 23 ) {
        hour = time.getHour();
        minutes = time.getMinute();
    } else {
        hour = time.getCurrentHour();
        minutes = time.getCurrentMinute();
    }
    temp = new StringBuilder();
    temp.append(hour.toString()).append(":").append(minutes.toString());
    String timeStr = temp.toString();

    Toast.makeText(add_exam.this, dateStr  + ", " + timeStr, Toast.LENGTH_LONG).show();
}
Run Code Online (Sandbox Code Playgroud)

Bas*_*que 6

TL;博士

使用:

  • 严格定义的ISO 8601字符串格式
  • 调整为UTC
  • T 在中间(没有SPACE字符)
  • Z 最后:

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

2016-09-17T18:28:27Z

严格的ISO 8601格式

GVillani82答案是正确的,应该被接受.唯一的问题是引用错误地指出的SQLite文档:

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

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

T 在中间

严格来说,中间的SPACE格式不符合ISO 8601标准.ISO 8601标准要求T在中间而不是空间.引用总结ISO 8601标准的维基百科页面:

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

所述T可与由发送方和数据接收方的只有在特殊结构的空间来替换.

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

小数部分

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

三位数表示毫秒,六位是微秒,九位是纳秒.这些都是主流面向商业的应用程序中常用的.任何更精细的分数只能在科学/工程类型的应用程序中看到.

与仅限于毫秒的旧日期时间Java类相比,java.time类使用纳秒分辨率.

实用性

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

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

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

Instant instant = Instant.parse( "2016-09-17T18:28:27Z" );
Run Code Online (Sandbox Code Playgroud)

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

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

如果您在日期时间函数SQLite文档页面中阅读得足够深,您会看到它提到:

...根据ISO-8601的要求,"T"是分隔日期和时间的文字字符

TSQLite及其日期时间函数支持此格式.此外,文档指出的时区指标ZZuluUTC也支持.

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

因此,我建议在SQLite中存储片刻的最明智的方法是调整为UTC并使用严格的ISO 8601格式,包括Z最后:2016-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以来的秒数.

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

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

其他数据库

请注意,SQLite从未打算成为一个严重的重型数据库.因此,lite在SQLite中.本发明人解释说,他打算SQLite的是一个升压替代编写的纯文本文件.他从不打算让SQLite成为更严肃的数据库的竞争对手.

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

在Android环境中,我首先考虑的是H2数据库引擎.这种纯Java,开源,免费的数据库产品正在积极开发并越来越受欢迎.看到:


GVi*_*i82 5

根据Sqlite文档(第2.2节),您应该以下列方式之一存储日期:

  • 文本为ISO8601字符串(“ 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'
Run Code Online (Sandbox Code Playgroud)

或者,也许您想获取所有带有时间的行09:40:

SELECT * FROM table
WHERE strftime('%H:%M', your_date_column) == '09:40'
Run Code Online (Sandbox Code Playgroud)

或者:

SELECT * FROM table
WHERE strftime('%s', your_date_column) < strftime('%s','now')
Run Code Online (Sandbox Code Playgroud)

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

简介:该函数strftime对数据库存储模式进行了抽象,因此实际上并不会确定您使用哪种模式。