4

I have postgresql with TIMESTAMP field, which have Instant with date and time. For example: 2021-10-13T15:04:24.944921Z.

As you can see, there are 6 digits after comma - 944921. But what if I have more digits, for example: 2021-10-13T07:14:47.791616921Z. How can I correctly round such Instant to 2021-10-13T07:14:47.791617Z?

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
SlandShow
  • 443
  • 3
  • 13
  • I wouldn't focus on the textual representation, but what you're trying to do. It sounds like you're trying to round to an integer number of microseconds within the second. I suggest you use `Instant.getNano()` to find the number of nanoseconds within the second, use the `%` operator to find how the "nanosecond of microsecond" and subtract that many nanoseconds... – Jon Skeet Oct 13 '21 at 15:49
  • Or if you're actually *only* interested in the text representation, you could use an appropriate formatter that only formats as far as microseconds. It's somewhat unclear what you're trying to achieve. – Jon Skeet Oct 13 '21 at 15:50
  • @JonSkeet I thought about textual format parser. Looks like that java API not support such things with `truncatedTo(ChronoUnit.MICROS)` method. – SlandShow Oct 13 '21 at 15:52
  • Are you trying to parse, or format? Again, your question is pretty unclear - and I'd not spotted the `truncatedTo` method before, but that should make it easy. When you say "Looks like that java API not support such things" what exactly do you mean? The clearer you can make your question, including in terms of what you've tried and what happened, the better. – Jon Skeet Oct 13 '21 at 15:59
  • @JonSkeet about achievements. I have a timestamp in the database, which has a fixed number of digits after the comma in date&time format. It's really important, that such dates are automatically rounded. So, they rounded by 6 digits after comma, as in the example above. But JPA produces instants (date&time) with 9 digits after comma, not 6. And I want to round it. – SlandShow Oct 13 '21 at 16:02
  • It's important to separate the textual representation from the value being represented. It sounds like your database only stores data to a precision of microseconds - fine. Now, when you say "JPA produces instants" what *exactly* do you mean? Are you trying to truncate values once you've fetched them from the database? (If so, that sounds odd as if the database only stored to microseconds, there shouldn't be anything more than that anyway.) Or before storing them in the database? All of this is very unclear at the moment. But don't answer in comments - clarify the question itself. – Jon Skeet Oct 13 '21 at 16:12
  • @JonSkeet I'm sorry for answering in the comments. But how exactly does this information helpful in solving the problem if the question clearly describes the conditions and the problems? Yes, I may not have described the question as well because of the language barrier, but still, why describe so much detail? – SlandShow Oct 13 '21 at 18:02
  • The question *doesn't* clearly describe the conditions and the problem - otherwise I wouldn't be asking for clarification, and I suspect someone (whether me or someone else) would have answered it by now. – Jon Skeet Oct 13 '21 at 18:31

1 Answers1

8

The code is short but requires a bit of explanation. You are right, the best we have is the truncatedTo method, and it always rounds down. Instead we want the half-up rounding that we learned in school: If the 7th decimal is 4 or less, round down. If it is 5 or higher, round up.

To obtain this I first add 500 nanoseconds. Then truncate. If the 7th decimal was 4 or less, it is still 9 or less, so the digits before it have not been changed, and truncation will do the rounding-down that we want. If the 7th decimal was 5 or more, adding 500 nanoseconds will overflow it, the 6th decimal will be incremented by 1, and the 7th decimal will end up in the 0 through 4 range. Then truncation will effectively do the rounding up of the original value that we wanted.

    Instant sourceValue = Instant.parse("2021-10-13T07:14:47.791616921Z");
    Instant rounded = sourceValue.plusNanos(500).truncatedTo(ChronoUnit.MICROS);
    System.out.println(rounded);

Output is the desired:

2021-10-13T07:14:47.791617Z

Let’s try the edge cases too.

    Instant sourceValue = Instant.parse("2021-10-13T07:14:00.000000499Z");

2021-10-13T07:14:00Z

    Instant sourceValue = Instant.parse("2021-10-13T07:14:59.999999500Z");

2021-10-13T07:15:00Z

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161