常用的日期处理类有:SimpleDateFormat、FastDateFormat和Joda-Time,下面分别进行介绍。
SimpleDateFormat
SimpleDateFormat和FastDateFormat主要都是对时间的格式化
SimpleDateFormat在对时间进行格式化的方法format中,会先对calendar对象进行setTime的赋值,若是有多个线程同时操作一个SimpleDateFormat实例的话,就会对calendar的赋值进行覆盖,进而产生问题。
有三种方法可以解决这个问题:
1、在每次需要使用的时候,进行SimpleDateFormat实例的创建,这种方式会导致创建一些对象实例,占用一些内存,不建议这样使用。
2、使用同步的方式,在调用方法的时候加上synchronized,这样可以让线程调用方法时,进行加锁,也就是会造成线程间的互斥,对性能影响比较大。
3、使用ThreadLocal进行保存,相当于一个线程只会有一个实例,进而减少了实例数量,也防止了线程间的互斥,推荐使用这种方式。
代码实例
由于SimpleDateFormat是JDK标准库提供的,因此不需要引入任何的依赖。
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SimpleDateFormatTest {
public static void main(String[] args) {
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(format.format(System.currentTimeMillis()));
System.out.println(format.format(new Date()));
}
}
测试结果
2018-12-16 16:56:18
2018-12-16 16:56:18
单线程的场景下,可以满足基本的时间和日期格式化的需求,但是实际的工程场景下,如果使用不注意,可能会导致一些一些不安全的问题。具体参考参考资料3。
FastDateFormat
SimpleDateFormat来做Date到String的类型转换,建议使用Apache commons-lang中的FastDateFormat。因为JDK里自带的SimpleDateFormat存在线程不安全问题。
引入包依赖
首先需要保证引入的如下的包依赖:
org.apache.commons
commons-lang3
3.4
代码实例
附上测试的参考代码:
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.TimeZone;
public class FastDateFormatTest {
// 定义日期格式
private static final String PATTERN = "yyyy-MM-dd HH:mm:ss";
private static final TimeZone ZONE = TimeZone.getTimeZone("Asia/Shanghai");
// 格式化
private static final FastDateFormat format = FastDateFormat.getInstance(PATTERN, ZONE);
public static void main(String[] args) throws ParseException {
// 1. 时区对象 TimeZone
TimeZone timeZone = TimeZone.getDefault();
System.out.println("时区对象:" + timeZone.toString());
System.out.println("时区名称:" + timeZone.getDisplayName());
System.out.println("时区的ID:" + timeZone.getID());
System.out.println("相对于UTC的偏移量:" + timeZone.getOffset(System.currentTimeMillis()) + "ms");
// 2. 利用Date对象获取格式化的时间
System.out.println(format.format(new Date()));
// 可以使用DateFormatUtils类来操作,方法里面也是使用的FastDateFormat类来做的
System.out.println(DateFormatUtils.format(new Date(), PATTERN, ZONE));
// 3. 反解析时间为Date对象,如果因为格式错误,造成解析失败则会抛出 java.text.ParseException 异常
Date date = format.parse("2018-12-16 15:47:23");
System.out.println(format.format(date));
}
}
测试结果:
时区对象:sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
时区名称:中国标准时间
时区的ID:Asia/Shanghai
相对于UTC的偏移量:28800000ms
2018-12-16 16:39:03
2018-12-16 16:39:03
2018-12-16 15:47:23
更多FastDateFormat的使用方法可以参考资料2的官方接口方法使用文档。
Joda-Time
Joda-Time与以上两种有所区别,不仅仅可以对时间进行格式化输出,而且可以生成瞬时时间值,并与Calendar、Date等对象相互转化,极大的方便了程序的兼容性。
Joda-Time的类具有不可变性,因此他们的实例是无法修改的,就跟String的对象一样。这种不可变性体现在所有API方法中,这些方法返回的都是新的类实例,与原来实例不同。
因此,在后期的工程代码的编写中,如果涉及到较多的日期时间处理的化,推荐使用Joda-Time。
下面会结合一些具体的示例展示一些Joda-Time的使用方法。
引入包依赖
joda-time
joda-time
2.9.4
参考代码
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.LocalDate;
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 JodaTimeTest {
public static void main(String[] args) {
// 得到当前时间
Date currentDate = new Date();
DateTime dateTime = new DateTime(); // DateTime.now()
System.out.println(currentDate.getTime());
System.out.println(dateTime.getMillis());
// 指定某一个时间,如2016-08-29 15:57:02
Date oneDate = new Date(1472457422728L);
DateTime oneDateTime = new DateTime(1472457422728L);
DateTime oneDateTime1 = new DateTime(2016, 8, 29, 15, 57, 2, 728);
System.out.println(oneDate.toString());
System.out.println(oneDateTime.toString()); // datetime默认的输出格式为yyyy-MM-ddTHH:mm:ss.SSS
System.out.println(oneDateTime1.toString("yyyy-MM-dd HH:mm:ss.SSS")); // 直接就可以输出规定的格式
// DateTime和Date之间的转换
Date convertDate = new Date();
DateTime dt1 = new DateTime(convertDate);
System.out.println(dt1.toString());
Date d1 = dt1.toDate();
System.out.println(d1.toString());
// DateTime和Calendar之间的转换
Calendar c1 = Calendar.getInstance();
DateTime dt2 = new DateTime(c1);
System.out.println(dt2.toString());
Calendar c2 = dt2.toCalendar(null); // 默认时区Asia/Shanghai
System.out.println(c2.getTimeZone());
// 时间格式化
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
DateTime dt3 = DateTime.parse("2016-08-29 13:32:33", formatter);
System.out.println(dt3.toString());
// 若是不指定格式,会采用默认的格式,yyyy-MM-ddTHH:mm:ss.SSS,若被解析字符串只到年月日,后面的时分秒会全部默认为0
DateTime dt4 = DateTime.parse("2016-08-29T");
System.out.println(dt4.toString());
// 输出locale 输出2016年08月29日 16:43:14 星期一
System.out.println(new DateTime().toString("yyyy-MM-dd HH:mm:ss EE", Locale.CHINESE));
// 计算两个日期间隔的天数
LocalDate start = new DateTime().toLocalDate();
LocalDate end = new LocalDate(2016, 8, 25);
System.out.println(Days.daysBetween(start, end).getDays()); // 这里要求start必须早于end,否则计算出来的是个负数
// 相同的还有间隔年数、月数、小时数、分钟数、秒数等计算
// 类如Years、Hours等
// 对日期的加减操作
DateTime dt5 = new DateTime();
dt5 = dt5.plusYears(1) // 增加年
.plusMonths(1) // 增加月
.plusDays(1) // 增加日
.minusHours(1) // 减小时
.minusMinutes(1) // 减分钟
.minusSeconds(1); // 减秒数
System.out.println(dt5.toString());
// 判断是否闰月
DateTime dt6 = new DateTime();
DateTime.Property month = dt6.monthOfYear();
System.out.println(month.isLeap());
}
}
测试结果
1544950421339
1544950421359
Mon Aug 29 15:57:02 CST 2016
2016-08-29T15:57:02.728+08:00
2016-08-29 15:57:02.728
2018-12-16T16:53:41.522+08:00
Sun Dec 16 16:53:41 CST 2018
2018-12-16T16:53:41.542+08:00
sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
2016-08-29T13:32:33.000+08:00
2016-08-29T00:00:00.000+08:00
2018-12-16 16:53:41 星期日
-843
2020-01-17T15:52:40.585+08:00
false
参考资料
- FastDateFormat 时间戳转换
- FastDateFormat
- Apache Commons 系列简介 之 Lang