0

I've made a simple method which is used to convert a timestamp retrieved from a database into a LocalDate. However for some reason I keep getting the wrong day in the conversion. I've shared the code below.

    private LocalDate getLocalDateFromTimeStamp(Row row, String key){
        return LocalDate.parse(row.getTimestamp(key).toInstant().atZone(ZoneOffset.UTC).toLocalDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
}

So the date I'm expecting is 2022-12-21 but what I get is 2022-12-22.

When I debug and check what

row.getTimestamp(key)

gets me a Date object of Wed Dec 21 20:47:46 CST 2022 which is what I expect.

When I check what

row.getTimestamp(key).toInstant()

does, I get "2022-12-22T02:47:46.299Z". And I think this is where the problem is popping up and I'm not sure why it's happening. The LocalDate that's returned by the method is "2022-12-22".

If anyone could shine a light on this I'd really appreciate it as I'm lost as to why this is happening.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
skippy130
  • 303
  • 3
  • 12
  • What type does `getTimeStamp(key)` return? – WJS Feb 14 '23 at 20:27
  • @WJS It returns a Date object. – skippy130 Feb 14 '23 at 20:35
  • Which `Row` class is that? A homegrown one? I find it very confusing that `getTimestamp()` returns a `Date`. Which, BTW, is a poorly designed and long outdated class, so if you can upgrade the class to be able to return a modern type, do. I do realize that for now this would probably just move the cause of your error elsewhere. – Ole V.V. Feb 15 '23 at 06:21
  • The `Instant` is independent of time zone and is not he problem. What confused you was that it prints in UTC. Your error comes from doing `.atZone(ZoneOffset.UTC)`, which gives you the UTC date, December 22, which you don’t want. Instead you need `return row.getTimestamp(key).toInstant().atZone(ZoneId.systemDefault()).toLocalDate();`. When running in America/Chicago time zone it gives the desired `2022-12-21` (not knowing whether that is your time zone, just given as an example). – Ole V.V. Feb 15 '23 at 06:35
  • Does this answer your question? [Convert java.util.Date to java.time.LocalDate](https://stackoverflow.com/questions/21242110/convert-java-util-date-to-java-time-localdate) – Ole V.V. Feb 15 '23 at 06:40

2 Answers2

2

Try it like this. Check out DateTimeFormatter for details on the following arguments.

String date = "Wed Dec 21 20:47:46 CST 2022";
  • EEE three letter day of week
  • MMM three letter month
  • dd integer day
  • HH:mm:ss time using 24 hour clock
  • z time zone name (CST)
  • yyyy year
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss z yyyy");
LocalDate dt = LocalDateTime.parse(date, dtf).toLocalDate();
System.out.println(dt);

prints

2022-12-21

Updated

If you actually have an instance of Date you might try the following:

LocalDate ldt = LocalDate.ofInstant(date.toInstant(), 
    ZoneId.systemDefault());
WJS
  • 36,363
  • 4
  • 24
  • 39
  • Thank you that worked! If you have a second could you explain that format to be, the pattern you used to make the DateTimeFormatter? Also do you have any idea why the code I had would behave the way it does? I don't get why it would just change the day like that. – skippy130 Feb 14 '23 at 20:40
  • I'm not certain why it added the day unless it was adjusted based on the time zone to a different default local time. – WJS Feb 14 '23 at 20:47
  • One of life's mysteries I guess. Could you explain what the "EEE LLL" section in the format means? I'm unfamiliar with it. Or could you point me to a resource where I could learn a bit about it? – skippy130 Feb 14 '23 at 21:03
  • Is it possible to make it so that the date is not tied to a specific timezone as it is in this format? Meaning so that it can localize the time in whatever timezone it happens to be in? – skippy130 Feb 14 '23 at 22:05
  • 1
    *Or could you point me to a resource where I could learn a bit about it?* The [java.time](https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/time/package-summary.html) package replaces the outdated and buggy `Date` and related legacy classes. The `DateTimeFormatter` documentation is linked in my answer. – WJS Feb 14 '23 at 22:32
  • The OP has got an old-fashioned 'Date`. We don’t need to go through `toString()` and parsing again to convert to `LocalDate`. Also `LLL` in the format pattern string is wrong. It needs to be `MMM`. You also need to specify a locale for your formatter (in practice it will probably work if you correct only one of the two errors, but it will be confusing). – Ole V.V. Feb 15 '23 at 06:25
  • Your updated way is very good. I suggest you either push the bad way to the end of your question or leave it out. – Ole V.V. Feb 15 '23 at 06:52
  • *M/L for Number/Text* does *not* mean M for number and L for text. M and L both work for both numbers and text. The difference is (quote) *Pattern letters 'L', 'c', and 'q' specify the stand-alone form of the text styles.* While in English the stand-alone form of a month abbreviation should be no different, there are languages where it *is* different. A reader familiar with such a language will be confused about `LLL` with day-of-month. – Ole V.V. Feb 15 '23 at 12:30
  • 1
    @OleV.V. OK. Thanks for the explanation. I'll change it to M. – WJS Feb 15 '23 at 12:38
1

tl;dr

Avoid unnecessary string manipulation. Use date-time types for date-time values.

myResultSet
.getObject( … , OffsetDateTime.class )  // For any database column of a data type akin to the SQL standard type of `TIMESTAMP WITH TIME ZONE`.
.toLocalDate()  // Extract the date portion from the returned `OffsetDateTime` object. 
.toString()     // Generate text in standard ISO 8601 format. 

Details

The Timestamp class is part of the terrible date-time classes that are now legacy. Use only their replacements, the modern java.time classes defined in JSR 310.

Instead of Timestamp, use OffsetDateTime with JDBC 4.2 and later. Do this for any database column of a data type akin to the SQL standard type of TIMESTAMP WITH TIME ZONE.

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

Your Question is not clear, but you seem to want the date portion of that moment as seen with an offset from UTC of zero hours-minutes-seconds.

The retrieved OffsetDateTime is likely already in UTC. But let’s be sure:

OffsetDateTime odtUtc = odt.withOffsetSameInstant( ZoneOffset.UTC ) ;

Extract the date portion.

LocalDate localDate = odtUtc.toLocalDate() ;

To generate text in standard ISO 8601 format, call toString.

String output = localDate.toString() ;
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154