0

I am currently getting an IllegalArgumentException thrown at these lines of code:

Time timeIn = Time.valueOf( res.getCheckInTime().toString());
Time timeOut = Time.valueOf( res.getCheckOutTime().toString());

Both arguments are LocalTime storing the local time such as 11:05:

  • res.getCheckInTime()
  • res.getCheckOutTime()

Can anyone help me out here?

hc_dev
  • 8,389
  • 1
  • 26
  • 38
Ali
  • 11
  • 1
  • can you share what res.getCheckInTime().toString() prints out? – hovanessyan Dec 29 '21 at 18:38
  • 11:05 in string format – Ali Dec 29 '21 at 18:39
  • well as already mentioned - you are probably hitting this else snippet - https://github.com/openjdk/jdk/blob/master/src/java.sql/share/classes/java/sql/Time.java#L108 – hovanessyan Dec 29 '21 at 18:41
  • From JavaDoc the instance itself stores nanosecond precision, but [`LocalTime.toString()`](https://docs.oracle.com/javase/8/docs/api/java/time/LocalTime.html#toString--) may __omit zeros for seconds__ ("format used will be the shortest that outputs the full value of the time where the omitted parts are implied to be zero"). So it may not satisfy `Time.valueOf` requirements. – hc_dev Dec 29 '21 at 20:16
  • By the way, `java.sql.Time` is part of the terribly flawed date-time classes bundled with the earliest versions of Java. Those classes, including `Time`, were years ago supplanted by the modern *java.time* classes defined in JSR 310. If your methods are already using `java.time.LocalTime`, you should generally have no reason to convert to `Time`. – Basil Bourque Dec 29 '21 at 23:38

3 Answers3

2

Why do you need to use res.getCheckInTime().toString()? Couldn't use the Time.valueOf(LocalTime s) method?

See the following example:

 public static void main(String[] args) {
     LocalTime checkInTime = LocalTime.now();
     LocalTime checkOutTime = LocalTime.now().plusHours(12);

     Time timeIn = Time.valueOf(checkInTime);
     Time timeOut = Time.valueOf(checkOutTime);

     System.out.println("timeIn => " + timeIn);
     System.out.println("timeOut => " + timeOut);
 }

To convert a java.time.LocalTime object to java.sql.Time:

Time t = Time.valueOf( myLocalTime ) ;

To go the other direction, from legacy class java.sql.Time to modern class java.time.LocalTime:

LocalTime lt = t.toLocalTime() ;

No strings involved.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • @Basil your addition illustrates a helpful reversal. Recognized a subtle detail: Why did you attribute the `java.sqlTime` as_legacy class_. – hc_dev Dec 30 '21 at 00:19
  • 1
    @hc_dev Because Sun, Oracle, and the JCP community gave up on those classes with the unanimous adoption of [JSR 310](https://jcp.org/en/jsr/detail?id=310). The *java.time* classes replace entirely the functionality of the legacy date-time classes. No need to ever use Time, Timestamp, Date, Calendar, SimpleDateFormat, etc. – Basil Bourque Dec 30 '21 at 00:32
1

TL;DR: use the safe & concise Time.valueOf(LocalTime) as suggested by Samuel Cruz, e.g. Time.valueOf( res.getCheckInTime() ).

Pitfalls when using String-based conversion

The String-variant has 2 pitfalls:

  1. LocalTime.toString() produces variable output-formats that may cause given IllegalArgumentException when used with Time.valueOf(String) - two implied assumptions: (a) non-deterministic output causes (b) message-less, thus not very helpful, exception that let's the user guess which value was passed as argument and why it was illegal.

  2. Also the JavaDoc for Time.valueOf(String) as quoted by Jim Garrison is misleading. In current implementation (OpenJDK) you can pass any other integer-sequence separated by 2 colons, like -33:9999:88. Although not a valid ISO-time in format HH:mm:ss, it is apparently interpreted as time-value and may lead to serious side-effects! Did you expect "13:40:28" == Time.valueOf("-33:9999:88") as result ?

time.LocalTime has non-deterministic toString format

Don't use LocalTime.toString() because it's output format is non-deterministic and may vary in precision, worst-case omitting seconds! It tries to reduce the output to significant parts, minimum format is "HH:mm" as in JavaDoc summary.

For reliable and SQL-compliant String-format use LocalTime.format(DateTimeFormatter.ISO_LOCAL_TIME). It may always produce format like HH:mm:ss as required by java.sql.Time.

java.sql.Time can convert LocalTime directly

It should work when passing the argument to factory-method java.sql.Time.valueOf():

  • either as ISO-formatted time String("11:05:00") (up to seconds!). ⚠️ Warning: Potential side-effects if passed String contains invalid ISO-time, e.g. "99:123:88" or even "0:-1:-3" (see example below).
  • or directly as java.time.LocalTime. ✔️ Recommended as safest.

Solution: illustrates argument variants & side-effects

    public static void main(String[] args) throws java.lang.Exception {
        Reservation res = new Reservation("11:05", "18:05");
        System.out.println(res);

        System.out.printf("toString returns '11:05:00' as expected? %s\n", "11:05:00".equals(res.getCheckInTime()));
        String isoTimeFormat = res.getCheckOutTime().format(DateTimeFormatter.ISO_LOCAL_TIME);
        System.out.printf("explicit format() returns '%s' as expected by SQL? %s\n", isoTimeFormat, "18:05:00".equals(isoTimeFormat));
        
        Time timeIn = Time.valueOf(res.getCheckInTime()); // recommended argument type
        Time timeOut = Time.valueOf(res.getCheckOutTime().format(DateTimeFormatter.ISO_LOCAL_TIME));
        Time timeTest = Time.valueOf("33:99:88"); // dangerous fun-fact

        System.out.println("sql.Time in: " + timeIn + " , out: " + timeOut);
    }

    static class Reservation {
        LocalTime checkIn;
        LocalTime checkOut;

        public Reservation(String in, String out) {
            this.checkIn = LocalTime.parse(in);
            this.checkOut = LocalTime.parse(out);
        }

        public LocalTime getCheckInTime() {
            return checkIn;
        }

        public LocalTime getCheckOutTime() {
            return checkOut;
        }

        public String toString() {
            return String.format("Reservation {checkIn: %s, checkOut: %s}", getCheckInTime(), getCheckOutTime());
        }
    }

Try runnable demo on IDEone.

See also:

hc_dev
  • 8,389
  • 1
  • 26
  • 38
0

Javadoc:

public static Time valueOf​(String s)

Converts a string in JDBC time escape format to a Time value.

Parameters:
    s - time in format "hh:mm:ss"
Returns:
    a corresponding Time object 

The only accepted format is hh:mm:ss

Jim Garrison
  • 85,615
  • 20
  • 155
  • 190
  • so I tried this too... Time timeIn = Time.valueOf( res.getCheckInTime().toString() + ":00"); and it still threw the exception – Ali Dec 29 '21 at 18:39
  • @Fred - can you write a reproducible test case in which you pass the string literal "11:05:00" to Time.valueOf() and see if you still get the same exception? If yes - try to attach a debugger to see the exact place in Time's class source code where the exception is thrown – hovanessyan Dec 29 '21 at 18:47
  • @Fred, Don't use `LocalTime.toString()` because it's output format is __non-deterministic__ and may vary in precision, worst-case omitting seconds! For reliable and SQL-compliant String-format use [` LocalTime.format(DateTimeFormatter.ISO_LOCAL_TIME)`](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ISO_LOCAL_TIME). – hc_dev Dec 29 '21 at 20:29