传统时间格式化的线程安全问
/**
* 测试传统时间格式化的线程安全问题
*/
public class TestSimpleDateFormat {
public static void main(String[] args) throws ExecutionException, InterruptedException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
Callable<Date> task = new Callable<Date>() {
@Override
public Date call() throws Exception {
return sdf.parse("20180805");
}
};
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<Date>> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
for (Future<Date> future : results) {
System.out.println(future.get());
}
}
}
运行会发生安全问题报错
Java8之前的解决方法:
public class DateFormatThreadLocal {
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyyMMdd");
}
};
public static Date convert(String source) throws ParseException {
return df.get().parse(source);
}
}
/**
* 测试传统时间格式化的线程安全问题
*/
public class TestSimpleDateFormat {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
Callable<Date> task = new Callable<Date>() {
@Override
public Date call() throws Exception {
// return sdf.parse("20180805");
return DateFormatThreadLocal.convert("20170805");
}
};
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<Date>> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
for (Future<Date> future : results) {
System.out.println(future.get());
}
pool.shutdown();
}
}
Java8的解决方法:
/**
* 测试传统时间格式化的线程安全问题
*/
public class TestSimpleDateFormat {
public static void main(String[] args) throws ExecutionException, InterruptedException {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
Callable<LocalDate> task = new Callable<LocalDate>() {
@Override
public LocalDate call() throws Exception {
return LocalDate.parse("20180805", dtf);
}
};
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<LocalDate>> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
for (Future<LocalDate> future : results) {
System.out.println(future.get());
}
pool.shutdown();
}
}
使用 LocalDate、LocalTime、LocalDateTime
LocalDate、LocalTime、LocalDateTime 类的实 例是不可变的对象,分别表示使用 ISO-8601日 历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息。
// LocalDate LocalTime LocalDateTime 使用方法类似
@Test
public void test1(){
LocalDateTime ldt = LocalDateTime.now();//获取当前时间
System.out.println(ldt);
System.out.println("------------------");
//指定时间
LocalDateTime ldf2 = LocalDateTime.of(2018, 01, 01, 00, 00, 59);
System.out.println(ldf2);
System.out.println("------------------");
//加时间
LocalDateTime ldt3 = ldt.plusYears(2);//加两年
System.out.println(ldt3);
System.out.println("------------------");
//减
LocalDateTime ldt4 = ldt.minusMonths(2);//减两个月
System.out.println(ldt4);
System.out.println("------------------");
//获取日期年月日时分秒
System.out.println(ldt.getYear());
System.out.println(ldt.getMonthValue());
System.out.println(ldt.getDayOfMonth());
System.out.println(ldt.getHour());
System.out.println(ldt.getMinute());
System.out.println(ldt.getSecond());
}
运行结果:
2018-08-05T22:58:32.333
------------------
2018-01-01T00:00:59
------------------
2020-08-05T22:58:32.333
------------------
2018-06-05T22:58:32.333
------------------
2018
8
5
22
58
32
Instant 时间戳
用于“时间戳”的运算。它是以Unix元年(传统 的设定为UTC时区1970年1月1日午夜时分)开始 所经历的描述进行运算
//Instant 时间戳
@Test
public void test2(){
Instant ins1 = Instant.now();//默认获取的UTC(本初子午线)时区
System.out.println(ins1);
OffsetDateTime odt1 = ins1.atOffset(ZoneOffset.ofHours(8));//偏移量运算 偏移8个小时
System.out.println(odt1);
System.out.println(ins1.toEpochMilli());//获取毫秒值 时间戳
}
2018-08-05T15:05:34.158Z
2018-08-05T23:05:34.158+08:00
1533481534158
计算日期时间间隔Duration 和 Period
Duration:用于计算两个“时间”间隔
Period:用于计算两个“日期”间隔
Duration
@Test
public void test3(){
Instant ins1 = Instant.now();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Instant ins2 = Instant.now();
Duration dur = Duration.between(ins1, ins2);
System.out.println(dur.toMillis());
System.out.println("------------------");
LocalTime lt1 = LocalTime.now();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LocalTime lt2 = LocalTime.now();
System.out.println(Duration.between(lt1,lt2).toMillis());
}
1001
------------------
1000
Period
@Test
public void test4(){
LocalDate ld1 = LocalDate.of(2018, 1, 1);
LocalDate ld2 = LocalDate.now();
Period per = Period.between(ld1, ld2);
System.out.println(per);//
System.out.println(per.getYears());
System.out.println(per.getMonths());
System.out.println(per.getDays());
}
P7M4D
0
7
4
时间校正器
//TemporalAdjuster 时间校正器
@Test
public void test5(){
LocalDateTime ldt1 = LocalDateTime.now();
System.out.println(ldt1);
LocalDateTime ldt2 = ldt1.withDayOfMonth(10);//当前月10号
System.out.println(ldt2);
LocalDateTime ldt3 = ldt1.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));//下周日
System.out.println(ldt3);
//自定义:下一个工作日
LocalDateTime ldt5 = ldt1.with((l) -> {
LocalDateTime ldt4 = (LocalDateTime) l;
DayOfWeek dow = ldt4.getDayOfWeek();
if (dow.equals(DayOfWeek.FRIDAY)){
return ldt4.plusDays(3);
}else if (dow.equals(DayOfWeek.SATURDAY)){
return ldt4.plusDays(2);
}else{
return ldt4.plusDays(1);
}
});
System.out.println(ldt5);
}
2018-08-05T23:42:46.459
2018-08-10T23:42:46.459
2018-08-12T23:42:46.459
2018-08-06T23:42:46.459
DateTimeFormatter 格式化时间/日期
@Test
public void test6(){
DateTimeFormatter dtf1 = DateTimeFormatter.ISO_DATE;
LocalDateTime ldt = LocalDateTime.now();
String strDate1 = ldt.format(dtf1);
System.out.println(strDate1);
System.out.println("------------------");
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
String strDate2 = dtf2.format(ldt);
System.out.println(strDate2);
LocalDateTime newDate = ldt.parse(strDate2, dtf2);
System.out.println(newDate);
}
2018-08-05
------------------
2018年08月05日 23:55:16
2018-08-05T23:55:16
时区的处理
Java8 中加入了对时区的支持,带时区的时间为分别为:ZonedDate、ZonedTime、ZonedDateTime其中每个时区都对应着 ID,地区ID都为 “{区域}/{城市}”的格式
例如 :Asia/Shanghai 等
ZoneId:该类中包含了所有的时区信息
getAvailableZoneIds() : 可以获取所有时区时区信息
of(id) : 用指定的时区信息获取 ZoneId 对象
@Test
public void test7(){
LocalDateTime ldt1 = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println(ldt1);
ZonedDateTime zdt1 = ldt1.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println(zdt1);
}
2018-08-06T00:01:13.727
2018-08-06T00:01:13.727+08:00[Asia/Shanghai]