SimpleDateFormat and not allowing it to go above 1

2019-09-22 07:03发布

问题:

So I have this method that I wanted to expand on to have the user input a valid 12 hour time. And I have it to this which works okay. But I want it so that if the hour is over 12 hours or the minutes is over 59 then it will prompt to do it again. But right now it will just convert the time by adding it. Also is there a more efficient way to write this? (Like without having the Date newTime = sdf.parse(startTime); And have it so that the user can just input a string and have it check if it's in the correct format?

public static void userInput(){
    Scanner in = new Scanner(System.in);
    SimpleDateFormat sdf = new SimpleDateFormat("hh:mm aa");
    String startTime;

    System.out.print("What is the start time?: ");
    boolean success = false;
    while (success != true){
        try{
        startTime = in.nextLine();
        Date newTime = sdf.parse(startTime);
        startTime = sdf.format(newTime);


        System.out.println(startTime);

        success = true;
        }
        catch(Exception e){
            System.out.println("Not a valid time. Please use this format (HH:MM AM)");
        }
    }
}

回答1:

You are experiencing SimpleDateFormat behaving as designed. It’s a behaviour that comes as a negative surprise to most.

There are two solutions: the recommended one and the discouraged one.

Recommended solution: LocalTime

        DateTimeFormatter timeFormat = DateTimeFormatter.ofPattern("hh:mm a", Locale.ROOT);
        try {
            LocalTime lt = LocalTime.parse(startTime, timeFormat);
            startTime = lt.format(timeFormat);
            System.out.println(startTime);
        } catch (DateTimeParseException e) {
            System.out.println("Not a valid time. Please use this format (HH:MM AM)");
        }

LocalTime and a bunch of other better designed and more programmer-friendly classes were introduced in Java 8. If you cannot use Java 8, there are again two solutions: (1) Resort to the discouraged solution, see below. (2) Use the backport of the Java 8 date and time classes to Java 6 and 7: ThreeTen Backport (I haven’t got experience with ThreeTen Backport).

In the code, please specify the correct locale instead of Locale.ROOT. I don’t know whether AM and PM may have other names in some locales, so I would want to make sure we are using a locale that agrees with the user input on this point.

Discouraged solution: setLenient()

    SimpleDateFormat sdf = new SimpleDateFormat("hh:mm aa");
    sdf.setLenient(false);

The SimpleDateFormat is lenient as default and accepts 09:63 as 10:03 and 14:00 AM as 02:00 PM. When you setLenient(false), it will no longer accept out-of-range values this way, but will throw a ParseException as you had expected.

Just checking if the format is correct

In either solution, the best way to check the format is what you are already doing: you try to parse it and catch the relevant exception type. Only don’t just catch Exception, be specific since exceptions may come of many other causes. That is, catch DateTimeParseException with the recommended solution and ParseException with the discouraged solution.

Also, if you want to store the time for use later, it is probably more convenient and object-oriented to store it as a LocalTime (or what class best reflects your needs) rather than String.