【问题标题】:strftim() in sqlite gives wrong resultssqlite 中的 strftime() 给出错误的结果
【发布时间】:2019-02-19 16:12:15
【问题描述】:

我将date 字段存储在数据库中作为自纪元以来的数:

对于图像中的时间戳(1550591783——代表2019-02-19 19:26:23),sqlite 应返回 50 作为一年中的某一天,但它返回 40

这是PurchaseDao中的查询:

@Query("SELECT strftime('%j', date, 'unixepoch', 'localtime') AS day " +
       "FROM purchase " +
       "WHERE ...")
abstract List<Cost> getCosts();

这是日期转换器:

public class DateConverter {

    @TypeConverter
    public static Date fromTimestamp(Long value) {
        return value == null ? null : new Date(value * 1_000); // multiply by 1000 to make it milliseconds
    }

    @TypeConverter
    public static Long toTimestamp(Date date) {
        return date == null ? null : date.getTime() / 1_000; // divide by 1000 to store as seconds
    }
}

即使我将now 作为参数传递给查询(实际上我什至将它应用到一个新的方法中而没有任何其他干扰)我得到同样的错误结果:

@Query("SELECT strftime('%j', 'now', 'localtime')")

我尝试删除 'localtime' 参数,更改日期转换器以将日期存储为字符串(例如格式为 2019-02-19)并在 AVD 中运行应用程序,但在所有情况下我都得到相同的错误结果。

另一方面,当我使用 Calendar (Calendar.getInstance().get(Calendar.DAY_OF_YEAR);) 获取一年中的某一天或使用 stetho 在我的 PC 中运行查询时,结果是正确的。

任何帮助表示赞赏。

【问题讨论】:

  • 您确定这是正在评估的时间戳吗?如果对查询中的时间戳进行硬编码,是否会得到相同的结果?
  • @GabrielNegut 是的,我用硬编码的1550591783 得到了相同的结果。问题是,当我在我的 计算机(使用 stetho)上的应用程序数据库上运行查询时,结果是正确的。

标签: android sql sqlite android-room strftime


【解决方案1】:

正如我已经解释过的 here,UNIX 时间的转换是这样工作的:

SELECT DATE(dateColumn, 'unixepoch') AS isodate FROM tableName

或存储自纪元开始以来的毫秒数,这在 Android Java 中很常见:

SELECT DATE(ROUND(dateColumn / 1000), 'unixepoch') AS isodate FROM tableName

这不需要TypeConverter 中的任何乘法或除法。而TypeConverter 中的这个乘法/除法操作数1_000 对我来说看起来很奇怪。

这里的问题可能与纯 SQL 完全相同...date.getTime() / 1000 在 999/1000 的情况下只能表示为 float 值而不是直接的 long 整数值 - 除非四舍五入将该值设置为 long 整数值。 ROUND(dateColumn / 1000) 会被 SQL 阻止。不知何故,这个问题缺乏背景;请在下方评论您需要获得哪个特定价值 - 以及为什么需要获得它;那么我也许可以扩展我的答案 - 因为我真的不明白获得购买日期的目的。我宁愿期望它是购买后的几天。

【讨论】:

  • 我想显示从例如 90 天前到今天的总成本每天。因此,似乎唯一可以提供唯一结果(唯一日期编号)的格式化程序是 %j%J。我想你没有读过这个问题。即使我使用 now 参数运行查询,结果也是错误的:@Query("SELECT strftime('%j', 'now', 'localtime')") 无论如何,我尝试了您的解决方案并得到了另一个错误的结果。
  • @Mahozad 您应该为此使用域聚合函数SUM(),并结合WHERE dateColumn IN BETWEENGROUP BY isodate。这就是 SQL 的美妙之处,而且比“手动”执行此操作要容易得多。 noSQL 不支持这个。
  • 是的,我已经使用了group by day,但在问题的查询中没有提到它。问题仍然是一年中的一天!
  • @Mahozad 这不是必需的,在转换为 isodate 时...因为可以应用 GROUP BY - 只要它是数值(代表秒或毫秒,但不代表单日,可以GROUP BY)。
  • 请再次注意group bysum 按预期工作。问题是它给出了错误的日期数
【解决方案2】:

也许它在某个地方的 strftime 一年中的某天实施中存在错误;我得到了正确的结果。如果更新 SQLite 或库不是一个选项,那么也许您可以通过从 date 的儒略日中减去 date 当年 1 月 1 日的儒略日来解决此问题。 em>(在cmets之后进行了简化和修正):

SELECT cast (
       julianday(date, 'unixepoch', 'localtime') 
     - julianday(date, 'unixepoch', 'localtime', 'start of year')
     as int
     )
     + 1
from Purchase

这当然假设 julianday 函数可以正常工作。

【讨论】:

  • 您的查询可以简化:SELECT round(julianday(date, 'unixepoch', 'localtime') - julianday('now', 'localtime', 'start of year')) FROM Purchaseround() 应该用于将浮点数转换为 int 并确保结果正确。
  • 嗯...关于简化的好点,但在这种情况下,我认为您需要转换为 int 并添加一个(我忘了这样做);我会编辑答案。这种方法适用于您的目标系统吗?
  • @Mahozad; PS:以上假设您的时间戳存储在 UTC 中。
  • 谢谢。您提出的解决方法按预期工作,但我完全改变了查询数据的方式并解决了问题。
猜你喜欢
  • 2014-04-13
  • 1970-01-01
  • 1970-01-01
  • 2019-01-02
  • 2014-09-16
  • 2011-11-15
  • 2019-02-06
  • 2014-01-30
  • 2018-08-01
相关资源
最近更新 更多