22

We need to convert Google Proto buffer time stamp to a normal date. In that circumstance is there any way to convert Google Proto buffer timestamp to a Java LocalDate directly?

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Thirunavukkarasu
  • 361
  • 1
  • 2
  • 13
  • Do you mean by _Java8 Date_ `LocalDate`? `Date` itself is Java 1 :-) – LuCio Oct 04 '18 at 11:29
  • Sorry Its typo. changed the question now @LuCio, – Thirunavukkarasu Oct 04 '18 at 11:32
  • Actually Google Proto Buffer returning com.google.protobuf.Timestamp instance. In that place we need to convert it into LocalDate or what ever its ! – Thirunavukkarasu Oct 04 '18 at 11:33
  • 3
    Use `Instant.ofEpochSecond(yourTimestamp.getSeconds(), yourTimestamp.getNanos())` and continue from the `Instant` you get. If in doubt, search for how to convert `Instant` to `LocalDate`, it’s described in many places. – Ole V.V. Oct 04 '18 at 12:13
  • 1
    Dear Down-Voter, please leave a criticism along with your vote to explain how this Question is not helpful, constructive, or well-formed. Seems to me to be a perfectly fine Question for Stack Overflow. – Basil Bourque Oct 04 '18 at 16:40

5 Answers5

32

tl;dr

As a moment in UTC, convert to java.time.Instant. Then apply a time zone to get a ZonedDateTime. Extract the date-only portion as a LocalDate.

One-liner:

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

Convert

First step is to convert the Timestamp object’s count of seconds and fractional second (nanoseconds) to the java.time classes. Specifically, java.time.Instant. Just like Timestamp, an Instant represents a moment in UTC with a resolution of nanoseconds.

Instant instant =  Instant.ofEpochSecond( ts.getSeconds() , ts.getNanos() ) ;

Determining a date requires a time zone. For any given moment, the date varies around the globe by zone.

Apply a ZoneId to our Instant to get a ZonedDateTime. Same moment, same point on the timeline, different wall-clock time.

ZoneId z = ZoneId( "Pacific/Auckland" ) ; 
ZonedDateTime zdt = instant.atZone( z ) ;

Extract the date-only portion as a LocalDate. A LocalDate has no time-of-day and no time zone.

LocalDate ld = zdt.toLocalDate() ;

Caution: Do not use LocalDateTime class for this purpose, as unfortunately shown in another Answer. That class purposely lacks any concept of time zone or offset-from-UTC. As such it cannot represent a moment, is not a point on the timeline. See class documentation.

Convert

Best to entirely avoid the terribly troublesome legacy date-time classes including Date, Calendar, SimpleDateFormat. But if you must interoperate with old code not yet updated to java.time you can convert back-and-forth. Call new conversion methods added to the old classes.

GregorianCalendar gc = GregorianCalendar.from( zdt ) ;

To represent a date-only value as a GregorianCalendar we must specify a time-of-day and a time zone. You’ll likely want to use the first moment of the day as the time-of-day component. Never assume the first moment is 00:00:00. Anomalies such as Daylight Saving Time mean the first moment might be another time such as 01:00:00. Let java.time determine first moment.

ZonedDateTime firstMomentOfDay = ld.atZone( z ) ;
GregorianCalendar gc = GregorianCalendar.from( firstMomentOfDay ) ;

Table of date-time types in Java (both legacy and modern) and in standard SQL


About java.time

The 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.

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

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The 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.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
1

First some heads up: Protobuf's TimeStamp has a higher resolution (Seconds and fractions of seconds) than Java's LocalDate (Days) so you will be loosing some information by converting from TimeStamp to a LocalDate

See this excerpt of TimeStamp's 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.


This very same JavaDoc tells us, the value is a representation based on Epoch time, that means we can use Java's LocalDateTime#ofEpochSeconds to convert without loss (because LocalDateTime also stores time) and from there on strip the time to get a LocalDate.

By using a LocalDateTime (em: Local) we can make sure we use the same TimeZone offset as the TimeStamp Class does, which is UTC (thats again from the 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);

Output

seconds: 86399 = 1970-01-01

seconds: 86400 = 1970-01-02


EDIT after asking for conversion to java.util.Date

Looking at possible constructors for Date and their JavaDoc we find:

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.

And because GMT beeing the older Standard for zoned time representation and it is basically UTC +/- 0, this constructor fits our needs:

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

Output:

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

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

ifloop
  • 8,079
  • 2
  • 26
  • 35
  • And more thing like is there any way to convert java.util.Date from proto timestamp instead of LocalDate. – Thirunavukkarasu Oct 04 '18 at 13:38
  • **Incorrect.** `LocalDateTime` is exactly the wrong class to be using here. That class cannot represent a moment, as explained in its class documentation. That class represents a range of *potential* moments along a range of about 26-27 hours (the range of time zones around the globe). Instead, use `java.time.Instant` to represent a moment in UTC. – Basil Bourque Oct 04 '18 at 16:08
  • @BasilBourque I agree that `Instant` would be more elegant by hiding all the epoch/time zone stuff, though it would not be that much of an explanation to OP of what exactly happens. You are right saying that `LocalDateTime` _can_ represent a range. But by specifying the time zone offset non of that matters. That would be the same as saying don't use `int` for numbers between `0` and `10`, because `int` can also be `10.000`. – ifloop Oct 05 '18 at 06:44
  • @BasilBourque and could you please provide an example where my conversion aproach doesn't work? Because I am really open to learn new things and that would be a justification for the downvote and the bold 'incorrect' labeling. Thanks in advance! – ifloop Oct 05 '18 at 06:50
1
long millis = com.google.protobuf.util.Timestamps.toMillis(protoTimestamp);

LocalDate result = Instant.ofEpochMilli(millis).atZone(ZoneId.systemDefault()).toLocalDate();

see @ https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/util/Timestamps.html#toMillis-com.google.protobuf.Timestamp-

4b0
  • 21,981
  • 30
  • 95
  • 142
linczy
  • 160
  • 1
  • 5
0

The simplest way to do it is to extract the date from the buffer Timestamp to a String so it can be multiused and then:

Timestamp timestamp;
String sDate1 = timestamp.toString();  
Date date1 = new SimpleDateFormat("dd/MM/yyyy").parse(sDate1);

In case you want to make it a LocalDate you do the following:

Timestamp timestamp;
String sDate1 = timestamp.toString();    
LocalDate localDate = LocalDate.parse(sDate1);

In case you cannot extract the String timestamp and you have Miliseconds you can also create the LocalDate and Date object from that as well, as they both have contractors for it.

    Timestamp timestamp;
    long timeInMilliSeconds = Timestamp.toMillis(timestamp);
    Date currentDate = new Date(timeInMilliSeconds);
    LocalDate date = Instant.ofEpochMilli(timeInMilliSeconds).atZone(ZoneId.systemDefault()).toLocalDate();
Sir. Hedgehog
  • 1,260
  • 3
  • 17
  • 40
  • Actually Google Proto Buffer returning com.google.protobuf.Timestamp instance. In that place we need to convert it into LocalDate or what ever its ! – Thirunavukkarasu Oct 04 '18 at 11:34
  • @Thirunavukkarasu there has to be a way to make the timestamp a String. After you manage that it is very easy to do whatever you want with it – Sir. Hedgehog Oct 04 '18 at 11:35
  • 1
    Actually google proto buffter timestamp returning the below value seconds: 988221139 nanos: 516000000 – Thirunavukkarasu Oct 04 '18 at 11:40
  • Can you pls check com.google.protobuf.Timestamp class ? @hedgehog – Thirunavukkarasu Oct 04 '18 at 11:42
  • i did and i saw that both toString and getMili exists. i edited my answer, refresh and check @Thirunavukkarasu – Sir. Hedgehog Oct 04 '18 at 11:47
  • @Thirunavukkarasu use this and you will be fine: LocalDate date = Instant.ofEpochMilli(timeInMilliSeconds).atZone(ZoneId.systemDefault()).toLocalDate(); – Sir. Hedgehog Oct 04 '18 at 11:49
  • 1
    I'm using com.google.protobuf.Timestamp not com.google.protobuf.util.Timestamps – Thirunavukkarasu Oct 04 '18 at 12:03
  • @Thirunavukkarasu just go in here and see the methods that we are telling you. com.google.protobuf.Timestamp , toSeconds(), toNanoSeconds() is there. USE THEM!!! – Sir. Hedgehog Oct 04 '18 at 12:19
  • @OleV.V. As you can see i have different examples for Date and LocalDate, I only added date for future reference to others getting help from this post. – Sir. Hedgehog Oct 04 '18 at 12:20
  • 1
    According to [the documentation](https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/Timestamp) there is no `getTime` method. – Ole V.V. Oct 04 '18 at 12:21
-1
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()); 

The above code will definitely work.

  • 1
    Thanks for wanting to contribute. Are you contributing much that isn’t already in the other answers? It’s more the explanations that we learn from, so if you could add some, please? I believe that your code works. – Ole V.V. Mar 26 '20 at 06:24