如何将 Google 原始时间戳转换为 Java LocalDate?

How to convert Google proto timestamp to Java LocalDate?

我们需要将 Google Proto 缓冲区时间戳转换为正常日期。在这种情况下,有没有办法将 Google Proto 缓冲区时间戳直接转换为 Java LocalDate

最简单的方法是从缓冲区 Timestamp 中提取日期到 String,这样它就可以被多次使用,然后:

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);

如果您无法提取字符串时间戳并且您有毫秒,您也可以从中创建 LocalDate 和 Date 对象,因为它们都有承包商。

    Timestamp timestamp;
    long timeInMilliSeconds = Timestamp.toMillis(timestamp);
    Date currentDate = new Date(timeInMilliSeconds);
    LocalDate date = Instant.ofEpochMilli(timeInMilliSeconds).atZone(ZoneId.systemDefault()).toLocalDate();

首先请注意:Protobuf 的 TimeStamp 比 Java 的 LocalDate(天)具有更高的分辨率(秒和秒的分数),因此您会丢失一些信息通过从 TimeStamp 转换为 LocalDate

查看 TimeStamp JavaDoc 的这段摘录:

A Timestamp represents a point in time independent of any time zone or calendar, represented as seconds and fractions of seconds at nanosecond resolution in UTC Epoch time.


这个JavaDoc告诉我们,这个值是基于Epoch时间的表示,也就是说我们可以用Java的LocalDateTime#ofEpochSeconds进行无损转换(因为LocalDateTime 也存储时间)并从那里剥离时间以获得 LocalDate.

通过使用 LocalDateTime(em:本地),我们可以确保我们使用与 TimeStamp Class 相同的时区偏移量,即 UTC(再次来自Java文档):

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);

输出

seconds: 86399 = 1970-01-01

seconds: 86400 = 1970-01-02


EDIT 在要求转换为 java.util.Date

之后

查看 Date 及其 JavaDoc 的可能构造函数,我们发现:

Allocates a Date object and initializes it to represent the specified number of milliseconds since the standard base time known as "the epoch", namely January 1, 1970, 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);

输出:

seconds: 86399 = Thu Jan 01 23:59:59 CET 1970

seconds: 86400 = Fri Jan 02 00:00:00 CET 1970

tl;博士

作为 UTC 时刻,转换为 java.time.Instant。然后申请一个时区得到一个ZonedDateTime。将仅限日期的部分提取为 LocalDate

一行:

Instant
.ofEpochSecond( ts.getSeconds() , ts.getNanos() ) 
.atZone( ZoneId.of( "America/Montreal" ) ) 
.toLocalDate() 

转换

第一步是转换Timestamp object’s count of seconds and fractional second (nanoseconds) to the java.time classes. Specifically, 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 ) ;

将仅限日期的部分提取为 LocalDateLocalDate 没有时间和时区。

LocalDate ld = zdt.toLocalDate() ;

警告:不要为此目的使用LocalDateTimeclass,不幸的是在另一个答案中显示。 class 故意缺少任何时区或与 UTC 的偏移量的概念。因此它不能代表一个时刻,不是时间轴上的一个点。请参阅 class 文档。

转换

最好完全避免非常麻烦的遗留日期时间 classes,包括 DateCalendarSimpleDateFormat。但是,如果您必须与尚未更新到 java.time 的旧代码进行互操作,您可以来回转换。调用添加到旧 classes 的新转换方法。

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.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

您可以直接与数据库交换 java.time 对象。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* classes.

从哪里获得java.time classes?

ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

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()); 

上面的代码肯定可以。