1. 名词解释

1.1 UTC

Coordinated Universal Time协调世界时, 是个世界标准时间。

1.2 GMT

GMT(Greenwich Mean Time,格林威治时间)是时区时间。

GMT = UTC +0, 和 UTC 的值是一样的。

Tue Mar 15 03:25:32 GMT 2022

1.3 CST

北京时间,CST = GMT + 8

Tue Mar 15 11:27:12 CST 2022

1.4 T

连接 date 和 time 的字符,没有意义。

2022-03-15T03:27:12.556410

1.5 Z

ISO 8601中规定:对 UTC 时间最后加一个大写字母 Z

1970-01-01T00:00:00Z

2. 时区问题

使用 Java 处理时间时,我们可能会经常发现时间不对,比如相差 8 个小时等等,其真实原因便是 TimeZone。只有正确合理的运用 TimeZone,才能保证系统时间无论何时都是准确的。

上文提到的 gmt 和 cst 就是时区。

2.1 timestamp

我们先说下时间戳

Date date = new Date();
Date date2 = Calendar.getInstance().getTime();
long time = date.getTime();

Date 对象本身所存储的毫秒数可以通过 date.getTime() 方法得到;该函数返回自 1970年1月1日 00:00:00 GMT 以来此对象表示的毫秒数。它与时区和地域没有关系(可以认为是GMT时间)

同样的

long currentTimeMillis = System.currentTimeMillis();

该函数返回自 1970年1月1日 00:00:00 GMT 以来此对象表示的毫秒数。它与时区和地域没有关系(可以认为是GMT时间)

2.2 TimeZone

旧版 API, 新版使用 ZoneId

TimeZone 对象给我们的是原始的偏移量,也就是与 GMT 相差的微秒数,即 TimeZone 表示时区偏移量,本质上以毫秒数保存与 GMT 的差值。

获取 TimeZone 可以通过时区 ID,如 “America/New_York”,也可以通过 GMT+/-hh:mm 来设定,例如北京时间可以表示为 GMT+8:00。

TimeZone.getRawOffset() 方法可以用来得到当前时区的标准时间到 GMT 的偏移量。上段提到的 “America/New_York” 和 “GMT+8:00” 两个时区的偏移量分别为 -18000000 和 28800000。

TimeZone timeZone = TimeZone.getTimeZone("GMT+8");
TimeZone timeZone1 = TimeZone.getTimeZone("GMT+8:00");
TimeZone timeZone2 = TimeZone.getTimeZone("Asia/Shanghai");
System.out.println(timeZone2.getRawOffset());

2.3 Locale

一个 Locale 对象代表一个特定的地理、政治或文化区域。需要指定一个 Locale 对象来执行其任务的操作称为区域敏感的操作,并使用 Locale 对象为用户提供特定区域的定制信息。例如,打印日期是一个区分区域设置的操作,该文本应该根据用户所在国家、地区或文化的习俗和惯例进行格式化。

一个 Locale 对象由客户端给出的 language & country & script(比如四川话) & varient(变体语种) 等信息确定。

Locale.getDefault(); 静态方法,会给出根据服务器操作系统中设置的 language / country 等信息,得到系统默认的 Locale 对象。

// 调用默认的locale
Locale locale = Locale.getDefault();
// zh-CN 华-中国  zh-HK 华-香港 SARzh-MO 华-澳门
System.out.println("locale:"+locale);

Locale 类中定义了以下常量实例:

static public final Locale CHINESE = createConstant("zh", "");

static public final Locale SIMPLIFIED_CHINESE = createConstant("zh", "CN");

static public final Locale TRADITIONAL_CHINESE = createConstant("zh", "TW");	

static public final Locale CHINA = SIMPLIFIED_CHINESE;

static public final Locale PRC = SIMPLIFIED_CHINESE;


static public final Locale ENGLISH = createConstant("en", "");

static public final Locale UK = createConstant("en", "GB");


static public final Locale US = createConstant("en", "US");

static public final Locale FRENCH = createConstant("fr", "");

static public final Locale FRANCE = createConstant("fr", "FR");


static public final Locale CANADA = createConstant("en", "CA");

static public final Locale CANADA_FRENCH = createConstant("fr", "CA");
...

Locale类中还提供了以下工具方法:

public static Locale[] getAvailableLocales() 					{	......    }
public static synchronized void setDefault(Locale newLocale) 	{	......    }
public static String[] getISOCountries() 						{	......    }
public static String[] getISOLanguages() 						{	......    }	

2.4 Calendar

新版不建议使用

Calendar 是个和时区相关的东西,Calendar 的 getInstance() 方法有参数为 TimeZone 和 Locale 的重载,可以使用指定时区和语言环境获得一个日历。无参则使用默认时区和语言环境(系统默认,从系统获取)获得日历。

2.4.1 Locale 对 Calendar 的影响

Locale 对 calendar 时间没什么影响,我们一般用不到,影响体现在下面两个地方。

  1. Locale 使 DateFormat 按所配置的地区特性来输出文字(例如中国,美国,法国不同地区对日期的表示格式不一样, 中国可能是2001年10月5日)
  2. Local 对 calendar.getFirstDayOfWeek() 有影响,不用地区的每周第一天是不相同的,美国是周末,法国是周一。

2.4.2 TimeZone 对 Calendar 的影响

TimeZone 对 Calendar 获取到的时间有直接的影响,最常见的就是 8 小时问题。

Date date = new Date(1391174450000L); // 2014-1-31 21:20:50
System.out.println(date); // Fri Jan 31 21:20:50 CST 2014

Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
calendar.setTime(date);
System.out.println(calendar.get(Calendar.HOUR_OF_DAY) + ":" + calendar.get(Calendar.MINUTE)); // 13:20

可以看到,首先我们使用 系统时区来获取时间,获取到 CST 时间,然后使用 GMT 获取时间,两个时间差了 8 个 小时。

我们可以在项目中设置统一时区,来防止不同操作系统造成时区差。

设置完时区后,我们不能用 calendar.getTime() 来直接获取 Date 日期,因为此时的日期与一开始 setTime 时是相同值,要想获取某时区的时间,正确的做法是用 calendar.get() 方法。

2.5 Date

上文我们好几处用到了 Date 对象,Date 本身是没有时区概念的,就是一个时间戳。

但我们将 Date 格式化字符串和将字符串解析为 Date 对象的时候,是涉及到时区概念的。

格式化 Date 为字符串

Date date = new Date();
// 默认是系统时区
System.out.println(date); // Tue Mar 15 14:04:01 CST 2022
// 修改默认时区
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
System.out.println(date);  // Tue Mar 15 06:04:01 GMT 2022

Date date1 = new Date();
// 默认是系统时区
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(dateFormat.format(date1)); // 2022-03-15 06:05:08
// 设置时区
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+1:00"));
System.out.println(dateFormat.format(date1)); // 2022-03-15 07:05:08

解析字符串为 Date

String dateStr = "2019-12-10 08:00:00";
// 默认是系统时区
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date1 = dateFormat.parse(dateStr);
System.out.println(date1.getTime()); // 1575936000000

// 设置时区
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+1:00"));
Date date2 = dateFormat.parse(dateStr);
System.out.println(date2.getTime()); // 1575961200000

可见时区不同,解析出的时间戳是不相同的

3. java8 改动

java.time 是对 java.util.Date 强有力的补充,解决了 Date 类的大部分痛点:

  1. 非线程安全
  2. 时区处理麻烦
  3. 各种格式化、和时间计算繁琐
  4. 设计有缺陷,Date 类同时包含日期和时间;还有一个 java.sql.Date,容易混淆。

java.util.Date 既包含日期又包含时间,而 java.time 把它们进行了分离

LocalDateTime.class //日期+时间 format: yyyy-MM-ddTHH:mm:ss.SSS
LocalDate.class //日期 format: yyyy-MM-dd
LocalTime.class //时间 format: HH:mm:ss

总结:

  • Clock 类提供了访问当前日期和时间的方法,Clock 是时区敏感的,可以用来取代 System.currentTimeMillis() 来获取当前的微秒数。
  • 某一个特定的时间点可以使用 Instant 类来表示,Instant 类也可以用来创建旧版本的 java.util.Date 对象。
  • 在新 API 中时区使用 ZoneId 来表示,代替旧版的 TimeZone。时区可以很方便的使用静态方法 of 来获取到。 抽象类 ZoneId(在java.time包中)表示一个区域标识符。 它有一个名为 getAvailableZoneIds 的静态方法,它返回所有区域标识符。
  • jdk1.8 中新增了 LocalDate 与 LocalDateTime 等类来解决日期处理方法。可以使用 LocalDateTime 代替 Calendar。
  • 同时引入了一个新的类 DateTimeFormatter 来解决日期格式化问题,可以使用 DateTimeFormatter 代替 SimpleDateFormat。

4. DateTimeFormatter

参考:【Java 8 新特性】Java DateTimeFormatter 日期时间格式化器

我们之前使用 SimpleDateFormat 来格式化日期,java 8 之后,可使用 DateTimeFormatter。它是不可变的,且线程安全。

SimpleDateFormat 线程不安全。

Java 8 之前 转换都需要借助 SimpleDateFormat 类,而 Java 8 之后只需要 LocalDateLocalTimeLocalDateTimeofparse 方法。

4.1 自定义格式化

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 格式化
String dateTime = formatter.format(LocalDateTime.now());
System.out.println(dateTime); //2018-Dec-21 11:14:12
// 解析
LocalDateTime ldt = LocalDateTime.parse("2018-12-20 08:25:30", formatter);
System.out.println(ldt); //2018-12-20T08:25:30
ldt = LocalDateTime.of(2021, 1, 26, 12, 12, 22);
System.out.println(ldt); //2021-01-26T12:12:22

4.2 预定义的格式器

ZonedDateTime zDateTime = ZonedDateTime.now(); 
System.out.println(		zDateTime.format(DateTimeFormatter.BASIC_ISO_DATE)); // 20220511+0800
System.out.println(		zDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE)); // 2022-05-11
System.out.println(		zDateTime.format(DateTimeFormatter.ISO_DATE)); // 2022-05-11+08:00
System.out.println(		zDateTime.format(DateTimeFormatter.ISO_OFFSET_DATE)); // 2022-05-11+08:00
System.out.println(		zDateTime.format(DateTimeFormatter.ISO_INSTANT)); // 2022-05-11T12:36:36.575Z
System.out.println(		zDateTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)); // 2022-05-11T20:36:36.575+08:00[Asia/Shanghai]
System.out.println(		zDateTime.format(DateTimeFormatter.ISO_ORDINAL_DATE)); // 2022-131+08:00
System.out.println(		zDateTime.format(DateTimeFormatter.ISO_WEEK_DATE));		// 2022-W19-3+08:00

4.3 与 Local 相关的格式器

对于日期时间,有4种相关的格式化风格:

  • FormatStyle.SHORT
  • FormatStyle.MEDIUM
  • FormatStyle.LONG
  • FormatStyle.FULL

DateTimeFormatter 类中提供了获取这些风格的实例的静态方法:

  • ofLocalizedDate(FormatStyle)
  • ofLocalizedTime(FormatStyle)
  • ofLocalizedDateTime(FormatStyle)

可以使用DateTimeFormatter的静态方法来获取这些风格的实例,并且这些风格随着Locale改变而不同,如:

ZonedDateTime zDateTime = ZonedDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT);
System.out.println(formatter.format(zDateTime));	// 默认的Locale是根据当前服务器操作系统设置的language、country而获得的Locale。即Locale.CHINESE   22-5-11
System.out.println(formatter.withLocale(Locale.CANADA).format(zDateTime));	// 将Locale设置为CANADA  11/05/22
System.out.println(formatter.withLocale(Locale.ENGLISH).format(zDateTime)); // 5/11/22

5. LocalDateTime

参考:
再也不为日期烦恼——LocalDate的使用
LocalDateTime总结

5.1 各类介绍

  • LocalDate: 只对年月日处理,不涉及时区。
  • LocalTime:只对时分秒纳秒处理,不涉及时区。
  • LocalDateTime:同时处理年月日时分秒,不涉及时区。
  • ZonedDateTime: 包含时区的完整的日期时间,偏移量是以 UTC 时间为基准的。

5.2 LocalDateTime 与时区无关

证明 LocalDateTime 和时区无关:

LocalDateTime now = LocalDateTime.now();
long ms1 = now.toEpochSecond(ZoneOffset.of("+8"));
long ms2 = now.toEpochSecond(ZoneOffset.UTC);
long ms3 = System.currentTimeMillis();
System.out.println(ms1);
System.out.println(ms2);
System.out.println(ms3 / 1000);

// 1664422021
// 1664450821
// 1664422021

我们可以看到我们用 “+8” 时区去获取时间戳时和 GMT 时间戳一致。

这是因为我们获取 LocalDateTime 时使用的是 “GMT+8” 时区,获取到了一个和时区无关的时间。然后我们把这个时间转换为时间戳,ZoneOffset.of("+8") 声明了此时间所用时区,才得到了与 System.currentTimeMillis() 相同的值。

now.toEpochSecond(ZoneOffset.UTC), 相当于我们用 “GMT+8” 获取的时间,本来就多了 8 小时,我们又把它当成 “GMT+0” 时区的值去获取时间戳,就会多 8 个小时。

5.3 基础用法

public static void main(String[] args) {
    //------------------------------【获取当前时间】---------------------------------------------------
    LocalDateTime nowTime = LocalDateTime.now();
    //结果:2021-02-20T09:45:44.527
    System.out.println(nowTime);


    //------------------------------【获取年月日】-----------------------------------------------------
    //【方法1】
    String a = nowTime.format(DateTimeFormatter.ISO_DATE);
    //结果:2021-02-20
    System.out.println(a);

    //【方法2】
    String b = nowTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    //结果:2021-02-20
    System.out.println(b);


    //------------------------------【获取时分秒】-----------------------------------------------------
    String c = nowTime.format(DateTimeFormatter.ofPattern("HH:mm:ss"));
    //结果:09:45:44
    System.out.println(c);


    //------------------------------【获取年月日时分秒毫秒】--------------------------------------------
    String d = nowTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS"));
    //结果:2021-02-20 09:45:44:527
    System.out.println(d);


    //------------------------------【获取秒数】-------------------------------------------------------
    Long second = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
    //结果:1613785755
    System.out.println(second);


    //------------------------------【获取毫秒数】-----------------------------------------------------
    Long milliSecond = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
    //结果:1613785815633
    System.out.println(milliSecond);


    //------------------------------【毫秒转LocalDateTime】--------------------------------------------
    Long longTime = 1613786449976L;
    LocalDateTime ldt = Instant.ofEpochMilli(longTime).atZone(ZoneId.systemDefault()).toLocalDateTime();
    //结果:2021-02-20T10:00:49.976
    System.out.println(ldt);


    //------------------------------【String转LocalDateTime】------------------------------------------
    String dateTimeStr = "2021-02-20 09:50:15";
    DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    LocalDateTime dateTime = LocalDateTime.parse(dateTimeStr, df);
    //结果:2021-02-20T09:50:15
    System.out.println(dateTime);


    //------------------------------【LocalDateTime转String】------------------------------------------
    //除了下面的方法之外,上面类型转换的都可以看成是LocalDateTime转String  ZoneOffset.of("+8")意思为:默认时区为东8区
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    String dateTime2 = LocalDateTime.now(ZoneOffset.of("+8")).format(formatter);
    //结果:2021-02-20 09:55:52
    System.out.println(dateTime2);


    //------------------------------【LocalDateTime转Date】--------------------------------------------
    Date date = new Date();
    LocalDateTime localDateTime = date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime();
    //结果:2021-02-20T09:59:14.719
    System.out.println(localDateTime);


    //------------------------------【Date转LocalDateTime】--------------------------------------------
    LocalDateTime localDateTime1 = LocalDateTime.now();
    Date date1 = Date.from(localDateTime1.toInstant(ZoneOffset.of("+8")));
    //结果:Sat Feb 20 10:00:49 CST 2021
    System.out.println(date1);


    //------------------------------【LocalDateTime获取年】--------------------------------------------
    System.out.println("当前时间为:" + LocalDateTime.now() + " == " + "2021-02-20T10:23:45.041");
    int year = LocalDateTime.now().getYear();
    //结果:2021
    System.out.println(year);


    //------------------------------【LocalDateTime获取月】--------------------------------------------
    Month month = LocalDateTime.now().getMonth();
    //结果:FEBRUARY
    System.out.println(month);
    int monthValue = LocalDateTime.now().getMonthValue();
    //结果:2
    System.out.println(monthValue);


    //------------------------------【LocalDateTime获取日】--------------------------------------------
    int dayOfMonth = LocalDateTime.now().getDayOfMonth();
    //结果:20
    System.out.println(dayOfMonth);
    DayOfWeek dayOfWeek = LocalDateTime.now().getDayOfWeek();
    //结果:SATURDAY
    System.out.println(dayOfWeek);
    int dayOfYear = LocalDateTime.now().getDayOfYear();
    //结果:51
    System.out.println(dayOfYear);


    //------------------------------【LocalDateTime获取时】--------------------------------------------
    int hour = LocalDateTime.now().getHour();
    //结果:10
    System.out.println(hour);


    //------------------------------【LocalDateTime获取分】--------------------------------------------
    int minute = LocalDateTime.now().getMinute();
    //结果:23
    System.out.println(minute);


    //------------------------------【LocalDateTime获取秒】--------------------------------------------
    int second1 = LocalDateTime.now().getSecond();
    //结果:45
    System.out.println(second1);


    //------------------------------【LocalDateTime获取特定日期】---------------------------------------
    LocalDateTime of = LocalDateTime.of(2021, 2, 20, 10, 37, 20);
    //结果:2021-02-20T10:37:20
    System.out.println(of);


    //------------------------------【LocalDateTime时间对比】-------------------------------------------
    LocalDateTime of1 = LocalDateTime.of(2021, 2, 20, 10, 37, 20);
    LocalDateTime of2 = LocalDateTime.of(2021, 2, 20, 10, 37, 22);
    LocalDateTime of3 = LocalDateTime.of(2021, 2, 20, 10, 37, 22);
    //【方法1】
    //结果:false
    System.out.println(of1.equals(of2));
    //结果:true
    System.out.println(of2.equals(of3));
    //结果:-1 of1 < of2
    System.out.println(of1.compareTo(of2));
    //结果:0 of2 = of3
    System.out.println(of2.compareTo(of3));
    //结果:1 of3 > of1
    System.out.println(of3.compareTo(of1));

    //【方法2】
    //结果:false
    System.out.println(of1.isAfter(of2));
    //结果:true
    System.out.println(of1.isBefore(of2));
    //结果:true
    System.out.println(of2.isEqual(of3));


    //------------------------------【LocalDateTime获取一天前/后日期】-----------------------------------
    LocalDateTime localDateTime2 = LocalDateTime.now().plusDays(1);
    //结果:2021-02-21T10:48:26.911
    System.out.println(localDateTime2);
    LocalDateTime localDateTime3 = LocalDateTime.now().plusDays(-1);
    //结果:2021-02-19T10:48:26.911
    System.out.println(localDateTime3);


    //------------------------------【LocalDateTime获取一小时前/后日期】-----------------------------------
    LocalDateTime localDateTime4 = LocalDateTime.now().plusHours(1);
    //结果:2021-02-20T11:49:32.320
    System.out.println(localDateTime4);
    LocalDateTime localDateTime5 = LocalDateTime.now().plusHours(-1);
    //结果:2021-02-20T09:49:32.320
    System.out.println(localDateTime5);


    //------------------------------【LocalDateTime获取一年前/后日期】-------------------------------------
    LocalDateTime localDateTime6 = LocalDateTime.now().plusYears(1);
    //结果:2022-02-20T10:51:47.585
    System.out.println(localDateTime6);
    LocalDateTime localDateTime7 = LocalDateTime.now().plusYears(-1);
    //结果:2020-02-20T10:51:47.585
    System.out.println(localDateTime7);


    //------------------------------【LocalDateTime获取一分钟前/后日期】-----------------------------------
    LocalDateTime localDateTime8 = LocalDateTime.now().plusMinutes(1);
    //结果:2021-02-20T13:54:39.209
    System.out.println(localDateTime8);
    LocalDateTime localDateTime9 = LocalDateTime.now().plusMinutes(-1);
    //结果:2021-02-20T13:52:39.209
    System.out.println(localDateTime9);


    //------------------------------【LocalDateTime获取一月前/后日期】-------------------------------------
    LocalDateTime localDateTime10 = LocalDateTime.now().plusMonths(1);
    //结果:2021-03-20T13:55:12.718
    System.out.println(localDateTime10);
    LocalDateTime localDateTime11 = LocalDateTime.now().plusMonths(-1);
    //结果:2021-01-20T13:55:12.718
    System.out.println(localDateTime11);


    //------------------------------【LocalDateTime获取一秒前/后日期】-------------------------------------
    LocalDateTime localDateTime12 = LocalDateTime.now().plusSeconds(1);
    //结果:2021-02-20T13:57:51.966
    System.out.println(localDateTime12);
    LocalDateTime localDateTime13 = LocalDateTime.now().plusSeconds(-1);
    //结果:2021-02-20T13:57:49.966
    System.out.println(localDateTime13);


    //------------------------------【LocalDateTime获取一周前/后日期】-------------------------------------
    LocalDateTime localDateTime14 = LocalDateTime.now().plusWeeks(1);
    //结果:2021-02-27T13:59:46.615
    System.out.println(localDateTime14);
    LocalDateTime localDateTime15 = LocalDateTime.now().plusWeeks(-1);
    //结果:2021-02-13T13:59:46.615
    System.out.println(localDateTime15);
}

6. Clock

Clock 类提供了访问当前日期和时间的方法,Clock 是时区敏感的,可以用来取代 System.currentTimeMillis() 来获取当前的微秒数。某一个特定的时间点也可以使用 Instant 类来表示,Instant 类也可以用来创建旧版本的java.util.Date 对象。

Clock clock = Clock.systemDefaultZone();
long millis = clock.millis();
System.out.println(millis);//1552379579043
Instant instant = clock.instant();
System.out.println(instant);
Date legacyDate = Date.from(instant); //2019-03-12T08:46:42.588Z
System.out.println(legacyDate);//Tue Mar 12 16:32:59 CST 2019

7. ZoneId

在新 API 中时区使用 ZoneId 来表示。时区可以很方便的使用静态方法 of 来获取到。 抽象类ZoneId(在java.time包中)表示一个区域标识符。 它有一个名为getAvailableZoneIds 的静态方法,它返回所有区域标识符。

//输出所有区域标识符
System.out.println(ZoneId.getAvailableZoneIds());

ZoneId zone1 = ZoneId.of("Europe/Berlin");
ZoneId zone2 = ZoneId.of("Brazil/East");
System.out.println(zone1.getRules());// ZoneRules[currentStandardOffset=+01:00]
System.out.println(zone2.getRules());// ZoneRules[currentStandardOffset=-03:00]

8. ZoneOffset

时区偏移量,是时区相对于格林威治时间精确到秒。
有三个常量实例:

public static final ZoneOffset UTC = ZoneOffset.ofTotalSeconds(0);  // 0时区,即UTC时间。偏移量为0秒。
public static final ZoneOffset MIN = ZoneOffset.ofTotalSeconds(-MAX_SECONDS);  // -18:00。偏移量为-18*60*60秒
public static final ZoneOffset MAX = ZoneOffset.ofTotalSeconds(MAX_SECONDS);   // +18:00。偏移量为+18*60*60秒

可以通过静态方法ZoneOffset.of( String offsetId ) 获取一个ZoneOffset的实例。

ZoneOffset offset = ZoneOffset.of("+08:00");
System.out.println(offset);		// +08:00
System.out.println(ZoneOffset.ofHours(8)); // +08:00
System.out.println(ZoneOffset.ofHoursMinutes(-8, -23));   //  时、分、秒的正负号必须完全一致  -08:23
System.out.println(offset.getTotalSeconds()); // 28800
System.out.println(ZoneOffset.ofTotalSeconds(28800));	 // +08:00

9. ChronoUnit

计时单位的封装类

LocalTime time1 = LocalTime.of(1, 1, 0);
LocalTime time2 = LocalTime.of(2, 2, 0);
System.out.println(ChronoUnit.HOURS.between(time1, time2)); //1
System.out.println(ChronoUnit.MINUTES.between(time1, time2)); //61

10. Duration

TemporalAmount 接口是定义时间量的框架级接口,如“6小时”、“8天”或“2年零3个月”。

Duration 类是 TemporalAmout 接口的实现类,类中定义了一些生成 Duration 实例的静态方法:

System.out.println( Duration.ofDays(2));	// 2天
System.out.println( Duration.ofHours(2));	// 2小时
System.out.println( Duration.ofMinutes(2));
System.out.println( Duration.ofSeconds(2));
System.out.println( Duration.ofMillis(2));
System.out.println( Duration.ofNanos(2));	// 2纳秒,注意毫秒和纳秒中间没有微秒:micro

11. Instant

Instant 表示一个时间戳,和旧版的 Date 可以互转。

记录着自 1970年1月1日 00:00:00 UTC 到当前时间的刻度。

Instant instant = Instant.now();
System.out.println(instant.toEpochMilli());    // 1684676392921
System.out.println(instant.getEpochSecond());    // 1684676392
// Date 与  Instant 互转
Date date = new Date();
System.out.println(date.toInstant());    // Date 转 Instant
System.out.println(Date.from(instant));    // Instant 转 Date

12. ZonedDateTime

ZonedDateTime 与 LocalDateTime 的区别就是: LocalDateTime 总是表示本地日期和时间,而 ZonedDateTime 表示一个带时区的日期和时间。

常用方法:

//从默认时区中的系统时钟中获取当前日期时间
static ZonedDateTime now() 

//从指定的时钟中获取当前日期时间
static ZonedDateTime now(Clock clock) 
 
//从指定时区中的系统时钟中获得当前日期时间  
static ZonedDateTime now(ZoneId zone) 

//获得 ZonedDateTime实例从年,月,日,小时,分钟,秒,纳秒和时区
static ZonedDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond, ZoneId zone) 
  
//获得 ZonedDateTime实例从本地日期和时间
static ZonedDateTime of(LocalDate date, LocalTime time, ZoneId zone) 
 
//获得 ZonedDateTime实例从本地日期时间
static ZonedDateTime of(LocalDateTime localDateTime, ZoneId zone) 
  
//获得 ZonedDateTime实例从一个文本字符串,如 2007-12-03T10:15:30+01:00[Europe/Paris]
static ZonedDateTime parse(CharSequence text) 

//获得 ZonedDateTime实例从使用特定格式的文本字符串
static ZonedDateTime parse(CharSequence text, DateTimeFormatter formatter) 
   
//返回此日期时间的副本,以不同的时区,保留即时
ZonedDateTime withZoneSameInstant(ZoneId zone) 

//结合时间与时区来创建一个 ZonedDateTime
ZonedDateTime atZone(ZoneId zone)

转换为 ms:

ZonedDateTime now = ZonedDateTime.now();
System.out.println(now.toInstant().toEpochMilli());

例子:

//当前时区时间
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println("当前时区时间: " + zonedDateTime);

//东京时间
ZoneId zoneId = ZoneId.of(ZoneId.SHORT_IDS.get("JST"));
ZonedDateTime tokyoTime = zonedDateTime.withZoneSameInstant(zoneId);
System.out.println("东京时间: " + tokyoTime);

// ZonedDateTime 转 LocalDateTime
LocalDateTime localDateTime = tokyoTime.toLocalDateTime();
System.out.println("东京时间转当地时间: " + localDateTime);

//LocalDateTime 转 ZonedDateTime
ZonedDateTime localZoned = localDateTime.atZone(ZoneId.systemDefault());
System.out.println("本地时区时间: " + localZoned);

//打印结果
当前时区时间: 2021-01-27T14:43:58.735+08:00[Asia/Shanghai]
东京时间: 2021-01-27T15:43:58.735+09:00[Asia/Tokyo]
东京时间转当地时间: 2021-01-27T15:43:58.735
当地时区时间: 2021-01-27T15:53:35.618+08:00[Asia/Shanghai]

13. OffsetDateTime

带有时区偏移量信息的日期时间对象。
与 ZoneDateTime 的区别是,只有时区偏移量信息,没有时区ID。即仅记录时区偏移量,而不记录时区ID。

System.out.println(OffsetDateTime.now());
System.out.println(OffsetDateTime.of(2011, 5, 11, 15, 43, 23, 0, ZoneOffset.of("+08:00")));

// 2022-05-11T15:35:20.837+08:00
// 2011-05-11T15:43:23+08:00

14. TemporalAdjusters 时间调整器

所有实现了 Temporal 接口的类的实例,都可以调用自身的 with( ajuster ) 方法,来调整自身的日期时间。

工具类 TemporalAdjusters 提供了许多生成 adjuster 实例的静态方法,这些 adjuster 可以实现复杂的日期调整。
需要注意的是,TemporalAdjusters 只提供了调整日期、星期的 adjuster 生成器,没有年、月、时、分、秒相关的生成器。


LocalDateTime localDateTime = LocalDateTime.now();

System.out.println(localDateTime.with(    TemporalAdjusters.firstDayOfMonth()));   // 将instant的日期调整为本月第一天,注意只是day变了,年月、时间都没变。
System.out.println(localDateTime.with(    TemporalAdjusters.firstDayOfYear()));
System.out.println(localDateTime.with(    TemporalAdjusters.firstDayOfNextMonth()));
System.out.println(localDateTime.with(    TemporalAdjusters.firstDayOfNextYear()));
System.out.println(localDateTime.with(    TemporalAdjusters.lastDayOfMonth()));
System.out.println(localDateTime.with(    TemporalAdjusters.lastDayOfYear()));
System.out.println(localDateTime.with(    TemporalAdjusters.ofDateAdjuster(localDate->localDate.plusDays(2))));

System.out.println(localDateTime.with(    TemporalAdjusters.firstInMonth(DayOfWeek.FRIDAY)));  	// 当月的第一个周五
System.out.println(localDateTime.with(    TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY)));  	// 当月的最后一个周五
System.out.println(localDateTime.with(    TemporalAdjusters.dayOfWeekInMonth(2, DayOfWeek.FRIDAY)));  	// 当月的第2个周五
System.out.println(localDateTime.with(    TemporalAdjusters.previous( DayOfWeek.FRIDAY)));  	// 今天之前、距离今天最近的周五
System.out.println(localDateTime.with(    TemporalAdjusters.next( DayOfWeek.FRIDAY)));  		// 今天之后、距离今天最近的周五
System.out.println(localDateTime.with(    TemporalAdjusters.previousOrSame( DayOfWeek.FRIDAY)));  // 今天或之前的最近的一个周五
System.out.println(localDateTime.with(    TemporalAdjusters.nextOrSame( DayOfWeek.FRIDAY)));  	// 今天或之后最近的一个周五