package com.dongguo.java8.date;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
/**
* @author Dongguo
* @date 2021/8/15 0015-13:38
* @description:java8之前时间格式化方式
*/
public class TestSimpleDateFormat {
public static void main(String[] args) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
Callable task = new Callable() {
@Override
public Date call() throws Exception {
return sdf.parse("20210816");
}
};
ExecutorService pool = Executors.newFixedThreadPool(10);
List results =new ArrayList();
for (int i = 0; i < 10; i++) {
//模拟并发SimpleDateFormat
results.add(pool.submit(task));
}
for (Future future:results) {
System.out.println(future.get());
}
}
}
运行会发生安全问题报错
Mon Aug 16 00:00:00 CST 2021
Mon Aug 16 00:00:00 CST 2021
Mon Aug 16 00:00:00 CST 2021
Mon Aug 16 00:00:00 CST 2021
Mon Aug 16 00:00:00 CST 2021
Mon Aug 16 00:00:00 CST 2021
Mon Aug 16 00:00:00 CST 2021
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NumberFormatException: multiple points
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at com.dongguo.java8.date.TestSimpleDateFormat.main(TestSimpleDateFormat.java:33)
Caused by: java.lang.NumberFormatException: multiple points
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
at java.text.DigitList.getDouble(DigitList.java:169)
at java.text.DecimalFormat.parse(DecimalFormat.java:2089)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2162)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at com.dongguo.java8.date.TestSimpleDateFormat$1.call(TestSimpleDateFormat.java:22)
at com.dongguo.java8.date.TestSimpleDateFormat$1.call(TestSimpleDateFormat.java:19)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Java8之前的解决方法:
使用ThreadLocal解决SimpleDateFormat线程安全问题
package com.dongguo.java8.date;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author Dongguo
* @date 2021/8/16 0016-8:35
* @description:
*/
public class DateFormatThreadLocal {
private static final ThreadLocal df = new ThreadLocal(){
protected DateFormat initialValue(){
return new SimpleDateFormat("yyyyMMdd");
}
};
public static Date convert(String source) throws ParseException {
return df.get().parse(source);
}
}
package com.dongguo.java8.date;
import org.junit.jupiter.api.Test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
/**
* @author Dongguo
* @date 2021/8/15 0015-13:38
* @description:java8之前时间格式化方式
*/
public class TestSimpleDateFormat {
public static void main(String[] args) throws Exception {
Callable task = new Callable() {
@Override
public Date call() throws Exception {
return DateFormatThreadLocal.convert("20210816");
}
};
ExecutorService pool = Executors.newFixedThreadPool(10);
List results =new ArrayList();
for (int i = 0; i < 10; i++) {
//模拟并发SimpleDateFormat
results.add(pool.submit(task));
}
for (Future future:results) {
System.out.println(future.get());
}
}
}
运行结果:
Mon Aug 16 00:00:00 CST 2021
Mon Aug 16 00:00:00 CST 2021
Mon Aug 16 00:00:00 CST 2021
Mon Aug 16 00:00:00 CST 2021
Mon Aug 16 00:00:00 CST 2021
Mon Aug 16 00:00:00 CST 2021
Mon Aug 16 00:00:00 CST 2021
Mon Aug 16 00:00:00 CST 2021
Mon Aug 16 00:00:00 CST 2021
Mon Aug 16 00:00:00 CST 2021
JAVA8之后可以使用
package com.dongguo.java8.date;
import org.junit.jupiter.api.Test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
/**
* @author Dongguo
* @date 2021/8/15 0015-13:38
* @description:java8时间格式化方式
*/
public class TestSimpleDateFormat {
public static void main(String[] args) throws Exception {
//java8 时间格式化方式
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
Callable task = new Callable() {
@Override
public LocalDate call() throws Exception {
return LocalDate.parse("20210816",dtf);
}
};
ExecutorService pool = Executors.newFixedThreadPool(10);
List results =new ArrayList();
for (int i = 0; i < 10; i++) {
//模拟并发SimpleDateFormat
results.add(pool.submit(task));
}
for (Future future:results) {
System.out.println(future.get());
}
}
}
使用 LocalDate、LocalTime、LocalDateTime
LocalDate, LocalTime, LocalDateTime类的实例是不可变的对象,分别表示使用ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。
注: ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法
//LocalDate, LocalTime, LocalDateTime
@Test
public void test1() {
//获得当前时间
LocalDateTime ldt1 = LocalDateTime.now();
System.out.println(ldt1);
//指定时间日期
LocalDateTime ldt2 = LocalDateTime.of(2021, 01, 01, 23, 59, 59);
System.out.println(ldt2);
//日期增加2年
LocalDateTime ldt3 = ldt1.plusYears(2);
System.out.println(ldt3);
//日期减2个月
LocalDateTime ldt4 = ldt1.minusMonths(2);
System.out.println(ldt4);
System.out.println(ldt1.getYear());//年
System.out.println(ldt1.getMonthValue());//月
System.out.println(ldt1.getDayOfMonth());//日
System.out.println(ldt1.getHour());//时
System.out.println(ldt1.getMinute());//分
System.out.println(ldt1.getSecond());//秒
}
运行结果:
2021-08-16T10:09:37.241
2021-01-01T23:59:59
2023-08-16T10:09:37.241
2021-06-16T10:09:37.241
2021
8
16
10
9
37
Instant 时间戳
用于“时间戳”的运算。它是以Unix元年(传统 的设定为UTC时区1970年1月1日午夜时分)开始 所经历的描述进行运算
//Instant
@Test
public void test2() {
Instant now = Instant.now();//默认获取UTC时区
System.out.println(now);//2021-08-16T02:13:27.666Z
OffsetDateTime odt = now.atOffset(ZoneOffset.ofHours(8));//相差8个时区,偏移8个小时
System.out.println(odt);
long milli = now.toEpochMilli();//获得毫秒值
System.out.println(milli);
Instant instant = Instant.ofEpochSecond(1000);//从Unix元年加上1000s
System.out.println(instant);
}
运行结果
2021-08-16T02:19:57.160Z
2021-08-16T10:19:57.160+08:00
1629080397160
1970-01-01T00:16:40Z
计算日期时间间隔Duration 和 Period
Duration:用于计算两个“时间”间隔 Period:用于计算两个“日期”间隔
Duration
//Duration:计算两个 时间 之间的间隔
@Test
public void test3() {
Instant start = Instant.now();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Instant end = Instant.now();
Duration between = Duration.between(start, end);
// System.out.println(between.getSeconds());//秒
System.out.println(between.toMillis());//毫秒
}
运行结果
1000
Period
//Period:计算两个 日期 之间的间隔
@Test
public void test4() {
LocalDate ld1 = LocalDate.of(2020, 1, 1);
LocalDate ld2 = LocalDate.now();
Period period = Period.between(ld1, ld2);
System.out.println(period.getYears());
System.out.println(period.getMonths());
System.out.println(period.getDays());
}
运行结果
1
7
15
时间校正器
TemporalAdjuster :时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。
TemporalAdjusters :该类通过静态方法提供了大量的常用TemporalAdjuster的实现。
@Test
public void test5() {
LocalDateTime now = LocalDateTime.now();
System.out.println(now);//2021-08-16T12:41:19.605
LocalDateTime date1 = now.withDayOfMonth(10);//指定天为10号
System.out.println(date1);//2021-08-10T12:41:19.605
LocalDateTime date2 = now.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));//下周的周日
System.out.println(date2);//2021-08-22T12:41:19.605
//自定义下一个工作日
LocalDateTime date3 = now.with((d) -> {
LocalDateTime ldt = (LocalDateTime) d;
DayOfWeek day = ldt.getDayOfWeek();
if (day.equals(DayOfWeek.FRIDAY)) {
return ldt.plusDays(3);//周五 增加3天
} else if (day.equals(DayOfWeek.SATURDAY)) {
return ldt.plusDays(2);//周六 增加2天
} else {
return ldt.plusDays(1);//其他 增加1天
}
});
System.out.println(date3);//2021-08-17T12:41:19.605
}
DateTimeFormatter 格式化时间/日期
@Test
public void test6() {
DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE;
LocalDateTime now = LocalDateTime.now();
System.out.println(now);//2021-08-16T12:57:59.397
String date1 = now.format(dtf);
System.out.println(date1);//2021-08-16
System.out.println("-----------------------");
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH::mm::ss");
String date2 = now.format(dtf2);
//String date3 = dtf2.format(now);
System.out.println(date2);//2021年08月16日 12::57::59
// System.out.println(date3);//2021年08月16日 12::57::59
System.out.println("-----------------------");
//将字符串日期转回LocalDateTime
LocalDateTime newDate = LocalDateTime.parse(date3,dtf2);
System.out.println(newDate);//2021-08-16T12:57:59
}
时区的处理
Java8 中加入了对时区的支持,带时区的时间为分别为:ZonedDate、ZonedTime、ZonedDateTime其中每个时区都对应着 ID,地区ID都为 “{区域}/{城市}”的格式 例如 :Asia/Shanghai 等 ZoneId:该类中包含了所有的时区信息 getAvailableZoneIds() : 可以获取所有时区时区信息 of(id) : 用指定的时区信息获取 ZoneId 对象
@Test
public void test7() {
//获得所有时区
Set zoneIds = ZoneId.getAvailableZoneIds();
zoneIds.forEach(System.out::println);
}
@Test
public void test8() {
//指定时区的时间
LocalDateTime ldt = LocalDateTime.now(ZoneId.of("US/Alaska"));
System.out.println(ldt);//2021-08-15T21:08:46.406
//指定时区的时间
LocalDateTime ldt2 = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));//2021-08-16T13:08:46.403
//返回指定时区的时间与某个时区相差时区的时间
ZonedDateTime zdt = ldt2.atZone(ZoneId.of("Chile/Continental"));
System.out.println(zdt);//2021-08-16T13:08:46.408-04:00[Chile/Continental]
}