1
由于传统时间格式化存在很多线程安全问题,Java8更新全新的时间API
使用LocalDate、LocalTime、LocalDateTime
LocalDate、LocalTime、LocalDateTime 类的实例是不可变的对象,
分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。
它们提供了简单的日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息。
1.1 引出线程安全问题
public static void main(String[] args) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
Callable<Date> task = new Callable<Date>() {
@Override
public Date call() throws Exception {
return sdf.parse("20161121");
}
};
// 创建一个长度为10的线程池
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();
}
出现线程安全问题
1.2 传统方式解决线程安全问题
上锁
public class DateFormatThreadLocal {
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
protected DateFormat initialValue(){
return new SimpleDateFormat("yyyyMMdd");
}
};
public static final Date convert(String source) throws ParseException{
return df.get().parse(source);
}
}
//解决多线程安全问题
Callable<Date> task = new Callable<Date>() {
@Override
public Date call() throws Exception {
return DateFormatThreadLocal.convert("20161121");
}
};
1.3 Java8的方式解决
public static void main(String[] args) throws Exception {
// 这是java8提供
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
Callable<LocalDate> task = new Callable<LocalDate>() {
// 这里是产生一个新的实例,解决线程不安全
@Override
public LocalDate call() throws Exception {
LocalDate ld = LocalDate.parse("20161121", dtf);
return ld;
}
};
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();
}
2 本地时间与时间戳
使用
LocalDate、LocalTime、LocalDateTime
应用场景一:人读的时间
// 人读的时间
@Test
public void test() {
LocalDateTime ldt = LocalDateTime.now();
System.out.println("获取当前系统时间:" + ldt);
LocalDateTime of = LocalDateTime.of(2015, 10, 19, 13, 22, 33);
System.out.println(of);
// 加时间操作
LocalDateTime localDateTime = ldt.plusYears(2);
System.out.println(localDateTime);
// 减时间操作
LocalDateTime localDateTime1 = ldt.minusMonths(2);
System.out.println(localDateTime1);
System.out.println(ldt.getYear());
System.out.println(" 当前月份的第几日:"+ldt.getDayOfMonth());
System.out.println("当前几月份:"+ldt.getMonthValue());
}
应用场景二:机器读的时间 – 时间戳
//2. Instant : 时间戳。 (使用 Unix 元年 1970年1月1日 00:00:00 所经历的毫秒值)
@Test
public void test2(){
Instant ins = Instant.now(); //默认使用 UTC 时区
System.out.println(ins);
OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8));
System.out.println(odt);
System.out.println(ins.getNano());
Instant ins2 = Instant.ofEpochSecond(5);
System.out.println(ins2);
}
应用场景三:计算时间之间的间隔
Duration : 用于计算两个“时间”间隔
Period : 用于计算两个“日期”间隔
@Test
public void test2(){
Instant ins1 = Instant.now();
try {
// 手动加上间隔时间
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Instant ins2 = Instant.now();
Duration duration = Duration.between(ins1, ins2);
System.out.println("间隔的时间:"+duration.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());
}
LocalDate ld1 = LocalDate.now();
LocalDate ld2 = LocalDate.of(2011, 1, 1);
Period pe = Period.between(ld2, ld1);
System.out.println("间隔的年:"+pe.getYears());
System.out.println("间隔的月:"+pe.getMonths());
System.out.println("间隔的天:"+pe.getDays());
3 时间矫正器
日期的操纵
TemporalAdjuster : 时间校正器。
有时我们可能需要获取例如:将日期调整到“下个周日”等操作。
TemporalAdjusters: 该类通过静态方法提供了大量的常用 TemporalAdjuster 的实现。
例如获取下一个周日:
LocalDateTime nextSunday = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
//4. TemporalAdjuster : 时间校正器
@Test
public void test4(){
LocalDateTime ldt = LocalDateTime.now();
System.out.println("当前时间:"+ldt);
LocalDateTime ldt2 = ldt.withDayOfMonth(10);
System.out.println("指定月的日期为10:"+ldt2);
// 时间校正器 -- 进行特殊操作
LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println("下一个周日:"+ldt3);
//自定义:下一个工作日
LocalDateTime ldt5 = ldt.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);
}
4 时间格式化与时区的处理
解析与格式化
java.time.format.DateTimeFormatter类:该类提供了三种
格式化方法:
⚫ 预定义的标准格式
⚫ 语言环境相关的格式
⚫ 自定义的格式
时区的处理
Java8 中加入了对时区的支持,带时区的时间为分别为:
ZonedDate、ZonedTime、ZonedDateTime
其中每个时区都对应着 ID,地区ID都为 “{区域}/{城市}”的格式
例如 :Asia/Shanghai 等
ZoneId:该类中包含了所有的时区信息
getAvailableZoneIds() : 可以获取所有时区时区信息
of(id) : 用指定的时区信息获取ZoneId 对象
//5. DateTimeFormatter : 解析和格式化日期或时间
@Test
public void test5(){
// DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss E");
LocalDateTime ldt = LocalDateTime.now();
String strDate = ldt.format(dtf);
System.out.println(strDate);
LocalDateTime newLdt = ldt.parse(strDate, dtf);
System.out.println(newLdt);
}
时区
//6.ZonedDate、ZonedTime、ZonedDateTime : 带时区的时间或日期
@Test
public void test7(){
LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println(ldt);
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("US/Pacific"));
System.out.println(zdt);
}
@Test
public void test6(){
Set<String> set = ZoneId.getAvailableZoneIds();
set.forEach(System.out::println);
}