ソースを参照

add LocalDateTimeUtil

Looly 5 年 前
コミット
c12557eac4

+ 3 - 2
CHANGELOG.md

@@ -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)

+ 10 - 0
hutool-core/src/main/java/cn/hutool/core/date/DatePattern.java

@@ -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
 	 */
@@ -156,6 +157,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'
 	 */
 	public static final String UTC_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'";

+ 7 - 3
hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java

@@ -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);
 	}
 
 	/**

+ 305 - 0
hutool-core/src/main/java/cn/hutool/core/date/LocalDateTimeUtil.java

@@ -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));
+	}
+}

+ 28 - 0
hutool-core/src/main/java/cn/hutool/core/date/TemporalAccessorUtil.java

@@ -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();
+	}
+}

+ 99 - 0
hutool-core/src/test/java/cn/hutool/core/date/LocalDateTimeUtilTest.java

@@ -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());
+	}
+}

+ 3 - 4
hutool-db/src/main/java/cn/hutool/db/nosql/redis/RedisDS.java

@@ -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);
 	}
 }