mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
add LocalDateTimeUtil
This commit is contained in:
parent
25b9d61ffd
commit
c12557eac4
@ -3,13 +3,14 @@
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
|
||||
## 5.3.9 (2020-07-06)
|
||||
## 5.3.9 (2020-07-07)
|
||||
|
||||
### 新特性
|
||||
* 【core 】 DateUtil增加formatChineseDate(pr#932@Github)
|
||||
* 【core 】 ArrayUtil.isEmpty修改逻辑(pr#948@Github)
|
||||
* 【core 】 增强StrUtil中空判断后返回数据性能(pr#949@Github)
|
||||
* 【core 】 deprecate掉millsecond,改为millisecond(issue#I1M9P8@Github)
|
||||
* 【core 】 deprecate掉millsecond,改为millisecond(issue#I1M9P8@Gitee)
|
||||
* 【core 】 增加LocalDateTimeUtil(issue#I1KUVC@Gitee)
|
||||
|
||||
### Bug修复
|
||||
* 【core 】 修复NumberUtil.partValue有余数问题(issue#I1KX66@Gitee)
|
||||
|
@ -76,6 +76,7 @@ public class DatePattern {
|
||||
* ISO8601日期时间格式,精确到毫秒:yyyy-MM-dd HH:mm:ss,SSS
|
||||
*/
|
||||
public static final String ISO8601_PATTERN = "yyyy-MM-dd HH:mm:ss,SSS";
|
||||
|
||||
/**
|
||||
* ISO8601日期时间格式,精确到毫秒 {@link FastDateFormat}:yyyy-MM-dd HH:mm:ss,SSS
|
||||
*/
|
||||
@ -155,6 +156,15 @@ public class DatePattern {
|
||||
*/
|
||||
public static final FastDateFormat JDK_DATETIME_FORMAT = FastDateFormat.getInstance(JDK_DATETIME_PATTERN, Locale.US);
|
||||
|
||||
/**
|
||||
* UTC时间:yyyy-MM-dd'T'HH:mm:ss
|
||||
*/
|
||||
public static final String UTC_SIMPLE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss";
|
||||
/**
|
||||
* UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss
|
||||
*/
|
||||
public static final FastDateFormat UTC_SIMPLE_FORMAT = FastDateFormat.getInstance(UTC_SIMPLE_PATTERN, TimeZone.getTimeZone("UTC"));
|
||||
|
||||
/**
|
||||
* UTC时间:yyyy-MM-dd'T'HH:mm:ss'Z'
|
||||
*/
|
||||
|
@ -834,6 +834,9 @@ public class DateUtil extends CalendarUtil {
|
||||
} else if (length == DatePattern.UTC_MS_WITH_ZONE_OFFSET_PATTERN.length() + 2 || length == DatePattern.UTC_MS_WITH_ZONE_OFFSET_PATTERN.length() + 3) {
|
||||
// 格式类似:2018-09-13T05:34:31.999+0800 或 2018-09-13T05:34:31.999+08:00
|
||||
return parse(utcString, DatePattern.UTC_MS_WITH_ZONE_OFFSET_FORMAT);
|
||||
} else if(length == DatePattern.UTC_SIMPLE_PATTERN.length()-2){
|
||||
// 格式类似:2018-09-13T05:34:31
|
||||
return parse(utcString, DatePattern.UTC_SIMPLE_FORMAT);
|
||||
}
|
||||
}
|
||||
// 没有更多匹配的时间格式
|
||||
@ -1868,9 +1871,10 @@ public class DateUtil extends CalendarUtil {
|
||||
* @param instant {@link Instant}
|
||||
* @return {@link LocalDateTime}
|
||||
* @since 5.0.5
|
||||
* @see LocalDateTimeUtil#of(Instant)
|
||||
*/
|
||||
public static LocalDateTime toLocalDateTime(Instant instant) {
|
||||
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
|
||||
return LocalDateTimeUtil.of(instant);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1879,10 +1883,10 @@ public class DateUtil extends CalendarUtil {
|
||||
* @param date {@link Date}
|
||||
* @return {@link LocalDateTime}
|
||||
* @since 5.0.5
|
||||
* @see LocalDateTimeUtil#of(Date)
|
||||
*/
|
||||
public static LocalDateTime toLocalDateTime(Date date) {
|
||||
final DateTime dateTime = date(date);
|
||||
return LocalDateTime.ofInstant(dateTime.toInstant(), dateTime.getZoneId());
|
||||
return LocalDateTimeUtil.of(date);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,305 @@
|
||||
package cn.hutool.core.date;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.time.temporal.TemporalUnit;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* JDK8+中的{@link LocalDateTime} 工具类封装
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.3.9
|
||||
*/
|
||||
public class LocalDateTimeUtil {
|
||||
|
||||
/**
|
||||
* 当前时间,默认时区
|
||||
*
|
||||
* @return {@link LocalDateTime}
|
||||
*/
|
||||
public static LocalDateTime now() {
|
||||
return LocalDateTime.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Instant}转{@link LocalDateTime},使用默认时区
|
||||
*
|
||||
* @param instant {@link Instant}
|
||||
* @return {@link LocalDateTime}
|
||||
*/
|
||||
public static LocalDateTime of(Instant instant) {
|
||||
return of(instant, ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Instant}转{@link LocalDateTime},使用UTC时区
|
||||
*
|
||||
* @param instant {@link Instant}
|
||||
* @return {@link LocalDateTime}
|
||||
*/
|
||||
public static LocalDateTime ofUTC(Instant instant) {
|
||||
return of(instant, ZoneId.of("UTC"));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ZonedDateTime}转{@link LocalDateTime}
|
||||
*
|
||||
* @param zonedDateTime {@link ZonedDateTime}
|
||||
* @return {@link LocalDateTime}
|
||||
*/
|
||||
public static LocalDateTime of(ZonedDateTime zonedDateTime) {
|
||||
if (null == zonedDateTime) {
|
||||
return null;
|
||||
}
|
||||
return zonedDateTime.toLocalDateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Instant}转{@link LocalDateTime}
|
||||
*
|
||||
* @param instant {@link Instant}
|
||||
* @param zoneId 时区
|
||||
* @return {@link LocalDateTime}
|
||||
*/
|
||||
public static LocalDateTime of(Instant instant, ZoneId zoneId) {
|
||||
if (null == instant) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return LocalDateTime.ofInstant(instant, ObjectUtil.defaultIfNull(zoneId, ZoneId.systemDefault()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Instant}转{@link LocalDateTime}
|
||||
*
|
||||
* @param instant {@link Instant}
|
||||
* @param timeZone 时区
|
||||
* @return {@link LocalDateTime}
|
||||
*/
|
||||
public static LocalDateTime of(Instant instant, TimeZone timeZone) {
|
||||
if (null == instant) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return of(instant, ObjectUtil.defaultIfNull(timeZone, TimeZone.getDefault()).toZoneId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 毫秒转{@link LocalDateTime},使用默认时区
|
||||
*
|
||||
* <p>注意:此方法使用默认时区,如果非UTC,会产生时间偏移</p>
|
||||
*
|
||||
* @param epochMilli 从1970-01-01T00:00:00Z开始计数的毫秒数
|
||||
* @return {@link LocalDateTime}
|
||||
*/
|
||||
public static LocalDateTime of(long epochMilli) {
|
||||
return of(Instant.ofEpochMilli(epochMilli));
|
||||
}
|
||||
|
||||
/**
|
||||
* 毫秒转{@link LocalDateTime},使用UTC时区
|
||||
*
|
||||
* @param epochMilli 从1970-01-01T00:00:00Z开始计数的毫秒数
|
||||
* @return {@link LocalDateTime}
|
||||
*/
|
||||
public static LocalDateTime ofUTC(long epochMilli) {
|
||||
return ofUTC(Instant.ofEpochMilli(epochMilli));
|
||||
}
|
||||
|
||||
/**
|
||||
* 毫秒转{@link LocalDateTime},根据时区不同,结果会产生时间偏移
|
||||
*
|
||||
* @param epochMilli 从1970-01-01T00:00:00Z开始计数的毫秒数
|
||||
* @param zoneId 时区
|
||||
* @return {@link LocalDateTime}
|
||||
*/
|
||||
public static LocalDateTime of(long epochMilli, ZoneId zoneId) {
|
||||
return of(Instant.ofEpochMilli(epochMilli), zoneId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 毫秒转{@link LocalDateTime},结果会产生时间偏移
|
||||
*
|
||||
* @param epochMilli 从1970-01-01T00:00:00Z开始计数的毫秒数
|
||||
* @param timeZone 时区
|
||||
* @return {@link LocalDateTime}
|
||||
*/
|
||||
public static LocalDateTime of(long epochMilli, TimeZone timeZone) {
|
||||
return of(Instant.ofEpochMilli(epochMilli), timeZone);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Date}转{@link LocalDateTime},使用默认时区
|
||||
*
|
||||
* @param date Date对象
|
||||
* @return {@link LocalDateTime}
|
||||
*/
|
||||
public static LocalDateTime of(Date date) {
|
||||
if (null == date) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (date instanceof DateTime) {
|
||||
return of(date.toInstant(), ((DateTime) date).getZoneId());
|
||||
}
|
||||
return of(date.toInstant());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Date}转{@link LocalDateTime},使用默认时区
|
||||
*
|
||||
* @param temporalAccessor {@link TemporalAccessor}
|
||||
* @return {@link LocalDateTime}
|
||||
*/
|
||||
public static LocalDateTime of(TemporalAccessor temporalAccessor) {
|
||||
if (null == temporalAccessor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if(temporalAccessor instanceof LocalDate){
|
||||
return ((LocalDate)temporalAccessor).atStartOfDay();
|
||||
}
|
||||
|
||||
return LocalDateTime.of(
|
||||
TemporalAccessorUtil.get(temporalAccessor, ChronoField.YEAR),
|
||||
TemporalAccessorUtil.get(temporalAccessor, ChronoField.MONTH_OF_YEAR),
|
||||
TemporalAccessorUtil.get(temporalAccessor, ChronoField.DAY_OF_MONTH),
|
||||
TemporalAccessorUtil.get(temporalAccessor, ChronoField.HOUR_OF_DAY),
|
||||
TemporalAccessorUtil.get(temporalAccessor, ChronoField.MINUTE_OF_HOUR),
|
||||
TemporalAccessorUtil.get(temporalAccessor, ChronoField.SECOND_OF_MINUTE),
|
||||
TemporalAccessorUtil.get(temporalAccessor, ChronoField.NANO_OF_SECOND)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析日期时间字符串为{@link LocalDateTime},仅支持yyyy-MM-dd'T'HH:mm:ss格式,例如:2007-12-03T10:15:30
|
||||
*
|
||||
* @param text 日期时间字符串
|
||||
* @return {@link LocalDateTime}
|
||||
*/
|
||||
public static LocalDateTime parse(CharSequence text) {
|
||||
return parse(text, (DateTimeFormatter)null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析日期时间字符串为{@link LocalDateTime},格式支持日期时间、日期、时间
|
||||
*
|
||||
* @param text 日期时间字符串
|
||||
* @param formatter 日期格式化器,预定义的格式见:{@link DateTimeFormatter}
|
||||
* @return {@link LocalDateTime}
|
||||
*/
|
||||
public static LocalDateTime parse(CharSequence text, DateTimeFormatter formatter) {
|
||||
if (null == text) {
|
||||
return null;
|
||||
}
|
||||
if (null == formatter) {
|
||||
return LocalDateTime.parse(text);
|
||||
}
|
||||
|
||||
return of(formatter.parse(text));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析日期时间字符串为{@link LocalDateTime}
|
||||
*
|
||||
* @param text 日期时间字符串
|
||||
* @param format 日期格式,类似于yyyy-MM-dd HH:mm:ss,SSS
|
||||
* @return {@link LocalDateTime}
|
||||
*/
|
||||
public static LocalDateTime parse(CharSequence text, String format) {
|
||||
if (null == text) {
|
||||
return null;
|
||||
}
|
||||
return parse(text, DateTimeFormatter.ofPattern(format));
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化日期时间为指定格式
|
||||
*
|
||||
* @param time {@link LocalDateTime}
|
||||
* @param formatter 日期格式化器,预定义的格式见:{@link DateTimeFormatter}
|
||||
* @return 格式化后的字符串
|
||||
*/
|
||||
public static String format(LocalDateTime time, DateTimeFormatter formatter) {
|
||||
if (null == time) {
|
||||
return null;
|
||||
}
|
||||
return time.format(formatter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化日期时间为指定格式
|
||||
*
|
||||
* @param time {@link LocalDateTime}
|
||||
* @param format 日期格式,类似于yyyy-MM-dd HH:mm:ss,SSS
|
||||
* @return 格式化后的字符串
|
||||
*/
|
||||
public static String format(LocalDateTime time, String format) {
|
||||
if (null == time) {
|
||||
return null;
|
||||
}
|
||||
return format(time, DateTimeFormatter.ofPattern(format));
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期偏移,根据field不同加不同值(偏移会修改传入的对象)
|
||||
*
|
||||
* @param time {@link LocalDateTime}
|
||||
* @param number 偏移量,正数为向后偏移,负数为向前偏移
|
||||
* @param field 偏移单位,见{@link ChronoField},不能为null
|
||||
* @return 偏移后的日期时间
|
||||
*/
|
||||
public static LocalDateTime offset(LocalDateTime time, long number, TemporalUnit field) {
|
||||
if (null == time) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return time.plus(number, field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取两个日期的差,如果结束时间早于开始时间,获取结果为负。
|
||||
* <p>
|
||||
* 返回结果为{@link Duration}对象,通过调用toXXX方法返回相差单位
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return 时间差 {@link Duration}对象
|
||||
*/
|
||||
public static Duration between(LocalDateTime startTime, LocalDateTime endTime) {
|
||||
return Duration.between(startTime, endTime);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 修改为一天的开始时间,例如:2020-02-02 00:00:00,000
|
||||
*
|
||||
* @param time 日期时间
|
||||
* @return 一天的开始时间
|
||||
*/
|
||||
public static LocalDateTime beginOfDay(LocalDateTime time) {
|
||||
return time.with(LocalTime.of(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改为一天的结束时间,例如:2020-02-02 23:59:59,999
|
||||
*
|
||||
* @param time 日期时间
|
||||
* @return 一天的结束时间
|
||||
*/
|
||||
public static LocalDateTime endOfDay(LocalDateTime time) {
|
||||
return time.with(LocalTime.of(23, 59, 59, 999_999_999));
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package cn.hutool.core.date;
|
||||
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.time.temporal.TemporalField;
|
||||
|
||||
/**
|
||||
* {@link TemporalAccessor} 工具类封装
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.3.9
|
||||
*/
|
||||
public class TemporalAccessorUtil {
|
||||
|
||||
/**
|
||||
* 安全获取时间的某个属性,属性不存在返回0
|
||||
*
|
||||
* @param temporalAccessor 需要获取的时间对象
|
||||
* @param field 需要获取的属性
|
||||
* @return 时间的值,如果无法获取则默认为 0
|
||||
*/
|
||||
public static int get(TemporalAccessor temporalAccessor, TemporalField field) {
|
||||
if (temporalAccessor.isSupported(field)) {
|
||||
return temporalAccessor.get(field);
|
||||
}
|
||||
|
||||
return (int)field.range().getMinimum();
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package cn.hutool.core.date;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
public class LocalDateTimeUtilTest {
|
||||
|
||||
@Test
|
||||
public void nowTest() {
|
||||
Assert.assertNotNull(LocalDateTimeUtil.now());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ofTest() {
|
||||
String dateStr = "2020-01-23T12:23:56";
|
||||
final DateTime dt = DateUtil.parse(dateStr);
|
||||
|
||||
LocalDateTime of = LocalDateTimeUtil.of(dt);
|
||||
Assert.assertNotNull(of);
|
||||
Assert.assertEquals(dateStr, of.toString());
|
||||
|
||||
of = LocalDateTimeUtil.ofUTC(dt.getTime());
|
||||
Assert.assertEquals(dateStr, of.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseTest() {
|
||||
final LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56", DateTimeFormatter.ISO_DATE_TIME);
|
||||
Assert.assertEquals("2020-01-23T12:23:56", localDateTime.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseTest2() {
|
||||
final LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23", DatePattern.NORM_DATE_PATTERN);
|
||||
Assert.assertEquals("2020-01-23T00:00", localDateTime.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseTest3() {
|
||||
final LocalDateTime localDateTime = LocalDateTimeUtil.parse("12:23:56", DatePattern.NORM_TIME_PATTERN);
|
||||
Assert.assertEquals("12:23:56", localDateTime.toLocalTime().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseTest4() {
|
||||
final LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56");
|
||||
Assert.assertEquals("2020-01-23T12:23:56", localDateTime.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formatTest() {
|
||||
final LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56");
|
||||
String format = LocalDateTimeUtil.format(localDateTime, DatePattern.NORM_DATETIME_PATTERN);
|
||||
Assert.assertEquals("2020-01-23 12:23:56", format);
|
||||
|
||||
format = LocalDateTimeUtil.format(localDateTime, DatePattern.NORM_DATE_PATTERN);
|
||||
Assert.assertEquals("2020-01-23", format);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void offset() {
|
||||
final LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56");
|
||||
LocalDateTime offset = LocalDateTimeUtil.offset(localDateTime, 1, ChronoUnit.DAYS);
|
||||
// 非同一对象
|
||||
Assert.assertNotSame(localDateTime, offset);
|
||||
|
||||
Assert.assertEquals("2020-01-24T12:23:56", offset.toString());
|
||||
|
||||
offset = LocalDateTimeUtil.offset(localDateTime, -1, ChronoUnit.DAYS);
|
||||
Assert.assertEquals("2020-01-22T12:23:56", offset.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void between() {
|
||||
final Duration between = LocalDateTimeUtil.between(
|
||||
LocalDateTimeUtil.parse("2019-02-02T00:00:00"),
|
||||
LocalDateTimeUtil.parse("2020-02-02T00:00:00"));
|
||||
Assert.assertEquals(365, between.toDays());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beginOfDayTest() {
|
||||
final LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56");
|
||||
final LocalDateTime beginOfDay = LocalDateTimeUtil.beginOfDay(localDateTime);
|
||||
Assert.assertEquals("2020-01-23T00:00", beginOfDay.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void endOfDayTest() {
|
||||
final LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56");
|
||||
final LocalDateTime endOfDay = LocalDateTimeUtil.endOfDay(localDateTime);
|
||||
Assert.assertEquals("2020-01-23T23:59:59.999999999", endOfDay.toString());
|
||||
}
|
||||
}
|
@ -1,8 +1,5 @@
|
||||
package cn.hutool.db.nosql.redis;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.setting.Setting;
|
||||
@ -11,6 +8,8 @@ import redis.clients.jedis.JedisPool;
|
||||
import redis.clients.jedis.JedisPoolConfig;
|
||||
import redis.clients.jedis.Protocol;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
||||
/**
|
||||
* Jedis数据源
|
||||
*
|
||||
@ -174,7 +173,7 @@ public class RedisDS implements Closeable{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
public void close() {
|
||||
IoUtil.close(pool);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user