现有API存在问题

Date类

Java 1.0版本,java.util.Date

  • 无法表示日期,只能以毫秒的精度表示
  • 年份起始为1900,月份起始为0

Calendar类

Java 1.1版本,java.util.Date中的很多方法被废弃,取而代之的是java.util.Calendar

  • 同时存在DateCalendar两个类,使用比较困惑
  • DateFormat中方法有问题(不是线程安全的)

Java8中引入了新的日期时间APIjava.time包,整合了很多Joda-Time的特性。

LocalDate和LocalTime

LocalDate

该类提供日期处理(不包含时间,时区等信息),是一个不可变对象,通过静态工厂方法of创建LocalDate实例。

代码示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// 创建日期实例
LocalDate date = LocalDate.of(2013, 3, 8);
System.out.println(date);  // 2013-03-08
System.out.println(date.getYear());  // 2013
System.out.println(date.get(ChronoField.YEAR));  // 2013
System.out.println(date.getMonth());  // MARCH
System.out.println(date.get(ChronoField.MONTH_OF_YEAR));  // 3
System.out.println(date.getDayOfMonth()); // 8
System.out.println(date.get(ChronoField.DAY_OF_MONTH));    // 8
System.out.println(date.getDayOfWeek());  // FRIDAY
System.out.println(date.get(ChronoField.DAY_OF_WEEK));  // 5
System.out.println(date.lengthOfMonth());   // 31
// 一年中第多少天
System.out.println(date.get(ChronoField.DAY_OF_YEAR));  // 67
// LocalDate.now() 获取当前日期
System.out.println(LocalDate.now()); // 2018-09-21
// 日期解析:String -》 LocalDate,默认格式为yyyy-MM-dd
// 其它时间格式使用parse(CharSequence text, DateTimeFormatter formatter)方法
System.out.println(LocalDate.parse("2018-09-21"));  // 2018-09-21

LocalDate#get()方法接收一个TemporalField接口参数,ChronoField实现了该接口。

LocalTime

时间处理类,通过of静态工厂方法创建实例。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//创建时间实例
LocalTime time = LocalTime.of(23, 58, 59);
System.out.println(time);  // 23:58:59
System.out.println(time.getHour()); //23
System.out.println(time.get(ChronoField.HOUR_OF_DAY));      //23
System.out.println(time.get(ChronoField.HOUR_OF_AMPM));     //11
System.out.println(time.get(ChronoField.MINUTE_OF_HOUR));   //58
System.out.println(time.get(ChronoField.SECOND_OF_MINUTE)); //59
//一天中的多少秒
System.out.println(time.get(ChronoField.SECOND_OF_DAY));    //86339
//当前时间
System.out.println(LocalTime.now());                        //16:07:02.851
//时间解析:String -》 LocalTime
System.out.println(LocalTime.parse("08:01:23"));            //08:01:23

LocalDateTime合并日期和时间

LocalDateTimeLocalDateLocalTime的合体,表示日期和时间,但不包含时区信息。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 1 创建日期时间实例
LocalDateTime dateTime = LocalDateTime.of(2018, 9, 21, 15, 23, 29);
System.out.println(dateTime);  //2018-09-21T15:23:29
LocalDate date = LocalDate.of(2013, 3, 8);
LocalTime time = LocalTime.of(23, 58, 59);
// 2 创建日期时间实例(基于日期和时间实例)
LocalDateTime dt  = LocalDateTime.of(date, time);
System.out.println(dt);     //2013-03-08T23:58:59
// 3 创建日期时间实例(基于日期)
LocalDateTime dt2 = date.atTime(02, 12, 34);
System.out.println(dt2); //2013-03-08T02:12:34
LocalDateTime dt3 = date.atTime(time);
System.out.println(dt3); //2013-03-08T23:58:59
// 4 创建日期时间实例(基于时间)
LocalDateTime dt4 = time.atDate(date);
System.out.println(dt4); //2013-03-08T23:58:59
// LocalDateTime -> LocalDate
System.out.println(dateTime.toLocalDate());  //2018-09-21
// LocalDateTime -> LocalTime
System.out.println(dateTime.toLocalTime());  //15:23:29

机器的日期和时间 Instant

新的java.time.Instant类对时间建模的方式,是以Unix元年时间(UTC时区1970年1月1日午夜时分)开始所经历的秒数进行计算。可通过ofEpochSecond静态工厂方法创建实例。

1
2
3
4
5
6
7
8
Instant.ofEpochSecond(3);
// 2秒+100万纳秒(1秒)
Instant.ofEpochSecond(2, 1_000_000_000);
// 2秒-100万纳秒(1秒)
Instant.ofEpochSecond(2, -1_000_000_000);
System.out.println(Instant.ofEpochSecond(20));  //1970-01-01T00:00:20Z
System.out.println(Instant.now());   //2018-09-21T08:41:44.100Z
System.out.println(Instant.now().getEpochSecond());  // 1537519304

Duration或Period

  • Duration主要用于以秒和纳秒衡量时间的长短。

    1
    2
    3
    
    Duration d1 = Duration.between(localDate1, localDate2);
    Duration d2 = Duration.between(localTime1, localTime2);
    Duration d3 = Duration.between(localDateTime1, localDateTime2);
    1
    2
    3
    
    // 3分钟
    Duration threeMinutes = Duration.ofMinutes(3);
    threeMinutes = Duration.of(3, ChronoUnit.MINUTES);
  • Period以年、月或者日的方式对多个时间单位建模。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    // 3周
    Period threeWeeks = Period.ofWeeks(3);
    // 9天
    Period nineDays = Period.ofDays(9);
    // 2年6个月1天
    Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);
    // 10天
    Period tenDays = Period.between(LocalDate.of(2018, 9, 11),
                LocalDate.of(2018, 9, 21));

还包括from,parse,get等,更多方法查看API。

操作、解析、格式化日期

操作日期

  • 通过withAttribute with方法

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
    // 创建日期实例
    LocalDate date = LocalDate.of(2013, 3, 8);
    System.out.println(date);  //2013-03-08
    //修改年
    LocalDate date2 = date.withYear(2018);
    System.out.println(date2); // 2018-03-08
    //修改日
    LocalDate date3 = date.withDayOfMonth(9);
    System.out.println(date3); //2013-03-09
    //修改月
    LocalDate date4 = date.with(ChronoField.MONTH_OF_YEAR, 10);
    System.out.println(date4); //2013-10-08
    //修改为10月,加2年,减10天
    LocalDate date5 = date.with(ChronoField.MONTH_OF_YEAR, 10);
    date5 = date5.plusYears(2).minusDays(10);
    System.out.println(date5);  //2015-09-28

LocalTime, LocalDateTime也提供了同样类似的方法。

注意: 每次方法调用都会产生一个新对象,对原有对象没有任何影响。

  • 通过TemporalAdjuster

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    // 创建日期实例
    LocalDate date = LocalDate.of(2013, 3, 8);
    System.out.println(date);  //2013-03-08
    System.out.println(date.getDayOfWeek());  //FRIDAY
    //同一周的星期天
    LocalDate date6 = date.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
    System.out.println(date6);  //2013-03-10
    //本月最后一天
    LocalDate date7 = date.with(TemporalAdjusters.lastDayOfMonth());
    System.out.println(date7);  //2013-03-31
  • 可定制实现TemporalAdjuster接口

    1
    2
    3
    4
    5
    6
    
    @FunctionalInterface
    public interface TemporalAdjuster {
    
    Temporal adjustInto(Temporal temporal);
    
    }

解析及格式化日期-时间

  • 日期-时间 -》 字符串,format实例方法

    1
    2
    3
    4
    
    String dateStr1 = date.format(DateTimeFormatter.BASIC_ISO_DATE);
    System.out.println(dateStr1);  //20130308
    String dateStr2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE)
    System.out.println(dateStr2);  //2013-03-08
  • 字符串 -》 日期-时间,parse静态方法

    1
    2
    3
    4
    5
    6
    
    LocalDate date8 = LocalDate.parse("20130308",
                DateTimeFormatter.BASIC_ISO_DATE);
    System.out.println(date8); //2013-03-08
    LocalDate date9 = LocalDate.parse("2013-03-08",
                DateTimeFormatter.ISO_LOCAL_DATE);
    System.out.println(date9); //2013-03-08
  • 自定义格式

    1
    2
    3
    4
    5
    
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
    String dateStr3 = date.format(formatter);
    System.out.println(dateStr3);  // 08/03/2013
    LocalDate date10 = LocalDate.parse(dateStr3, formatter);
    System.out.println(date10);    // 2013-03-08
  • 使用DateTimeFormatterBuilder创建复杂的自定义格式

    1
    2
    3
    4
    5
    6
    7
    8
    
    DateTimeFormatter italianFormatter = newDateTimeFormatterBuilder()
    .appendText(ChronoField.DAY_OF_MONTH) .appendLiteral(". ")
    .appendText(ChronoField. MONTH_OF_YEAR) .appendLiteral(" ")
    .appendText(ChronoField. YEAR)
    .parseCaseInsensitive()
    .toFormatter(Locale. ITALIAN);
    String dateStr4 = date.format(italianFormatter);
    System.out.println(dateStr4);  //8. marzo 2013

时区和历法

时区

新的java.time.ZoneId类替代java.util.TimeZone

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
ZoneId romeZone = ZoneId.of("Europe/Rome");
LocalDate date = LocalDate.of(2013, 3, 8);
ZonedDateTime zdt = date.atStartOfDay(romeZone);
System.out.println(zdt);  //2013-03-08T00:00+01:00[Europe/Rome]
LocalDateTime dateTime = LocalDateTime.of(2018, 9, 21,
15, 23, 29);
ZonedDateTime zdt2 = dateTime.atZone(romeZone);
System.out.println(zdt2);  //2018-09-21T15:23:29+02:00[Europe/Rome]
Instant instant = Instant.now();
ZonedDateTime zdt3 = instant.atZone(romeZone);
System.out.println(zdt3);  //2018-09-21T11:59:59.936+02:00[Europe/Rome]
  • TimeZone –> ZoneId

    1
    
    ZoneId zoneId = TimeZone.getDefault().toZoneId();
  • Instant –> LocalDateTime

    1
    
    LocalDateTime dateTime1 = LocalDateTime.ofInstant(instant, romeZone);
  • LocalDateTime –> Instant

    1
    2
    
    ZoneOffset zoneOffset = ZoneOffset.of("-05:00");
    Instant instant1 = dateTime.toInstant(zoneOffset);

ZoneOffsetZoneId的子类,当前时区和UTC/格林尼治的固定偏差。

其它的日历系统

ISO-8601日历系统是世界文明日历系统的事实标准。 但是,Java8中另外还提供了4种其他的日历系统。 分别是ThaiBuddhistDate、MinguoDate、JapaneseDate以及HijrahDate。