Java中的时间处理

时间:2022-03-25 00:42:04
  1. 日期时间组件使用

    java.util.Date:实现类,其对象具有时间、日期组件。
    java.util.Calendar:抽象类,其对象具有时间、日期组件。
    java.sql.Date:实现类,其对象具有日期组件。
    java.sql.Time:实现类,其对象具有时间组件。
    java.sql.Timestamp:实现类,其对象具有时间日期组件。
    java.text.DateFormat:抽象类,其对象格式化时间日期。
    java.text.DateFormatSymbols:实现类,其对象为格式化时间日期提供参数。

  2. long currentTime = System.currentTimeMillis(); //获取当前时间戳,从1970-01-01以来的毫秒数
    LOGGER.info("{}", currentTime);

    java.util.Date utilDate
    = new java.util.Date(currentTime);
    LOGGER.info(
    "{}|{}", utilDate, utilDate.getTime());

    Calendar calendar
    = Calendar.getInstance(); //除某些特殊地区外,默认等效于new GregorianCalendar()
    calendar.setTimeInMillis(currentTime);
    LOGGER.info(
    "{}", calendar);

    java.sql.Date sqlDate
    = new java.sql.Date(currentTime);
    LOGGER.info(
    "{}|{}", sqlDate, sqlDate.getTime());

    java.sql.Time sqlTime
    = new java.sql.Time(currentTime);
    LOGGER.info(
    "{}|{}", sqlTime, sqlTime.getTime());

    java.sql.Timestamp sqlTimestamp
    = new java.sql.Timestamp(currentTime);
    LOGGER.info(
    "{}|{}", sqlTimestamp, sqlTimestamp.getTime());

            DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //默认是SimpleDateFormat
    LOGGER.info("{}", dateFormat.format(utilDate));
    LOGGER.info("{}", dateFormat.parse("2017-05-25 23:00:00"));

    输出:

    1495724884755 
    Thu May
    25 23:08:04 CST 2017|1495724884755
    java.util.GregorianCalendar[time
    =1495724884755,...,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai"...]
    2017-05-25|1495724884755
    23:08:04|1495724884755
    2017-05-25 23:08:04.755|1495724884755
    2017-05-25 23:08:04
    Thu May
    25 23:00:00 CST 2017
  3. java.text.SimpleDateFormat的线程不安全问题
    问题重现:

            //final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    Callable
    <Date> task = new Callable<Date>() {
    @Override
    public Date call() throws Exception {
    LOGGER.info(
    "executing task...");
    try{
    return new SimpleDateFormat("yyyy-MM-dd").parse("2017-05-25");
    //return format.parse("2017-05-25"); //if use shared SimpleDateFormat, executing result is not expected.
    }catch (Exception ex){
    LOGGER.error(
    "Exception:", ex);
    return new Date(0);
    }
    }
    };
    // pool with 5 threads
    ExecutorService exec = Executors.newFixedThreadPool(5);
    List
    <Future<Date>> results = new ArrayList<Future<Date>>();
    // perform 10 date conversions
    for (int i = 0; i < 10; i++) {
    LOGGER.info(
    "submit task[{}]", i);
    results.add(exec.submit(task));
    }
    exec.shutdown();
    LOGGER.info(
    "shutdown");
    // look at the results
    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    for (Future<Date> result : results) {
    LOGGER.info(
    "{}", dateFormat.format(result.get()));
    }

    LOGGER.info("completed");

    输出(使用共享的SimpleDateFormat时,执行结果并不是预期结果):

    Java中的时间处理Java中的时间处理
    submit task[0] 
    submit task[
    1]
    submit task[
    2]
    executing task...
    submit task[
    3]
    executing task...
    submit task[
    4]
    executing task...
    executing task...
    submit task[
    5]
    executing task...
    submit task[
    6]
    submit task[
    7]
    submit task[
    8]
    submit task[
    9]
    shutdown
    executing task...
    executing task...
    executing task...
    executing task...
    executing task...
    Exception:
    java.lang.NumberFormatException: multiple points
    at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:
    1890)
    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)
    at com.liq.DateTimeTest$
    1.call(DateTimeTest.java:65)
    at com.liq.DateTimeTest$
    1.call(DateTimeTest.java:59)
    at java.util.concurrent.FutureTask.run(FutureTask.java:
    266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:
    1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:
    617)
    at java.lang.Thread.run(Thread.java:
    745)
    Exception:
    java.lang.NumberFormatException: empty String
    at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:
    1842)
    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)
    at com.liq.DateTimeTest$
    1.call(DateTimeTest.java:65)
    at com.liq.DateTimeTest$
    1.call(DateTimeTest.java:59)
    at java.util.concurrent.FutureTask.run(FutureTask.java:
    266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:
    1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:
    617)
    at java.lang.Thread.run(Thread.java:
    745)
    Exception:
    java.lang.NumberFormatException: For input string:
    "E.2505E2"
    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)
    at com.liq.DateTimeTest$
    1.call(DateTimeTest.java:65)
    at com.liq.DateTimeTest$
    1.call(DateTimeTest.java:59)
    at java.util.concurrent.FutureTask.run(FutureTask.java:
    266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:
    1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:
    617)
    at java.lang.Thread.run(Thread.java:
    745)
    Exception:
    java.lang.NumberFormatException: For input string:
    "E.2505"
    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)
    at com.liq.DateTimeTest$
    1.call(DateTimeTest.java:65)
    at com.liq.DateTimeTest$
    1.call(DateTimeTest.java:59)
    at java.util.concurrent.FutureTask.run(FutureTask.java:
    266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:
    1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:
    617)
    at java.lang.Thread.run(Thread.java:
    745)
    1970-01-01 08:00:00
    1970-01-01 08:00:00
    1970-01-01 08:00:00
    1970-01-01 08:00:00
    2017-05-25 00:00:00
    2017-05-25 00:00:00
    2017-05-25 00:00:00
    2017-05-25 00:00:00
    2017-05-25 00:00:00
    2017-05-25 00:00:00
    completed
    View Code

    产生原因:
    SimpleDateFormat维护一个共享的Calendar对象,如果共享SimpleDateFomat时这个Calendar也是共享的。由多个线程并发进行操作就会有问题了。

    protected Calendar calendar;

    解决:
    (1)使用局部变量
    (2)使用 ThreadLocal

            final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
    @Override
    protected DateFormat initialValue() {
    return new SimpleDateFormat("yyyy-MM-dd");
    }
    };
    //final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    Callable
    <Date> task = new Callable<Date>() {
    @Override
    public Date call() throws Exception {
    LOGGER.info(
    "executing task...");
    try{
    //return new SimpleDateFormat("yyyy-MM-dd").parse("2017-05-25");
    //return format.parse("2017-05-25"); //if use shared SimpleDateFormat, executing result is not expected.
    return df.get().parse("2017-05-25");
    }
    catch (Exception ex){
    LOGGER.error(
    "Exception:", ex);
    return new Date(0);
    }
    }
    };

    (3)同步代码块 synchronized(code)
    (4)使用第三方的日期处理函数:比如使用 commons-lang 包中的 FastDateFormat 工具类

  4.  

参考:

  1. 如何在Java 8中愉快地处理日期和时间
  2. Java中的时间日期处理
  3. 详解SimpleDateFormat的线程安全问题与解决方案
  4. java中关于时间的处理 *
  5. 日历字段之间的转换