1

I seem to be getting different representations of Java's time stamp as follows:

en_US
2017-06-10 14:34:35.088
2017-06-10T18:34:35.102Z

with following invocations:

System.out.println(Locale.getDefault());
System.out.println(new Timestamp(System.currentTimeMillis()));
System.out.println(Instant.now());

What additional configurations will help to equalize the the 4 hr offset between time representations shown above? Thanks.

Simeon Leyzerzon
  • 18,658
  • 9
  • 54
  • 82
  • 1
    One is UTC, one is your local time zone. Which one do you prefer? – Marvin Jun 10 '17 at 18:54
  • I would prefer local but would like to see the code of how to normalize the offset both ways, if possible. – Simeon Leyzerzon Jun 10 '17 at 18:58
  • To give more context, I'm getting a UTC-based timestamp in a toString() implementation of a 3rd party library (which uses ```Instant.now())``` and would like to know if there are some global Java settings so that whatever that library returns is already converted into my local time, or should I write some extra logic in my own code to handle this conversion? Thank you. – Simeon Leyzerzon Jun 10 '17 at 19:08
  • 2
    I may be repeating myself here, sorry if it’s getting annoying. The extra logic in your own code is easy and clear and clearly preferred. – Ole V.V. Jun 10 '17 at 20:25

1 Answers1

2

One is your local time and one is UTC. This depends more on the way it is printed than on the actual (internal) value.

To have both shown equal, you can

  • Set your local time zone to UTC by passing -Duser.timezone=UTC to your VM.
  • Format the Instant to your local time zone:

    DateTimeFormatter formatter = 
    DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(ZoneId.systemDefault());
    System.out.println(formatter.format(Instant.now()));
    
  • Convert the Timestamp to an Instant:

    new Timestamp(System.currentTimeMillis()).toInstant();
    

Full code:

public static void main(String[] args) {
    DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(ZoneId.systemDefault());
    Timestamp timestamp = new Timestamp(System.currentTimeMillis());
    Instant instant = Instant.now();
    System.out.println("Timestamp: " + timestamp);
    System.out.println("Instant: " + instant);
    System.out.println("Instant to local: " + formatter.format(instant));
    System.out.println("Timestamp to UTC: " + timestamp.toInstant());
}

Will print out (in my local time zone):

Timestamp: 2017-06-10 21:17:13.935
Instant: 2017-06-10T19:17:13.935Z
Instant to local: 2017-06-10T21:17:13.935
Timestamp to UTC: 2017-06-10T19:17:13.935Z

So there is "some global setting", although it might not do what you want. An Instant will always be printed in UTC by default.

If you want to work with local dates only and you are not restricted to Instant, there are two* more options:

  • Convert the Instant to ZonedDateTime:

    ZonedDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());
    
  • Or skip the Instant altogether in favor of ZonedDateTime.now()

*Depending on your needs you can exchange ZonedDateTime with OffsetDateTime or LocalDateTime.


Edit: The magical way.

public static void main(String[] args) throws Exception {
    Field field = DateTimeFormatter.class.getField("ISO_INSTANT");
    field.setAccessible(true);
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    field.set(null, DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(ZoneId.systemDefault()));

    System.out.println(new Timestamp(System.currentTimeMillis()));
    System.out.println(Instant.now());
}

Use reflection to change DateTimeFormatter.ISO_INSTANT to a formatter that uses your local time. Might not work with a SecurityManager and might have one or another unwanted side effect. See e.g. Change private static final field using Java reflection for details.

Marvin
  • 13,325
  • 3
  • 51
  • 57
  • Is there a way to reset the VM default so that it applies the local timezone to handling Instant invocations instead of it using UTC by default? – Simeon Leyzerzon Jun 10 '17 at 19:26
  • 3
    May I suggest you use one of `LocalDateTime`, `OffsetDateTime` or `ZonedDateTime` instead? – Joe C Jun 10 '17 at 19:44
  • 2
    @SimeonLeyzerzon, if you mean you want `Instant.toString()` to produce your local time, the answer is no. You will have to live with the explicit formatting shown in the answer. – Ole V.V. Jun 10 '17 at 19:46
  • I would like to set the VM in such a way that an external framework which uses Instant.now() while creating its toString() representations defaults to using the zone that I will explicitly provide to it via such configuration. This way the output of this 3rd party lib will result in messages written in local time. Is that not possible? The Instant, according to its documentation, seems to be using internal Clock object, is there a way to set that Clock to local time on a global level, instead of using UTC all the time? – Simeon Leyzerzon Jun 10 '17 at 19:59
  • While you can provide a custom `Clock` object, `Instant` is literally hardcoded to UTC. – Marvin Jun 10 '17 at 20:18
  • 1
    You can, and it’s the recipe for a catastrophe: Tell your computer that it is in the UTC time zone and then incorrectly set its clock to New York time (or what your local time is) so that it really disagrees with UTC. Remember to adjust the clock manually at DST transitions. It’s lying to your computer and ultimately to yourself. I discourage it very, very much. – Ole V.V. Jun 10 '17 at 20:19
  • 2
    @OleV.V. See my update for a maybe-not-so-disruptive alternative. You can always do magic. Still figuring out what to do with the rest of my answer, though. – Marvin Jun 10 '17 at 20:23