This commit is contained in:
Looly 2019-09-18 17:29:29 +08:00
parent 8b94fa0c5d
commit 8e70a2aca7
6 changed files with 346 additions and 279 deletions

View File

@ -7,7 +7,10 @@
### 新特性
* 【core】 ImgUtil.rotate支持负数issue#543@Github
* 【http】 body方法传null跳过而非报错issue#I12AP2@Github
* 【core】 TimeInterval增加intervalPretty方法issue#I12A6T@Github
### Bug修复
* 【core】 修复DateUtil.offset导致的时区错误问题issue#I1294O@Github
-------------------------------------------------------------------------------------------------------------

View File

@ -19,16 +19,21 @@ import cn.hutool.core.util.StrUtil;
* 包装java.util.Date
*
* @author xiaoleilu
*
*/
public class DateTime extends Date {
private static final long serialVersionUID = -5395712593979185936L;
/** 是否可变对象 */
/**
* 是否可变对象
*/
private boolean mutable = true;
/** 一周的第一天,默认是周一, 在设置或获得 WEEK_OF_MONTH 或 WEEK_OF_YEAR 字段时Calendar 必须确定一个月或一年的第一个星期,以此作为参考点。 */
/**
* 一周的第一天默认是周一 在设置或获得 WEEK_OF_MONTH WEEK_OF_YEAR 字段时Calendar 必须确定一个月或一年的第一个星期以此作为参考点
*/
private Week firstDayOfWeek = Week.MONDAY;
/** 时区 */
/**
* 时区
*/
private TimeZone timeZone;
/**
@ -68,10 +73,10 @@ public class DateTime extends Date {
/**
* 构造
*
* @see DatePattern
* @param dateStr Date字符串
* @param format 格式
* @return {@link DateTime}
* @see DatePattern
*/
public static DateTime of(String dateStr, String format) {
return new DateTime(dateStr, format);
@ -87,9 +92,9 @@ public class DateTime extends Date {
}
// -------------------------------------------------------------------- Constructor start
/**
* 当前时间
*
*/
public DateTime() {
this(TimeZone.getDefault());
@ -111,7 +116,10 @@ public class DateTime extends Date {
* @param date 日期
*/
public DateTime(Date date) {
this(date.getTime(), TimeZone.getDefault());
this(
date.getTime(),//
(date instanceof DateTime) ? ((DateTime) date).timeZone : TimeZone.getDefault()
);
}
/**
@ -131,7 +139,7 @@ public class DateTime extends Date {
* @param calendar {@link Calendar}
*/
public DateTime(Calendar calendar) {
this(calendar.getTime(), (TimeZone) null);
this(calendar.getTime(), calendar.getTimeZone());
}
/**
@ -141,7 +149,7 @@ public class DateTime extends Date {
* @since 4.1.2
*/
public DateTime(long timeMillis) {
this(timeMillis, (TimeZone) null);
this(timeMillis, TimeZone.getDefault());
}
/**
@ -161,9 +169,9 @@ public class DateTime extends Date {
/**
* 构造
*
* @see DatePattern
* @param dateStr Date字符串
* @param format 格式
* @see DatePattern
*/
public DateTime(String dateStr, String format) {
this(dateStr, new SimpleDateFormat(format));
@ -172,9 +180,9 @@ public class DateTime extends Date {
/**
* 构造
*
* @see DatePattern
* @param dateStr Date字符串
* @param dateFormat 格式化器 {@link SimpleDateFormat}
* @see DatePattern
*/
public DateTime(String dateStr, DateFormat dateFormat) {
this(parse(dateStr, dateFormat), dateFormat.getTimeZone());
@ -183,9 +191,9 @@ public class DateTime extends Date {
/**
* 构造
*
* @see DatePattern
* @param dateStr Date字符串
* @param dateParser 格式化器 {@link DateParser}可以使用 {@link FastDateFormat}
* @see DatePattern
*/
public DateTime(String dateStr, DateParser dateParser) {
this(parse(dateStr, dateParser), dateParser.getTimeZone());
@ -194,6 +202,7 @@ public class DateTime extends Date {
// -------------------------------------------------------------------- Constructor end
// -------------------------------------------------------------------- offset start
/**
* 调整日期和时间<br>
* 如果此对象为可变对象返回自身否则返回新对象设置是否可变对象见{@link #setMutable(boolean)}
@ -229,6 +238,7 @@ public class DateTime extends Date {
// -------------------------------------------------------------------- offset end
// -------------------------------------------------------------------- Part of Date start
/**
* 获得日期的某个部分<br>
* 例如获得年的部分则使用 getField(DatePart.YEAR)
@ -506,8 +516,8 @@ public class DateTime extends Date {
/**
* 是否闰年
*
* @see DateUtil#isLeapYear(int)
* @return 是否闰年
* @see DateUtil#isLeapYear(int)
*/
public boolean isLeapYear() {
return DateUtil.isLeapYear(year());
@ -764,6 +774,7 @@ public class DateTime extends Date {
}
// -------------------------------------------------------------------- toString start
/**
* 转为"yyyy-MM-dd yyyy-MM-dd HH:mm:ss " 格式字符串<br>
* 如果时区被设置会转换为其时区对应的时间否则转换为当前地点对应的时区

View File

@ -1325,7 +1325,7 @@ public class DateUtil {
}
/**
* 获取指定日期偏移指定时间后的时间
* 获取指定日期偏移指定时间后的时间生成的偏移日期不影响原日期
*
* @param date 基准日期
* @param dateField 偏移的粒度大小小时月等{@link DateField}
@ -1333,10 +1333,7 @@ public class DateUtil {
* @return 偏移后的日期
*/
public static DateTime offset(Date date, DateField dateField, int offset) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(dateField.getValue(), offset);
return new DateTime(cal.getTime());
return dateNew(date).offset(dateField, offset);
}
/**

View File

@ -7,9 +7,8 @@ import java.io.Serializable;
* 计算某个过程花费的时间精确到毫秒
*
* @author Looly
*
*/
public class TimeInterval implements Serializable{
public class TimeInterval implements Serializable {
private static final long serialVersionUID = 1L;
private long time;
@ -44,26 +43,40 @@ public class TimeInterval implements Serializable{
/**
* 重新开始计算时间重置开始时间
*
* @return this
* @since 3.0.1
*/
public TimeInterval restart(){
public TimeInterval restart() {
time = DateUtil.current(isNano);
return this;
}
//----------------------------------------------------------- Interval
/**
* 从开始到当前的间隔时间毫秒数<br>
* 如果使用纳秒计时返回纳秒差否则返回毫秒差
*
* @return 从开始到当前的间隔时间毫秒数
*/
public long interval() {
return DateUtil.current(isNano) - time;
}
/**
* 从开始到当前的间隔时间毫秒数返回XX天XX小时XX分XX秒XX毫秒
*
* @return 从开始到当前的间隔时间毫秒数
* @since 4.6.7
*/
public String intervalPretty() {
return DateUtil.formatBetween(intervalMs());
}
/**
* 从开始到当前的间隔时间毫秒数
*
* @return 从开始到当前的间隔时间毫秒数
*/
public long intervalMs() {
@ -72,41 +85,46 @@ public class TimeInterval implements Serializable{
/**
* 从开始到当前的间隔秒数取绝对值
*
* @return 从开始到当前的间隔秒数取绝对值
*/
public long intervalSecond(){
public long intervalSecond() {
return intervalMs() / DateUnit.SECOND.getMillis();
}
/**
* 从开始到当前的间隔分钟数取绝对值
*
* @return 从开始到当前的间隔分钟数取绝对值
*/
public long intervalMinute(){
public long intervalMinute() {
return intervalMs() / DateUnit.MINUTE.getMillis();
}
/**
* 从开始到当前的间隔小时数取绝对值
*
* @return 从开始到当前的间隔小时数取绝对值
*/
public long intervalHour(){
public long intervalHour() {
return intervalMs() / DateUnit.HOUR.getMillis();
}
/**
* 从开始到当前的间隔天数取绝对值
*
* @return 从开始到当前的间隔天数取绝对值
*/
public long intervalDay(){
public long intervalDay() {
return intervalMs() / DateUnit.DAY.getMillis();
}
/**
* 从开始到当前的间隔周数取绝对值
*
* @return 从开始到当前的间隔周数取绝对值
*/
public long intervalWeek(){
public long intervalWeek() {
return intervalMs() / DateUnit.WEEK.getMillis();
}

View File

@ -1,20 +1,13 @@
package cn.hutool.core.date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.TimeZone;
import org.junit.Assert;
import org.junit.Test;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.BetweenFormater.Level;
import org.junit.Assert;
import org.junit.Test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 时间工具单元测试<br>
@ -25,7 +18,6 @@ import cn.hutool.core.date.BetweenFormater.Level;
* </pre>
*
* @author Looly
*
*/
public class DateUtilTest {
@ -289,13 +281,13 @@ public class DateUtilTest {
}
@Test
public void parseTest4() throws ParseException {
public void parseTest4() {
String ymd = DateUtil.parse("2019-3-21 12:20:15", "yyyy-MM-dd").toString(DatePattern.PURE_DATE_PATTERN);
Assert.assertEquals("20190321", ymd);
}
@Test
public void parseTest5() throws ParseException {
public void parseTest5() {
// 测试时间解析
String time = DateUtil.parse("22:12:12").toString(DatePattern.NORM_TIME_FORMAT);
Assert.assertEquals("22:12:12", time);
@ -321,21 +313,32 @@ public class DateUtilTest {
}
@Test
public void parseTest6() throws ParseException {
public void parseTest6() {
String str = "Tue Jun 4 16:25:15 +0800 2019";
DateTime dateTime = DateUtil.parse(str);
Assert.assertEquals("2019-06-04 16:25:15", dateTime.toString());
}
@Test
public void parseTest7() throws ParseException {
public void parseTest7() {
String str = "2019-06-01T19:45:43.000 +0800";
DateTime dateTime = DateUtil.parse(str, "yyyy-MM-dd'T'HH:mm:ss.SSS Z");
Assert.assertEquals("2019-06-01 19:45:43", dateTime.toString());
}
@Test
public void parseDateTest() throws ParseException {
public void parseAndOffsetTest() {
// 检查UTC时间偏移是否准确
String str = "2019-09-17T13:26:17.948Z";
DateTime dateTime = DateUtil.parse(str);
Assert.assertEquals("2019-09-17 13:26:17", dateTime.toString());
DateTime offset = DateUtil.offsetHour(dateTime, 8);
Assert.assertEquals("2019-09-17 21:26:17", offset.toString());
}
@Test
public void parseDateTest() {
String dateStr = "2018-4-10";
Date date = DateUtil.parseDate(dateStr);
String format = DateUtil.format(date, DatePattern.NORM_DATE_PATTERN);
@ -411,7 +414,7 @@ public class DateUtilTest {
}
@Test
public void parseUTCTest() throws ParseException {
public void parseUTCTest() {
String dateStr1 = "2018-09-13T05:34:31Z";
DateTime dt = DateUtil.parseUTC(dateStr1);
@ -471,7 +474,7 @@ public class DateUtilTest {
}
@Test
public void parseJDkTest() throws ParseException {
public void parseJDkTest() {
String dateStr = "Thu May 16 17:57:18 GMT+08:00 2019";
DateTime time = DateUtil.parse(dateStr);
Assert.assertEquals("2019-05-16 17:57:18", time.toString());
@ -521,7 +524,7 @@ public class DateUtilTest {
try {
range.next();
Assert.fail("已超过边界,下一个元素不应该存在!");
} catch (NoSuchElementException e) {
} catch (NoSuchElementException ignored) {
}
// 测试多步进的情况
@ -535,7 +538,7 @@ public class DateUtilTest {
try {
range.next();
Assert.fail("不包含结束时间情况下,下一个元素不应该存在!");
} catch (NoSuchElementException e) {
} catch (NoSuchElementException ignored) {
}
}

View File

@ -69,8 +69,8 @@ public class HttpRequest extends HttpBase<HttpRequest> {
* 获取Cookie管理器用于自定义Cookie管理
*
* @return {@link CookieManager}
* @since 4.1.0
* @see GlobalCookieManager#getCookieManager()
* @since 4.1.0
*/
public static CookieManager getCookieManager() {
return GlobalCookieManager.getCookieManager();
@ -80,8 +80,8 @@ public class HttpRequest extends HttpBase<HttpRequest> {
* 自定义{@link CookieManager}
*
* @param customCookieManager 自定义的{@link CookieManager}
* @since 4.5.14
* @see GlobalCookieManager#setCookieManager(CookieManager)
* @since 4.5.14
*/
public static void setCookieManager(CookieManager customCookieManager) {
GlobalCookieManager.setCookieManager(customCookieManager);
@ -90,8 +90,8 @@ public class HttpRequest extends HttpBase<HttpRequest> {
/**
* 关闭Cookie
*
* @since 4.1.9
* @see GlobalCookieManager#setCookieManager(CookieManager)
* @since 4.1.9
*/
public static void closeCookie() {
GlobalCookieManager.setCookieManager(null);
@ -100,37 +100,67 @@ public class HttpRequest extends HttpBase<HttpRequest> {
private String url;
private URLStreamHandler urlHandler;
private Method method = Method.GET;
/** 默认连接超时 */
/**
* 默认连接超时
*/
private int connectionTimeout = HttpGlobalConfig.timeout;
/** 默认读取超时 */
/**
* 默认读取超时
*/
private int readTimeout = HttpGlobalConfig.timeout;
/** 存储表单数据 */
/**
* 存储表单数据
*/
private Map<String, Object> form;
/** 文件表单对象,用于文件上传 */
/**
* 文件表单对象用于文件上传
*/
private Map<String, Resource> fileForm;
/** Cookie */
/**
* Cookie
*/
private String cookie;
/** 连接对象 */
/**
* 连接对象
*/
private HttpConnection httpConnection;
/** 是否禁用缓存 */
/**
* 是否禁用缓存
*/
private boolean isDisableCache;
/** 是否对url中的参数进行编码 */
/**
* 是否对url中的参数进行编码
*/
private boolean encodeUrlParams;
/** 是否是REST请求模式 */
/**
* 是否是REST请求模式
*/
private boolean isRest;
/** 重定向次数计数器,内部使用 */
/**
* 重定向次数计数器内部使用
*/
private int redirectCount;
/** 最大重定向次数 */
/**
* 最大重定向次数
*/
private int maxRedirectCount;
/** Chuncked块大小0或小于0表示不设置Chuncked模式 */
/**
* Chuncked块大小0或小于0表示不设置Chuncked模式
*/
private int blockSize;
/** 代理 */
/**
* 代理
*/
private Proxy proxy;
/** HostnameVerifier用于HTTPS安全连接 */
/**
* HostnameVerifier用于HTTPS安全连接
*/
private HostnameVerifier hostnameVerifier;
/** SSLSocketFactory用于HTTPS安全连接 */
/**
* SSLSocketFactory用于HTTPS安全连接
*/
private SSLSocketFactory ssf;
/**
@ -146,6 +176,7 @@ public class HttpRequest extends HttpBase<HttpRequest> {
}
// ---------------------------------------------------------------- static Http Method start
/**
* POST请求
*
@ -317,6 +348,7 @@ public class HttpRequest extends HttpBase<HttpRequest> {
}
// ---------------------------------------------------------------- Http Request Header start
/**
* 设置contentType
*
@ -422,6 +454,7 @@ public class HttpRequest extends HttpBase<HttpRequest> {
// ---------------------------------------------------------------- Http Request Header end
// ---------------------------------------------------------------- Form start
/**
* 设置表单数据<br>
*
@ -474,7 +507,6 @@ public class HttpRequest extends HttpBase<HttpRequest> {
* @param value
* @param parameters 参数对奇数为名偶数为值
* @return this
*
*/
public HttpRequest form(String name, Object value, Object... parameters) {
form(name, value);
@ -491,7 +523,6 @@ public class HttpRequest extends HttpBase<HttpRequest> {
*
* @param formMap 表单内容
* @return this
*
*/
public HttpRequest form(Map<String, Object> formMap) {
if (MapUtil.isNotEmpty(formMap)) {
@ -608,6 +639,7 @@ public class HttpRequest extends HttpBase<HttpRequest> {
// ---------------------------------------------------------------- Form end
// ---------------------------------------------------------------- Body start
/**
* 设置内容主体<br>
* 请求体body参数支持两种类型
@ -686,8 +718,9 @@ public class HttpRequest extends HttpBase<HttpRequest> {
* @return this
*/
public HttpRequest body(byte[] bodyBytes) {
Assert.notNull(bodyBytes, "Body must be not null !");
if (null != bodyBytes) {
this.bodyBytes = bodyBytes;
}
return this;
}
// ---------------------------------------------------------------- Body end
@ -833,9 +866,9 @@ public class HttpRequest extends HttpBase<HttpRequest> {
* ...
* </pre>
*
* @see SSLSocketFactoryBuilder
* @param protocol 协议
* @return this
* @see SSLSocketFactoryBuilder
*/
public HttpRequest setSSLProtocol(String protocol) {
if (null == this.ssf) {
@ -889,7 +922,6 @@ public class HttpRequest extends HttpBase<HttpRequest> {
*
* <p>
* 一般执行完execute之后会把响应内容全部读出来放在一个 byte数组里如果你响应的内容太多内存就爆了此法是发送完请求不直接读响应内容等有需要的时候读
*
* @return 异步对象使用get方法获取HttpResponse对象
*/
@ -943,11 +975,12 @@ public class HttpRequest extends HttpBase<HttpRequest> {
}
// ---------------------------------------------------------------- Private method start
/**
* 初始化网络连接
*/
private void initConnecton() {
if(null != this.httpConnection) {
if (null != this.httpConnection) {
// 执行下次请求时自动关闭上次请求常用于转发
this.httpConnection.disconnectQuietly();
}
@ -1077,7 +1110,7 @@ public class HttpRequest extends HttpBase<HttpRequest> {
private void sendMultipart() throws IOException {
setMultipart();// 设置表单类型为Multipart
try(OutputStream out = this.httpConnection.getOutputStream()) {
try (OutputStream out = this.httpConnection.getOutputStream()) {
writeFileForm(out);
writeForm(out);
formEnd(out);
@ -1087,6 +1120,7 @@ public class HttpRequest extends HttpBase<HttpRequest> {
}
// 普通字符串数据
/**
* 发送普通表单内容
*
@ -1152,6 +1186,7 @@ public class HttpRequest extends HttpBase<HttpRequest> {
}
// 添加结尾数据
/**
* 上传表单结束
*