在 Java 中日期、时间戳及字符串形式的日期等等之间的相互转换基本借助 SimpleDateFormat 类和 Date 类,也可以借助 joda-time 库。

日期时间转字符串形式

通过 SimpleDateFormat

如果日期时间是 java.util.Date 类型,步骤如下:

创建指定字符串格式形式的 java.text.SimpleDateFormat 类。

调用 java.text.SimpleDateFormat 类的 format 方法,将指定的时间实例(java.util.Date 类实例)传入方法。

示例:

import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo {
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
String dateStr = sdf.format(date);
System.out.println(dateStr);
}
}

通过 joda-time 库

利用第三方日期时间库 joda-time,步骤如下:

joda-time 可将 java.util.Date 或 java.util.Calendar 对象转换为 joda-time 的 org.joda.time.DateTime 类对象。

调用 org.joda.time.DateTime 类的 toString 方法,toString 方法支持日期时间格式转化(pattern)。

示例:

import org.joda.time.DateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
public class Demo {
public static void main(String[] args) {
Date date = new Date();
DateTime dateTime = new DateTime(date);
System.out.println(dateTime.toString("yyyy-MM-dd HH:mm:ss"));
System.out.println(dateTime.toString("yyyy年MM月dd日 HH:mm:ss EE", Locale.CHINESE));
Calendar calendar = Calendar.getInstance();
DateTime dateTime1 = new DateTime(calendar);
System.out.println(dateTime1.toString("yyyy/MM/dd HH:mm:ss,SSS"));
System.out.println(dateTime1.toString("yyyy-MM-dd hh:mm:ss a EE", Locale.US));
}
}

字符串转日期时间类型

通过 SimpleDateFormat

步骤如下:

创建指定字符串格式形式的 java.text.SimpleDateFormat 类。

调用 java.text.SimpleDateFormat 类的 parse 方法。

示例:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse("2019-07-28 22:39:13");
System.out.println(date);
// 转时间戳
System.out.println(date.getTime());
}
}

通过 joda-time 库

joda-time 库的日期时间转换都是围绕着 org.joda.time.DateTime 类,具体步骤如下:

利用 joda-time 库的 DateTimeFormat 和 DateTimeFormatter 将日期时间字符串转换成 org.joda.time.DateTime 类对象。

然后可以选择性地可将 org.joda.time.DateTime 类对象转换为 java.util.Date 类对象或者 java.util.Calendar 类对象。

示例:

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
public class Demo {
public static void main(String[] args) {
DateTimeFormatter format = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
DateTime dateTime = DateTime.parse("2017-08-12 04:05:00", format);
System.out.println(dateTime);
// 转换成 java.util.Date 类
Date date = dateTime.toDate();
System.out.println(date);
// 转换成 java.util.Calendar 类
Calendar calendar = dateTime.toCalendar(Locale.CHINA);
System.out.println(calendar);
}
}

线程安全形式

DateFormat 和 SimpleDateFormat 类都不是线程安全的,在多线程环境下调用 format() 和 parse() 方法应该使用同步代码来避免问题。

如下列出了 4 种解决方法。

需要时创建新实例

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
public static String format(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
public static Date parse(String strDate) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.parse(strDate);
}
}

使用同步方法

当线程较多时,当一个线程调用方法时,其他想要调用此方法的线程就会 block,多线程并发量大的时候会对性能有一定的影响。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static String format(Date date) {
synchronized (sdf) {
return sdf.format(date);
}
}
public static Date parse(String strDate) throws ParseException {
synchronized (sdf) {
return sdf.parse(strDate);
}
}
}

利用 ThreadLocal

使用 ThreadLocal,也是将共享变量变为独享,线程独享肯定能比方法独享在并发环境中能减少不少创建对象的开销。如果对性能要求比较高的情况下,一般推荐使用这种方法。

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
private static ThreadLocal threadLocal = new ThreadLocal() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
public static Date parse(String dateStr) throws ParseException {
return threadLocal.get().parse(dateStr);
}
public static String format(Date date) {
return threadLocal.get().format(date);
}
}

另一种写法:

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static ThreadLocal threadLocal = new ThreadLocal();
public static DateFormat getDateFormat() {
DateFormat df = threadLocal.get();
if (df == null) {
df = new SimpleDateFormat(DATE_FORMAT);
threadLocal.set(df);
}
return df;
}
public static String formatDate(Date date) {
return getDateFormat().format(date);
}
public static Date parse(String strDate) throws ParseException {
return getDateFormat().parse(strDate);
}
}

抛弃 JDK,使用第三方时间相关库

使用 Apache commons 里的 FastDateFormat,宣称是既快又线程安全的 SimpleDateFormat,可惜的是,它只能对日期进行 format,不能对日期串进行解析。

使用 Joda-Time 类库来处理时间相关问题。

总结

经过压力测试后,发现方法一最慢,方法三最快,但是就算是最慢的方法一性能也不差,一般系统方法一和方法二就可以满足,所以说在这个点上很难成为您系统的瓶颈所在。从简单的角度来说,建议使用方法一或者方法二,如果在必要的时候,追求那么一点性能提升的话,可以考虑用方法三,用 ThreadLocal 做缓存。

此外,Joda-Time 类库对时间处理方式也比较完美,建议使用。