关于SimpleDateFormat的陷阱

July 15, 2010 – 12:36 am

代码中总是有那么多神奇的事情发生, 神奇的背后总是朴实无华的事实

1. SimpleDateFormat 是有 TimeZone的

1)每一个SimpleDateFormat的实例都会和一个TimeZone的实例相关联

2)在不指定TimeZone的情况下,SimpleDateFormat默认会使用OS的TimeZone

看下面的代码片段:

SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
Date date = sdf.parse("18-07-1976");
System.out.println(date.getTime());

这段代码展示的时间一般情况下是没问题的, 但是在两台不同TimeZone的OS上跑的时候获得的
时间是不一样的,在分布式环境中这个还是需要注意下的

  修复后的代码

方法一:在期盼的日期格式中带上TimeZone

SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy z");
Date date = sdf.parse("18-07-1976 ICT");
System.out.println(date.getTime());

方法二: 用指定的TimeZone来解释所有的时间

SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
Date date = sdf.parse("18-07-1976");

2. 验证时间字符串的合法性

  一般传入一个时间字符串, 我们会用异常的形式判断其合法性 如:

SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
Date d = null;
try {
    d = sdf.parse(dateStr);
} catch (ParseException e) {   
    showErrorMessage("Incorrect date format. "+e.getMessage());
}

1)传入 “10//10/2008″ 肯定会抛出异常
2)传入 “40/20/2009″ 能顺利parse, 结果是按照SimpleDateFormat自己的推测计算出来的结果,一般不是我们期盼的

能顺利parse的原因是SimpleDateFormat默认是lenient(宽容)的,它会尽可能正确的解释传入的时间字符串, 而我们一
般是不希望它这么去处理, 我们可以它的模式:

SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
sdf.setLenient(false);

这样SimpleDateFormt会按照严格的方式去处理时间了

3. 线程安全

SimpleDateFormat 是线程不安全的, 而创建一个SimpleDateFormat是比较耗资源的 。
 为了既能减少多次创建的开销,并且防止race condition . 看到老外搞了如下一段代码:

import java.lang.ref.SoftReference;
import java.text.*;
import java.util.Date;

public class DateConverter {
  private static final ThreadLocal<SoftReference<DateFormat>> tl
      = new ThreadLocal<SoftReference<DateFormat>>();

  private static DateFormat getDateFormat() {
    SoftReference<DateFormat> ref = tl.get();
    if (ref != null) {
      DateFormat result = ref.get();
      if (result != null) {
        return result;
      }
    }
    DateFormat result = new SimpleDateFormat("yyyy/MM/dd");
    ref = new SoftReference<DateFormat>(result);
    tl.set(ref);
    return result;
  }

  public void testConvert(String date) {
    try {
      DateFormat formatter = getDateFormat();
      Date d = formatter.parse(date);
      String newDate = formatter.format(d);
      if (!date.equals(newDate)) {
        System.out.println(date + " converted to " + newDate);
      }
    } catch (Exception e) {
      System.out.println(e);
    }
  }
}

参考资料:
 http://eyalsch.wordpress.com/2009/05/29/sdf/
 http://www.javaspecialists.eu/archive/Issue172.html

Post a Comment