Java基础-时间日期[2]

java.time

jdk引入新的时间体系:

  1. LocalDate只保留日期
  2. LocalTime只保留时间
  3. LocalDateTime同时保留时间和日期
  4. ZonedDateTime保留了时区、时间、日期
  5. Instant:是不带时区一个时时间点,与java.util.Date类似,但是精确到纳秒。
@Test
public void testJdk8Date() {
    System.out.println(LocalDate.now());
    System.out.println(LocalTime.now());
    System.out.println(ZonedDateTime.now());
    System.out.println(LocalDateTime.now());
    System.out.println(Instant.now());
    System.out.println(MinguoDate.now());
}
//2021-04-14
//11:54:14.660177900
//2021-04-14T11:54:14.661181200+08:00[Asia/Shanghai]
//2021-04-14T11:54:14.661181200
//2021-04-14T03:54:14.661181200Z
//Minguo ROC 110-04-14

从上面也可以看出Instant是无时区的,代表UTC的时间戳,Z在时区里面表示UTC

LocalDate.now()

public static LocalDate now() {
    return now(Clock.systemDefaultZone());
}
public static LocalDate now(Clock clock) {
    Objects.requireNonNull(clock, "clock");
    final Instant now = clock.instant();  // called once
    return ofInstant(now, clock.getZone());
}
public static LocalDate ofInstant(Instant instant, ZoneId zone) {
    Objects.requireNonNull(instant, "instant");
    Objects.requireNonNull(zone, "zone");
    ZoneRules rules = zone.getRules();
    ZoneOffset offset = rules.getOffset(instant);
    //计数utc和时区偏差后的总时间
    long localSecond = instant.getEpochSecond() + offset.getTotalSeconds();
    long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY);
    return ofEpochDay(localEpochDay);
}
//此时这里处理的已经是加上时区偏差的时间了
//如东八区: epochDay = utcSec + 8*60*60
public static LocalDate ofEpochDay(long epochDay) {
    EPOCH_DAY.checkValidValue(epochDay);
    long zeroDay = epochDay + DAYS_0000_TO_1970;
    // find the march-based year
    zeroDay -= 60;  // adjust to 0000-03-01 so leap day is at end of four year cycle
    long adjust = 0;
    if (zeroDay < 0) {
        // adjust negative years to positive for calculation
        long adjustCycles = (zeroDay + 1) / DAYS_PER_CYCLE - 1;
        adjust = adjustCycles * 400;
        zeroDay += -adjustCycles * DAYS_PER_CYCLE;
    }
    long yearEst = (400 * zeroDay + 591) / DAYS_PER_CYCLE;
    long doyEst = zeroDay - (365 * yearEst + yearEst / 4 - yearEst / 100 + yearEst / 400);
    if (doyEst < 0) {
        // fix estimate
        yearEst--;
        doyEst = zeroDay - (365 * yearEst + yearEst / 4 - yearEst / 100 + yearEst / 400);
    }
    yearEst += adjust;  // reset any negative year
    int marchDoy0 = (int) doyEst;

    // convert march-based values back to january-based
    int marchMonth0 = (marchDoy0 * 5 + 2) / 153;
    int month = (marchMonth0 + 2) % 12 + 1;
    int dom = marchDoy0 - (marchMonth0 * 306 + 5) / 10 + 1;
    yearEst += marchMonth0 / 10;

    // check year now we are certain it is correct
    int year = YEAR.checkValidIntValue(yearEst);
    return new LocalDate(year, month, dom);
}
private LocalDate(int year, int month, int dayOfMonth) {
    this.year = year;
    this.month = (short) month;
    this.day = (short) dayOfMonth;
}

最后LocalDate(int year, int month, int dayOfMonth) 完全和时区没有关系,只保存了所在时区应该显示的年月日,简单理解就是时间的字符串

LocalDateTime转时间戳

  1. ZoneId:表示时间对应的时区,常见的是:区域/ 城市,如Asia/Shanghai,Asia/Tokyo
  2. ZoneOffset:ZoneId的子类,使用时间偏移量表示
@Test
public void testZone() {
    System.out.println(ZoneId.systemDefault());
    System.out.println(ZoneId.of("Asia/Shanghai"));
    System.out.println(ZoneId.of("Asia/Hong_Kong"));
    System.out.println(ZoneOffset.UTC);
    System.out.println(ZoneId.of("Z"));
    System.out.println(ZoneId.of("+8"));
    System.out.println(ZoneId.of("UTC+8"));
    System.out.println(ZoneId.ofOffset("UTC",ZoneOffset.ofHours(8)));
    //具体见方法注释
    System.out.println(ZoneOffset.of("+08:30:20"));
}
//Asia/Shanghai
//Asia/Shanghai
//Asia/Hong_Kong
//Z
//Z
//+08:00
//UTC+08:00
//UTC+08:00
//2021-04-14T06:47:58.587
//2021-04-14T13:47:58.587

ZoneDateTime

ZoneDateTime在LocalDateTime的基础上增加了时区的概念,这样可以将同一个时间转换成不同时区的格式显示

@Test
public void testDateStr (){
    LocalDateTime leaving = LocalDateTime.of(2020, 6, 29, 19, 30);
    //这里也是2020/06/29 19:30:00,但是有了时区的概念,是上海时区的2020/06/29 19:30:00
    //而上面的LocalDateTime可以理解为只是一个时间字符串
    ZonedDateTime shanghaiTime = leaving.atZone(ZoneId.of("Asia/Shanghai"));
    System.out.println(shanghaiTime);
    //这里把上海时间加上半小时后,转化为了东京时间,东京时间与上海时间相差1个小时
    //所以这里显示的字符串是2020-06-29T21:00+09:00[Asia/Tokyo]
    ZonedDateTime tokyoTime = shanghaiTime.plusMinutes(30).withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
    System.out.println(tokyoTime);
}

理解LocalDateTime的无时区性

@Test
public void testLocalDtToIne (){
    long defaultZoneDt = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    long utcZoneDt = LocalDateTime.now().atZone(ZoneOffset.UTC).toInstant().toEpochMilli();
    System.out.println(defaultZoneDt);
    System.out.println(utcZoneDt);
    System.out.println(FgDateUtils.millsToDateTime(defaultZoneDt));
    System.out.println(FgDateUtils.millsToDateTime(utcZoneDt));
}
1618383299634
1618412099635
2021-04-14 14:54:59
2021-04-14 22:54:59

实际当前时间是上海时间2021-04-14 14:54:59

LocalDateTime.now()只是简单的字符串,在初始化的过程,已经将utc的时间搓,转换为当前时区的年月日,所以取名也有Local。

.atZone()则是将前面得到的时间字符串赋予时区,即让程序理解2021-04-14 14:54:59是那个时区的时间,如果理解为UTC的时间,那么对应上海时间还要在加上8个小时,也就是2021-04-14 22:54:59

封装的工具类

/**
* @author froggengo@qq.com
* @date 2021/4/14 9:30.
*/
public class FgTimeUtils {

   //时间搓转日期,mills为utc时间搓
   public static LocalDateTime millsToDateTime(long mills) {
       Instant instant = Instant.ofEpochMilli(mills);
       return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
   }

   //时间搓转日期
   public static LocalDateTime secToDateTime(int sec) {
       Instant instant = Instant.ofEpochSecond(sec);
       return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
   }

   /**
    * 字符串转时间搓,时间格式
    * yyyy-MM-dd HH:mm:ss
    * yyyy-MM-dd
    * yyyy-MM
    */
   public static LocalDateTime strToDateTime(String str) {
       Objects.requireNonNull(str);
       String dateStr;
       switch (str.length()) {
           case 19:
               dateStr = str;
               break;
           case 10:
               dateStr = str + " 00:00:00";
               break;
           case 7:
               dateStr = str + "-01 00:00:00";
               break;
           default:
               throw new UnsupportedOperationException();
       }
       return LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern(FgDateUtils.YYYY_MM_DD_HH_MM_SS));
   }

   /**
    * localDatetime转时间搓(utc),时间搓均指utc+0时间
    */
   public static long dateTimeToMills(LocalDateTime dateTime) {
       return dateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
   }

   /**
    * 字符串转时间搓,时间格式
    * yyyy-MM-dd HH:mm:ss
    * yyyy-MM-dd
    * yyyy-MM
    */
   public static long strToMills(String str) {
       Objects.requireNonNull(str);
       return dateTimeToMills(strToDateTime(str));
   }

   /**
    * 计算偏移量后当天时间0点ZoneDateTime
    * LocalDate的另一个方法会转成
    * LocalDate.now().atStartOfDay(ZoneId.systemDefault()
    */
   public static LocalDateTime startOfDayByOffset(int offset) {
       LocalDate now = LocalDate.now().plusDays(offset);
       return LocalDateTime.of(now, LocalTime.MIN);
   }

   /**
    * 计算偏移后当月第一天时间0点
    */
   public static LocalDateTime startOfMonthByOffset(int offset) {
       LocalDate now = LocalDate.now().plusMonths(offset);
       return LocalDateTime.of(now.with(TemporalAdjusters.firstDayOfMonth()), LocalTime.MIN);
   }

   /**
    * 将time包下的时间对象转成相应的字符串
    */
   public static <T extends Temporal> String formatTime(T time) {
       if (time instanceof LocalDateTime) {
           return ((LocalDateTime) time).format(DateTimeFormatter.ofPattern(FgDateUtils.YYYY_MM_DD_HH_MM_SS));
       } else if (time instanceof LocalDate) {
           return ((LocalDate) time).format(DateTimeFormatter.ofPattern(FgDateUtils.YYYY_MM_DD));
       } else if ((time instanceof LocalTime)) {
           return ((LocalTime) time).format(DateTimeFormatter.ofPattern(FgDateUtils.HH_MM_SS));
       } else if ((time instanceof ZonedDateTime)) {
           return ((ZonedDateTime) time).format(DateTimeFormatter.ofPattern(FgDateUtils.YYYY_MM_DD_HH_MM_SS));
       }
       throw new UnsupportedOperationException("only support LocalDateTime、LocalDate、LocalTime、ZonedDateTime");
   }

   public static String formatDate(LocalDateTime dateTime) {
       return dateTime.format(DateTimeFormatter.ofPattern(FgDateUtils.YYYY_MM_DD));
   }

   //    两个时间搓的时间差
   public static String duration(long max, long min) {
       Duration duration = Duration.ofMillis(max - min);
       return duration.toDaysPart() + "天" + duration.toHoursPart() + "小时" 
           + duration.toMinutesPart() + "分" + duration.toSecondsPart() + "秒";
   }
}
posted @ 2021-04-14 17:40  froggengo  阅读(172)  评论(0编辑  收藏  举报