Java8 Date Time API 简介

 

1、Java8 Date Time API 简介

1.1、新旧 Date Time API 对比

包位置对比

1、Java8之前的日期、时间分布在java.utiljava.sql的包中,此外用于格式化和解析的类在java.text包下,位置分散凌乱,不便于查找使用,类命名具有误导性,

示例:

java.util.Date:实际作用应该是DateTime,提供getDay(),getHours(),getMinutes()等方法,且getDay()方法获取的是星期几

java.sql.Date:代表日期,与java.util.Date同名,且大量过时方法,没有关联时区不支持国际化

java.sql.Timestamp

java.text.SimpleDateFormat:格式化

2、Java8开始,功能集中在java.time包下面,且各个包、类的命名及作用清晰,

每个类内提供 Format 和 Parse方法,可以直接调用java.time.LocalDateTime#getDayOfMonthjava.time.LocalDateTime#getHour 等方法

每个类提供通用方法,如:加、减、格式化、解析、从日期/时间中提取单独部分,等等。

java.time 根目录下: LocalDate、LocalTime、LocalDateTime、Instant类Chrono/chronology 日历系统:包含很多年表,包括日本、泰国、民国等format 格式化:时间格式化及解析temporal 时态包:时态的一些操作定义、底层框架和扩展特性,例如:获取月份最后一天zone 时区:时区的支持类

安全性

1、Java8之前 所有Date类都是可变的,在别处的修改会影响该日期值,所以应该返回该日期的克隆而不是日期本身

多线程下使用 静态的 SimpleDateFormat 实例 时存在线程安全问题,如: ava.lang.NumberFormatException: multiple points错误、格式化的时间与输入的时间不一致等问题。

原因:

java.text.SimpleDateFormat#format(java.util.Date, java.lang.StringBuffer, java.text.Format.FieldDelegate)

// 源码
private StringBuffer format(Date date, StringBuffer toAppendTo,
                               FieldDelegate delegate) {
   // 多线程问题,thread1 赋值之后,中断;thread2 赋值,中断;thread1回来数据值已变更
   calendar.setTime(date);
  ...
}

/**
解决:
1.作为方法内部局部变量使用,但每次调用就需要新创建一个实例
2.使用ThreadLocal,每个线程持有一个simpleDateFormat对象
3.同步simpleDateFormat对象,当一个线程调用此方法时,其他调用此方法的线程就要等待
*/
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static String formatDate(Date date) throws Exception{
   synchronized(sdf) {
       return sdf.formate(date);
  }
}

 

2、Java8时所有Date类都具有不变性(final 修饰类),且每次调用都会返回新的对象,所以每次的修改都能保证原对象不变。

// todo Java8 format 如何解决

// 源码
public LocalDate plusDays(long daysToAdd) {
   if (daysToAdd == 0) {
       return this;
  }
   long mjDay = Math.addExact(toEpochDay(), daysToAdd);
   return LocalDate.ofEpochDay(mjDay);
}

public static LocalDate ofEpochDay(long epochDay) {
   long zeroDay = epochDay + DAYS_0000_TO_1970;
   // .......
   return new LocalDate(year, month, dom);
}

 

2、Java8 Date Time API 示例

2.1 java.time.LocalDate 日期类,默认格式为 yyyy-MM-dd

// 当前日期
LocalDate today = LocalDate.now();
System.out.println("当前日期=" + today); // 当前日期=2018-12-27

// 指定日期
LocalDate firstDay_2018 = LocalDate.of(2018, Month.JANUARY, 1);
System.out.println("指定日期=" + firstDay_2018); // 指定日期=2018-01-01

// 其他时区当前日期
LocalDate todayNew_York = LocalDate.now(ZoneId.of("America/New_York"));
System.out.println("纽约当前日期=" + todayNew_York); //洛杉矶当前日期=2018-12-27

LocalDate dateFromBase = LocalDate.ofEpochDay(365); // 01/01/1970
System.out.println("开始日期增加 365 天 = " + dateFromBase); //开始日期增加 365 天 = 1971-01-01

LocalDate hundredDay2014 = LocalDate.ofYearDay(2018, 90);
System.out.println("年份第一天增加 90 天=" + hundredDay2014); //年份第一天增加 100 天=2018-03-31

2.2 java.time.LocalTime 时间类

LocalTime time = LocalTime.now();
System.out.println("当前时间=" + time); // 当前时间=14:55:50.494

LocalTime specificTime = LocalTime.of(12, 20, 25, 40);
System.out.println("具体时间=" + specificTime); // 具体时间=12:20:25.000000040

LocalTime timeLos_Angeles = LocalTime.now(ZoneId.of("America/Los_Angeles"));
System.out.println("洛杉矶当前时间=" + timeLos_Angeles); // 洛杉矶当前时间=22:55:50.495

LocalTime specificSecondTime = LocalTime.ofSecondOfDay(10000);
System.out.println("1000 秒= " + specificSecondTime); //10000th second time= 02:46:40

2.3 java.time.LocalDateTime 日期时间类

LocalDateTime today = LocalDateTime.now();
System.out.println("当前日期时间=" + today); // 当前日期时间=2018-12-28T15:15:01.364

today = LocalDateTime.of(LocalDate.now(), LocalTime.now());
System.out.println("当前日期时间=" + today); // 当前日期时间=2018-12-28T15:15:01.365

LocalDateTime specificDate = LocalDateTime.of(2014, Month.JANUARY, 1, 10, 10, 30);
System.out.println("具体日期时间=" + specificDate); // 具体日期时间=2014-01-01T10:10:30

LocalDateTime todayLos_Angeles = LocalDateTime.now(ZoneId.of("America/Los_Angeles"));
System.out.println("洛杉矶当前日期时间=" + todayLos_Angeles); // 洛杉矶当前日期时间=2018-12-27T23:15:01.366

LocalDateTime dateFromBase = LocalDateTime.ofEpochSecond(10000, 0, ZoneOffset.UTC);
System.out.println("初始日期加 10000 秒 = " + dateFromBase); // 初始日期加 10000 秒 = 1970-01-01T02:46:40

2.4 java.time.Instant 时间戳

Instant timestamp = Instant.now();
System.out.println("当前时间 = " + timestamp); //当前时间 = 2018-12-28T07:36:40.572Z

long timestampLong = timestamp.toEpochMilli();
System.out.println("时间戳 = "+ timestampLong); // 时间戳 = 1545982600572

Instant specificTime = Instant.ofEpochMilli(timestampLong);
System.out.println("具体时间 = " + specificTime); // 具体时间 = 2018-12-28T07:36:40.572Z

 

3、Java8 及旧 API 常用操作实例

3.1 获取当前日期

LocalDate newCurrentDate = LocalDate.now(); //2018-12-17
LocalDate newSetDate = LocalDate.of(2018, 12, 17);
Date oldCurrentDate = new Date(); // Thu Dec 27 11:20:47 CST 2018

3.2 获取月的某一天

  LocalDate lastDayOfThisMonth = newCurrentDate.with(TemporalAdjusters.lastDayOfMonth()); //2018-12-31
 LocalDate secondDayOfThisMonth = newCurrentDate.withDayOfMonth(2); //2018-12-02
 // 获取指定月份的最后一天
 LocalDate lastDayOfMonth = newCurrentDate.withMonth(11).with(TemporalAdjusters.lastDayOfMonth()); // 2018-11-30
  SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
 Calendar calendar = Calendar.getInstance();
 calendar.set(Calendar.DAY_OF_MONTH,
              calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
 String firstDayOfWeek = format.format(calendar.getTime());  // 2018-12-31
 // 获取指定月份的最后一天
 calendar.set(Calendar.MONTH, 11); // 月份从 0 开始
 calendar.set(Calendar.DAY_OF_MONTH, 0); // 2018-11-30
 

3.3 计算时间差

LocalDateTime start = LocalDateTime.of(2018, 11, 1, 0, 0, 0);
LocalDateTime end = LocalDateTime.of(2018, 12, 1, 0, 0, 0);
// Duration
Duration duration = Duration.between(start, end);
System.out.println("日期天数差:" + duration.toDays()); // 30
// Period
Period period = Period.between(start.toLocalDate(), end.toLocalDate());
System.out.println(period.getYears()+" 年 "+ period.getMonths()+" 月 "+ period.getDays()+" 日 "); // 0 年 1 月 0 日
// ChronoUnit
long daysDiff = ChronoUnit.DAYS.between(start, end);
System.out.println("日期天数差:" + daysDiff);
long hoursDiff = ChronoUnit.HOURS.between(start, end);
System.out.println("日期小时差:" + hoursDiff);
SimpleDateFormat simpleFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");
/*天数差*/
Date fromDate1 = simpleFormat.parse("2018-12-01 00:00");  
Date toDate1 = simpleFormat.parse("2018-12-30 12:00");  
long from1 = fromDate1.getTime();  
long to1 = toDate1.getTime();  
int days = (int) ((to1 - from1) / (1000 * 60 * 60 * 24));  
System.out.println("两个时间之间的天数差为:" + days);

3.4 格式化

DateTimeFormatter formatter =
                            DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);
String strDate = LocalDateTime.now().format(formatter);
System.out.println("系统自带年月日:"+ strDate); // 2018-12-17
DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String strDate1 = LocalDateTime.now().format(formatter1);
System.out.println("指定年月日:"+ strDate1); // 2018-12-17
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String strDate2 = simpleDateFormat.format(new Date());
System.out.println("系统自带:"+strDate2);  // 2018-12-17
// org.apache.commons.lang3.time.DateFormatUtils
String strDate3 = DateFormatUtils.format(new Date(), "yyyy-MM-dd");
System.out.println("工具类:"+ strDate3); // 2018-12-17

3.5 解析

LocalDateTime parseDate1 = LocalDateTime.parse("2018-12-17 18:04:01",     
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("Java8: "+ parseDate1); // Java8: 2018-12-17T18:04:01
try {
   SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
   Date date = simpleDateFormat.parse("2018-12-17 18:04:01");
   System.out.println("Java8 之前: " +date); // Java8 之前: Mon Dec 17 18:04:01 CST 2018
} catch (ParseException e) {
   e.printStackTrace();
}

3.6 获取毫秒

// 获取秒数
Long second = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
System.out.println("second = "+ second);
// 获取毫秒数
Long milliSecond = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
System.out.println("milliSecond = "+milliSecond);
// LocalDateTime 获取毫秒数
System.out.println(Timestamp.valueOf(LocalDateTime.now()).getTime());
// Date 获取毫秒数
System.out.println(new java.util.Date().getTime());

3.7 Java8 对旧 Date API 的支持

// Date to Instant
Instant timestamp = new Date().toInstant();

// Calendar to Instant
Instant time = Calendar.getInstance().toInstant();
System.out.println("Instant = "+time); // Instant = 2018-12-28T07:45:31.652Z

// Instant 转 Date
Date dt = Date.from(Instant.now());
System.out.println("date = "+ dt); // date = Fri Dec 28 15:45:31 CST 2018

// java.util.TimeZone 转 java.time.ZoneId
ZoneId defaultZone = TimeZone.getDefault().toZoneId();
System.out.println("defaultZone = "+defaultZone); // defaultZone = Asia/Shanghai

/**
* java.time.ZoneId 转 java.util.TimeZone
* TimeZone =sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,
*             useDaylight=false,transitions=19,lastRule=null]
*/
TimeZone tz = TimeZone.getTimeZone(defaultZone);
System.out.println("TimeZone = "+ tz);

//java.util.GregorianCalendar#GregorianCalendar() 转 java.time.ZonedDateTime
ZonedDateTime gregorianCalendarDateTime = new GregorianCalendar().toZonedDateTime();
System.out.println(gregorianCalendarDateTime); // 2018-12-28T15:45:31.672+08:00[Asia/Shanghai]

/**
* java.time.ZonedDateTime 转 java.util.GregorianCalendar#GregorianCalendar()
* GregorianCalendar = java.util.GregorianCalendar[time=1545983131672,areFieldsSet=true,
* areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",
* offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null],
* firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2018,MONTH=11,WEEK_OF_YEAR=52,
*WEEK_OF_MONTH=4,DAY_OF_MONTH=28,DAY_OF_YEAR=362,DAY_OF_WEEK=6,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,
*HOUR=3,HOUR_OF_DAY=15,MINUTE=45,SECOND=31,MILLISECOND=672,ZONE_OFFSET=28800000,DST_OFFSET=0]
*/
GregorianCalendar gc = GregorianCalendar.from(gregorianCalendarDateTime);
System.out.println("GregorianCalendar = "+gc);

 // Date 用的是 UTC 时间戳,所以与 LocalDateTime 等互转时需设置时区

   Date in = new Date();
   LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());
   Date out = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());

 

 

 

posted @ 2019-12-05 18:01  演说家  阅读(334)  评论(0编辑  收藏  举报