# java时间处理
| 🐉 获取一些常见日期和时间 | 🐉 常见又易错点 |
|---|
public static String getDatePoor(Date endDate, Date nowDate) {
long nd = 1000 - 24 - 60 - 60;
long nh = 1000 - 60 - 60;
long nm = 1000 - 60;
// long ns = 1000;
// 获得两个时间的毫秒时间差异
long diff = endDate.getTime() - nowDate.getTime();
// 计算差多少天
long day = diff / nd;
// 计算差多少小时
long hour = diff % nd / nh;
// 计算差多少分钟
long min = diff % nd % nh / nm;
// 计算差多少秒//输出结果
// long sec = diff % nd % nh % nm / ns;
return day + "天" + hour + "小时" + min + "分钟";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# java8时间
package com.chlm.mysession.common;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
/**
- java8时间测试
- <p>Instant 是用来表示时刻的,类似 Unix 的时间,表示从协调世界时1970年1月1日0时0分0秒起至现在的总秒数,也可以获取毫秒。</p>
- <p>Date 最多可以表示毫秒级别的时刻,而 Instant 可以表示纳秒级别的时刻。</p>
- <p>LocalDate 表示一个日期,只有年月日,没有时分秒</p>
- <p>LocalTime</p>
- <p>LocalDateTime 就是年月日时分秒了。</p>
- @author huting
*/
@Slf4j
public class TimeTest {
/**
- 线程安全,所以可以static
*/
private static final DateTimeFormatter DATE_TIME_FORMATTER_DAY = DateTimeFormatter.ofPattern("yyyy-MM-dd");
/**
- 12小时
*/
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
/**
- 24小时
*/
private static final DateTimeFormatter DATE_TIME_FORMATTER_24 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
- 12小时制,带am/pm
*/
private static final DateTimeFormatter DATE_TIME_FORMATTER_AP = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss a");
// private static SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd");
/**
- 通过ThreadLocal实现SimpleDateFormat的线程安全性
*/
private static final ThreadLocal<SimpleDateFormat> FORMAT_THREAD_LOCAL =
new InheritableThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
@Test
public void testInstant(){
Instant instant = Instant.now();
//输出当前时刻距离 1970年1月1日0时0分0秒 的秒和毫秒
log.info("现在的秒数1:[{}]",instant.getEpochSecond());
log.info("现在的毫秒数1:[{}]",instant.toEpochMilli());
LocalDateTime today = LocalDateTime.now();
//Instant 和 LocalDateTime(默认系统时区) 的互相转换,注意时区问题
Instant instantFromLocalDateTime = today.atZone(ZoneId.systemDefault()).toInstant();
log.info("现在的秒数2:[{}]",instantFromLocalDateTime.getEpochSecond());
log.info("现在的毫秒数2:[{}]",instantFromLocalDateTime.toEpochMilli());
LocalDateTime timeFromInstant = LocalDateTime.ofInstant(instant,ZoneId.systemDefault());
log.info("当前时间为2:[{}],时区为:[{}]",timeFromInstant.format(DATE_TIME_FORMATTER)
,ZoneId.systemDefault().toString());
Instant instantOfMillisecond = Instant.ofEpochSecond(1000);
log.info("时间:[{}]",instantOfMillisecond);
}
@Test
public void testLocalDateTime(){
LocalDateTime today = LocalDateTime.now();
log.info("当前时间为1:[{}]",today.format(DATE_TIME_FORMATTER));
LocalDateTime tomorrow = today.plusDays(1);
log.info("明天此时时间为:[{}]",tomorrow.format(DATE_TIME_FORMATTER));
LocalDateTime minusHours = tomorrow.minusHours(24);
log.info("明天此时时间减去4小时后为:[{}]",minusHours.format(DATE_TIME_FORMATTER));
log.info("tomorrow比today大,是真的吗?[{}]",tomorrow.isAfter(today));
log.info("minusHours与today一样,是真的吗?[{}]",minusHours.isEqual(today));
}
/**
- LocalDate 会根据系统中当前时刻和默认时区计算出年月日的信息
*/
@Test
public void testLocalDate(){
LocalDate localDate = LocalDate.now();
log.info("当前日期:[{}]",localDate);
localDate = LocalDate.of(2022,11,3);
log.info("修改后的日期:[{}]",localDate);
//根据天数,推算月、日
localDate = LocalDate.ofYearDay(2016,200);
log.info("修改后的日期:[{}]",localDate);
localDate = LocalDate.ofEpochDay(333);
log.info("修改后的日期:[{}]",localDate);
log.info("当前月份——[{}],星期——[{}]",localDate.getMonth().name()
,localDate.getDayOfWeek().name());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# localdate与String互转
默认情况下,
java日期是ISO8601格式,因此如果您有任何表示ISO8601格式的日期的字符串,那么您可以直接使用LocalDate.parse()或LocalDateTime.parse()方法
String armisticeDate = "2016-04-04";
LocalDate aLD = LocalDate.parse(armisticeDate);
System.out.println("Date: " + aLD);
String armisticeDateTime = "2016-04-04T11:50";
LocalDateTime aLDT = LocalDateTime.parse(armisticeDateTime);
System.out.println("Date/Time: " + aLDT);
1
2
3
4
5
6
7
2
3
4
5
6
7
/**
- localdate与String互转
*/
@Test
public void testLocalDate2String(){
LocalDate localDate = LocalDate.now();
log.info("LocalDate转成String:[{}]",localDate.format(DATE_TIME_FORMATTER_DAY));
LocalDateTime localDateTime = LocalDateTime.now();
log.info("LocalDate转成String:[{}]",localDateTime.format(DATE_TIME_FORMATTER));
log.info("LocalDate转成String:[{}]",localDateTime.format(DATE_TIME_FORMATTER_24));
log.info("LocalDate转成String(预定义):[{}]",localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
log.info("LocalDate转成String:[{}]",localDateTime.format(DATE_TIME_FORMATTER_AP));
log.info(now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"))));
}
@Test
public void testString2LocalDate(){
String timeStr = "2019-06-28 13:13:13";
LocalDate date = LocalDate.parse(timeStr,DATE_TIME_FORMATTER_24);
LocalDateTime time = LocalDateTime.parse(timeStr,DATE_TIME_FORMATTER_24);
log.info("String转LocalDate:[{}]",date);
log.info("String转LocalDateTime:[{}]",time);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# sqlTime转LocalDateTime
//import java.sql.Timestamp;转LocalDateTime
@Test
public void sqlTimeStamp2Local(){
Timestamp timestamp = Timestamp.valueOf(LocalDateTime.now());
log.info("TimeStamp为:[{}]",timestamp);
LocalDateTime now = timestamp.toLocalDateTime();
log.info("LocalDateTime为:[{}]",now);
}
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 获取所在季度开始与结束时间
/**
* 获取时间所在季度的开始或者结束时间
*
* @param localDateTime 时间,若为空,则直接获取当前时间
* @param isStart true:开始时间,false:结束时间
* @return 时间
*/
public static LocalDateTime getStartOrEndDayOfQuarter(LocalDateTime localDateTime, boolean isStart) {
LocalDateTime resultTime = LocalDateTime.now();
localDateTime = localDateTime == null ? resultTime : localDateTime;
Month month = localDateTime.getMonth();
Month firstMonth = month.firstMonthOfQuarter();
Month endMonth = Month.of(firstMonth.getValue() + 2);
return isStart
? LocalDateTime.of(
LocalDate.of(localDateTime.getYear(), firstMonth, 1),
LocalTime.MIN)
: LocalDateTime.of(
LocalDate.of(localDateTime.getYear(), endMonth, endMonth.length(localDateTime.toLocalDate().isLeapYear())),
LocalTime.MAX);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 获取所在周的开始与结束日期
/**
* 获取传入时间或者当前时间所在周的周一或者周天
* @param localDateTime 时间
* @param isStart true:周一,false:周天
* @return 时间
*/
public static LocalDateTime getStartOrEndOfWeek(LocalDateTime localDateTime, boolean isStart) {
LocalDateTime resultTime = LocalDateTime.now();
localDateTime = localDateTime == null ? resultTime : localDateTime;
return localDateTime.with(temporal -> {
int calNow = temporal.get(ChronoField.DAY_OF_WEEK);
return isStart
? temporal.plus(1 - calNow, ChronoUnit.DAYS)
: temporal.plus(7 - calNow, ChronoUnit.DAYS);
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 获取当天最小最大时间
LocalDateTime.now().with(LocalTime.MIN);
LocalDateTime.now().with(LocalTime.MAX);
1
2
2
# SimpleDateFormat非线程安全
SimpleDateFormat是非线程安全的,时间处理,基本所有项目上都是需要使用到的,往往很多初学者会把SimpleDateFormat定义为static类型,然后在进行时间转化的时候没有做加锁处理。如下:
public class Main {
private final static SimpleDateFormat SDFT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) throws ParseException {
System.out.println(SDFT.parse("2019-05-29 12:12:12"));
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
@Test
public void testSimpleDateFormat(){
for (int i = 1; i < 31; i++) {
int ii = i;
new Thread(() -> {
Date date = null;
try {
String s = "2019-05-" + ii;
date = SDF.parse(s);
System.out.println("" + ii + ":" + date.getDate());
} catch (ParseException e) {
e.printStackTrace();
}
}).start();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
12:12
2:2
9:7
11:11
6:6
8:6
21:21
10:10
24:24
25:25
28:28
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
根据预期结果,两边应该相等,但是却出现了不相等的情况。原因:因为SimpleDateFormat定义为了共享的,所以其类里的属性calendar也是多个线程共享的,这就造成了线程安全问题。 解决方案:
- 1.加锁处理
try {
String s = "2019-05-" + ii;
synchronized (TimeTest.class){
date = SDF.parse(s);
}
System.out.println("" + ii + ":" + date.getDate());
} catch (ParseException e) {
e.printStackTrace();
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- 2.每次都创建SimpleDateFormat实例
- 3.使用LocalThread
private static final ThreadLocal<SimpleDateFormat> FORMAT_THREAD_LOCAL =
new InheritableThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
@Test
public void testSimpleDateFormat(){
for (int i = 1; i < 31; i++) {
int ii = i;
new Thread(() -> {
Date date = null;
try {
String s = "2019-05-" + ii;
// synchronized (TimeTest.class){
// date = SDF.parse(s);
// }
date = FORMAT_THREAD_LOCAL.get().parse(s);
System.out.println("" + ii + ":" + date.getDate());
} catch (ParseException e) {
e.printStackTrace();
}
}).start();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
- 4.使用JDK1.8提供的
DateTimeFormatter来处理时间
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Test
public void testSimpleDateFormat(){
for (int i = 1; i < 31; i++) {
int ii = i;
new Thread(() -> {
LocalDate date = null;
try {
String s = ii<10?("2019-05-0" + ii):("2019-05-" + ii);
// synchronized (TimeTest.class){
// date = SDF.parse(s);
// }
date = LocalDate.parse(s,DATE_TIME_FORMATTER);
System.out.println("" + ii + ":" + date.getDayOfMonth());
} catch (NullPointerException e) {
e.printStackTrace();
}
}).start();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# java8时间_new
新API基于ISO标准日历系统,java.time包下的所有类都是不可变类型而且线程安全。
| 编号 | 类的名称 | 描述 |
|---|---|---|
| 1 | Instant | 时间戳 |
| 2 | Duration | 持续时间,时间差 |
| 3 | LocalDate | 只包含日期,比如:2018-02-05 |
| 4 | LocalTime | 只包含时间,比如:23:12:10 |
| 5 | LocalDateTime | 包含日期和时间,比如:2018-02-05 23:14:21 |
| 6 | Period | 时间段 |
| 7 | ZoneOffset | 时区偏移量,比如:+8:00 |
| 8 | ZonedDateTime | 带时区的时间 |
| 9 | Clock | 时钟,比如获取目前美国纽约的时间 |
| 10 | java.time.format.DateTimeFormatter | 时间格式化 |
# Clock
Clock是时区敏感的,可以用来取代 System.currentTimeMillis() 来获取当前的微秒数
@Test
public void testClock(){
Clock clock = Clock.systemUTC();
log.info("毫秒:【{}】,",clock.millis());
Clock defaultClock = Clock.systemDefaultZone();
log.info("毫秒:【{}】,",defaultClock.millis());
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 处理时区ZoneDateTime
# 检查闰年
# 两个日期之间的天数和月数
LocalDateTime cu = LocalDateTime.now();
LocalDateTime d1 = cu.minusDays(2L).with(LocalTime.MIN);
System.out.println(cu);
System.out.println(d1);
LocalDateTime d2 = cu.with(LocalTime.MIN);
System.out.println(cu);
System.out.println(d2);
// Duration主要获取时分秒的差
System.out.println(Duration.between(d1, d2).toDays());
// Period获取天、月、年、周的差
Period.between(LocalDate.of(2021,11,1), LocalDate.of(2023,1,1)).getMonths(); // 2
// ChronoUnit都可以
ChronoUnit.MONTHS.between(LocalDate.of(2021,11,1), LocalDate.of(2023,1,1)); // 14
System.out.println(ChronoUnit.DAYS.between(d1, d2));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 各种时间、字符串等之间的互相转换
- LocalDate转Date
LocalDate nowLocalDate = LocalDate.now();
Date date = Date.from(localDate.atStartOfDay(ZoneOffset.ofHours(8)).toInstant());
1
2
2
- LocalDateTime转Date
LocalDateTime localDateTime = LocalDateTime.now();
Date date = Date.from(localDateTime.atZone(ZoneOffset.ofHours(8)).toInstant());
1
2
2
- Date转LocalDateTime(LocalDate)
Date date = new Date();
LocalDateTime localDateTime = date.toInstant().atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
LocalDate localDate = date.toInstant().atZone(ZoneOffset.ofHours(8)).toLocalDate();
1
2
3
2
3
- LocalDate转时间戳
LocalDate localDate = LocalDate.now();
long timestamp = localDate.atStartOfDay(ZoneOffset.ofHours(8)).toInstant().toEpochMilli();
1
2
2
- LocalDateTime转时间戳
LocalDateTime localDateTime = LocalDateTime.now();
long timestamp = localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
1
2
2
- 时间戳转LocalDateTime(LocalDate)
long timestamp = System.currentTimeMillis();
LocalDate localDate = Instant.ofEpochMilli(timestamp).atZone(ZoneOffset.ofHours(8)).toLocalDate();
LocalDateTime localDateTime = Instant.ofEpochMilli(timestamp).atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
1
2
3
2
3
- LocalDateTime与TimeStamp互转
LocalDateTime localDateTime = timeStamp.toLocalDateTime();
TimeStamp timeStamp = TimeStamp.valueOf(LocalDateTime.now());
1
2
3
2
3
# 日期相关工具类
# DateUtils DateFormatUtils
Date now = new Date();
// Date 加 1 天
Date addDays = DateUtils.addDays(now, 1);
// Date 加 33 分钟
Date addMinutes = DateUtils.addMinutes(now, 33);
// Date 减去 233 秒
Date addSeconds = DateUtils.addSeconds(now, -233);
// 判断是否 同一天
boolean sameDay = DateUtils.isSameDay(addDays, addMinutes);
// 过滤时分秒,若 now 为 2020-05-07 22:13:00 调用 truncate 方法以后
// 返回时间为 2020-05-07 00:00:00
Date truncate = DateUtils.truncate(now, Calendar.DATE);
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12