【问题标题】:How to Query According to Different Time Zones?如何根据不同时区查询?
【发布时间】:2021-05-24 05:42:48
【问题描述】:

我想通过根据用户日期进行过滤来显示我存储在 Firebase 实时数据库中的数据。我在数据库中的数据始终按照 GMT+3 时区工作。但如果我的用户时间与 GMT+3 不同,则查询将无法正常工作。我需要一些信息和代码。有谁知道我该怎么做?

我将我的数据作为 unixtime 存储在数据库中。 例如:1621717200

startData = FirebaseDatabase.getInstance().getReference()
                .child("totals")
                .child(moTracks);
        PeriodHow = startData.orderByChild("availableDate").startAt(1621717200).endAt(1621803599);
        PeriodHow.addListenerForSingleValueEvent(new ValueEventListener() {
}
}

问候。

【问题讨论】:

  • 因为无论如何您都需要进行转换,请考虑以 UTC (GMT+0) 存储时间以作为时区中性表示。

标签: java android firebase firebase-realtime-database


【解决方案1】:

tl;博士

LocalDate
.of( 2021 , Month.SEPTEMBER , 26 )
.atStartOfDay( 
    ZoneId.of( "Africa/Tunis" )
)
.toInstant()
.getEpochSecond()

要完成您的查询,请执行相同的操作,但通过在第二天的第一时间致电 LocalDate#plusDays( 1 ) 添加一天。

详情

我不知道 Firebase。但我可以向您展示如何计算自 epoch reference 以来的秒数。

couple dozen commonly used epoch references 中,我假设您的 Firebase 工具使用的是 1970 年的第一刻,如 UTC 所示(零时分秒偏移量)。

获取当前时刻,作为自 1970-01-01T00:00Z 以来的整秒数。我们使用Instant 来表示UTC 中的时刻。

long secondsSinceEpoch = Instant.now().getEpochSecond() ;

显然,您希望使用自纪元以来的整秒计数来表示一整天,但要跟踪在特定时区中看到的这一天。请注意,对于任何特定时刻,日期在全球范围内都会因时区而异。在日本东京可能是“明天”,而在美国俄亥俄州托莱多仍然是“昨天”。

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;

您可以选择使用 JVM 当前的默认时区。

ZoneId z = ZoneId.systemDefault() ;

此外,政客们经常更改时区规则,造成异常情况,例如天数长于或短于 24 小时,以及日期不是从 00:00 开始。所以我们让 java.time 通过调用LocalDate#atStartOfDay 来确定一天中的第一个时刻。

LocalDate ld = LocalDate.of( 2021 , Month.SEPTEMBER , 26 ) ;
ZonedDateTime zdtStart = ld.atStartOfDay( z ) ;

通过从我们的ZonedDateTime 对象中提取Instant 对象,将该时刻从特定时区调整为UTC。我们有相同的时刻,时间轴上的相同点,但我们感知到的挂钟时间不同。

Instant instantStart = zdtStart.toInstant() ;

Instant,我们得到自纪元引用以来的整秒计数。小心数据丢失:当然,Instant 上的任何小数秒都会被忽略。

long start = instantStart.getEpochSecond() ;

对于一天的开始,我们使用半开方法,其中一天的开始包含,而结束不包含。因此,一天从一天的第一刻开始,一直持续到但不包括第二天的第一刻。这样就避免了确定无限可分最后时刻的问题。

LocalDate nextDay = ld.plusDays( 1 ) ;
ZonedDateTime zdtEnd = nextDay.atStartOfDay( z ) ;

计算经过的时间,即该日期在该区域的一天的长度。

Duration d = Duration.between( zdtStart , zdtEnd ) ;  // 23 hours, not 24, for Daylight Saving Time (DST) cut-over on that date in that zone.

调整为 UTC(零偏移)。提取自纪元引用以来的整秒计数。

Instant instantEnd = zdtEnd.toInstant() ;
long end = instantEnd.getEpochSecond() ;

现在您可以使用startend 执行查询。确保您的查询被框定为半开。 不要使用 SQL 命令BETWEEN。条件应该是“不在开始之前,在结束之前”的逻辑。

SELECT *
FROM some_table_
WHERE when_ !< ?
AND when_ < ?
;

...同时使用 startend longs 作为插入这些占位符的值。

顺便说一句,如果您的数据库提供日期时间数据类型,那么您应该使用这些类型作为表列的类型,而不是仅仅使用整数。


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。 Hibernate 5 & JPA 2.2 支持 java.time

从哪里获取 java.time 类?

【讨论】:

  • 首先,感谢您的精彩回答。但是当我运行代码时,我无法得到日志输出
  • @MehmetAslan 我脑海中的那个代码,未经测试。可能需要修复。如果你现在不能修复它,我稍后会在我使用电脑时修复它。
  • 我无法运行代码。如果你能修复它,我将不胜感激,问候
  • @MehmetAslan 代码运行成功,无需任何更改。见code run live at IdeOne.com。您可以分叉该页面并输入您自己的时区。
  • 我的错。那么 ZoneId z = ZoneId.of("Pacific/Auckland") 有什么办法可以自动从用户那里得到这个信息呢?
猜你喜欢
  • 1970-01-01
  • 2013-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-12
  • 2020-08-30
  • 2022-01-22
  • 2021-12-04
相关资源
最近更新 更多