java 日期时间处理

java的日期时间处理,在java8之前主要使用Date和Calender类来进行构造,在java8后,在java.time包中引入了更多的类来处理日期时间。具有更好的可读性、易用性和线程安全性。

旧日期时间处理类

Date

java.util.Date类表示特定的时间瞬间,精度为毫秒级,其实现了Serializable, Cloneable和Comparable三个接口。有以下构造函数:

  • Date() : 代表当前实际
  • Date(long milliseconds) : 根据给定毫秒数创建,毫秒数从January 1, 1970, 00:00:00 GMT开始计算。
  • Date(int year, int month, int date)
  • Date(int year, int month, int date, int hrs, int min)
  • Date(int year, int month, int date, int hrs, int min, int sec)
  • Date(String s)

后面的4个构造函数都标记为已过时,但是这里还是要说以下其中参数year是要减去1900,也就是实际的年份是参数year+1900。月份是0-11.

如下

Date date = new Date(2024,2,28);
System.out.println(date);
//输出Fri Mar 28 00:00:00 CST 3924。是3924年部署2024年。

无参构造函数实际上是使用System.currentTimeMillis()获取当前秒数来构造时间。

Date对象有以下几个常用的方法:

  • **boolean after(Date date) : **检测当前对象表示时间是否晚于给定时间
  • **boolean before(Date date) : **检测当前对象表示时间是否早于给定时间
  • **int compareTo(Date date) : **时间比较,相等返回0,小于0表示早于给定时间,反之晚于。
  • long getTime() : 获取该日期标识的毫秒数
  • void setTime(long time) : 重新根据毫秒数设置时间.
Calendar

由于使用Date构造函数初始化特定的时间不太方便,又有了Calendar类。Calendar提供了对日期和时间进行计算、格式化和解析等功能。Calendar 类是一个抽象类,不能直接实例化,通常通过调用 Calendar.getInstance() 方法来获取 Calendar 对象。

Calendar将日期分成多个属性部分,使用常量来进行标识:

年份相关:

  • Calendar.YEAR:年份
  • Calendar.MONTH:月份(注意月份从 0 开始,即 0 表示一月,11 表示十二月)

月份相关:

  • Calendar.JANUARY - Calendar.DECEMBER:一月至十二月的常量

周相关:

  • Calendar.DAY_OF_WEEK:星期几(周日至周六分别对应 1 到 7)
  • Calendar.DAY_OF_WEEK_IN_MONTH:月中第几周的周几
  • Calendar.WEEK_OF_YEAR:年中的周数
  • Calendar.WEEK_OF_MONTH:月中的周数

天数相关:

  • Calendar.DAY_OF_MONTH:月份中的天数
  • Calendar.DAY_OF_YEAR:年份中的天数

时间相关:

  • Calendar.HOUR:12 小时制的小时
  • Calendar.HOUR_OF_DAY:24 小时制的小时
  • Calendar.MINUTE:分钟
  • Calendar.SECOND:秒
  • Calendar.MILLISECOND:毫秒

可以使用get()方法对这些日期属性进行获取,add()方法对日期进行加减操作。set()设置对应的值。

get():

Calendar c = Calendar.getInstance();
System.out.println(c.get(Calendar.YEAR));
System.out.println(c.get(Calendar.MONTH));
System.out.println(c.get(Calendar.DAY_OF_MONTH));
System.out.println(c.get(Calendar.HOUR_OF_DAY));
System.out.println(c.get(Calendar.MINUTE));

add()

Calendar c = Calendar.getInstance();
c.add(Calendar.DAY_OF_MONTH,5);//天数+5
c.add(Calendar.YEAR,1);//年份+1
System.out.println(c.getTime());//getTime()返回Date类型

add方法指定的值可以是负数,表示减去对应的当量。

set():

Calendar c = Calendar.getInstance();
c.set(Calendar.DAY_OF_MONTH,1);
c.set(Calendar.MONTH,4);
System.out.println(c.getTime());
//这里set年份是自然年,月份有对应的常量值
c.set(2024,Calendar.MARCH,1);
System.out.println(c.getTime());

另外Calendar还支持时区设置

Calendar c2 = Calendar.getInstance(TimeZone.getTimeZone("America/New_York");
System.out.println(c2.getTime());
SimpleDateFormat
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//日期格式化成字符串
String dataStr = sdf.format(new Date());
System.out.println(dataStr);
//字符串转换成日期
Date date = sdf.parse(dataStr);
System.out.println(date);

SimpleDateFormat创建时候需要指定一个日期pattern格式,支持常用格式如下:

  • y:年份(如 2024)
  • M:月份(1-12)
  • d:日期(1-31)
  • H:小时(0-23)
  • m:分钟(0-59)
  • s:秒(0-59)
  • S:毫秒
  • E:星期几(如 “星期一”)
  • D:一年中的第几天(1-365)
  • F:一个月中的第几个星期几(1-5,每月的第一个星期几)
  • w:一年中的第几个星期(1-53)
  • W:一个月中的第几个星期(1-5)

这里要注意:这些格式是区分大小写的,像y和Y代表的就是不同的,Y代表的周年数。

如下:

SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd");
Date date = sdf.parse("2023-12-31");
System.out.println(date);
sdf = new SimpleDateFormat("yyyy-MM-dd");
System.out.println(sdf.format(date));

最后输出的是:2023-01-01。

另外一点需要注意SimpleDateFormat是线程不安全的,这是在于其内部维护了一个 Calendar 对象来进行日期时间的格式化和解析操作。如果它们共享同一个 SimpleDateFormat 实例,就会共享同一个 Calendar 对象,这样就可能导致 Calendar 对象的状态被多个线程同时修改,从而破坏了线程安全性。多线程情况下最好是每个线程创建一个对应的SimpleDateFormat实例进行日期操作。

新日期时间处理类

在java8之前使用上面说的几个类进行日期处理。Java 8 引入了全新的日期时间处理类,主要位于 java.time 包中。其中最常用的类包括:

  1. LocalDate:表示一个不含时区的日期,例如 2024-02-29。
  2. LocalTime:表示一个不含时区的时间,例如 12:30:00。
  3. LocalDateTime:表示一个不含时区的日期时间,例如 2024-02-29T12:30:00。
  4. ZonedDateTime:表示一个带时区的日期时间。
  5. Instant:表示时间戳,通常用于机器时间。
  6. Duration:表示时间段,例如持续几小时、几分钟等。
  7. Period:表示日期段,例如持续几天、几个月等。

LocalDate用来表示日期,LocalTime用来表示时间,LocalDateTime表示一个完整的日期时间。这三个类中的操作方法差不多,都可以根据指定的值来构造日期时间,日期计算和格式化输出。

LocalDate
//当前日期
LocalDate date = LocalDate.now();
System.out.println(date.toString());//2024-02-29
//指定日期创建
date = LocalDate.of(2024, 4, 1);
System.out.println(date.toString());//2024-04-01
//字符串转换
date = LocalDate.parse("2024-03-01");    
//2024年3月1日
System.out.println(date.getYear()+"年"+date.getMonthValue()+"月"+date.getDayOfMonth()+"日");
//日期格式化
System.out.println(date.format(DateTimeFormatter.ofPattern("yy年MM月dd日")));
//日期计算:天数+1
System.out.println(date.plusDays(1));
//日期计算,按指定单位计算
System.out.println(date.minus(1, ChronoUnit.MONTHS));
//判断是否是闰年
date.isLeapYear();
//日期比较
date.isAfter(LocalDate.parse("2023-12-31"));
LocalTime
LocalTime time = LocalTime.now();
System.out.println(time);
time = LocalTime.of(12,30,0);
System.out.println(time);
time = LocalTime.parse("12:31:30");
System.out.println(time.getHour());
//时间计算
System.out.println(time.plusHours(1));
System.out.println(time.minus(5,ChronoUnit.MINUTES));
//时间比较
time.isAfter(LocalTime.parse("07:30"));
time = LocalTime.MAX;//23:59:59.99
time = LocalTime.MIN;//00:00
//格式化
System.out.println(time.format(DateTimeFormatter.ofPattern("HH:mm:ss")));
LocalDateTime
LocalDateTime dateTime = LocalDateTime.now();
//日期转字符串
String dataStr = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(" " + dataStr);
//根据LocalDate和LocalTime获取时间
dateTime = LocalDateTime.of(LocalDate.of(2024,2,29),LocalTime.now());
//根据年月日时分秒获取时间
dateTime = LocalDateTime.of(2024,2,29,12,30,29);

//字符串转日期
dateTime = LocalDateTime.parse("2024-02-29T06:30:00");
LocalDateTime.parse("2024-02-29 06:30:00",DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(dateTime.getDayOfYear());

//日期计算
dateTime.plusDays(1);
dateTime.minus(1,ChronoUnit.MONTHS);

//日期比较
dateTime.isEqual(LocalDateTime.now());
ZonedDateTime

带时区的时间

//所有可用的时区
Set<String> allZoneIds = ZoneId.getAvailableZoneIds();
for (String zoneId : allZoneIds) {
System.out.println(zoneId);
}
//根据LocalDateTime和ZoneId创建ZonedDateTime
ZonedDateTime zdt = ZonedDateTime.now();
System.out.println(zdt);//2024-02-29T15:42:41.095+08:00[Asia/Shanghai]
zdt = ZonedDateTime.of(LocalDateTime.now(),ZoneId.of("Asia/Tokyo"));
System.out.println(zdt);//2024-02-29T15:42:41.096+09:00[Asia/Tokyo]
System.out.println(zdt.getHour());//15
zdt =ZonedDateTime.parse("2024-02-29T10:30:30+08:00[Asia/Shanghai]");
//时区转换
zdt =zdt.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
System.out.println(zdt);//2024-02-29T11:30:30+09:00[Asia/Tokyo]
OffsetDateTime odt = OffsetDateTime.of(LocalDateTime.now(),ZoneOffset.of("+02:00"));
System.out.println(odt);//2024-02-29T15:42:41.119+02:00

withZoneSameInstant()方法可以进行时区转换,自动按时区差值进行计算对应的小时。

Instant

当前时点时间戳时间,从1970年开始计算秒数。

Instant instant = Instant.now();
//获取秒数
System.out.println(instant.getEpochSecond());
//获取纳秒数
System.out.println(instant.getNano());
Period & Duration

Period 和Duration都表示时间之间的间隔,只不过Period是以月、日、年这种为单位表示的一段时间,用来计算LocalDate,Duration是以秒,纳秒为单位表示持续一段时间,用来计算LocalTime。

LocalDate startDate = LocalDate.parse("2024-01-15");
LocalDate endDate = startDate.plus(Period.ofDays(45));
System.out.println("结束日期:"+endDate);
Period period = Period.between(startDate, endDate);
System.out.println("相差:"+period.getYears()+"年"+period.getMonths()+"月"+period.getDays()+"日");
System.out.println("相差:"+ChronoUnit.DAYS.between(startDate,endDate)+"天");
/**
输出:
结束日期:2024-02-29
相差:0年1月14日
相差:45天
*/

LocalTime startTime = LocalTime.of(12,30,0);
LocalTime endTime = startTime.plus(Duration.ofMinutes(15)).plus(Duration.ofHours(1));
System.out.println("结束时间:"+endTime.format(DateTimeFormatter.ofPattern("HH:mm:ss")));
Duration duration = Duration.between(startTime, endTime);
System.out.println(duration.getSeconds());
/**
结束时间:13:45:00
相差:4500
相差:4500
*/

这里看到计算两个日期时间差用Period时最后计算的结果是进行按对应时间属性进行计算的,差xx年xx月xx日这种,不会返回相隔实际天数,完全转换成相隔天数需要使用ChronoUnit.DAYS。

兼容旧日期时间

LocalDateTime可以兼容早期的Date和Calendar类

LocalDateTime dateTime = LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault());
Calendar calendar = Calendar.getInstance();
dateTime = LocalDateTime.ofInstant(calendar.toInstant(),ZoneId.systemDefault());
//根据时间戳秒创建时间
dateTime = LocalDateTime.ofEpochSecond(new Date().getTime()/1000,0,ZoneOffset.ofHours(8));
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值