【Java 8系列】Java日期时间的新主宰者:LocalDate、LocalTime、LocalDateTime、ZonedDateTime

热门系列:


目录

1、前言

2、正文

2.1 LocalDate、LocalTime、LocalDateTime

2.1.1 获取当前时间

2.1.2 获取当前时间毫秒数/时间戳

2.1.3 获取年、月、日与星期几

2.1.4 设置自定义时间

2.1.5 日期/时间替换成指定的日期/时间

2.1.6 日期时间与字符串相互转换

2.1.7 Date与LocalDateTime相互转换

2.1.8 获取偏移日期时间

2.2 ZonedDateTime

2.3 ZoneId

2.4 Instant

2.5 时间工具类

3、总结


1、前言

本系列一直在分享Java8的新增特性与API,今天和大家一起扒一扒Java的新时间类库:java.time

之前我在往期博文:【Java编程系列】Java判断世界各时区的夏令时、冬令时 中就有提到过,为什么现在不适用Date时间类,而改用java.time库中的时间类。答案很简单:就是多线程安全性问题


2、正文

本文主要分享和举例一些常用的类,下面正式开整!!本篇的时间类,都是使用的ISO-8601日历系统时间

ISO-8601日历系统是当今世界上大多数地方使用的现代民用日历系统。它等效于多用的格里高利历系统,该系统在今天一直适用today年的规则。对于当今编写的大多数应用程序,ISO-8601规则完全适用。但是,任何利用历史日期并要求它们准确的应用程序都将发现ISO-8601方法不合适。

2.1 LocalDate、LocalTime、LocalDateTime

注:以上3个类,在使用和功能上基本一样,只是所代表的对象不同,所以放在一起讲解!

  • LocalDate:是一个不可变的日期时间对象,代表一个日期,通常被视为年-月-日;此类是不可变的并且是线程安全的。
  • LocalTime:是一个不可变的日期时间对象,代表一个时间,通常被视为时分秒。时间以纳秒精度表示;此类是不可变的并且是线程安全的。
  • LocalDateTime:是一个不变的日期时间对象,代表一个日期时间,通常被视为年-月-日-时-分-秒。也可以访问其他日期和时间字段,例如,一年中的某天,一周中的某天和一周中的某周。时间以纳秒精度表示;此类是不可变的并且是线程安全的。

下面来看看常用的一些方法,三者取其一,咱们以LocalDateTime为例展开(举栗子一目了然,所以下面尽可能以代码形式讲解):

2.1.1 获取当前时间

public static void main(String[] args) {
		//获取当前时间
		LocalDateTime localDateTime = LocalDateTime.now();
		System.out.println("本地当前日期时间:"+localDateTime);
		System.out.println();
		
		//设置时区为美国纽约
		System.out.println("美国当前日期时间:"+LocalDateTime.now(ZoneId.of("America/New_York")));
		System.out.println();
		
}

输出:

本地当前日期时间:2020-12-28T15:25:20.707

美国当前日期时间:2020-12-28T02:25:20.708

2.1.2 获取当前时间毫秒数/时间戳

public static void main(String[] args) {
		//获取当前时间
		LocalDateTime localDateTime = LocalDateTime.now();
		System.out.println("本地当前日期时间:"+localDateTime);
		
		long millisecond = localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
		System.out.println("获取时间戳:"+millisecond/1000);
		System.out.println("获取毫秒数:"+millisecond);
		System.out.println();
}

输出:

本地当前日期时间:2020-12-28T16:43:32.971
获取时间戳:1609145012
获取毫秒数:1609145012971

2.1.3 获取年、月、日与星期几

public static void main(String[] args) {
		//获取当前时间
		LocalDateTime localDateTime = LocalDateTime.now();
		System.out.println("本地当前日期时间:"+localDateTime);
		
		//获取年、月、日、星期几
		int year = localDateTime.getYear();
		System.out.println("获取年份(第一种方式):"+year);
		year = localDateTime.get(ChronoField.YEAR);
		System.out.println("获取年份(第二种方式):"+year);
		System.out.println();
			
		int month = localDateTime.getMonthValue();
		System.out.println("获取月份(第一种方式):"+month);
		month = localDateTime.getMonth().getValue();
		System.out.println("获取月份(第二种方式):"+month);
		month = localDateTime.get(ChronoField.MONTH_OF_YEAR);
		System.out.println("获取月份(第三种方式):"+month);
		System.out.println();
		
		int dayOfWeek = localDateTime.getDayOfWeek().getValue();
		System.out.println("获取星期几(第一种方式):"+dayOfWeek);
		dayOfWeek = localDateTime.get(ChronoField.DAY_OF_WEEK);
		System.out.println("获取星期几(第二种方式):"+dayOfWeek);
		System.out.println();
		
		int dayOfMonth = localDateTime.getDayOfMonth();
		System.out.println("获取本月当日(第一种方式):"+dayOfMonth);
		dayOfMonth = localDateTime.get(ChronoField.DAY_OF_MONTH);
		System.out.println("获取本月当日(第二种方式):"+dayOfMonth);
		System.out.println();
		
		int dayOfYear = localDateTime.getDayOfYear();
		System.out.println("获取本年当日(第一种方式):"+dayOfYear);
		dayOfYear = localDateTime.get(ChronoField.DAY_OF_YEAR);
		System.out.println("获取本年当日(第二种方式):"+dayOfYear);
		
}

输出:

本地当前日期时间:2020-12-29T10:32:49.698
获取年份(第一种方式):2020
获取年份(第二种方式):2020

获取月份(第一种方式):12
获取月份(第二种方式):12
获取月份(第三种方式):12

获取星期几(第一种方式):2
获取星期几(第二种方式):2

获取本月当日(第一种方式):29
获取本月当日(第二种方式):29

获取本年当日(第一种方式):364
获取本年当日(第二种方式):364

2.1.4 设置自定义时间

public static void main(String[] args) {
		
		//设置自定义时间
		LocalDateTime tempTime = LocalDateTime.of(2020,11,28,15,00,01);
		System.out.println("自定义设置的日期时间:"+tempTime);
		System.out.println();

		//设置自定义日期
		LocalDate tempDate = LocalDate.of(2019,10,1);
		System.out.println("自定义设置的日期:"+tempDate);
		System.out.println();

}

输出:

自定义设置的日期时间:2020-11-28T15:00:01

自定义设置的日期:2019-10-01

2.1.5 日期/时间替换成指定的日期/时间

这里会用到一个方法,adjustInto():将指定的时间对象调整为具有与此对象相同的日期和时间

public static void main(String[] args) {
		//获取当前时间
		LocalDateTime localDateTime = LocalDateTime.now();
		System.out.println("本地当前日期时间:"+localDateTime);
		System.out.println();
		
		//设置自定义时间
		LocalDateTime tempTime = LocalDateTime.of(2020,11,28,15,00,01);
		System.out.println("自定义设置的日期时间:"+tempTime);
		System.out.println();

//		//设置自定义日期
		LocalDate tempDate = LocalDate.of(2019,10,1);
		System.out.println("自定义设置的日期:"+tempDate);
		System.out.println();

		//通过adjustInto方法实现日期转换
		//将指定的时间对象调整为具有与此对象相同的日期和时间
		localDateTime = (LocalDateTime)tempDate.adjustInto(localDateTime);
		tempTime = (LocalDateTime)tempDate.adjustInto(tempTime);
		System.out.println("转换后的localDateTime:"+localDateTime);
		System.out.println("转换后的tempTime:"+tempTime);
		System.out.println();
		
}

输出:

本地当前日期时间:2020-12-28T15:30:20.609

自定义设置的日期时间:2020-11-28T15:00:01

自定义设置的日期:2019-10-01

转换后的localDateTime:2019-10-01T15:30:20.609
转换后的tempTime:2019-10-01T15:00:01

2.1.6 日期时间与字符串相互转换

private static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
private static final String YYYYMMDD_HH_MM_SS = "yyyy/MM/dd HH:mm:ss";

public static void main(String[] args) {
	//获取当前时间
	LocalDateTime localDateTime = LocalDateTime.now();
	System.out.println("本地当前日期时间:"+localDateTime);
	
	//日期与字符串相互转换
	//日期转字符串
	String dateTimeStr = localDateTime.format(DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS));
	System.out.println("日期时间格式化成字符串后:"+dateTimeStr);
	
	//字符串转日期
	String convertTime = "2020/12/12 12:12:12";
	LocalDateTime convertLocalDateTime = LocalDateTime.parse(convertTime,DateTimeFormatter.ofPattern(YYYYMMDD_HH_MM_SS));
	System.out.println("由字符串转换成的日期时间为:"+convertLocalDateTime);
}

输出:

本地当前日期时间:2020-12-29T10:43:45.295
日期时间格式化成字符串后:2020-12-29 10:43:45
由字符串转换成的日期时间为:2020-12-12T12:12:12

2.1.7 Date与LocalDateTime相互转换

private static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
private static final String YYYYMMDD_HH_MM_SS = "yyyy/MM/dd HH:mm:ss";

public static void main(String[] args) {
	//获取当前时间
	LocalDateTime localDateTime = LocalDateTime.now();
	System.out.println("本地当前日期时间:"+localDateTime);
	
	//Date转换成LocalDateTime
	Date date = new Date();
	LocalDateTime date2LocalDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
	System.out.println("Date转换成LocalDateTime后>>>>"+date2LocalDateTime);
		
	//LocalDateTime转换成Date
	Date localDateTime2Date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
	System.out.println("LocalDateTime转换成Date后>>>>"+localDateTime2Date);
	System.out.println("格式化一下>>>>"+FastDateFormat.getInstance(YYYY_MM_DD_HH_MM_SS).format(localDateTime2Date));
}

输出:

本地当前日期时间:2020-12-29T11:02:16.883
Date转换成LocalDateTime后>>>>2020-12-29T11:02:16.884
LocalDateTime转换成Date后>>>>Tue Dec 29 11:02:16 CST 2020
格式化一下>>>>2020-12-29 11:02:16

2.1.8 获取偏移日期时间

此处需要提一下,会常用的两个类似功能的方法:minus()和 plus()其中还有一些类似的指定方法,如:

LocalDateTimeminus​(long amountToSubtract, TemporalUnit unit)
返回此日期时间的副本,减去指定的数量。
LocalDateTimeminus​(TemporalAmount amountToSubtract)
返回此日期时间的副本,减去指定的数量。
LocalDateTimeminusDays​(long days)
返回此副本的副本,LocalDateTime其中减去指定的天数。
LocalDateTimeminusHours​(long hours)
返回此副本的副本,LocalDateTime其中减去指定的小时数。
LocalDateTimeminusMinutes​(long minutes)
返回此副本的副本,LocalDateTime其中减去指定的分钟数。
LocalDateTimeminusMonths​(long months)
返回此副本的副本,LocalDateTime其中减去指定的月数。
LocalDateTimeminusNanos​(long nanos)
返回此副本的副本,LocalDateTime其中减去指定的纳秒数。
LocalDateTimeminusSeconds​(long seconds)
返回此副本的副本,LocalDateTime其中减去指定的秒数。
LocalDateTimeminusWeeks​(long weeks)
返回此副本的副本,LocalDateTime其中减去指定的周数。
LocalDateTimeminusYears​(long years)
返回此副本的副本,LocalDateTime其中减去指定的年数。
LocalDateTimeplus​(long amountToAdd, TemporalUnit unit)
返回此日期时间的副本,其中添加了指定的数量。
LocalDateTimeplus​(TemporalAmount amountToAdd)
返回此日期时间的副本,其中添加了指定的数量。
LocalDateTimeplusDays​(long days)
返回LocalDateTime带有指定天数的副本。
LocalDateTimeplusHours​(long hours)
返回LocalDateTime带有指定小时数的副本。
LocalDateTimeplusMinutes​(long minutes)
返回LocalDateTime带有指定分钟数的副本。
LocalDateTimeplusMonths​(long months)
返回LocalDateTime带有指定月份数的副本。
LocalDateTimeplusNanos​(long nanos)
返回此副本的副本,LocalDateTime其中添加了指定的纳秒数。
LocalDateTimeplusSeconds​(long seconds)
返回此副本的副本,LocalDateTime其中添加了指定的秒数。
LocalDateTimeplusWeeks​(long weeks)
返回此副本的副本,LocalDateTime其中添加了指定的周数。
LocalDateTimeplusYears​(long years)
返回LocalDateTime带有指定年限的副本。

下面列举几个常用栗子:

public static void main(String[] args) {
	//获取当前时间
	LocalDateTime localDateTime = LocalDateTime.now(ZoneId.systemDefault());
	System.out.println("本地当前日期时间:"+localDateTime);
	
	//获取偏移时间
	//偏移天(正数:往后偏移;负数:往过去时间偏移)
	System.out.println("明日此时:"+localDateTime.plusDays(1));
	System.out.println("明日此时1:"+localDateTime.minusDays(-1));
	System.out.println("前天此时:"+localDateTime.plusDays(-2));
	//偏移时
	System.out.println("当前时间后2个小时:"+localDateTime.plusHours(2));
	System.out.println("当前时间前5个小时:"+localDateTime.plusHours(-5));
	//偏移年
	System.out.println("去年此时:"+localDateTime.plusYears(-1));
	//偏移月
	System.out.println("下月此时:"+localDateTime.plusMonths(1));
	//偏移周
	System.out.println("上周此时:"+localDateTime.plusWeeks(-1));
}

输出:

本地当前日期时间:2020-12-29T11:22:24.914
明日此时:2020-12-30T11:22:24.914
明日此时1:2020-12-30T11:22:24.914
前天此时:2020-12-27T11:22:24.914
当前时间后2个小时:2020-12-29T13:22:24.914
当前时间前5个小时:2020-12-29T06:22:24.914
去年此时:2019-12-29T11:22:24.914
下月此时:2021-01-29T11:22:24.914
上周此时:2020-12-22T11:22:24.914

2.2 ZonedDateTime

ZonedDateTime其实就是含有时区的LocalDateTime,其实就是LocalDateTime+ZoneId,其对照关系可以如图:

所以,其对应的功能基本与上述一致,因此不再赘述!


2.3 ZoneId

ZoneId是时区ID类,主要包含了各时区信息!一般对时区没有要求的情况,我们会使用:

ZoneId.systemDefault()

来设置默认时区!

通过源代码查看,目前常用的时区映射关系有如下这些:

public static final Map<String, String> SHORT_IDS;

/** 
* 当前为ZoneId源码,除了   
* 以下中文注释为作者个人翻译而来,非源码注释
**/
static {
    Map<String, String> map = new HashMap<>(64);

    //"Australia/Darwin","澳洲/达尔文"
    map.put("ACT", "Australia/Darwin");

    //"Australia/Sydney","澳洲/悉尼"
    map.put("AET", "Australia/Sydney");

    //"America/Argentina/Buenos_Aires","美洲/阿根廷/布宜诺斯艾利斯"
    map.put("AGT", "America/Argentina/Buenos_Aires");

    //"Africa/Cairo","非洲/开罗"
    map.put("ART", "Africa/Cairo");

    //"America/Anchorage","美洲/安克雷奇"
    map.put("AST", "America/Anchorage");

    //"America/Sao_Paulo","美洲/圣保罗"
    map.put("BET", "America/Sao_Paulo");

    //"Asia/Dhaka","亚洲/达卡"
    map.put("BST", "Asia/Dhaka");

    //"Africa/Harare","非洲/哈拉雷"
    map.put("CAT", "Africa/Harare");
    
    //"America/St_Johns","美洲/圣约翰"
    map.put("CNT", "America/St_Johns");

    //"America/Chicago","美洲/芝加哥"
    map.put("CST", "America/Chicago");

    //"Asia/Shanghai","亚洲/上海"
    map.put("CTT", "Asia/Shanghai");

    //"Africa/Addis_Ababa","非洲/亚的斯亚贝巴"
    map.put("EAT", "Africa/Addis_Ababa");

    //"Europe/Paris","欧洲/巴黎"
    map.put("ECT", "Europe/Paris");

    //"America/Indiana/Indianapolis","美洲/印第安纳州/印第安纳波利斯"
    map.put("IET", "America/Indiana/Indianapolis");

    //"Asia/Kolkata","亚洲/加尔各答"
    map.put("IST", "Asia/Kolkata");

    //"Asia/Tokyo","亚洲/东京"
    map.put("JST", "Asia/Tokyo");

    //"Pacific/Apia","太平洋/阿皮亚"
    map.put("MIT", "Pacific/Apia");

    //"Asia/Yerevan","亚洲/埃里温"
    map.put("NET", "Asia/Yerevan");

    //"Pacific/Auckland","太平洋/奥克兰"
    map.put("NST", "Pacific/Auckland");

    //"Asia/Karachi","亚洲/卡拉奇"
    map.put("PLT", "Asia/Karachi");

    //"America/Phoenix","美洲/凤凰城"
    map.put("PNT", "America/Phoenix");

    //"America/Puerto_Rico","美洲/波多黎各"
    map.put("PRT", "America/Puerto_Rico");

    //"America/Los_Angeles","美洲/洛杉矶"
    map.put("PST", "America/Los_Angeles");

    //"Pacific/Guadalcanal","太平洋/瓜达尔卡纳尔岛"
    map.put("SST", "Pacific/Guadalcanal");
    
    //"Asia/Ho_Chi_Minh","亚洲/胡志明市"
    map.put("VST", "Asia/Ho_Chi_Minh");

    //"-05:00","东部标准时间"(纽约、华盛顿)
    map.put("EST", "-05:00");

    //"-07:00","山地标准时间"
    map.put("MST", "-07:00");
    
    //"-10:00","夏威夷-阿留申标准时区"
    map.put("HST", "-10:00");
    SHORT_IDS = Collections.unmodifiableMap(map);
}

举个栗子:

System.out.println("美国纽约当前日期时间:"+LocalDateTime.now(ZoneId.of("America/New_York")));

//输出:
美国纽约当前日期时间:2020-12-29T04:10:53.257

2.4 Instant

Instant类,是包含时间线上的某一个瞬时点。

举个栗子:

public static void main(String[] args) {
	//获取当前时间
	LocalDateTime localDateTime = LocalDateTime.now(ZoneId.systemDefault());
	System.out.println("本地当前日期时间戳:"+localDateTime.atZone(ZoneId.systemDefault()).toEpochSecond());
	System.out.println();

	Instant instant = Instant.from(localDateTime.atZone(ZoneId.systemDefault()).plusHours(-1));
	System.out.println("前一小时此时的时间戳:"+instant.getEpochSecond());
	System.out.println("前一小时此时的毫秒数:"+instant.toEpochMilli());
}

输出:

本地当前日期时间戳:1609235707

//刚好相差3600秒
前一小时此时的时间戳:1609232107
前一小时此时的毫秒数:1609232107078

 


2.5 时间工具类

/**
 * Date工具类
 * @author Yangy
 */
public class DateUtils {

	private static final ZoneId ZONE_ID = ZoneId.systemDefault();

	/**
	 * LocalDateTime转化为Date
	 * 
	 * @param localDateTime
	 * @return
	 */
	public static Date toDate(LocalDateTime localDateTime) {
		return Date.from(localDateTime.atZone(ZONE_ID).toInstant());
	}

	/**
	 * LocalDateTime转化为Date
	 * 
	 * @param localDateTime
	 * @return
	 */
	public static Date toDate(LocalDate localDate) {
		return Date.from(localDate.atStartOfDay(ZONE_ID).toInstant());
	}

	/**
	 * Date转化为LocalDateTime
	 * 
	 * @param date
	 * @return
	 */
	public static LocalDateTime toLocalDateTime(Date date) {
		return LocalDateTime.ofInstant(date.toInstant(), ZONE_ID);
	}

	/**
	 * LocalDate转化为LocalDateTime
	 * 
	 * @param localDate
	 * @return
	 */
	public static LocalDateTime toLocalDateTime(LocalDate localDate) {
		return LocalDateTime.of(localDate, LocalTime.MIN);
	}

	/**
	 * Date转化为LocalDate
	 * 
	 * @param date
	 * @return
	 */
	public static LocalDate toLocalDate(Date date) {
		return date.toInstant().atZone(ZONE_ID).toLocalDate();
	}

	/**
	 * Date转化为字符串
	 * 
	 * @param date
	 * @param formatter
	 * @return
	 */
	public static String format(Date date, DateFormatter formatter) {
		LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZONE_ID);
		return formatter.getDateTimeFormatter().format(localDateTime);
	}

	/**
	 * 字符串转化为Date
	 * 
	 * @param text
	 * @param formatter
	 * @return
	 */
	public static Date parse(String text, DateFormatter formatter) {
		return formatter.parse(text);
	}

	public static enum DateFormatter {

		/**
		 * 格式yyyy
		 *
		 */
		YEAR_FORMATTER(DateTimeFormatter.ofPattern("yyyy", Locale.CHINA)) {
			@Override
			public Date parse(String text) {
				Year year = Year.parse(text, dateTimeFormatter);
				return Date.from(year.atDay(1).atStartOfDay(ZONE_ID).toInstant());
			}
		},

		/**
		 * 
		 * 格式yyyy-MM
		 *
		 */
		YEAR_MONTH_FORMATTER(DateTimeFormatter.ofPattern("yyyy-MM", Locale.CHINA)) {
			@Override
			public Date parse(String text) {
				YearMonth yearMonth = YearMonth.parse(text, dateTimeFormatter);
				return Date.from(yearMonth.atDay(1).atStartOfDay(ZONE_ID).toInstant());
			}
		},

		/**
		 * 
		 * 格式yyyy-MM-dd
		 * 
		 * @author Val Song Dec 17, 2017 7:26:25 PM
		 *
		 */
		DATE_FORMATTER(DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.CHINA)) {
			@Override
			public Date parse(String text) {
				LocalDate localDate = LocalDate.parse(text, dateTimeFormatter);
				return Date.from(localDate.atStartOfDay(ZONE_ID).toInstant());
			}
		},

		/**
		 * 格式yyyy-MM-dd HH:mm:ss
		 *
		 */
		DATE_TIME_FORMATTER(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.CHINA)) {
			@Override
			public Date parse(String text) {
				LocalDateTime localDateTime = LocalDateTime.parse(text, dateTimeFormatter);
				return Date.from(localDateTime.atZone(ZONE_ID).toInstant());
			}
		},

		/**
		 * 格式yyyyMMdd_HHmmss
		 *
		 */
		YYYYMMDD_HHMMSS_FORMATTER(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss", Locale.CHINA)) {
			@Override
			public Date parse(String text) {
				LocalDateTime localDateTime = LocalDateTime.parse(text, dateTimeFormatter);
				return Date.from(localDateTime.atZone(ZONE_ID).toInstant());
			}
		};

		protected DateTimeFormatter dateTimeFormatter;

		private DateFormatter(DateTimeFormatter dateTimeFormatter) {
			this.dateTimeFormatter = dateTimeFormatter;
		}

		public DateTimeFormatter getDateTimeFormatter() {
			return dateTimeFormatter;
		}

		public abstract Date parse(String text);
	}

}

3、总结

1.切记,LocalDate、LocalTime、LocalDateTime这几个类,都是没有设置时区的日期时间类,所以,对时区有要求的时候,必须手动设置指定时区;否则,采用默认时区即可

2.除了国内,世界其他很多地区都有夏冬令时的区分!所以,业务针对时区和夏冬令时有区别的,需要注意这一点!

最后,希望此文如果对你有帮助的话,请点赞、留言、关注我吧!

扫描左侧二维码,即可查看作者公众号内的更多精彩内容,先感谢各位朋友的支持啦!!!

善良勤劳勇敢而又聪明的老杨 CSDN认证博客专家 Java Spring Mysql
一个喜欢学习,热爱分享的Java技术人!年轻人,要坚持学习,耗子尾汁!
微信搜索关注时代名猿,免费领取VIP精品学习视频、BAT大厂面试资料、IT技术电子书籍
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 博客之星2020 设计师:CY__ 返回首页