NumberFormatException while parsing date with SimpleDateFormat.parse()

前端 未结 7 1304
我寻月下人不归
我寻月下人不归 2020-12-08 07:11

Have a function that creates a time-only Date object. (why this is required is a long story which is irrelevant in this context but I need to compare to some stuff in XML wo

相关标签:
7条回答
  • 2020-12-08 07:42

    The diagnosis in the accepted answer is correct. I am providing the modern answer: do use java.time, the modern Java date and time API, for your date and time work. In Java 7 too. SimpleDateFormat is notoriously troublesome, its lack of thread safety is only one of its many problems. So don’t use that class.

    OffsetTime.now() and ThreeTen Backport

    You want the current time only, though with an offset from UTC, if your format pattern is to be believed. We have got a method exactly for that in java.time, the modern Java date and time API. So no reason to format into a string and parse back.

        OffsetTime timeOnly = OffsetTime.now(ZoneId.systemDefault());
        System.out.println(timeOnly);
    

    When I ran the code just now in my time zone, Europe/Copenhagen, on jdk.1.7.0_67, the output was:

    06:21:55.419+01:00

    By the way this is also the XML format for the concept of a time with time zone. Are we done?

    The Date class you were returning is poorly designed and long outdated, so avoid it if you can. If you need one for a legacy API that you cannot afford to change just now, convert like this:

        Instant asInstant = LocalDate.of(1970, Month.JANUARY, 1)
                .atTime(timeOnly)
                .toInstant();
        Date oldfashionedDateObject = DateTimeUtils.toDate(asInstant);
    
        System.out.println("As java.util.Date: " + oldfashionedDateObject);
    

    As java.util.Date: Thu Jan 01 06:21:55 CET 1970

    Question: Does it work on Java 7?

    Environment: Java 7

    java.time just requires at least Java 6.

    • In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in. In this case use Date.from(asInstant) for converting from Instant to Date instead of the way shown in the code above.
    • In non-Android Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
    • On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.

    Links

    • Oracle tutorial: Date Time explaining how to use java.time.
    • Java Specification Request (JSR) 310, where java.time was first described.
    • ThreeTen Backport project, the backport of java.time to Java 6 and 7 (ThreeTen for JSR-310).
    • ThreeTenABP, Android edition of ThreeTen Backport
    • Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
    • Wikipedia article: ISO 8601
    0 讨论(0)
  • 2020-12-08 07:46

    Joda-Time

    FYI, the Joda-Time 2.3 library provides a class expressly for your purpose, time-only, without any date: LocalTime. And, it is thread-safe (immutable instances). Seems a much better option than manhandling the troublesome java.util.Date class.

    LocalTime localTime = new LocalTime();
    

    Dump to console…

    System.out.println( "localTime: " + localTime );
    

    When run…

    localTime: 16:26:28.065
    

    java.time

    Java 8 brings the new java.time package, inspired by Joda-Time, defined by JSR 310.

    In java.time, you will find a LocalTime class similar to the one in Joda-Time.

    0 讨论(0)
  • 2020-12-08 07:48

    While the correct answer is the one by Clockwork-Muse (the cause of the problems is the fact that SimpleDateFormat isn't thread safe) I just wanted to deliver another method of creating a time-only Date object:

    public static Date getCurrentTimeOnly() {
    
        Calendar rightNow = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
        int hour = rightNow.get(Calendar.HOUR_OF_DAY);
        int minute = rightNow.get(Calendar.MINUTE);
        int second = rightNow.get(Calendar.SECOND);
        int msecond = rightNow.get(Calendar.MILLISECOND);
    
        long millisSinceMidnight
                = (hour * 60 * 60 * 1000)
                + (minute * 60 * 1000)
                + (second * 1000)
                + (msecond);
        return new Date(millisSinceMidnight);
    }
    

    This method is somewhat more formally correct, i.e. it handles leap-seconds. It doesn't assume, like other methods, that all days since epoch has always had 24*60*60*1000 milliseconds in them.

    It doesn't however handle the case where the leap second is on the current day.

    0 讨论(0)
  • 2020-12-08 07:50

    You can use "sychronized" block to make it thread safe. Something like:

    synchronized (lastUpdatedFormat) {
                date = 
             lastUpdatedFormat.parse(lastUpdatedFormat.format(currentDate));
            }
    
    0 讨论(0)
  • 2020-12-08 07:54

    SimpleDateFormat is not thread safe. the following program will reproduce NumberFormatException while parsing string represented date to date object.

    public class MaintainEqualThreadsPatallel {
        static int parallelCount = 20;
        public static void main(String[] args) throws Exception {
            ExecutorService executorPool = Executors.newFixedThreadPool(parallelCount);
    
            int numberOfThreads = 150; // Total thread count = 150*2= 300.
            List<Future<Object>> futureReturns = new LinkedList<Future<Object>>();
            for (int i = 0; i < numberOfThreads; i++) {
                int uniqueRandomValues = uniqueRandomValues(1, 10);
    
                // Callable Thread - call()
                Future<Object> submit = executorPool.submit( new WorkerCallable(uniqueRandomValues) );
                futureReturns.add(submit);
    
                // Runnable Thread - run()
                executorPool.execute( new WorkerThread(uniqueRandomValues) );
            }
    
            // WorkerCallable: Blocking main thread until task completes.
            waitTillThreadsCompleteWork(futureReturns);
            // Terminate Pool threads in-order to terminate main thread
            executorPool.shutdown();
        }
        private static final SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
        public static Date numberFormatEx(Date date) throws ParseException { // synchronized
            String dateStr = sdf.format(date);
            Date dateParsed = sdf.parse(dateStr); // NumberFormatException: For input string: "186E.2186E2"
            System.out.println("Date :"+ dateParsed);
            return dateParsed;
        }
    
        protected void loopFunction(int repeatCount) {
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName +":START");
            for (int i = 1; i <= repeatCount; i++) {
                try {
                    System.out.println(threadName +":"+ i);
                    sleepThread(100);
    
                    numberFormatEx(new Date());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            System.out.println(threadName +":END");
        }
    
        public static void waitTillThreadsCompleteWork(List<Future<Object>> futureReturns) throws Exception {
            for (Future<Object> future : futureReturns) {
                int threadReturnVal = (int) future.get();
                System.out.println("Future Response : "+threadReturnVal);
            }
        }
    
        public static int uniqueRandomValues(int min, int max) {
            int nextInt = ThreadLocalRandom.current().nextInt(min, max);
            System.out.println("Random Vlaue : "+nextInt);
            return nextInt;
        }
        public void sleepThread(long mills) {
            try {
                Thread.sleep(mills);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    class WorkerThread extends MaintainEqualThreadsPatallel implements Runnable {
        int randomValue = 0;
        public WorkerThread(int randomValue) {
            this.randomValue = randomValue;
        }
    
        @Override
        public void run() {
            // As separate stack run() function doesn't accepts parameters, pass to constructor.
            loopFunction(randomValue);
        }
    }
    class WorkerCallable extends MaintainEqualThreadsPatallel implements Callable<Object> {
        int randomValue = 0;
        public WorkerCallable(int randomValue) {
            this.randomValue = randomValue;
        }
    
        public Object call() {
            // As separate stack run() function doesn't accepts parameters, pass to constructor.
            loopFunction(randomValue);
            return randomValue;
        }
    }
    

    NumberFormatException with different messages:

    java.lang.NumberFormatException: multiple points
    java.lang.NumberFormatException: For input string: ""
    java.lang.NumberFormatException: For input string: "186E.2"
    java.lang.NumberFormatException: For input string: "186E.2186E2"
    java.lang.NumberFormatException: For input string: "22200222E.222002224EE4"
    
    java.lang.NumberFormatException: For input string: "22200222E.222002224EE44"
        at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)
        at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
        at java.lang.Double.parseDouble(Double.java:538)
        at java.text.DigitList.getDouble(DigitList.java:169)
        at java.text.DecimalFormat.parse(DecimalFormat.java:2056)
        at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
        at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
        at java.text.DateFormat.parse(DateFormat.java:364)
    

    In Multi-Threading/Web Application with Multi-Requests concept parse function leads to NumberFormatException which can be handled using synchronized block.


    To overcome NumberFormatException on parse() function use any of the following scenarios.

    1. Separate Object: Every request/thread works on its own object.
    public static Date numberFormatEx(Date date) throws ParseException {
        SimpleDateFormat ObjInstance = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
        String dateStr = ObjInstance.format(date);
        Date dateParsed = ObjInstance.parse(dateStr);
        System.out.println("Date :"+ dateParsed);
        return dateParsed;
    }
    

    Unnecessary creating reusable object for each thread.

    1. Static Object synchronized block: Every request/thread shares the common object to perform operation. As multiple threads share same object at same time then the object data gets clear/overrride ""/"186E.2186E2" at some point and leads to error.
    static SimpleDateFormat objStatic = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
    public static synchronized Date numberFormatEx(Date date) throws ParseException {
        String dateStr = objStatic.format(date);
        Date dateParsed = objStatic.parse(dateStr); // NumberFormatException: For input string: "186E.2186E2"
        System.out.println("Date :"+ dateParsed);
        return dateParsed;
    }
    

    NOTE: In case of Memory management it better to use synchronized block with static object which is reusable.

    0 讨论(0)
  • 2020-12-08 07:56

    The likely cause is the fact that SimpleDateFormat isn't threadsafe, and you're referencing it from multiple threads. While extremely difficult to prove (and about as hard to test for), there is some evidence this is the case:

    1. .11331133EE22 - notice how everything is doubled
    2. 880044E.3880044E3 - same here

    You probably have at least two threads interleaving. The E was throwing me, I was thinking it was attempting to deal with scientific notation (1E10, etc), but it's likely part of the time zone.

    Thankfully, the (formatting) basic fix is simple:

    private static final String FORMAT_STRING = "HH:mm:ss.SSSZ";    
    
    public static Date getCurrentTimeOnly() {
    
        SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_STRING);
    
        String onlyTimeStr = formatter.format(new Date());
        return formatter.parse(onlyTimeStr);
    }
    

    There's a couple of other things you could be doing here, too, with a few caveats:

    1 - If the timezone is UTC (or any without DST), this is trivial

    public static Date getCurrentTimeOnly() {
    
        Date time = new Date();
    
        time.setTime(time.getTime() % (24 * 60 * 60 * 1000));
    
        return time;
    }
    

    2 - You're going to have trouble testing this method, because you can't safely pause the clock (you can change the timezone/locale). For a better time dealing with date/time in Java, use something like JodaTime. Note that LocalTime doesn't have a timezone attached, but Date only returns an offset in integer hours (and there are zones not on the hour); for safety, you need to either return a Calendar (with the full timezone), or just return something without it:

    // This method is now more testable.  Note this is only safe for non-DST zones
    public static Calendar getCurrentTimeOnly() {
    
        Calendar cal = new Calendar();
    
        // DateTimeUtils is part of JodaTime, and is a class allowing you to pause time!
        cal.setTimeInMillis(DateTimeUtils.currentTimeMillis() % (24 * 60 * 60 * 1000));
    
        return cal;
    }
    
    0 讨论(0)
提交回复
热议问题