0

I want to convert string-presented date in custom format into ISO format. The first step is pretty easy:

val parsed = DateTimeFormatter.ofPattern(pattern).parse(input)

Now I want to present this parsed value in ISO string, the problem is that input value can be "13:35:23" with pattern "HH:mm:ss", and I want to be able to convert it to ISO as well, filling missed year/month/day etc with some default values, for example now(), so the resulting string will be for example 2020-07-09T13:35:23.000Z

Similar behaviour has toIsoString() method in JavaScript, if there are some ways to do that in Java?

P.S. Input can contain date/zone/ofset, so it is not only about parsing patterns like "HH:mm:ss"

CameronCoob
  • 81
  • 1
  • 11
  • 1
    You could parse a `LocalTime` from that `String`, take `LocalDate.now()` as the default date and then create an `OffsetDateTime` using them and adding a `ZoneOffset.UTC`. Afterwards, just print the `OffsetDateTime`. – deHaar Jul 09 '20 at 14:58
  • It will not work in case input contains date, for example "2020-07-09T22:48:12" – CameronCoob Jul 09 '20 at 19:48
  • In that case, parse a `LocalDateTime` and make it an `OffsetDateTime` by adding the mentioned `ZoneOffset.UTC`. – deHaar Jul 10 '20 at 06:07

3 Answers3

3

tl;dr

Instead of thinking in terms of one elaborate formatting pattern for parsing, think in terms of combining parts.

Here we get the current moment as seen in UTC. Then we move to your desired time-of-day.

OffsetDateTime.now( ZoneOffset.UTC ).with( LocalTime.parse( "13:35:23" ) ).toInstant().toString()         

Details

LocalTime

Parse your input appropriately.

LocalTime lt = LocalTime.parse( "13:35:23" ) ;

ZonedDateTime

Then combine with a date and time zone to determine a moment.

For any given moment, the date varies around the globe by time zone. So a time zone is crucial here.

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
LocalDate ld = LocalDate.now( z ) ;
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z ) ;

Instant

Adjust to UTC, an offset of zero hours-minutes-seconds, by extracting a Instant.

Instant instant = zdt.toInstant() ;

Generate your string output, in standard ISO 8691 format.

String output = instant.toString() ;

OffsetDateTime

If you want the date and your time input to be seen as being for UTC eprather than some other time zone, use ZoneOffset.UTC constant. Use OffsetDateTime rather than ZonedDateTime. Use with to use al alternate part, such as here where we substitute the current time-of-day part with your input time-of-day.

OffsetDateTime                      // Represent a moment as date,time, and offset -from-UTC (a number of hours-minutes-seconds).
.now( ZoneOffset.UTC )              // Capture current moment as seen in UTC.
.with( 
    LocalTime.parse( "13:35:23" )
)
.toInstant()                        // Extract the more basic `Instant` object, always in UTC by definition.
.toString()                         // Generate text representing the value of this date-time object. Use standard ISO 8601 format.

enter image description here

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • The problem is that I dont know whether input string will be just time or a time with date/zone/offset etc – CameronCoob Jul 09 '20 at 19:45
  • @CameronCoob (A) You should have said so in your Question. (B) Use try-catch in a series of attempts to parse. If `DateTimeParseException` is thrown, move on to the next attempt. If you exhaust attempts for all expected kinds of input, then you know you have faulty input. – Basil Bourque Jul 09 '20 at 20:34
2

If you used LocalTime.parse instead of DateTimeFormatter.parse directly, you would get a "local time" object, which you can then add to a "local date" giving you a date time:

LocalTime time = LocalTime.parse(input, DateTimeFormatter.ofPattern(pattern));
LocalDateTime dateTime = LocalDate.now().atTime(time)

You can then format dateTime in whatever format you want.

Joni
  • 108,737
  • 14
  • 143
  • 193
2

Use DateTimeFormatterBuilder to provide defaults.

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;

class Main {


    public static void main(String[] args)  {
        LocalDateTime now = LocalDateTime.now();
        
        DateTimeFormatter formatter =
                // builder for formatters
                new DateTimeFormatterBuilder()

                // append both patterns inclosed by []
                .appendPattern("[yyyy-MM-dd HH:mm:ss][HH:mm:ss]")
                
                // provide defaults for year, month and day
                .parseDefaulting(ChronoField.YEAR_OF_ERA, now.getYear())
                .parseDefaulting(ChronoField.MONTH_OF_YEAR, now.getMonthValue())
                .parseDefaulting(ChronoField.DAY_OF_MONTH, now.getDayOfMonth())
                
                // build the formatter
                .toFormatter();
        
        String a = "13:35:23";
        String b = "1234-01-01 13:35:23";
        
        System.out.println(LocalDateTime.parse(a, formatter));
        System.out.println(LocalDateTime.parse(b, formatter));
        
        System.out.println(formatter.parse(a));
        System.out.println(formatter.parse(b));
    }
}

akuzminykh
  • 4,522
  • 4
  • 15
  • 36