2

I'm trying to convert a Brazil local date to UTC format. I have developed my solution but I'm sure that it can be improved. I have been searching for others questions but without success.

My problem is that when I process the Date object with:

Instant endDateTime = questionDate.toInstant();

I receive a UTC date as "2017-11-16T00:00:00Z" but this should be Brazil local date (not correct because it has a trailing "Z") and when I try to convert to UTC, I receive the same output.

In another hand, if I use ZoneDateTime class and build the date with LocalDateTime object I lose the seconds in the output: "2017-11-16T02:00Z". This happens when I use:

LocalTime.of(hour, minutes, seconds);

I search into LocalTime class and I think this is because minutes or seconds are 0 but I'm not sure of it.

Problems of the solution:

  1. The response hasn't seconds
  2. I hope Java 8 has a set of functions to make this more simple and clear

Precondition:

  • I can't use Joda library
  • Result has to be OffsetDateTime class
  • Input: Date "2017-11-16"
  • Output: "2017-11-16T02:00:00Z"

This is my solution:

private static OffsetDateTime processDate(Date questionDate) {
  Instant endDateTime = questionDate.toInstant();
  ZoneId zoneId = ZoneId.of(ZONEID);
  String [] date = endDateTime.toString().split("T");
  LocalDateTime localDateTime = convertLocalTimeToUtc(date);
  ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, zoneId);
  ZonedDateTime utcDate = zonedDateTime.withZoneSameInstant(ZoneOffset.UTC);
  return utcDate.toOffsetDateTime();
}

private static LocalDateTime convertLocalTimeToUtc(String[] dateFromCountry) {
  LocalDate date = processDate(dateFromCountry[0]);
  LocalTime time = processTime(dateFromCountry[1]);
  return  LocalDateTime.of(date, time);
}

private static LocalDate processDate(String dateFromCountry) {
  String [] partsOfDate = dateFromCountry.split("-");
  int year = Integer.parseInt(partsOfDate[0]);
  int month = Integer.parseInt(partsOfDate[1]);
  int day = Integer.parseInt(partsOfDate[2]);
  return LocalDate.of(year, month, day);
}

private static LocalTime processTime(String dateFromCountry) {
  String [] partsOfTime = dateFromCountry.split(":");
  int hour = Integer.parseInt(partsOfTime[0]);
  int minutes = Integer.parseInt(partsOfTime[1]);
  int seconds = Integer.parseInt(partsOfTime[2].substring(0,1));
  return LocalTime.of(hour,minutes,seconds);
}
Guido
  • 46,642
  • 28
  • 120
  • 174
  • Try the static method of OffsetDateTime class - ofInstant(Instant instant, ZoneId zone) returns the OffsetDateTime... – Grasshopper Nov 16 '17 at 16:11
  • @Grasshopper I tried but It returns `"2017-11-16T22:00-02:00"` . It's like it was taking the original date as UTC date. I'm looking for the opposite behavior – Guillermo Pastor Díez Nov 16 '17 at 16:28
  • 2
    What is your input? A java.util.Date or a java.sql.Date? Can you show the exact input (something like: `long millis = xxx; Date d = new Date(millis)`)? – assylias Nov 16 '17 at 16:51
  • 2
    And where do you get the date from? If from a database the problem could be solved by using a different column type. – assylias Nov 16 '17 at 17:00
  • Possible duplicate of [Java 8 Convert given time and time zone to UTC time](https://stackoverflow.com/questions/34605691/java-8-convert-given-time-and-time-zone-to-utc-time) – Ole V.V. Nov 16 '17 at 18:29

1 Answers1

6

If your input is a java.util.Date, you can get rid of all the string manipulation:

//simulate your input
Instant input = Instant.parse("2017-11-16T00:00:00Z");
Date d = Date.from(input);    

//transformation code starts here
Instant instant = d.toInstant();

ZonedDateTime localInstant = instant.atZone(ZoneOffset.UTC);
ZonedDateTime sameLocalInBrazil = utcInstant.withZoneSameLocal(ZoneId.of("Brazil/East"));
OffsetDateTime sameInstantUtc = sameLocalInBrazil.toOffsetDateTime()
                                      .withOffsetSameInstant(ZoneOffset.UTC);

This return an OffsetDateTime with value 2017-11-16T02:00Z as you expect.

Note that an OffsetDateTime has no formatting - so the object does know that its seconds are set to 0 but the default toString method doesn't print them. If you want to print it with seconds, you can use a formatter:

//Formatting
System.out.println(sameInstantUtc.format(DateTimeFormatter.ISO_INSTANT));

which prints 2017-11-16T02:00:00Z


If your input is a java.sql.Date, you can use a slightly different strategy:

LocalDate d = sqlDate.toLocalDate();
ZonedDateTime localInstant = d.atStartOfDay(ZoneOffset.UTC);

The rest of the code would be the same.

assylias
  • 321,522
  • 82
  • 660
  • 783