基础代码
/**
* 时间转字符串
* @param date
* @param format
* @return
*/
public static String format(Date date, String format) {
SimpleDateFormat sdf = new SimpleDateFormat(format);
return sdf.format(date);
}
/**
* 字符串转时间
* @param dateStr
* @param format
* @return
*/
public static Date parse(String dateStr,String format){
SimpleDateFormat sdf = new SimpleDateFormat(format);
Date date=null;
try {
date = sdf.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
注意:
1.SimpleDateFormat是线程不安全。
原因
// 全局时间对象
protected Calendar calendar;
// Called from Format after creating a FieldDelegate
private StringBuffer format(Date date, StringBuffer toAppendTo,
FieldDelegate delegate) {
// Convert input date to time field list
// 重点是这个位置,calendar是一个全局对象这就导致了并发情况下
// 多线程设置的date会被覆盖
// 导致下面的subFormat解析时间时按照被覆盖的数据进行处理
calendar.setTime(date);
boolean useDateFormatSymbols = useDateFormatSymbols();
for (int i = 0; i < compiledPattern.length; ) {
int tag = compiledPattern[i] >>> 8;
int count = compiledPattern[i++] & 0xff;
if (count == 255) {
count = compiledPattern[i++] << 16;
count |= compiledPattern[i++];
}
switch (tag) {
case TAG_QUOTE_ASCII_CHAR:
toAppendTo.append((char)count);
break;
case TAG_QUOTE_CHARS:
toAppendTo.append(compiledPattern, i, count);
i += count;
break;
default:
// 他的代码太多了就不粘贴了 大家自己看下
subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
break;
}
}
return toAppendTo;
}
解决办法
1.最简单的解决方案我们可以把static去掉,这样每个新的线程都会有一个自己的sdf实例,从而避免线程安全的问题。然而,使用这种方法,在高并发的情况下会大量的new sdf以及销毁sdf,这样是非常耗费资源的。
2.下面是一个使用ThreadLocal解决sdf多线程问题的例子
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
*
* @version $Id$
*/
public class DateUtil {
/** 锁对象 */
private static final Object lockObj = new Object();
/** 存放不同的日期模板格式的sdf的Map */
private static Map<String, ThreadLocal<SimpleDateFormat>> sdfMap = new HashMap<String, ThreadLocal<SimpleDateFormat>>();
/**
* 返回一个ThreadLocal的sdf,每个线程只会new一次sdf
*
* @param pattern
* @return
*/
private static SimpleDateFormat getSdf(final String pattern) {
ThreadLocal<SimpleDateFormat> tl = sdfMap.get(pattern);
// 此处的双重判断和同步是为了防止sdfMap这个单例被多次put重复的sdf
if (tl == null) {
synchronized (lockObj) {
tl = sdfMap.get(pattern);
if (tl == null) {
// 只有Map中还没有这个pattern的sdf才会生成新的sdf并放入map
System.out.println("put new sdf of pattern " + pattern + " to map");
// 这里是关键,使用ThreadLocal<SimpleDateFormat>替代原来直接new SimpleDateFormat
tl = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
System.out.println("thread: " + Thread.currentThread() + " init pattern: " + pattern);
return new SimpleDateFormat(pattern);
}
};
sdfMap.put(pattern, tl);
}
}
}
return tl.get();
}
/**
* 是用ThreadLocal<SimpleDateFormat>来获取SimpleDateFormat,这样每个线程只会有一个SimpleDateFormat
*
* @param date
* @param pattern
* @return
*/
public static String format(Date date, String pattern) {
return getSdf(pattern).format(date);
}
public static Date parse(String dateStr, String pattern) throws ParseException {
return getSdf(pattern).parse(dateStr);
}
}
2.yyyy和YYYY的区别
Y表示的是本周所属的年份,Java语言中一周从周日开始,周六结束,假如本周跨年,那么这一周获取的年份都是第二年。所以正常情况下都是用y
3.HH和hh的区别
H是24小时制格式化时间
h是12小时制格式化时间
4.常见的时间表达式
以下来自SimpleDateFormat的注释
Date and Time Pattern | Result | yyyy.MM.dd G 'at' HH:mm:ss z | 2001.07.04 AD at 12:08:56 PDT | "EEE, MMM d, ''yy" | Wed, Jul 4, '01 | "h:mm a" | 12:08 PM | "hh 'o''clock' a, zzzz" | 12 o'clock PM, Pacific Daylight Time | "K:mm a, z" | 0:08 PM, PDT | "yyyyy.MMMMM.dd GGG hh:mm aaa" | 02001.July.04 AD 12:08 PM | "EEE, d MMM yyyy HH:mm:ss Z" | Wed, 4 Jul 2001 12:08:56 -0700 | "yyMMddHHmmssZ" | 010704120856-0700 | "yyyy-MM-dd'T'HH:mm:ss.SSSZ" 20 | 2001-07-04T12:08:56.235-0700 | "yyyy-MM-dd'T'HH:mm:ss.SSSXXX" | 2001-07-04T12:08:56.235-07:00 | "YYYY-'W'ww-u" | 2001-W27-3 |
5.SimpleDateFormat Locale 参数 区别
SimpleDateFormat s1 = new SimpleDateFormat("GGGG yyyy/MMMM/dd HH:mm:ss EEE aaa ?zzzz",Locale.CHINA); SimpleDateFormat s2 = new SimpleDateFormat("GGGG yyyy/MMMM/dd HH:mm:ss EEE aaa ?zzzz",Locale.US); ?
//结果
公元 2016/三月/27 23:32:10 星期日 下午 ?中国标准时间 AD 2016/March/27 23:32:10 Sun PM ?China Standard Time
String datdString1="Tue Feb 14 2017 14:06:32 GMT+0800";
String datdString2="Fri Mar 11 09:06:46 +0800 2022";
datdString1 = datdString1.replace("GMT", "").replaceAll("\\(.*\\)", "");
String pattern1 = "EEE MMM dd yyyy hh:mm:ss z";
String pattern2 = "EEE MMM dd hh:mm:ss z yyyy";
SimpleDateFormat format1 = new SimpleDateFormat(pattern1, Locale.ENGLISH);
SimpleDateFormat format2 = new SimpleDateFormat(pattern2, Locale.ENGLISH);
Date dateTrans1 = format1.parse(datdString1);
Date dateTrans2 = format2.parse(datdString2);
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(dateTrans1));
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(dateTrans2));
|