The central problem is this: A java.sql.Timestamp
object represents an instant in time without timezone information.
Meaning, what you want is utterly impossible - your timestamp object just tracks a moment in time, and at some moment in time, it was January 5th 2022 in one locale, and january 6th in another. 'some moment in time' doesn't translate to a date. Not without localizing it to some timezone.
Given that it is java.sql.Timestamp
, 2 things seem obvious:
- You got this from a database.
- Likely that database represents some date/time reckoning where 'round to the nearest day even makes sense' (TIMESTAMP WITHOUT TIME ZONE certainly would not!!), and you used
.getTimestamp()
to get this value, which is incorrect - that's a common mistake, because the entire java.util.Date
hierarchy of classes are all incorrect and badly named (for example, Date represents a moment in time and not a date at all. Simply read the deprecated notice on e.g. .getYear()
for some proof).
This means what you need to really do is read the data out of the database properly. getDate
and getTimestamp
are all obsolete methods you should never use - because the types they return are broken. The right call is something like .getObject(idx, LocalDate.class)
, or .getObject(idx, ZonedDateTime.class)
. The JDBC spec (v5) guarantees that this will work, but best thing to do here is to check what your database types actually are, read up on what they actually represent, then pick the right type from the java.time
package, then try it to confirm this works.
If for some reason you're stuck with legacy code, then know that political timezone changes are GOING to happen and WILL break stuff (such as the most recent JDKs dropping pre-1970s support in the timezone files because tzdata project decided to do that, or countries simply switching timezones.. Which all of mainland europe is going to soon, so this isn't some 'yeah yeah never gonna happen' hypothetical!) - be prepare for weirdo errors where all of a sudden your 'nearest date' rounder starts rounding 11:20 in the morning up when you expected it to round down, that sort of thing.
Nevertheless, best to write as clean as you can, so, take your obsolete instant in time and turn it into the non-obsolete data type to represent instants in time, and from there, do your conversions:
Instant i = timestamp.toInstant(); // move away from obsolete types
ZonedDateTime zdt = i.atZone(ZoneId.of("Europe/Amsterdam");
// you'll need to know what the timezone is that is represented
// with the timestamp, as the timestamp type doesn't know.
// That, or, do as I said and avoid java.sql.Timestamp in the first place.
LocalDateTime ldt = zdt.toLocalDateTime();
// now, what does 'round' mean? Days do not neccessarily have 24 hours.
// They can have 23, 25, or in rare cases, even weirder values.
// Perhaps it is more sensible to say, 12 noon and up rounds up,
// instead of going for e.g. 11:30 or 12:30 on daylight savings switch days.
LocalDate answer = ldt.toLocalDate();
if (ldt.getHour() > 11) answer = answer.plusDays(1);
return answer;
If you actually want 'nearest', even though that is kinda weird, there is no baked in method to do this. Remaining in ZonedDateTime
land, truncate to the nearest day, add a day, take the average between the two, then check whether you're past this midpoint or not to know if you need to add a day.