Java8 时间

特此说明:此文章来源于初学Java8时间API时的笔记

格式化编码

字母描述示例
G纪元标记AD
y四位年份2001
M月份July or 07
d一个月的日期10
hA.M./P.M. (1~12)格式小时12
H一天中的小时 (0~23)22
m分钟数30
s秒数55
S毫秒数234
E星期几Tuesday
D一年中的日子360
F一个月中第几周的周几2 (second Wed. in July)
w一年中第几周40
W一个月中第几周1
aA.M./P.M. 标记PM
k一天中的小时(1~24)24
KA.M./P.M. (0~11)格式小时10
z时区Eastern Standard Time
"单引号`

使用printf格式化日期

printf 方法可以很轻松地格式化时间和日期。使用两个字母格式,它以 %t 开头并且以下面表格中的一个字母结尾

转换符说明示例
c包括全部日期和时间信息星期六 十月 27 14:21:20 CST 2007
F"年-月-日"格式2007-10-27
D"月/日/年"格式10/27/07
r"HH:MM:SS PM"格式(12时制)02:25:51 下午
T"HH:MM:SS"格式(24时制)14:28:16
R"HH:MM"格式(24时制)14:28
public void test21() {
  System.out.printf("%tF %<tT\n", new Date());// 2018-05-05 16:59:35
  // 引用值必须紧跟在%后面,而且必须以$结束
  System.out.printf("%tF %1$tT\n", new Date());
  System.out.printf("%1$tF %1$tT\n", LocalDateTime.now());
  System.out.printf("%1$s %2$tF %2$tT\n", "现在时间为:", LocalDateTime.now());
  System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));


  String time = String.format(new SimpleDateFormat("[a]好,现在是[y年M月d日 E H点m分]").format(new Date()));
  String time = LocalDateTime.now().format(
    DateTimeFormatter.ofPattern("'['a']'好,现在是'['y年M月d日 E H点m分']'"));
  System.out.println(time);
  // [下午]好,现在是[2018年5月5日 周六 16点59分]
}

@Test
public void test22() {
  // localDate()方法有其默认的格式化属性
  System.out.printf("%tF\n", LocalDate.now());// 2018-05-05
  System.out.println(LocalDate.now());// 2018-05-05
  System.out.printf("%tF %<tT\n", LocalDateTime.now());
  // 2018-05-05 17:00:56
  String str = DateTimeFormatter.ofPattern("[a]好,现在是[y年M月d日 E H点m分]", 									Locale.CHINA).format(LocalDateTime.now());
  System.out.println(str);// 下午好,现在是2018年5月5日 周六 17点2分
}

@Test
public void test3() {
  DateTimeFormatter dtf = DateTimeFormatter.ofPattern("[a]好,现在是[y年M月d日 E H点m分]");
  String str = dtf.format(LocalDateTime.now());
  System.out.println(str);// 下午好,现在是2018年5月5日 周六 17点3分
  LocalDateTime ldt = LocalDateTime.now().parse(str, dtf);
  System.out.println(ldt);// 2018-05-05T17:03
}
LocalDateTime now = LocalDateTime.now();
// b的使用,月份简称
String str = String.format(Locale.US, "英文月份简称:%tb", now);
System.out.println(str); // Sep
System.out.printf("本地月份简称:%tb%n", now); // 九月
// B的使用,月份全称
str = String.format(Locale.US, "英文月份全称:%tB", now); 
System.out.println(str);// September
System.out.printf("本地月份全称:%tB%n", now);  // 九月
// a的使用,星期简称
str = String.format(Locale.US, "英文星期的简称:%ta", now); 
System.out.println(str); // Thu
// A的使用,星期全称
System.out.printf("本地星期的简称:%tA%n", now); // 星期四
// C的使用,年前两位
System.out.printf("年的前两位数字(不足两位前面补0):%tC%n", now); // 20
// y的使用,年后两位
System.out.printf("年的后两位数字(不足两位前面补0):%ty%n", now); // 20
// j的使用,一年的天数
System.out.printf("一年中的天数(即年的第几天):%tj%n", now);   // 261
// m的使用,月份
System.out.printf("两位数字的月份(不足两位前面补0):%tm%n", now); // 09
// d的使用,日(二位,不够补零)
System.out.printf("两位数字的日(不足两位前面补0):%td%n", now);   // 17
// e的使用,日(一位不补零)
System.out.printf("月份的日(前面不补0):%te", now);    // 17

Java8时间

打印输出日历

public static void main(String[] args) {
  String string = "2019-04-09";
  DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
  LocalDate date = LocalDate.parse(string, timeFormatter);

  int currDay = date.getDayOfMonth();// 9
  LocalDate first = date.with(TemporalAdjusters.firstDayOfMonth());
  LocalDate first2 = date.with(ChronoField.DAY_OF_MONTH, 1);
  LocalDate first3 = date.withDayOfMonth(1);
  LocalDate last = date.with(TemporalAdjusters.firstDayOfNextMonth());

  int MaxDay = date.lengthOfMonth();// 30
  LocalDate date2 = date.withDayOfMonth(1);

  int firstDay = date2.getDayOfWeek().getValue();
  System.out.println("日\t一\t二\t三\t四\t五\t六");
  // 在Java8的时间中,星期日不再是0,而是对应数字7了
  for (int j = 0; firstDay != 7 && j < firstDay; j++) {
    System.out.print("\t");
  }
  for (int j = 1; j <= MaxDay; j++) {
    System.out.print(j + "\t");
    if (date2.getDayOfWeek().getValue() == 6) {
      // if (date2.getDayOfWeek() == DayOfWeek.SATURDAY) {
      System.out.println();
    }
    // 可以把plusDays放在上面,这时if里面的条件就要变为以7或者星期日为标准了
    date2 = date2.plusDays(1);
  }
}

时间戳

// Instant:时间戳(以Unix元年,1970年1月1日 零点 到某个时间的毫秒值)
Instant instant = Instant.now();
System.out.println(instant);   // 2020-01-11T22:09:38.601Z
// 这个代表当前时间
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println(offsetDateTime);  // 2020-01-12T06:09:38.601+08:00

System.out.println(Instant.now().toEpochMilli() == System.currentTimeMillis()); // true

// 这个是相较于Unix元年的偏移量
Instant instant1 = Instant.ofEpochSecond(1);

时间

LocalDate

相隔天数
LocalDate start = LocalDate.of(1993, 9, 24);
LocalDate end = LocalDate.of(1994, 12, 6);
long days = start.until(end, ChronoUnit.DAYS);
long between = ChronoUnit.DAYS.between(start, end);
System.out.println(LocalDate.now().toString());  // 2020-01-13
System.out.println(LocalDateTime.now().toString()); // 2020-01-13T07:30:00.351
System.out.println(LocalTime.now().toString()); // 07:30:00.351
@Test
public void test1() {
  LocalDate now = LocalDate.now();

  // of(int year, int month, int dayOfMonth)
  LocalDate birth = LocalDate.of(1993, 9, 23);

  // 通过某一时间去调用指定自己想要的时间
  LocalDate birth2 = now.with(ChronoField.YEAR, 1993)
    .with(ChronoField.MONTH_OF_YEAR, 9)
    .with(ChronoField.DAY_OF_MONTH, 23);
  System.out.println(birth2);// 1993-09-23

  // 这个是用形参减去调用者(后面减前面)
  Period period = birth.until(now);
  System.out.println(period);// P25Y6M18D
  System.out.println(period.getYears());// 25
  System.out.println(period.getMonths());// 6
  System.out.println(period.getDays());// 18

  // 获取两个日期相差天数
  System.out.println(date.until(date2, ChronoUnit.DAYS));    
}

@Test
public void test2() {
  LocalDate now = LocalDate.now();
  LocalDate birth = LocalDate.of(1993, 9, 23);
  Period between = Period.between(birth, now);
  System.out.println(between);// P25Y6M18D
  /**
     * Period 是表示“日期”间隔的(LocalDate)
     * Duration是表示“时间”间隔的(LocalTime)
     */
  Period between2 = Period.between(LocalDate.of(1993, 9, 23), LocalDate.now());
  System.out.println(between2);// P25Y6M18D
}

LocalTime lt = LocalTime.now(); // 06:18:56.132
LocalTime lt2 = LocalTime.of(8, 0);
Duration duration = Duration.between(lt, lt2);
System.out.println(duration.getSeconds());
// get 方法只能用来获取秒和纳秒
// System.out.println(duration.get(ChronoUnit.DAYS));
System.out.println(duration.toDays());
System.out.println(duration.toMillis());
System.out.println(duration.toHours());

TemporalAdjuster:时间校正器。有时可能需要获取例如:将日期调整到“下个周日”等操作

TemporalAdjusters:该类通过静态方法提供了大量的常用TemporalAdjuster实现

public class FirstDay {
  public static void main(String[] args) {
    // 当前时间的下个周五
    LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
    LocalDate first = LocalDate.now()
      .with(TemporalAdjusters.firstDayOfMonth());
    // with(TemporalField field, long newValue) 
    LocalDate first2 = LocalDate.now().with(ChronoField.DAY_OF_MONTH, 1);
    LocalDate first3 = LocalDate.now().withDayOfMonth(1);
    System.out.println(first2.getDayOfWeek().getValue());

    LocalDate last = LocalDate.now()
      .with(TemporalAdjusters.firstDayOfNextMonth());
  }
}

public void test() {
  LocalDate now = LocalDate.of(2020, 1, 11);
  // 获取下一个工作日
  LocalDate localDate = now.with(temporal -> {
    LocalDate ld = (LocalDate) temporal;
    DayOfWeek dow = ld.getDayOfWeek();
    if (dow.equals(DayOfWeek.FRIDAY)) {
      return ld.plusDays(3);
    } else if (dow.equals(DayOfWeek.SATURDAY)) {
      return ld.plusDays(2);
    } else {
      return ld.plusDays(1);
    }
  });
  System.out.println(localDate);
}
public void test1() {
  LocalDate today = LocalDate.now();
  System.out.println(today);// 2018-05-05
  // 当前日期所在的天数
  System.out.println(today.lengthOfMonth());// 当前月最大天数31
  System.out.println(today.getDayOfWeek().getValue());// 6(代表星期六)
  System.out.println(today.getMonth().maxLength());// 31
  System.out.println(today.lengthOfYear());// 365
  System.out.println(today.isLeapYear());// false
  System.out.println(today.getMonth().name());// MAY
  // 增加、减少指定的天数
  System.out.println(today.plus(2, ChronoUnit.DAYS));// 2018-05-07
  System.out.println(today.plusDays(2));// 2018-05-07
  System.out.println(today.minusDays(2));// 2018-05-03
  // 获取当前年月日等信息
  System.out.println(today.getYear());// 2018
  System.out.println(today.getMonthValue());// 5
  System.out.println(today.getDayOfMonth());// 5
}

LocalDateTime修改

public void test2() {
  LocalDateTime now = LocalDateTime.now();
  /**
     * 将LocalDateTime转化为对应的LocalDate和LocalTime的时间
     */
  LocalDate date = now.toLocalDate();
  LocalTime time = now.toLocalTime();
  LocalDateTime now2 = LocalDateTime.of(date, time);// 2018-05-05T16:26:25.925296
  LocalDateTime now3 = date.atTime(time);// 2018-05-05T16:26:25.925296
  LocalDateTime now4 = date.atTime(13, 34);// 2018-05-05T13:34
  LocalDateTime now5 = time.atDate(date);// 2018-05-05T16:26:25.925296
  System.out.println(date);// 2018-05-05
  System.out.println(time);// 16:24:27.971391200
}
public void test3() {
  // 获取特定日期
  LocalDate dateOfBirth = LocalDate.of(1993, 8, 9);
  String s = "19931027";
  LocalDate date = LocalDate.parse(s, DateTimeFormatter.BASIC_ISO_DATE);// 1993-10-27
  LocalDate parse = LocalDate.parse("2017-12-12");// 2017-12-12
  LocalTime parse2 = LocalTime.parse("12:22:25");// 12:22:25

  // 使用自定义的格式器
  String s1 = "10 27 1993";
  DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM dd yyyy");
  LocalDate date1 = LocalDate.parse(s1, formatter);
  System.out.println(date1);// 1993-10-27
}

MonthDay修改

public static void main(String[] args) {
  // MonthDay类仅包含日月信息,不包含年,用于检查特定日期,出生日,节日等
  MonthDay birthDay = MonthDay.of(10, 12);
  System.out.println(MonthDay.now().equals(birthDay));// false
  // 从某一日期获取MonthDay
  LocalDate dateOfBirth = LocalDate.of(2000, 2, 29);
  MonthDay speilaDay = MonthDay.from(dateOfBirth);
  // 检查某年是否含有该日期(后面对应的月日在该年中是否存在)
  System.out.println(speilaDay.isValidYear(2020));// true
  // YearMonth用于检查信用卡过期信息等
  System.out.println(YearMonth.of(2020, Month.AUGUST));// 2020-08
}
public static void main(String[] args) {
  // Duration是用来计算两个给定的日期之间包含多少秒,多少毫秒,它是不可变类且线程安全的
  // Duration中的get方法只会换算到秒,秒为Duration最大单位
  LocalTime localTime = LocalTime.now();
  Duration duration = Duration.between(localTime, localTime.plusMinutes(3));
  System.out.println(duration.getSeconds());// 180
  System.out.println(duration.toMillis());// 180000
  System.out.println(duration.get(ChronoUnit.SECONDS));// 180
  //Period是用来计算两个给定的日期之间包含多少天,多少月或者多少年,它是不可变类且线程安全的
  // Period中的get方法只会换算到年,年为Period最大单位
  LocalDate localDate = LocalDate.now();// 300
  Period period2 = Period.between(localDate, localDate.plus(3, ChronoUnit.CENTURIES));
  Period period = Period.between(localDate.parse("1993-08-09"), localDate);
  System.out.println(period.getYears());// 24
  System.out.println(period.getMonths());// 8
  System.out.println(period.getDays());// 26
  Period period1 = Period.between(localDate, localDate.plus(3, ChronoUnit.DAYS));
  System.out.println(period1.getYears());// 0
  System.out.println(period1.getMonths());// 0
  System.out.println(period1.getDays());// 3
}

时区操作

Java8中加入了对时区的支持,带时区的时间分别为:ZonedDate、ZonedTime、ZonedDateTime

其中每个时区都有对应着ID,地区ID都为"{区域}/{城市}"的格式,例如:Asia/Shanghai等

ZoneId:该类中包含了所有的时区信息

  • getAvailableZoneIds:获取所有的时区信息
  • of(id):指定的时区信息获取ZoneId对象
LocalDateTime ctt = LocalDateTime.now(ZoneId.of("CTT", ZoneId.SHORT_IDS));
// 此两种方式都是获取上海时间
LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
// 获取的就是本地时区
LocalDateTime now = LocalDateTime.now();
ZonedDateTime zonedDateTime = now.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println(zonedDateTime); // 2020-01-12T06:54:46.474+08:00[Asia/Shanghai]
// 下面这种方式没意义
zonedDateTime = now.atZone(ZoneId.of("Europe/Paris"));
System.out.println(zonedDateTime); // 2020-01-12T06:54:46.474+01:00[Europe/Paris]

线程安全

private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() {
  @Override
  protected DateFormat initialValue() {
    return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  }
};

public static Date parse(String dateStr) throws ParseException {
  return threadLocal.get().parse(dateStr);
}

public static String format(Date date) {
  return threadLocal.get().format(date);
}

推荐使用 Spring ConversionService自定义格式化转化器 https://blog.csdn.net/weixin_43476471/article/details/114484451

private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

public static String formatDate(LocalDateTime date) {
  return formatter.format(date);
}

public static LocalDateTime parse(String dateNow) {
  return LocalDateTime.parse(dateNow, formatter);
}

附录

UTC和GMT的区别

讲解TimeZone时提到了UTC和GMT,下面对这两个概念进行科普

  • GMT:格林尼治标准时间。指位于伦敦郊区的皇家格林尼治天文台的标准时间(所以它是天文学时间),因为本初子午线被定义在通过那里的经线。随着科技的进步,人们发现该时间不够精确,因为地球自转的速度是不均衡的,而且自转速度也会越来越慢,所以不再使用
  • UTC:世界协调时间(世界统一时间)。是基于原子物理学的特性,它的精确度更高,所以是现在全球都在使用的时间

UTC是根据原子钟来计算时间,非常的精确和平均;而GMT是根据地球的自转和公转来计算时间,准确度不够已被淘汰

粗略的理解:可认为它俩是一回事。只不过UTC的称呼更为正式一点。两者的区别在于前者是一个天文上的概念,而后者是基于一个原子钟。在UTC中,每一年或两年会有一个“闰秒”

计算公式

UTC + 时区差 = 本地时间。如UTC+8表示北京时间(同GMT+8/GMT+08:00)。

Java中的应用示例

在JDK1.1时使用TimeZone类来表示时区,Java8新增了ZoneOffset来表示时区偏移量

System.out.println(TimeZone.getDefault());
System.out.println(TimeZone.getTimeZone("Asia/Shanghai"));
System.out.println(TimeZone.getTimeZone("GMT+8"));
System.out.println(TimeZone.getTimeZone("GMT+8:00"));
System.out.println(TimeZone.getTimeZone("GMT"));
System.out.println(TimeZone.getTimeZone("UTC"));
System.out.println(TimeZone.getTimeZone("UTC+8"));


// ZoneOffset
System.out.println(ZoneOffset.systemDefault());
// System.out.println(ZoneOffset.of("Asia/Shanghai")); //抛错
// System.out.println(ZoneOffset.of("+8:00")); // 抛错
System.out.println(ZoneOffset.of("+08:00"));
System.out.println(ZoneOffset.ofHours(8));
// System.out.println(ZoneOffset.of("GMT")); // 抛错

通过现象至少能整理出如下结论:

  1. 在国内,TimeZone.getDefault()/getTimeZone("Asia/Shanghai") /getTimeZone("GMT+8")/getTimeZone("GMT+8:00")完全等效
  2. TimeZone.getTimeZone("GMT")/getTimeZone("UTC")对程序来说可以认为是完全一样的(只不过一个id叫GMT,一个叫UTC)
  3. TimeZone.getTimeZone("UTC+8")其实并不支持UTC+8这种写法(这么写不报错,但效果同UTC
  4. ZoneOffset.systemDefault()的底层源码其实是TimeZone.getDefault().toZoneId()这么一句话
  5. ZoneOffset.of("+08:00")效果完全同ZoneOffset.ofHours(8)
  6. 另请注意代码中我备注的抛错的几条语句,书写的时候需要注意规范性
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

①笶侕濄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值