我们需要将Google Proto buffer时间戳转换为普通日期。在此情况下,是否有任何方法可以直接将Google Proto buffer时间戳转换为Java LocalDate
?
将一个UTC时间转换为java.time.Instant
,然后应用一个时区以获取一个ZonedDateTime
。提取仅包含日期的部分作为LocalDate
。
一行代码:
Instant
.ofEpochSecond( ts.getSeconds() , ts.getNanos() )
.atZone( ZoneId.of( "America/Montreal" ) )
.toLocalDate()
第一步是将Timestamp
对象的秒数和小数秒(纳秒)转换为java.time类。具体来说,是java.time.Instant
。与Timestamp
一样,Instant
表示UTC中的一个瞬间,分辨率为纳秒。
Instant instant = Instant.ofEpochSecond( ts.getSeconds() , ts.getNanos() ) ;
ZoneId
应用于我们的Instant
,就可以得到一个ZonedDateTime
。同一时刻,在时间线上相同的点,不同的挂钟时间。ZoneId z = ZoneId( "Pacific/Auckland" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
LocalDate ld = zdt.toLocalDate() ;
注意:不要使用LocalDateTime
类来实现此目的,正如另一个答案所不幸显示的那样。该类故意缺乏任何时区或UTC偏移量的概念。因此它不能表示一个瞬间,也不是时间轴上的一个点。请参阅类文档。
最好完全避免可怕的旧日期时间类,包括Date
、Calendar
和SimpleDateFormat
。但如果您必须与尚未更新为java.time的旧代码进行交互,您可以进行相互转换。调用添加到旧类中的新转换方法。
GregorianCalendar gc = GregorianCalendar.from( zdt ) ;
要将仅包含日期的值表示为GregorianCalendar
,我们必须指定一天中的时间和时区。您可能希望使用一天中的第一个时刻作为时间组件。永远不要假设第一个时刻是00:00:00。夏令时等异常情况可能导致第一个时刻变成另一个时间,例如01:00:00。让java.time确定第一个时刻。
ZonedDateTime firstMomentOfDay = ld.atZone( z ) ;
GregorianCalendar gc = GregorianCalendar.from( firstMomentOfDay ) ;
java.time框架是Java 8及以上版本内置的。这些类替代了老旧的legacy日期时间类,如java.util.Date
、Calendar
和SimpleDateFormat
。
Joda-Time项目现在处于maintenance mode维护模式,建议迁移到java.time类。
欲了解更多信息,请参阅Oracle Tutorial。您也可以在Stack Overflow上搜索许多示例和解释。规范为JSR 310。
您可以直接使用符合JDBC 4.2或更高版本的JDBC驱动程序与数据库交换java.time对象。无需字符串,也不需要java.sql.*
类。
在哪里获取java.time类?
Interval
, YearWeek
, YearQuarter
, 和more。首先提醒一下:Protobuf的TimeStamp
比Java的LocalDate
有更高的分辨率(秒和秒的分数),因此将TimeStamp
转换为LocalDate
会丢失一些信息。
请参阅JavaDoc
中TimeStamp的摘录JavaDoc:
时间戳表示独立于任何时区或日历的时间点,以UTC Epoch时间的秒和纳秒分辨率表示秒和分数。
LocalDateTime
也存储时间),然后剥离时间以获得LocalDate
。LocalDateTime
(本地时间),我们可以确保使用与TimeStamp
类相同的时区偏移量,即UTC(这也来自JavaDoc)。final Timestamp ts1 = Timestamp.newBuilder().setSeconds((60 * 60 * 24) - 1).build();
final Timestamp ts2 = Timestamp.newBuilder().setSeconds((60 * 60 * 24)).build();
final LocalDate ld1 = LocalDateTime.ofEpochSecond(ts1.getSeconds(), ts1.getNanos(), ZoneOffset.UTC).toLocalDate();
final LocalDate ld2 = LocalDateTime.ofEpochSecond(ts2.getSeconds(), ts2.getNanos(), ZoneOffset.UTC).toLocalDate();
System.out.println(ts1 + " = " + ld1);
System.out.println(ts2 + " = " + ld2);
输出
秒数:86399 = 1970年01月01日
秒数:86400 = 1970年01月02日
编辑 在要求转换为java.util.Date
之后
查看Date
的可能构造函数及其JavaDoc,我们发现:
分配一个
Date
对象并将其初始化为表示自1970年1月1日00:00:00 GMT以来指定的毫秒数。这是标准基准时间称为“时代”。
因为GMT是较旧的区域时间表示标准,而且它基本上是UTC +/- 0,所以这个构造函数符合我们的需求:
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
// making sure the Date objects use UTC as timezone
final Timestamp ts1 = Timestamp.newBuilder().setSeconds((60 * 60 * 24) - 1).build();
final Timestamp ts2 = Timestamp.newBuilder().setSeconds((60 * 60 * 24)).build();
final Date d1 = new Date(ts1.getSeconds() * 1000);
final Date d2 = new Date(ts2.getSeconds() * 1000);
System.out.println(ts1 + " = " + d1);
System.out.println(ts2 + " = " + d2);
输出:
秒数: 86399 = 1970年1月1日23时59分59秒 CET
秒数: 86400 = 1970年1月2日00时00分00秒 CET
LocalDateTime
是完全错误的。正如其类文档中所解释的那样,该类无法表示一个瞬间。该类表示一系列潜在的时刻,涵盖全球大约26-27个小时的时区范围。相反,应使用java.time.Instant
来表示UTC中的瞬间。 - Basil BourqueInstant
更加优雅,因为它隐藏了所有的时代/时区信息,但这对于 OP 来说并不是那么具体的解释。你说得对,LocalDateTime
可以 表示一个范围。但是通过指定时区偏移量,这些都无关紧要了。这就好比说不要使用 int
来表示 0
到 10
之间的数字,因为 int
也可以是 10.000
。 - iflooplong millis = com.google.protobuf.util.Timestamps.toMillis(protoTimestamp);
LocalDate result = Instant.ofEpochMilli(millis).atZone(ZoneId.systemDefault()).toLocalDate();
最简单的方法是从缓冲时间戳中提取日期到一个字符串,以便可以多次使用,然后:
Timestamp timestamp;
String sDate1 = timestamp.toString();
Date date1 = new SimpleDateFormat("dd/MM/yyyy").parse(sDate1);
如果你想将它转换为LocalDate,可以按照以下步骤进行:
Timestamp timestamp;
String sDate1 = timestamp.toString();
LocalDate localDate = LocalDate.parse(sDate1);
Timestamp timestamp;
long timeInMilliSeconds = Timestamp.toMillis(timestamp);
Date currentDate = new Date(timeInMilliSeconds);
LocalDate date = Instant.ofEpochMilli(timeInMilliSeconds).atZone(ZoneId.systemDefault()).toLocalDate();
PubsubMessage pubsubMessage = message.getPubsubMessage();
String data = pubsubMessage.getData().toStringUtf8();
//System.out.println(pubsubMessage.getMessageId());
Timestamp timestamp = pubsubMessage.getPublishTime();
//System.out.println(timestamp);
Instant instant = Instant.ofEpochSecond(timestamp.getSeconds() ,
timestamp.getNanos());
ZoneId z = ZoneId.of("America/Montreal");
ZonedDateTime zdt = instant.atZone(z);
LocalDateTime ldt = zdt.toLocalDateTime();
System.out.println("today's date==> "+ldt.getDayOfMonth());
上述代码肯定可以运行。
LocalDate
吗?Date
本身是Java 1 :-) - LuCioInstant.ofEpochSecond(yourTimestamp.getSeconds(), yourTimestamp.getNanos())
方法获取一个Instant对象,并从获取到的Instant
对象开始处理。如果不确定如何将Instant
转换为LocalDate
,可以搜索相关内容,这方面的信息在很多地方都有描述。 - Ole V.V.