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

时间:2018-10-04 11:24:03

标签: java date grpc grpc-java proto

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

4 个答案:

答案 0 :(得分:5)

tl; dr

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

将仅日期部分提取为LocalDateLocalDate没有时间,也没有时区。

LocalDate ld = zdt.toLocalDate() ;

警告:请勿为此目的使用LocalDateTime类,不幸的是在另一个Answer中显示。该类故意缺少时区或从UTC偏移的任何概念。因此,它不能代表一个时刻,也不是时间轴上的一点。请参阅课程文档。

转换

最好完全避免非常麻烦的旧式日期时间类,包括DateCalendarSimpleDateFormat。但是,如果必须与尚未更新为 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.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

目前位于Joda-Timemaintenance mode项目建议迁移到java.time类。

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

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

在哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

答案 1 :(得分:1)

最简单的方法是将日期从缓冲区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();

答案 2 :(得分:1)

首先要注意:Protobuf的TimeStamp比Java的LocalDate(天)具有更高的分辨率(秒和几分之一秒),因此您将通过从TimeStamp转换而丢失一些信息到LocalDate

请参见时间戳记JavaDoc的摘录:

  

时间戳表示与任何时区或   日历,以秒和秒的分数表示   毫秒的UTC纪元时间。


同一JavaDoc告诉我们,该值是基于大纪元时间的表示,这意味着我们可以使用Java的LocalDateTime#ofEpochSeconds进行转换而不会造成损失(因为LocalDateTime也存储时间)并从此抽出时间来获得LocalDate

通过使用LocalDateTime(em:Local),我们可以确保我们使用与TimeStamp类相同的TimeZone偏移量,即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

后进行

EDIT

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

  

分配一个Date对象,并将其初始化为表示自标准基准时间(即“纪元”)以来的指定毫秒数,即格林尼治标准时间1970年1月1日。

并且因为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欧洲中部时间

     

秒:86400 = 1970年1月2日星期五欧洲中部时间

答案 3 :(得分:0)

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

上面的代码肯定会起作用。