根据P3C优化代码

This commit is contained in:
smallchill 2018-12-27 17:23:44 +08:00
parent 0d4f08b475
commit 4b67b9a91f
47 changed files with 207 additions and 6440 deletions

View File

@ -72,7 +72,7 @@ public class RedisTemplateConfiguration {
@Bean @Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1)); // 设置缓存有效期一小时 .entryTtl(Duration.ofHours(1));
return RedisCacheManager return RedisCacheManager
.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory)) .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
.cacheDefaults(redisCacheConfiguration).build(); .cacheDefaults(redisCacheConfiguration).build();

View File

@ -15,6 +15,7 @@
*/ */
package org.springblade.core.boot.file; package org.springblade.core.boot.file;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.StringUtil; import org.springblade.core.tool.utils.StringUtil;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@ -31,6 +32,9 @@ public class BladeFileUtil {
* 定义允许上传的文件扩展名 * 定义允许上传的文件扩展名
*/ */
private static HashMap<String, String> extMap = new HashMap<String, String>(); private static HashMap<String, String> extMap = new HashMap<String, String>();
private static String IS_DIR = "is_dir";
private static String FILE_NAME = "filename";
private static String FILE_SIZE = "filesize";
/** /**
* 图片扩展名 * 图片扩展名
@ -53,7 +57,7 @@ public class BladeFileUtil {
* @return String 返回类型 * @return String 返回类型
*/ */
public static String getFileExt(String fileName) { public static String getFileExt(String fileName) {
return fileName.substring(fileName.lastIndexOf('.'), fileName.length()); return fileName.substring(fileName.lastIndexOf(StringPool.DOT));
} }
/** /**
@ -75,7 +79,21 @@ public class BladeFileUtil {
* 文件管理排序 * 文件管理排序
*/ */
public enum FileSort { public enum FileSort {
size, type, name;
/**
* 大小
*/
size,
/**
* 类型
*/
type,
/**
* 名称
*/
name;
/** /**
* 文本排序转换成枚举 * 文本排序转换成枚举
@ -97,12 +115,12 @@ public class BladeFileUtil {
public int compare(Object a, Object b) { public int compare(Object a, Object b) {
Hashtable hashA = (Hashtable) a; Hashtable hashA = (Hashtable) a;
Hashtable hashB = (Hashtable) b; Hashtable hashB = (Hashtable) b;
if (((Boolean) hashA.get("is_dir")) && !((Boolean) hashB.get("is_dir"))) { if (((Boolean) hashA.get(IS_DIR)) && !((Boolean) hashB.get(IS_DIR))) {
return -1; return -1;
} else if (!((Boolean) hashA.get("is_dir")) && ((Boolean) hashB.get("is_dir"))) { } else if (!((Boolean) hashA.get(IS_DIR)) && ((Boolean) hashB.get(IS_DIR))) {
return 1; return 1;
} else { } else {
return ((String) hashA.get("filename")).compareTo((String) hashB.get("filename")); return ((String) hashA.get(FILE_NAME)).compareTo((String) hashB.get(FILE_NAME));
} }
} }
} }
@ -112,14 +130,14 @@ public class BladeFileUtil {
public int compare(Object a, Object b) { public int compare(Object a, Object b) {
Hashtable hashA = (Hashtable) a; Hashtable hashA = (Hashtable) a;
Hashtable hashB = (Hashtable) b; Hashtable hashB = (Hashtable) b;
if (((Boolean) hashA.get("is_dir")) && !((Boolean) hashB.get("is_dir"))) { if (((Boolean) hashA.get(IS_DIR)) && !((Boolean) hashB.get(IS_DIR))) {
return -1; return -1;
} else if (!((Boolean) hashA.get("is_dir")) && ((Boolean) hashB.get("is_dir"))) { } else if (!((Boolean) hashA.get(IS_DIR)) && ((Boolean) hashB.get(IS_DIR))) {
return 1; return 1;
} else { } else {
if (((Long) hashA.get("filesize")) > ((Long) hashB.get("filesize"))) { if (((Long) hashA.get(FILE_SIZE)) > ((Long) hashB.get(FILE_SIZE))) {
return 1; return 1;
} else if (((Long) hashA.get("filesize")) < ((Long) hashB.get("filesize"))) { } else if (((Long) hashA.get(FILE_SIZE)) < ((Long) hashB.get(FILE_SIZE))) {
return -1; return -1;
} else { } else {
return 0; return 0;
@ -133,9 +151,9 @@ public class BladeFileUtil {
public int compare(Object a, Object b) { public int compare(Object a, Object b) {
Hashtable hashA = (Hashtable) a; Hashtable hashA = (Hashtable) a;
Hashtable hashB = (Hashtable) b; Hashtable hashB = (Hashtable) b;
if (((Boolean) hashA.get("is_dir")) && !((Boolean) hashB.get("is_dir"))) { if (((Boolean) hashA.get(IS_DIR)) && !((Boolean) hashB.get(IS_DIR))) {
return -1; return -1;
} else if (!((Boolean) hashA.get("is_dir")) && ((Boolean) hashB.get("is_dir"))) { } else if (!((Boolean) hashA.get(IS_DIR)) && ((Boolean) hashB.get(IS_DIR))) {
return 1; return 1;
} else { } else {
return ((String) hashA.get("filetype")).compareTo((String) hashB.get("filetype")); return ((String) hashA.get("filetype")).compareTo((String) hashB.get("filetype"));

View File

@ -26,7 +26,7 @@ import java.util.Properties;
* *
* @author smallchil * @author smallchil
*/ */
public class ConsulLauncherService implements LauncherService { public class ConsulLauncherServiceImpl implements LauncherService {
@Override @Override
public void launcher(SpringApplicationBuilder builder, String appName, String profile) { public void launcher(SpringApplicationBuilder builder, String appName, String profile) {

View File

@ -162,12 +162,7 @@ public class BladeProperties {
String value = prop.get(key); String value = prop.get(key);
if (value != null) { if (value != null) {
value = value.toLowerCase().trim(); value = value.toLowerCase().trim();
if ("true".equals(value)) { return Boolean.parseBoolean(value);
return Boolean.TRUE;
} else if ("false".equals(value)) {
return Boolean.FALSE;
}
throw new RuntimeException("The value can not parse to Boolean : " + value);
} }
return defaultValue; return defaultValue;
} }

View File

@ -43,7 +43,7 @@ public class ApiLogPublisher {
logApi.setTime(String.valueOf(time)); logApi.setTime(String.valueOf(time));
logApi.setMethodClass(methodClass); logApi.setMethodClass(methodClass);
logApi.setMethodName(methodName); logApi.setMethodName(methodName);
Map<String, Object> event = new HashMap<>(); Map<String, Object> event = new HashMap<>(16);
event.put(EventConstant.EVENT_LOG, logApi); event.put(EventConstant.EVENT_LOG, logApi);
event.put(EventConstant.EVENT_REQUEST, request); event.put(EventConstant.EVENT_REQUEST, request);
SpringUtil.publishEvent(new ApiLogEvent(event)); SpringUtil.publishEvent(new ApiLogEvent(event));

View File

@ -39,7 +39,7 @@ public class BladeLogPublisher {
logBlade.setLogLevel(level); logBlade.setLogLevel(level);
logBlade.setLogId(id); logBlade.setLogId(id);
logBlade.setLogData(data); logBlade.setLogData(data);
Map<String, Object> event = new HashMap<>(); Map<String, Object> event = new HashMap<>(16);
event.put(EventConstant.EVENT_LOG, logBlade); event.put(EventConstant.EVENT_LOG, logBlade);
event.put(EventConstant.EVENT_REQUEST, request); event.put(EventConstant.EVENT_REQUEST, request);
SpringUtil.publishEvent(new BladeLogEvent(event)); SpringUtil.publishEvent(new BladeLogEvent(event));

View File

@ -28,12 +28,27 @@ import java.util.stream.Collectors;
*/ */
public abstract class BaseEntityWrapper<E, V> { public abstract class BaseEntityWrapper<E, V> {
/**
* 单个实体类包装
* @param entity
* @return
*/
public abstract V entityVO(E entity); public abstract V entityVO(E entity);
/**
* 实体类集合包装
* @param list
* @return
*/
public List<V> listVO(List<E> list) { public List<V> listVO(List<E> list) {
return list.stream().map(this::entityVO).collect(Collectors.toList()); return list.stream().map(this::entityVO).collect(Collectors.toList());
} }
/**
* 分页实体类集合包装
* @param pages
* @return
*/
public IPage<V> pageVO(IPage<E> pages) { public IPage<V> pageVO(IPage<E> pages) {
List<V> records = listVO(pages.getRecords()); List<V> records = listVO(pages.getRecords());
IPage<V> pageVo = new Page<>(pages.getCurrent(), pages.getSize(), pages.getTotal()); IPage<V> pageVo = new Page<>(pages.getCurrent(), pages.getSize(), pages.getTotal());

View File

@ -20,9 +20,6 @@ import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.SignatureAlgorithm;
import org.springblade.core.secure.BladeUser; import org.springblade.core.secure.BladeUser;
import org.springblade.core.tool.date.DateField;
import org.springblade.core.tool.date.DateTime;
import org.springblade.core.tool.date.DateUtil;
import org.springblade.core.tool.utils.Func; import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringPool; import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.WebUtil; import org.springblade.core.tool.utils.WebUtil;
@ -31,6 +28,7 @@ import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter; import javax.xml.bind.DatatypeConverter;
import java.security.Key; import java.security.Key;
import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
@ -42,16 +40,17 @@ import java.util.Map;
public class SecureUtil { public class SecureUtil {
public static final String BLADE_USER_REQUEST_ATTR = "_BLADE_USER_REQUEST_ATTR_"; public static final String BLADE_USER_REQUEST_ATTR = "_BLADE_USER_REQUEST_ATTR_";
public final static String header = "Authorization"; public final static String HEADER = "Authorization";
public final static String bearer = "bearer"; public final static String BEARER = "bearer";
public final static String account = "account"; public final static String ACCOUNT = "account";
public final static String userId = "userId"; public final static String USER_ID = "userId";
public final static String roleId = "roleId"; public final static String ROLE_ID = "roleId";
public final static String userName = "userName"; public final static String USER_NAME = "userName";
public final static String roleName = "roleName"; public final static String ROLE_NAME = "roleName";
private static String base64Security = DatatypeConverter.printBase64Binary("SpringBlade".getBytes()); public final static Integer AUTH_LENGTH = 7;
private static String BASE64_SECURITY = DatatypeConverter.printBase64Binary("SpringBlade".getBytes());
/** /**6
* 获取用户信息 * 获取用户信息
* *
* @return * @return
@ -80,10 +79,10 @@ public class SecureUtil {
if (claims == null) { if (claims == null) {
return null; return null;
} }
Integer userId = Func.toInt(claims.get(SecureUtil.userId)); Integer userId = Func.toInt(claims.get(SecureUtil.USER_ID));
String roleId = Func.toStr(claims.get(SecureUtil.roleId)); String roleId = Func.toStr(claims.get(SecureUtil.ROLE_ID));
String account = Func.toStr(claims.get(SecureUtil.account)); String account = Func.toStr(claims.get(SecureUtil.ACCOUNT));
String roleName = Func.toStr(claims.get(SecureUtil.roleName)); String roleName = Func.toStr(claims.get(SecureUtil.ROLE_NAME));
BladeUser bladeUser = new BladeUser(); BladeUser bladeUser = new BladeUser();
bladeUser.setAccount(account); bladeUser.setAccount(account);
bladeUser.setUserId(userId); bladeUser.setUserId(userId);
@ -135,10 +134,10 @@ public class SecureUtil {
* @return * @return
*/ */
public static Claims getClaims(HttpServletRequest request) { public static Claims getClaims(HttpServletRequest request) {
String auth = request.getHeader(SecureUtil.header); String auth = request.getHeader(SecureUtil.HEADER);
if ((auth != null) && (auth.length() > 7)) { if ((auth != null) && (auth.length() > AUTH_LENGTH)) {
String HeadStr = auth.substring(0, 6).toLowerCase(); String headStr = auth.substring(0, 6).toLowerCase();
if (HeadStr.compareTo(SecureUtil.bearer) == 0) { if (headStr.compareTo(SecureUtil.BEARER) == 0) {
auth = auth.substring(7); auth = auth.substring(7);
return SecureUtil.parseJWT(auth); return SecureUtil.parseJWT(auth);
} }
@ -162,7 +161,7 @@ public class SecureUtil {
* @return * @return
*/ */
public static String getHeader(HttpServletRequest request) { public static String getHeader(HttpServletRequest request) {
return request.getHeader(header); return request.getHeader(HEADER);
} }
/** /**
@ -174,7 +173,7 @@ public class SecureUtil {
public static Claims parseJWT(String jsonWebToken) { public static Claims parseJWT(String jsonWebToken) {
try { try {
Claims claims = Jwts.parser() Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(base64Security)) .setSigningKey(DatatypeConverter.parseBase64Binary(BASE64_SECURITY))
.parseClaimsJws(jsonWebToken).getBody(); .parseClaimsJws(jsonWebToken).getBody();
return claims; return claims;
} catch (Exception ex) { } catch (Exception ex) {
@ -198,7 +197,7 @@ public class SecureUtil {
Date now = new Date(nowMillis); Date now = new Date(nowMillis);
//生成签名密钥 //生成签名密钥
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(base64Security); byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(BASE64_SECURITY);
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName()); Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
//添加构成JWT的类 //添加构成JWT的类
@ -227,9 +226,13 @@ public class SecureUtil {
* @return * @return
*/ */
public static long getExpire() { public static long getExpire() {
DateTime dateTime = DateUtil.endOfDay(new Date()); Calendar cal = Calendar.getInstance();
DateTime offset = DateUtil.offset(dateTime, DateField.HOUR, 3); cal.add(Calendar.DAY_OF_YEAR, 1);
cal.set(Calendar.HOUR_OF_DAY, 3);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTimeInMillis() - System.currentTimeMillis();
}
return offset.getTime() - System.currentTimeMillis();
}
} }

View File

@ -81,14 +81,14 @@ public class SystemConstant {
*/ */
private String contextPath = "/"; private String contextPath = "/";
private static final SystemConstant me = new SystemConstant(); private static final SystemConstant ME = new SystemConstant();
private SystemConstant() { private SystemConstant() {
} }
public static SystemConstant me() { public static SystemConstant me() {
return me; return ME;
} }
public String getUploadRealPath() { public String getUploadRealPath() {

View File

@ -1,157 +0,0 @@
package org.springblade.core.tool.date;
/**
* 时长格式化器
* @author Looly
*
*/
public class BetweenFormater {
/** 时长毫秒数 */
private long betweenMs;
/** 格式化级别 */
private Level level;
/** 格式化级别的最大个数 */
private int levelMaxCount;
/**
* 构造
* @param betweenMs 日期间隔
* @param level 级别按照天小时毫秒分为5个等级根据传入等级格式化到相应级别
*/
public BetweenFormater(long betweenMs, Level level) {
this(betweenMs, level, 0);
}
/**
* 构造
* @param betweenMs 日期间隔
* @param level 级别按照天小时毫秒分为5个等级根据传入等级格式化到相应级别
* @param levelMaxCount 格式化级别的最大个数假如级别个数为1但是级别到秒那只显示一个级别
*/
public BetweenFormater(long betweenMs, Level level, int levelMaxCount) {
this.betweenMs = betweenMs;
this.level = level;
this.levelMaxCount = levelMaxCount;
}
/**
* 格式化日期间隔输出<br>
*
* @return 格式化后的字符串
*/
public String format(){
if(betweenMs == 0){
return "0";
}
long day = betweenMs / DateUnit.DAY.getMillis();
long hour = betweenMs / DateUnit.HOUR.getMillis() - day * 24;
long minute = betweenMs / DateUnit.MINUTE.getMillis() - day * 24 * 60 - hour * 60;
long second = betweenMs / DateUnit.SECOND.getMillis() - ((day * 24 + hour) * 60 + minute) * 60;
long millisecond = betweenMs - (((day * 24 + hour) * 60 + minute) * 60 + second) * 1000;
StringBuilder sb = new StringBuilder();
final int level = this.level.value;
int levelCount = 0;
if(isLevelCountValid(levelCount) && 0 != day && level > 0){
sb.append(day).append("");
levelCount++;
}
if(isLevelCountValid(levelCount) && 0 != hour && level > 1){
sb.append(hour).append("小时");
levelCount++;
}
if(isLevelCountValid(levelCount) && 0 != minute && level > 2){
sb.append(minute).append("");
levelCount++;
}
if(isLevelCountValid(levelCount) && 0 != second && level > 3){
sb.append(second).append("");
levelCount++;
}
if(isLevelCountValid(levelCount) && 0 != millisecond && level > 4){
sb.append(millisecond).append("毫秒");
levelCount++;
}
return sb.toString();
}
/**
* 获得 时长毫秒数
* @return 时长毫秒数
*/
public long getBetweenMs() {
return betweenMs;
}
/**
* 设置 时长毫秒数
* @param betweenMs 时长毫秒数
*/
public void setBetweenMs(long betweenMs) {
this.betweenMs = betweenMs;
}
/**
* 获得 格式化级别
* @return 格式化级别
*/
public Level getLevel() {
return level;
}
/**
* 设置格式化级别
* @param level 格式化级别
*/
public void setLevel(Level level) {
this.level = level;
}
/**
* 格式化等级枚举<br>
* @author Looly
*/
public static enum Level {
/** 天 */
DAY(1),
/** 小时 */
HOUR(2),
/** 分钟 */
MINUTE(3),
/** 秒 */
SECOND(4),
/** 毫秒 */
MILLSECOND(5);
private int value;
private Level(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
@Override
public String toString() {
return format();
}
/**
* 等级数量是否有效<br>
* 有效的定义是levelMaxCount大于0被设置当前等级数量没有超过这个最大值
*
* @param levelCount 登记数量
* @return 是否有效
*/
private boolean isLevelCountValid(int levelCount){
return this.levelMaxCount <= 0 || levelCount < this.levelMaxCount;
}
}

View File

@ -1,70 +0,0 @@
package org.springblade.core.tool.date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Queue;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* 参考tomcat8中的并发DateFormat
* <p>
* {@link SimpleDateFormat}的线程安全包装器
* 不使用ThreadLocal创建足够的SimpleDateFormat对象来满足并发性要求
*
* @author L.cm
*/
public class ConcurrentDateFormat {
private final String format;
private final Locale locale;
private final TimeZone timezone;
private final Queue<SimpleDateFormat> queue = new ConcurrentLinkedQueue<>();
private ConcurrentDateFormat(String format, Locale locale, TimeZone timezone) {
this.format = format;
this.locale = locale;
this.timezone = timezone;
SimpleDateFormat initial = createInstance();
queue.add(initial);
}
public static ConcurrentDateFormat of(String format) {
return new ConcurrentDateFormat(format, Locale.getDefault(), TimeZone.getDefault());
}
public static ConcurrentDateFormat of(String format, TimeZone timezone) {
return new ConcurrentDateFormat(format, Locale.getDefault(), timezone);
}
public static ConcurrentDateFormat of(String format, Locale locale, TimeZone timezone) {
return new ConcurrentDateFormat(format, locale, timezone);
}
public String format(Date date) {
SimpleDateFormat sdf = queue.poll();
if (sdf == null) {
sdf = createInstance();
}
String result = sdf.format(date);
queue.add(sdf);
return result;
}
public Date parse(String source) throws ParseException {
SimpleDateFormat sdf = queue.poll();
if (sdf == null) {
sdf = createInstance();
}
Date result = sdf.parse(source);
queue.add(sdf);
return result;
}
private SimpleDateFormat createInstance() {
SimpleDateFormat sdf = new SimpleDateFormat(format, locale);
sdf.setTimeZone(timezone);
return sdf;
}
}

View File

@ -1,153 +0,0 @@
package org.springblade.core.tool.date;
import java.util.Calendar;
import java.util.Date;
/**
* 日期间隔
*
* @author Looly
*
*/
public class DateBetween {
/** 开始日期 */
private Date begin;
/** 结束日期 */
private Date end;
/**
* 创建<br>
* 在前的日期做为起始时间在后的做为结束时间间隔只保留绝对值正数
*
* @param begin 起始时间
* @param end 结束时间
* @return {@link DateBetween}
* @since 3.2.3
*/
public static DateBetween create(Date begin, Date end) {
return new DateBetween(begin, end);
}
/**
* 创建<br>
* 在前的日期做为起始时间在后的做为结束时间间隔只保留绝对值正数
*
* @param begin 起始时间
* @param end 结束时间
* @param isAbs 日期间隔是否只保留绝对值正数
* @return {@link DateBetween}
* @since 3.2.3
*/
public static DateBetween create(Date begin, Date end, boolean isAbs) {
return new DateBetween(begin, end, isAbs);
}
/**
* 构造<br>
* 在前的日期做为起始时间在后的做为结束时间间隔只保留绝对值正数
*
* @param begin 起始时间
* @param end 结束时间
*/
public DateBetween(Date begin, Date end) {
this(begin, end, true);
}
/**
* 构造<br>
* 在前的日期做为起始时间在后的做为结束时间
*
* @param begin 起始时间
* @param end 结束时间
* @param isAbs 日期间隔是否只保留绝对值正数
* @since 3.1.1
*/
public DateBetween(Date begin, Date end, boolean isAbs) {
if (isAbs && begin.after(end)) {
// 间隔只为正数的情况下如果开始日期晚于结束日期置换之
this.begin = end;
this.end = begin;
} else {
this.begin = begin;
this.end = end;
}
}
/**
* 判断两个日期相差的时长<br>
* 返回 给定单位的时长差
*
* @param unit 相差的单位相差 {@link DateUnit#DAY}小时{@link DateUnit#HOUR}
* @return 时长差
*/
public long between(DateUnit unit) {
long diff = end.getTime() - begin.getTime();
return diff / unit.getMillis();
}
/**
* 计算两个日期相差月数<br>
* 在非重置情况下如果起始日期的天小于结束日期的天月数要少算1不足1个月
*
* @param isReset 是否重置时间为起始时间重置天时分秒
* @return 相差月数
* @since 3.0.8
*/
public long betweenMonth(boolean isReset) {
final Calendar beginCal = DateUtil.calendar(begin);
final Calendar endCal = DateUtil.calendar(end);
final int betweenYear = endCal.get(Calendar.YEAR) - beginCal.get(Calendar.YEAR);
final int betweenMonthOfYear = endCal.get(Calendar.MONTH) - beginCal.get(Calendar.MONTH);
int result = betweenYear * 12 + betweenMonthOfYear;
if (false == isReset) {
endCal.set(Calendar.YEAR, beginCal.get(Calendar.YEAR));
endCal.set(Calendar.MONTH, beginCal.get(Calendar.MONTH));
long between = endCal.getTimeInMillis() - beginCal.getTimeInMillis();
if (between < 0) {
return result - 1;
}
}
return result;
}
/**
* 计算两个日期相差年数<br>
* 在非重置情况下如果起始日期的月小于结束日期的月年数要少算1不足1年
*
* @param isReset 是否重置时间为起始时间重置月天时分秒
* @return 相差年数
* @since 3.0.8
*/
public long betweenYear(boolean isReset) {
final Calendar beginCal = DateUtil.calendar(begin);
final Calendar endCal = DateUtil.calendar(end);
int result = endCal.get(Calendar.YEAR) - beginCal.get(Calendar.YEAR);
if (false == isReset) {
endCal.set(Calendar.YEAR, beginCal.get(Calendar.YEAR));
long between = endCal.getTimeInMillis() - beginCal.getTimeInMillis();
if (between < 0) {
return result - 1;
}
}
return result;
}
/**
* 格式化输出时间差<br>
*
* @param level 级别
* @return 字符串
*/
public String toString(BetweenFormater.Level level) {
return DateUtil.formatBetween(between(DateUnit.MS), level);
}
@Override
public String toString() {
return toString(BetweenFormater.Level.MILLSECOND);
}
}

View File

@ -1,31 +0,0 @@
package org.springblade.core.tool.date;
import org.springblade.core.tool.utils.StringUtil;
/**
* 工具类异常
* @author xiaoleilu
*/
public class DateException extends RuntimeException{
private static final long serialVersionUID = 8247610319171014183L;
public DateException(Throwable e) {
super(StringUtil.format("{}: {}", e.getClass().getSimpleName(), e.getMessage()), e);
}
public DateException(String message) {
super(message);
}
public DateException(String messageTemplate, Object... params) {
super(StringUtil.format(messageTemplate, params));
}
public DateException(String message, Throwable throwable) {
super(message, throwable);
}
public DateException(Throwable throwable, String messageTemplate, Object... params) {
super(StringUtil.format(messageTemplate, params), throwable);
}
}

View File

@ -1,130 +0,0 @@
package org.springblade.core.tool.date;
import java.util.Calendar;
/**
* 日期各个部分的枚举<br>
* 与Calendar相应值对应
*
* @author Looly
*
*/
public enum DateField {
/**
*
* @see Calendar#YEAR
*/
YEAR(Calendar.YEAR),
/**
*
* @see Calendar#MONTH
*/
MONTH(Calendar.MONTH),
/**
* 一年中第几周
* @see Calendar#WEEK_OF_YEAR
*/
WEEK_OF_YEAR(Calendar.WEEK_OF_YEAR),
/**
* 一月中第几周
* @see Calendar#WEEK_OF_MONTH
*/
WEEK_OF_MONTH(Calendar.WEEK_OF_MONTH),
/**
* 一月中的第几天
* @see Calendar#DAY_OF_MONTH
*/
DAY_OF_MONTH(Calendar.DAY_OF_MONTH),
/**
*一年中的第几天
* @see Calendar#DAY_OF_YEAR
*/
DAY_OF_YEAR(Calendar.DAY_OF_YEAR),
/**
*周几1表示周日2表示周一
* @see Calendar#DAY_OF_WEEK
*/
DAY_OF_WEEK(Calendar.DAY_OF_WEEK),
/**
* 天所在的周是这个月的第几周
* @see Calendar#DAY_OF_WEEK_IN_MONTH
*/
DAY_OF_WEEK_IN_MONTH(Calendar.DAY_OF_WEEK_IN_MONTH),
/**
* 上午或者下午
* @see Calendar#AM_PM
*/
AM_PM(Calendar.AM_PM),
/**
* 小时用于12小时制
* @see Calendar#HOUR
*/
HOUR(Calendar.HOUR),
/**
* 小时用于24小时制
* @see Calendar#HOUR
*/
HOUR_OF_DAY(Calendar.HOUR_OF_DAY),
/**
* 分钟
* @see Calendar#MINUTE
*/
MINUTE(Calendar.MINUTE),
/**
*
* @see Calendar#SECOND
*/
SECOND(Calendar.SECOND),
/**
* 毫秒
* @see Calendar#MILLISECOND
*/
MILLISECOND(Calendar.MILLISECOND);
// ---------------------------------------------------------------
private int value;
private DateField(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
/**
* {@link Calendar}相关值转换为DatePart枚举对象<br>
*
* @param calendarPartIntValue Calendar中关于Week的int值
* @return {@link DateField}
*/
public static DateField of(int calendarPartIntValue) {
switch (calendarPartIntValue) {
case Calendar.YEAR:
return YEAR;
case Calendar.MONTH:
return MONTH;
case Calendar.WEEK_OF_YEAR:
return WEEK_OF_YEAR;
case Calendar.WEEK_OF_MONTH:
return WEEK_OF_MONTH;
case Calendar.DAY_OF_MONTH:
return DAY_OF_MONTH;
case Calendar.DAY_OF_YEAR:
return DAY_OF_YEAR;
case Calendar.DAY_OF_WEEK:
return DAY_OF_WEEK;
case Calendar.DAY_OF_WEEK_IN_MONTH:
return DAY_OF_WEEK_IN_MONTH;
case Calendar.MINUTE:
return MINUTE;
case Calendar.SECOND:
return SECOND;
case Calendar.MILLISECOND:
return MILLISECOND;
default:
return null;
}
}
}

View File

@ -1,71 +0,0 @@
package org.springblade.core.tool.date;
import org.springblade.core.tool.date.format.FastDateFormat;
/**
* 日期格式化类提供常用的日期格式化对象
*
* @author Looly
*
*/
public class DatePattern {
//-------------------------------------------------------------------------------------------------------------------------------- Normal
/** 标准日期格式yyyy-MM-dd */
public final static String NORM_DATE_PATTERN = "yyyy-MM-dd";
/** 标准日期格式 {@link FastDateFormat}yyyy-MM-dd */
public final static FastDateFormat NORM_DATE_FORMAT = FastDateFormat.getInstance(NORM_DATE_PATTERN);
/** 标准时间格式HH:mm:ss */
public final static String NORM_TIME_PATTERN = "HH:mm:ss";
/** 标准时间格式 {@link FastDateFormat}HH:mm:ss */
public final static FastDateFormat NORM_TIME_FORMAT = FastDateFormat.getInstance(NORM_TIME_PATTERN);
/** 标准日期时间格式精确到分yyyy-MM-dd HH:mm */
public final static String NORM_DATETIME_MINUTE_PATTERN = "yyyy-MM-dd HH:mm";
/** 标准日期时间格式,精确到分 {@link FastDateFormat}yyyy-MM-dd HH:mm */
public final static FastDateFormat NORM_DATETIME_MINUTE_FORMAT = FastDateFormat.getInstance(NORM_DATETIME_MINUTE_PATTERN);
/** 标准日期时间格式精确到秒yyyy-MM-dd HH:mm:ss */
public final static String NORM_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
/** 标准日期时间格式,精确到秒 {@link FastDateFormat}yyyy-MM-dd HH:mm:ss */
public final static FastDateFormat NORM_DATETIME_FORMAT = FastDateFormat.getInstance(NORM_DATETIME_PATTERN);
/** 标准日期时间格式精确到毫秒yyyy-MM-dd HH:mm:ss.SSS */
public final static String NORM_DATETIME_MS_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS";
/** 标准日期时间格式,精确到毫秒 {@link FastDateFormat}yyyy-MM-dd HH:mm:ss.SSS */
public final static FastDateFormat NORM_DATETIME_MS_FORMAT = FastDateFormat.getInstance(NORM_DATETIME_MS_PATTERN);
//-------------------------------------------------------------------------------------------------------------------------------- Pure
/** 标准日期格式yyyyMMdd */
public final static String PURE_DATE_PATTERN = "yyyyMMdd";
/** 标准日期格式 {@link FastDateFormat}yyyyMMdd */
public final static FastDateFormat PURE_DATE_FORMAT = FastDateFormat.getInstance(PURE_DATE_PATTERN);
/** 标准日期格式HHmmss */
public final static String PURE_TIME_PATTERN = "HHmmss";
/** 标准日期格式 {@link FastDateFormat}HHmmss */
public final static FastDateFormat PURE_TIME_FORMAT = FastDateFormat.getInstance(PURE_TIME_PATTERN);
/** 标准日期格式yyyyMMddHHmmss */
public final static String PURE_DATETIME_PATTERN = "yyyyMMddHHmmss";
/** 标准日期格式 {@link FastDateFormat}yyyyMMddHHmmss */
public final static FastDateFormat PURE_DATETIME_FORMAT = FastDateFormat.getInstance(PURE_DATETIME_PATTERN);
/** 标准日期格式yyyyMMddHHmmssSSS */
public final static String PURE_DATETIME_MS_PATTERN = "yyyyMMddHHmmssSSS";
/** 标准日期格式 {@link FastDateFormat}yyyyMMddHHmmssSSS */
public final static FastDateFormat PURE_DATETIME_MS_FORMAT = FastDateFormat.getInstance(PURE_DATETIME_MS_PATTERN);
//-------------------------------------------------------------------------------------------------------------------------------- Others
/** HTTP头中日期时间格式EEE, dd MMM yyyy HH:mm:ss z */
public final static String HTTP_DATETIME_PATTERN = "EEE, dd MMM yyyy HH:mm:ss z";
/** HTTP头中日期时间格式 {@link FastDateFormat}EEE, dd MMM yyyy HH:mm:ss z */
public final static FastDateFormat HTTP_DATETIME_FORMAT = FastDateFormat.getInstance(HTTP_DATETIME_PATTERN);
/** HTTP头中日期时间格式EEE MMM dd HH:mm:ss zzz yyyy */
public final static String JDK_DATETIME_PATTERN = "EEE MMM dd HH:mm:ss zzz yyyy";
/** HTTP头中日期时间格式 {@link FastDateFormat}EEE MMM dd HH:mm:ss zzz yyyy */
public final static FastDateFormat JDK_DATETIME_FORMAT = FastDateFormat.getInstance(JDK_DATETIME_PATTERN);
}

View File

@ -1,753 +0,0 @@
package org.springblade.core.tool.date;
import org.springblade.core.tool.date.format.DateParser;
import org.springblade.core.tool.date.format.DatePrinter;
import org.springblade.core.tool.date.format.FastDateFormat;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.util.Assert;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* 包装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 必须确定一个月或一年的第一个星期,以此作为参考点。 */
private Week firstDayOfWeek = Week.MONDAY;
/**
* 转换JDK date为 DateTime
*
* @param date JDK Date
* @return DateTime
*/
public static DateTime of(Date date) {
if(date instanceof DateTime) {
return (DateTime)date;
}
return new DateTime(date);
}
/**
* 转换 {@link Calendar} DateTime
*
* @param calendar {@link Calendar}
* @return DateTime
*/
public static DateTime of(Calendar calendar) {
return new DateTime(calendar);
}
/**
* 构造
*
* @see DatePattern
* @param dateStr Date字符串
* @param format 格式
* @return {@link DateTime}
*/
public static DateTime of(String dateStr, String format) {
return new DateTime(dateStr, format);
}
/**
* 现在的时间
*
* @return 现在的时间
*/
public static DateTime now() {
return new DateTime();
}
// -------------------------------------------------------------------- Constructor start
/**
* 当前时间
*/
public DateTime() {
super();
}
/**
* 给定日期的构造
*
* @param date 日期
*/
public DateTime(Date date) {
this(date.getTime());
}
/**
* 给定日期的构造
*
* @param calendar {@link Calendar}
*/
public DateTime(Calendar calendar) {
this(calendar.getTime());
}
/**
* 给定日期毫秒数的构造
*
* @param timeMillis 日期毫秒数
*/
public DateTime(long timeMillis) {
super(timeMillis);
}
/**
* 构造
*
* @see DatePattern
* @param dateStr Date字符串
* @param format 格式
*/
public DateTime(String dateStr, String format) {
this(dateStr, new SimpleDateFormat(format));
}
/**
* 构造
*
* @see DatePattern
* @param dateStr Date字符串
* @param dateFormat 格式化器 {@link SimpleDateFormat}
*/
public DateTime(String dateStr, DateFormat dateFormat) {
this(parse(dateStr, dateFormat));
}
/**
* 构造
*
* @see DatePattern
* @param dateStr Date字符串
* @param dateParser 格式化器 {@link DateParser}可以使用 {@link FastDateFormat}
*/
public DateTime(String dateStr, DateParser dateParser) {
this(parse(dateStr, dateParser));
}
// -------------------------------------------------------------------- Constructor end
// -------------------------------------------------------------------- offset start
/**
* 调整日期和时间<br>
* 如果此对象为可变对象返回自身否则返回新对象设置是否可变对象见{@link #setMutable(boolean)}
*
* @param datePart 调整的部分 {@link DateField}
* @param offset 偏移量正数为向后偏移负数为向前偏移
* @return 如果此对象为可变对象返回自身否则返回新对象
*/
public DateTime offset(DateField datePart, int offset) {
final Calendar cal = toCalendar();
cal.add(datePart.getValue(), offset);
DateTime dt = mutable ? this : Func.clone(this);
return dt.setTimeInternal(cal.getTimeInMillis());
}
/**
* 调整日期和时间<br>
* 返回调整后的新{@link DateTime}不影响原对象
*
* @param datePart 调整的部分 {@link DateField}
* @param offset 偏移量正数为向后偏移负数为向前偏移
* @return 如果此对象为可变对象返回自身否则返回新对象
* @since 3.0.9
*/
public DateTime offsetNew(DateField datePart, int offset) {
final Calendar cal = toCalendar();
cal.add(datePart.getValue(), offset);
DateTime dt = Func.clone(this);
return dt.setTimeInternal(cal.getTimeInMillis());
}
// -------------------------------------------------------------------- offset end
// -------------------------------------------------------------------- Part of Date start
/**
* 获得日期的某个部分<br>
* 例如获得年的部分则使用 getField(DatePart.YEAR)
*
* @param field 表示日期的哪个部分的枚举 {@link DateField}
* @return 某个部分的值
*/
public int getField(DateField field) {
return getField(field.getValue());
}
/**
* 获得日期的某个部分<br>
* 例如获得年的部分则使用 getField(Calendar.YEAR)
*
* @param field 表示日期的哪个部分的int值 {@link Calendar}
* @return 某个部分的值
*/
public int getField(int field) {
return toCalendar().get(field);
}
/**
* 设置日期的某个部分<br>
* 如果此对象为可变对象返回自身否则返回新对象设置是否可变对象见{@link #setMutable(boolean)}
*
* @param field 表示日期的哪个部分的枚举 {@link DateField}
* @param value
* @return {@link DateTime}
*/
public DateTime setField(DateField field, int value) {
return setField(field.getValue(), value);
}
/**
* 设置日期的某个部分<br>
* 如果此对象为可变对象返回自身否则返回新对象设置是否可变对象见{@link #setMutable(boolean)}
*
* @param field 表示日期的哪个部分的int值 {@link Calendar}
* @param value
* @return {@link DateTime}
*/
public DateTime setField(int field, int value) {
Calendar calendar = toCalendar();
calendar.set(field, value);
DateTime dt = this;
if (false == mutable) {
dt = Func.clone(this);
}
return dt.setTimeInternal(calendar.getTimeInMillis());
}
@Override
public void setTime(long time) {
if (mutable) {
super.setTime(time);
} else {
throw new DateException("This is not a mutable object !");
}
}
/**
* 获得年的部分
*
* @return 年的部分
*/
public int year() {
return getField(DateField.YEAR);
}
/**
* 获得当前日期所属季度<br>
* 1第一季度<br>
* 2第二季度<br>
* 3第三季度<br>
* 4第四季度<br>
*
* @return 第几个季度
*/
public int season() {
return monthStartFromOne() / 4 + 1;
}
/**
* 获得当前日期所属季度<br>
*
* @return 第几个季度 {@link Season}
*/
public Season seasonEnum() {
return Season.of(season());
}
/**
* 获得月份从0开始计数
*
* @return 月份
*/
public int month() {
return getField(DateField.MONTH);
}
/**
* 获得月份从1开始计数<br>
* 由于{@link Calendar} 中的月份按照0开始计数导致某些需求容易误解因此如果想用1表示一月2表示二月则调用此方法
*
* @return 月份
*/
public int monthStartFromOne() {
return month() + 1;
}
/**
* 获得月份
*
* @return {@link Month}
*/
public Month monthEnum() {
return Month.of(month());
}
/**
* 获得指定日期是所在年份的第几周<br>
* 此方法返回值与一周的第一天有关比如<br>
* 2016年1月3日为周日如果一周的第一天为周日那这天是第二周返回2<br>
* 如果一周的第一天为周一那这天是第一周返回1
*
* @return
* @see #setFirstDayOfWeek(Week)
*/
public int weekOfYear() {
return getField(DateField.WEEK_OF_YEAR);
}
/**
* 获得指定日期是所在月份的第几周<br>
* 此方法返回值与一周的第一天有关比如<br>
* 2016年1月3日为周日如果一周的第一天为周日那这天是第二周返回2<br>
* 如果一周的第一天为周一那这天是第一周返回1
*
* @return
* @see #setFirstDayOfWeek(Week)
*/
public int weekOfMonth() {
return getField(DateField.WEEK_OF_MONTH);
}
/**
* 获得指定日期是这个日期所在月份的第几天<br>
*
* @return
*/
public int dayOfMonth() {
return getField(DateField.DAY_OF_MONTH);
}
/**
* 获得指定日期是星期几1表示周日2表示周一
*
* @return 星期几
*/
public int dayOfWeek() {
return getField(DateField.DAY_OF_WEEK);
}
/**
* 获得天所在的周是这个月的第几周
*
* @return
*/
public int dayOfWeekInMonth() {
return getField(DateField.DAY_OF_WEEK_IN_MONTH);
}
/**
* 获得指定日期是星期几
*
* @return {@link Week}
*/
public Week dayOfWeekEnum() {
return Week.of(dayOfWeek());
}
/**
* 获得指定日期的小时数部分<br>
*
* @param is24HourClock 是否24小时制
* @return 小时数
*/
public int hour(boolean is24HourClock) {
return getField(is24HourClock ? DateField.HOUR_OF_DAY : DateField.HOUR);
}
/**
* 获得指定日期的分钟数部分<br>
* 例如10:04:15.250 = 4
*
* @return 分钟数
*/
public int minute() {
return getField(DateField.MINUTE);
}
/**
* 获得指定日期的秒数部分<br>
*
* @return 秒数
*/
public int second() {
return getField(DateField.SECOND);
}
/**
* 获得指定日期的毫秒数部分<br>
*
* @return 毫秒数
*/
public int millsecond() {
return getField(DateField.MILLISECOND);
}
/**
* 是否为上午
*
* @return 是否为上午
*/
public boolean isAM() {
return Calendar.AM == getField(DateField.AM_PM);
}
/**
* 是否为下午
*
* @return 是否为下午
*/
public boolean isPM() {
return Calendar.PM == getField(DateField.AM_PM);
}
// -------------------------------------------------------------------- Part of Date end
/**
* 是否闰年
*
* @see DateUtil#isLeapYear(int)
* @return 是否闰年
*/
public boolean isLeapYear() {
return DateUtil.isLeapYear(year());
}
/**
* 转换为Calendar默认{@link TimeZone}默认 {@link Locale}
*
* @return {@link Calendar}
*/
public Calendar toCalendar() {
final Calendar cal = Calendar.getInstance();
cal.setFirstDayOfWeek(firstDayOfWeek.getValue());
cal.setTime(this);
return cal;
}
/**
* 转换为Calendar
*
* @param locale 地域 {@link Locale}
* @return {@link Calendar}
*/
public Calendar toCalendar(Locale locale) {
final Calendar cal = Calendar.getInstance(locale);
cal.setFirstDayOfWeek(firstDayOfWeek.getValue());
cal.setTime(this);
return cal;
}
/**
* 转换为Calendar
*
* @param zone 时区 {@link TimeZone}
* @return {@link Calendar}
*/
public Calendar toCalendar(TimeZone zone) {
return toCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
}
/**
* 转换为Calendar
*
* @param zone 时区 {@link TimeZone}
* @param locale 地域 {@link Locale}
* @return {@link Calendar}
*/
public Calendar toCalendar(TimeZone zone, Locale locale) {
final Calendar cal = Calendar.getInstance(zone, locale);
cal.setFirstDayOfWeek(firstDayOfWeek.getValue());
cal.setTime(this);
return cal;
}
/**
* 转换为 {@link Date}<br>
* 考虑到很多框架例如Hibernate的兼容性提供此方法返回JDK原生的Date对象
*
* @return {@link Date}
* @since 3.2.2
*/
public Date toJdkDate() {
return new Date(this.getTime());
}
/**
* 转为{@link Timestamp}
*
* @return {@link Timestamp}
*/
public Timestamp toTimestamp() {
return new Timestamp(this.getTime());
}
/**
* 转为 {@link java.sql.Date}
*
* @return {@link java.sql.Date}
*/
public java.sql.Date toSqlDate() {
return new java.sql.Date(getTime());
}
/**
* 计算相差时长
*
* @param date 对比的日期
* @return {@link DateBetween}
*/
public DateBetween between(Date date) {
return new DateBetween(this, date);
}
/**
* 计算相差时长
*
* @param date 对比的日期
* @param unit 单位 {@link DateUnit}
* @return 相差时长
*/
public long between(Date date, DateUnit unit) {
return new DateBetween(this, date).between(unit);
}
/**
* 计算相差时长
*
* @param date 对比的日期
* @param unit 单位 {@link DateUnit}
* @param formatLevel 格式化级别
* @return 相差时长
*/
public String between(Date date, DateUnit unit, BetweenFormater.Level formatLevel) {
return new DateBetween(this, date).toString(formatLevel);
}
/**
* 当前日期是否在日期指定范围内<br>
* 起始日期和结束日期可以互换
*
* @param beginDate 起始日期
* @param endDate 结束日期
* @return 是否在范围内
* @since 3.0.8
*/
public boolean isIn(Date beginDate, Date endDate) {
long beginMills = beginDate.getTime();
long endMills = endDate.getTime();
long thisMills = this.getTime();
return thisMills >= Math.min(beginMills, endMills) && thisMills <= Math.max(beginMills, endMills);
}
/**
* 是否在给定日期之前或与给定日期相等
*
* @param date 日期
* @return 是否在给定日期之前或与给定日期相等
* @since 3.0.9
*/
public boolean isBeforeOrEquals(Date date) {
if (null == date) {
throw new NullPointerException("Date to compare is null !");
}
return compareTo(date) <= 0;
}
/**
* 是否在给定日期之后或与给定日期相等
*
* @param date 日期
* @return 是否在给定日期之后或与给定日期相等
* @since 3.0.9
*/
public boolean isAfterOrEquals(Date date) {
if (null == date) {
throw new NullPointerException("Date to compare is null !");
}
return compareTo(date) >= 0;
}
/**
* 对象是否可变<br>
* 如果为不可变对象以下方法将返回新方法
* <ul>
* <li>{@link DateTime#offset(DateField, int)}</li>
* <li>{@link DateTime#setField(DateField, int)}</li>
* <li>{@link DateTime#setField(int, int)}</li>
* </ul>
* 如果为不可变对象{@link DateTime#setTime(long)}将抛出异常
*
* @return 对象是否可变
*/
public boolean isMutable() {
return mutable;
}
/**
* 设置对象是否可变 如果为不可变对象以下方法将返回新方法
* <ul>
* <li>{@link DateTime#offset(DateField, int)}</li>
* <li>{@link DateTime#setField(DateField, int)}</li>
* <li>{@link DateTime#setField(int, int)}</li>
* </ul>
* 如果为不可变对象{@link DateTime#setTime(long)}将抛出异常
*
* @param mutable 是否可变
* @return this
*/
public DateTime setMutable(boolean mutable) {
this.mutable = mutable;
return this;
}
/**
* 获得一周的第一天默认为周一
*
* @return 一周的第一天
*/
public Week getFirstDayOfWeek() {
return firstDayOfWeek;
}
/**
* 设置一周的第一天<br>
* JDK的Calendar中默认一周的第一天是周日Hutool中将此默认值设置为周一<br>
* 设置一周的第一天主要影响{@link #weekOfMonth()}{@link #weekOfYear()} 两个方法
*
* @param firstDayOfWeek 一周的第一天
* @return this
* @see #weekOfMonth()
* @see #weekOfYear()
*/
public DateTime setFirstDayOfWeek(Week firstDayOfWeek) {
this.firstDayOfWeek = firstDayOfWeek;
return this;
}
// -------------------------------------------------------------------- toString start
/**
* 转为"yyyy-MM-dd yyyy-MM-dd HH:mm:ss " 格式字符串
*
* @return "yyyy-MM-dd yyyy-MM-dd HH:mm:ss " 格式字符串
*/
@Override
public String toString() {
return toString(DatePattern.NORM_DATETIME_FORMAT);
}
/**
* 转为"yyyy-MM-dd " 格式字符串
*
* @return "yyyy-MM-dd " 格式字符串
* @since 4.0.0
*/
public String toDateStr() {
return toString(DatePattern.NORM_DATE_PATTERN);
}
/**
* 转为字符串
*
* @param format 日期格式常用格式见 {@link DatePattern}
* @return String
*/
public String toString(String format) {
return toString(FastDateFormat.getInstance(format));
}
/**
* 转为字符串
*
* @param format {@link DatePrinter} {@link FastDateFormat}
* @return String
*/
public String toString(DatePrinter format) {
return format.format(this);
}
/**
* 转为字符串
*
* @param format {@link SimpleDateFormat}
* @return String
*/
public String toString(DateFormat format) {
return format.format(this);
}
/**
* @return 输出精确到毫秒的标准日期形式
*/
public String toMsStr() {
return toString(DatePattern.NORM_DATETIME_MS_FORMAT);
}
// -------------------------------------------------------------------- toString end
/**
* 转换字符串为Date
*
* @param dateStr 日期字符串
* @param dateFormat {@link SimpleDateFormat}
* @return {@link Date}
*/
private static Date parse(String dateStr, DateFormat dateFormat) {
try {
return dateFormat.parse(dateStr);
} catch (Exception e) {
String pattern;
if (dateFormat instanceof SimpleDateFormat) {
pattern = ((SimpleDateFormat) dateFormat).toPattern();
} else {
pattern = dateFormat.toString();
}
throw new DateException(StringUtil.format("Parse [{}] with format [{}] error!", dateStr, pattern), e);
}
}
/**
* 转换字符串为Date
*
* @param dateStr 日期字符串
* @param parser {@link FastDateFormat}
* @return {@link Date}
*/
private static Date parse(String dateStr, DateParser parser) {
Assert.notNull(parser, "Parser or DateFromat must be not null !");
//Assert.notBlank(dateStr, "Date String must be not blank !");
try {
return parser.parse(dateStr);
} catch (Exception e) {
throw new DateException("Parse [{}] with format [{}] error!", dateStr, parser.getPattern(), e);
}
}
/**
* 设置日期时间
*
* @param time 日期时间毫秒
* @return this
*/
private DateTime setTimeInternal(long time) {
super.setTime(time);
return this;
}
}

View File

@ -1,33 +0,0 @@
package org.springblade.core.tool.date;
/**
* 日期时间单位每个单位都是以毫秒为基数
* @author Looly
*
*/
public enum DateUnit {
/** 一毫秒 */
MS(1),
/** 一秒的毫秒数 */
SECOND(1000),
/**一分钟的毫秒数 */
MINUTE(SECOND.getMillis() * 60),
/**一小时的毫秒数 */
HOUR(MINUTE.getMillis() * 60),
/**一天的毫秒数 */
DAY(HOUR.getMillis() * 24),
/**一周的毫秒数 */
WEEK(DAY.getMillis() * 7);
private long millis;
DateUnit(long millis){
this.millis = millis;
}
/**
* @return 单位对应的毫秒数
*/
public long getMillis(){
return this.millis;
}
}

View File

@ -1,118 +0,0 @@
package org.springblade.core.tool.date;
import java.util.Calendar;
/**
* 月份枚举<br>
* 与Calendar中的月份int值对应
*
* @see Calendar#JANUARY
* @see Calendar#FEBRUARY
* @see Calendar#MARCH
* @see Calendar#APRIL
* @see Calendar#MAY
* @see Calendar#JUNE
* @see Calendar#JULY
* @see Calendar#AUGUST
* @see Calendar#SEPTEMBER
* @see Calendar#OCTOBER
* @see Calendar#NOVEMBER
* @see Calendar#DECEMBER
* @see Calendar#UNDECIMBER
*
* @author Looly
*
*/
public enum Month {
/** 一月 */
JANUARY(Calendar.JANUARY),
/** 二月 */
FEBRUARY(Calendar.FEBRUARY),
/** 三月 */
MARCH(Calendar.MARCH),
/** 四月 */
APRIL(Calendar.APRIL),
/** 五月 */
MAY(Calendar.MAY),
/** 六月 */
JUNE(Calendar.JUNE),
/** 七月 */
JULY(Calendar.JULY),
/** 八月 */
AUGUST(Calendar.AUGUST),
/** 九月 */
SEPTEMBER(Calendar.SEPTEMBER),
/** 十月 */
OCTOBER(Calendar.OCTOBER),
/** 十一月 */
NOVEMBER(Calendar.NOVEMBER),
/** 十二月 */
DECEMBER(Calendar.DECEMBER),
/** 十三月,仅用于农历 */
UNDECIMBER(Calendar.UNDECIMBER);
// ---------------------------------------------------------------
private int value;
private Month(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
/**
* {@link Calendar}月份相关值转换为Month枚举对象<br>
*
* @see Calendar#JANUARY
* @see Calendar#FEBRUARY
* @see Calendar#MARCH
* @see Calendar#APRIL
* @see Calendar#MAY
* @see Calendar#JUNE
* @see Calendar#JULY
* @see Calendar#AUGUST
* @see Calendar#SEPTEMBER
* @see Calendar#OCTOBER
* @see Calendar#NOVEMBER
* @see Calendar#DECEMBER
* @see Calendar#UNDECIMBER
*
* @param calendarMonthIntValue Calendar中关于Month的int值
* @return {@link Month}
*/
public static Month of(int calendarMonthIntValue) {
switch (calendarMonthIntValue) {
case Calendar.JANUARY:
return JANUARY;
case Calendar.FEBRUARY:
return FEBRUARY;
case Calendar.MARCH:
return MARCH;
case Calendar.APRIL:
return APRIL;
case Calendar.MAY:
return MAY;
case Calendar.JUNE:
return JUNE;
case Calendar.JULY:
return JULY;
case Calendar.AUGUST:
return AUGUST;
case Calendar.SEPTEMBER:
return SEPTEMBER;
case Calendar.OCTOBER:
return OCTOBER;
case Calendar.NOVEMBER:
return NOVEMBER;
case Calendar.DECEMBER:
return DECEMBER;
case Calendar.UNDECIMBER:
return UNDECIMBER;
default:
return null;
}
}
}

View File

@ -1,61 +0,0 @@
package org.springblade.core.tool.date;
/**
* 季度枚举<br>
*
* @see #SPRING
* @see #SUMMER
* @see #AUTUMN
* @see #WINTER
*
* @author Looly
*
*/
public enum Season {
/** 春季(第一季度) */
SPRING(1),
/** 夏季(第二季度) */
SUMMER(2),
/** 秋季(第三季度) */
AUTUMN(3),
/** 冬季(第四季度) */
WINTER(4);
// ---------------------------------------------------------------
private int value;
private Season(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
/**
* 季度int转换为Season枚举对象<br>
*
* @see #SPRING
* @see #SUMMER
* @see #AUTUMN
* @see #WINTER
*
* @param intValue 季度int表示
* @return {@link Season}
*/
public static Season of(int intValue) {
switch (intValue) {
case 1:
return SPRING;
case 2:
return SUMMER;
case 3:
return AUTUMN;
case 4:
return WINTER;
default:
return null;
}
}
}

View File

@ -1,109 +0,0 @@
package org.springblade.core.tool.date;
/**
* 计时器<br>
* 计算某个过程花费的时间精确到毫秒
*
* @author Looly
*
*/
public class TimeInterval {
private long time;
private boolean isNano;
public TimeInterval() {
this(false);
}
public TimeInterval(boolean isNano) {
this.isNano = isNano;
start();
}
/**
* @return 开始计时并返回当前时间
*/
public long start() {
time = DateUtil.current(isNano);
return time;
}
/**
* @return 重新计时并返回从开始到当前的持续时间
*/
public long intervalRestart() {
long now = DateUtil.current(isNano);
long d = now - time;
time = now;
return d;
}
/**
* 重新开始计算时间重置开始时间
* @return this
* @since 3.0.1
*/
public TimeInterval restart(){
time = DateUtil.current(isNano);
return this;
}
//----------------------------------------------------------- Interval
/**
* 从开始到当前的间隔时间毫秒数<br>
* 如果使用纳秒计时返回纳秒差否则返回毫秒差
* @return 从开始到当前的间隔时间毫秒数
*/
public long interval() {
return DateUtil.current(isNano) - time;
}
/**
* 从开始到当前的间隔时间毫秒数
* @return 从开始到当前的间隔时间毫秒数
*/
public long intervalMs() {
return isNano ? interval() / 1000000L : interval();
}
/**
* 从开始到当前的间隔秒数取绝对值
* @return 从开始到当前的间隔秒数取绝对值
*/
public long intervalSecond(){
return intervalMs() / DateUnit.SECOND.getMillis();
}
/**
* 从开始到当前的间隔分钟数取绝对值
* @return 从开始到当前的间隔分钟数取绝对值
*/
public long intervalMinute(){
return intervalMs() / DateUnit.MINUTE.getMillis();
}
/**
* 从开始到当前的间隔小时数取绝对值
* @return 从开始到当前的间隔小时数取绝对值
*/
public long intervalHour(){
return intervalMs() / DateUnit.HOUR.getMillis();
}
/**
* 从开始到当前的间隔天数取绝对值
* @return 从开始到当前的间隔天数取绝对值
*/
public long intervalDay(){
return intervalMs() / DateUnit.DAY.getMillis();
}
/**
* 从开始到当前的间隔周数取绝对值
* @return 从开始到当前的间隔周数取绝对值
*/
public long intervalWeek(){
return intervalMs() / DateUnit.WEEK.getMillis();
}
}

View File

@ -1,120 +0,0 @@
package org.springblade.core.tool.date;
import java.util.Calendar;
/**
* 星期枚举<br>
* 与Calendar中的星期int值对应
*
* @see #SUNDAY
* @see #MONDAY
* @see #TUESDAY
* @see #WEDNESDAY
* @see #THURSDAY
* @see #FRIDAY
* @see #SATURDAY
*
* @author Looly
*
*/
public enum Week {
/** 周日 */
SUNDAY(Calendar.SUNDAY),
/** 周一 */
MONDAY(Calendar.MONDAY),
/** 周二 */
TUESDAY(Calendar.TUESDAY),
/** 周三 */
WEDNESDAY(Calendar.WEDNESDAY),
/** 周四 */
THURSDAY(Calendar.THURSDAY),
/** 周五 */
FRIDAY(Calendar.FRIDAY),
/** 周六 */
SATURDAY(Calendar.SATURDAY);
// ---------------------------------------------------------------
/** 星期对应{@link Calendar} 中的Week值 */
private int value;
/**
* 构造
*
* @param value 星期对应{@link Calendar} 中的Week值
*/
private Week(int value) {
this.value = value;
}
/**
* 获得星期对应{@link Calendar} 中的Week值
*
* @return 星期对应{@link Calendar} 中的Week值
*/
public int getValue() {
return this.value;
}
/**
* 转换为中文名
*
* @return 星期的中文名
* @since 3.3.0
*/
public String toChinese() {
switch (this) {
case SUNDAY:
return "星期日";
case MONDAY:
return "星期一";
case TUESDAY:
return "星期二";
case WEDNESDAY:
return "星期三";
case THURSDAY:
return "星期四";
case FRIDAY:
return "星期五";
case SATURDAY:
return "星期六";
default:
return null;
}
}
/**
* {@link Calendar}星期相关值转换为Week枚举对象<br>
*
* @see #SUNDAY
* @see #MONDAY
* @see #TUESDAY
* @see #WEDNESDAY
* @see #THURSDAY
* @see #FRIDAY
* @see #SATURDAY
*
* @param calendarWeekIntValue Calendar中关于Week的int值
* @return {@link Week}
*/
public static Week of(int calendarWeekIntValue) {
switch (calendarWeekIntValue) {
case Calendar.SUNDAY:
return SUNDAY;
case Calendar.MONDAY:
return MONDAY;
case Calendar.TUESDAY:
return TUESDAY;
case Calendar.WEDNESDAY:
return WEDNESDAY;
case Calendar.THURSDAY:
return THURSDAY;
case Calendar.FRIDAY:
return FRIDAY;
case Calendar.SATURDAY:
return SATURDAY;
default:
return null;
}
}
}

View File

@ -1,64 +0,0 @@
package org.springblade.core.tool.date.format;
import java.io.Serializable;
import java.util.Locale;
import java.util.TimeZone;
public abstract class AbstractDateBasic implements DateBasic, Serializable {
private static final long serialVersionUID = 6333136319870641818L;
/** The pattern */
protected final String pattern;
/** The time zone. */
protected final TimeZone timeZone;
/** The locale. */
protected final Locale locale;
/**
* 构造内部使用
* @param pattern 使用{@link java.text.SimpleDateFormat} 相同的日期格式
* @param timeZone 非空时区{@link TimeZone}
* @param locale 非空{@link Locale} 日期地理位置
*/
protected AbstractDateBasic(final String pattern, final TimeZone timeZone, final Locale locale) {
this.pattern = pattern;
this.timeZone = timeZone;
this.locale = locale;
}
// ----------------------------------------------------------------------- Accessors
@Override
public String getPattern() {
return pattern;
}
@Override
public TimeZone getTimeZone() {
return timeZone;
}
@Override
public Locale getLocale() {
return locale;
}
// ----------------------------------------------------------------------- Basics
@Override
public boolean equals(final Object obj) {
if (obj instanceof FastDatePrinter == false) {
return false;
}
final AbstractDateBasic other = (AbstractDateBasic) obj;
return pattern.equals(other.pattern) && timeZone.equals(other.timeZone) && locale.equals(other.locale);
}
@Override
public int hashCode() {
return pattern.hashCode() + 13 * (timeZone.hashCode() + 13 * locale.hashCode());
}
@Override
public String toString() {
return "FastDatePrinter[" + pattern + "," + locale + "," + timeZone.getID() + "]";
}
}

View File

@ -1,33 +0,0 @@
package org.springblade.core.tool.date.format;
import java.util.Locale;
import java.util.TimeZone;
/**
* 日期基本信息获取接口
*
* @author Looly
* @since 2.16.2
*/
public interface DateBasic {
/**
* 获得日期格式化或者转换的格式
*
* @return {@link java.text.SimpleDateFormat}兼容的格式
*/
String getPattern();
/**
* 获得时区
*
* @return {@link TimeZone}
*/
TimeZone getTimeZone();
/**
* 获得 日期地理位置
*
* @return {@link Locale}
*/
Locale getLocale();
}

View File

@ -1,68 +0,0 @@
package org.springblade.core.tool.date.format;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Calendar;
import java.util.Date;
/**
* 日期解析接口用于解析日期字符串为 {@link Date} 对象<br>
* Thanks to Apache Commons Lang 3.5
* @since 2.16.2
*/
public interface DateParser extends DateBasic {
/**
* 将日期字符串解析并转换为 {@link Date} 对象<br>
* 等价于 {@link java.text.DateFormat#parse(String)}
*
* @param source 日期字符串
* @return {@link Date}
* @throws ParseException 转换异常被转换的字符串格式错误
*/
Date parse(String source) throws ParseException;
/**
* 将日期字符串解析并转换为 {@link Date} 对象<br>
* 等价于 {@link java.text.DateFormat#parse(String, ParsePosition)}
*
* @param source 日期字符串
* @param pos {@link ParsePosition}
* @return {@link Date}
*/
Date parse(String source, ParsePosition pos);
/**
* 根据给定格式转换日期字符串
* Updates the Calendar with parsed fields. Upon success, the ParsePosition index is updated to indicate how much of the source text was consumed.
* Not all source text needs to be consumed.
* Upon parse failure, ParsePosition error index is updated to the offset of the source text which does not match the supplied format.
*
* @param source 被转换的日期字符串
* @param pos 定义开始转换的位置转换结束后更新转换到的位置
* @param calendar The calendar into which to set parsed fields.
* @return true, if source has been parsed (pos parsePosition is updated); otherwise false (and pos errorIndex is updated)
* @throws IllegalArgumentException when Calendar has been set to be not lenient, and a parsed field is out of range.
*/
boolean parse(String source, ParsePosition pos, Calendar calendar);
/**
* 将日期字符串解析并转换为 {@link Date} 对象<br>
*
* @param source A <code>String</code> whose beginning should be parsed.
* @return a <code>java.util.Date</code> object
* @throws ParseException if the beginning of the specified string cannot be parsed.
* @see java.text.DateFormat#parseObject(String)
*/
Object parseObject(String source) throws ParseException;
/**
* 根据 {@link ParsePosition} 给定将日期字符串解析并转换为 {@link Date} 对象<br>
*
* @param source A <code>String</code> whose beginning should be parsed.
* @param pos the parse position
* @return a <code>java.util.Date</code> object
* @see java.text.DateFormat#parseObject(String, ParsePosition)
*/
Object parseObject(String source, ParsePosition pos);
}

View File

@ -1,78 +0,0 @@
package org.springblade.core.tool.date.format;
import java.util.Calendar;
import java.util.Date;
/**
* 日期格式化输出接口<br>
* Thanks to Apache Commons Lang 3.5
* @author Looly
* @since 2.16.2
*/
public interface DatePrinter extends DateBasic {
/**
* 格式化日期表示的毫秒数
*
* @param millis 日期毫秒数
* @return the formatted string
* @since 2.1
*/
String format(long millis);
/**
* 使用 {@code GregorianCalendar} 格式化 {@code Date}
*
* @param date 日期 {@link Date}
* @return 格式化后的字符串
*/
String format(Date date);
/**
* <p>
* Formats a {@code Calendar} object.
* </p>
* 格式化 {@link Calendar}
*
* @param calendar {@link Calendar}
* @return 格式化后的字符串
*/
String format(Calendar calendar);
/**
* <p>
* Formats a millisecond {@code long} value into the supplied {@code Appendable}.
* </p>
*
* @param millis the millisecond value to format
* @param buf the buffer to format into
* @param <B> the Appendable class type, usually StringBuilder or StringBuffer.
* @return the specified string buffer
*/
<B extends Appendable> B format(long millis, B buf);
/**
* <p>
* Formats a {@code Date} object into the supplied {@code Appendable} using a {@code GregorianCalendar}.
* </p>
*
* @param date the date to format
* @param buf the buffer to format into
* @param <B> the Appendable class type, usually StringBuilder or StringBuffer.
* @return the specified string buffer
*/
<B extends Appendable> B format(Date date, B buf);
/**
* <p>
* Formats a {@code Calendar} object into the supplied {@code Appendable}.
* </p>
* The TimeZone set on the Calendar is only used to adjust the time offset. The TimeZone specified during the construction of the Parser will determine the TimeZone used in the formatted string.
*
* @param calendar the calendar to format
* @param buf the buffer to format into
* @param <B> the Appendable class type, usually StringBuilder or StringBuffer.
* @return the specified string buffer
*/
<B extends Appendable> B format(Calendar calendar, B buf);
}

View File

@ -1,392 +0,0 @@
package org.springblade.core.tool.date.format;
import java.text.*;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* <p>
* FastDateFormat 是一个线程安全的 {@link SimpleDateFormat} 实现
* </p>
*
* <p>
* 通过以下静态方法获得此对象: <br>
* {@link #getInstance(String, TimeZone, Locale)}<br>
* {@link #getDateInstance(int, TimeZone, Locale)}<br>
* {@link #getTimeInstance(int, TimeZone, Locale)}<br>
* {@link #getDateTimeInstance(int, int, TimeZone, Locale)}
* </p>
*
* Thanks to Apache Commons Lang 3.5
* @since 2.16.2
*/
public class FastDateFormat extends Format implements DateParser, DatePrinter {
private static final long serialVersionUID = 8097890768636183236L;
/** FULL locale dependent date or time style. */
public static final int FULL = DateFormat.FULL;
/** LONG locale dependent date or time style. */
public static final int LONG = DateFormat.LONG;
/** MEDIUM locale dependent date or time style. */
public static final int MEDIUM = DateFormat.MEDIUM;
/** SHORT locale dependent date or time style. */
public static final int SHORT = DateFormat.SHORT;
private static final FormatCache<FastDateFormat> cache = new FormatCache<FastDateFormat>(){
@Override
protected FastDateFormat createInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
return new FastDateFormat(pattern, timeZone, locale);
}
};
private final FastDatePrinter printer;
private final FastDateParser parser;
// -----------------------------------------------------------------------
/**
* 获得 {@link FastDateFormat} 实例使用默认格式和地区
*
* @return {@link FastDateFormat}
*/
public static FastDateFormat getInstance() {
return cache.getInstance();
}
/**
* 获得 {@link FastDateFormat} 实例使用默认地区<br>
* 支持缓存
*
* @param pattern 使用{@link SimpleDateFormat} 相同的日期格式
* @return {@link FastDateFormat}
* @throws IllegalArgumentException 日期格式问题
*/
public static FastDateFormat getInstance(final String pattern) {
return cache.getInstance(pattern, null, null);
}
/**
* 获得 {@link FastDateFormat} 实例<br>
* 支持缓存
*
* @param pattern 使用{@link SimpleDateFormat} 相同的日期格式
* @param timeZone 时区{@link TimeZone}
* @return {@link FastDateFormat}
* @throws IllegalArgumentException 日期格式问题
*/
public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone) {
return cache.getInstance(pattern, timeZone, null);
}
/**
* 获得 {@link FastDateFormat} 实例<br>
* 支持缓存
*
* @param pattern 使用{@link SimpleDateFormat} 相同的日期格式
* @param locale {@link Locale} 日期地理位置
* @return {@link FastDateFormat}
* @throws IllegalArgumentException 日期格式问题
*/
public static FastDateFormat getInstance(final String pattern, final Locale locale) {
return cache.getInstance(pattern, null, locale);
}
/**
* 获得 {@link FastDateFormat} 实例<br>
* 支持缓存
*
* @param pattern 使用{@link SimpleDateFormat} 相同的日期格式
* @param timeZone 时区{@link TimeZone}
* @param locale {@link Locale} 日期地理位置
* @return {@link FastDateFormat}
* @throws IllegalArgumentException 日期格式问题
*/
public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
return cache.getInstance(pattern, timeZone, locale);
}
// -----------------------------------------------------------------------
/**
* 获得 {@link FastDateFormat} 实例<br>
* 支持缓存
*
* @param style date style: FULL, LONG, MEDIUM, or SHORT
* @return 本地化 {@link FastDateFormat}
*/
public static FastDateFormat getDateInstance(final int style) {
return cache.getDateInstance(style, null, null);
}
/**
* 获得 {@link FastDateFormat} 实例<br>
* 支持缓存
*
* @param style date style: FULL, LONG, MEDIUM, or SHORT
* @param locale {@link Locale} 日期地理位置
* @return 本地化 {@link FastDateFormat}
*/
public static FastDateFormat getDateInstance(final int style, final Locale locale) {
return cache.getDateInstance(style, null, locale);
}
/**
* 获得 {@link FastDateFormat} 实例<br>
* 支持缓存
*
* @param style date style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone 时区{@link TimeZone}
* @return 本地化 {@link FastDateFormat}
*/
public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone) {
return cache.getDateInstance(style, timeZone, null);
}
/**
* 获得 {@link FastDateFormat} 实例<br>
* 支持缓存
*
* @param style date style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone 时区{@link TimeZone}
* @param locale {@link Locale} 日期地理位置
* @return 本地化 {@link FastDateFormat}
*/
public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone, final Locale locale) {
return cache.getDateInstance(style, timeZone, locale);
}
// -----------------------------------------------------------------------
/**
* 获得 {@link FastDateFormat} 实例<br>
* 支持缓存
*
* @param style time style: FULL, LONG, MEDIUM, or SHORT
* @return 本地化 {@link FastDateFormat}
*/
public static FastDateFormat getTimeInstance(final int style) {
return cache.getTimeInstance(style, null, null);
}
/**
* 获得 {@link FastDateFormat} 实例<br>
* 支持缓存
*
* @param style time style: FULL, LONG, MEDIUM, or SHORT
* @param locale {@link Locale} 日期地理位置
* @return 本地化 {@link FastDateFormat}
*/
public static FastDateFormat getTimeInstance(final int style, final Locale locale) {
return cache.getTimeInstance(style, null, locale);
}
/**
* 获得 {@link FastDateFormat} 实例<br>
* 支持缓存
*
* @param style time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of formatted time
* @return 本地化 {@link FastDateFormat}
*/
public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone) {
return cache.getTimeInstance(style, timeZone, null);
}
/**
* 获得 {@link FastDateFormat} 实例<br>
* 支持缓存
*
* @param style time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of formatted time
* @param locale {@link Locale} 日期地理位置
* @return 本地化 {@link FastDateFormat}
*/
public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone, final Locale locale) {
return cache.getTimeInstance(style, timeZone, locale);
}
// -----------------------------------------------------------------------
/**
* 获得 {@link FastDateFormat} 实例<br>
* 支持缓存
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @return 本地化 {@link FastDateFormat}
*/
public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle) {
return cache.getDateTimeInstance(dateStyle, timeStyle, null, null);
}
/**
* 获得 {@link FastDateFormat} 实例<br>
* 支持缓存
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param locale {@link Locale} 日期地理位置
* @return 本地化 {@link FastDateFormat}
*/
public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final Locale locale) {
return cache.getDateTimeInstance(dateStyle, timeStyle, null, locale);
}
/**
* 获得 {@link FastDateFormat} 实例<br>
* 支持缓存
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone 时区{@link TimeZone}
* @return 本地化 {@link FastDateFormat}
*/
public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone) {
return getDateTimeInstance(dateStyle, timeStyle, timeZone, null);
}
/**
* 获得 {@link FastDateFormat} 实例<br>
* 支持缓存
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone 时区{@link TimeZone}
* @param locale {@link Locale} 日期地理位置
* @return 本地化 {@link FastDateFormat}
*/
public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) {
return cache.getDateTimeInstance(dateStyle, timeStyle, timeZone, locale);
}
// ----------------------------------------------------------------------- Constructor start
/**
* 构造
*
* @param pattern 使用{@link SimpleDateFormat} 相同的日期格式
* @param timeZone 非空时区 {@link TimeZone}
* @param locale {@link Locale} 日期地理位置
* @throws NullPointerException if pattern, timeZone, or locale is null.
*/
protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale) {
this(pattern, timeZone, locale, null);
}
/**
* 构造
*
* @param pattern 使用{@link SimpleDateFormat} 相同的日期格式
* @param timeZone 非空时区 {@link TimeZone}
* @param locale {@link Locale} 日期地理位置
* @param centuryStart The start of the 100 year period to use as the "default century" for 2 digit year parsing. If centuryStart is null, defaults to now - 80 years
* @throws NullPointerException if pattern, timeZone, or locale is null.
*/
protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale, final Date centuryStart) {
printer = new FastDatePrinter(pattern, timeZone, locale);
parser = new FastDateParser(pattern, timeZone, locale, centuryStart);
}
// ----------------------------------------------------------------------- Constructor end
// ----------------------------------------------------------------------- Format methods
@Override
public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) {
return toAppendTo.append(printer.format(obj));
}
@Override
public String format(final long millis) {
return printer.format(millis);
}
@Override
public String format(final Date date) {
return printer.format(date);
}
@Override
public String format(final Calendar calendar) {
return printer.format(calendar);
}
@Override
public <B extends Appendable> B format(final long millis, final B buf) {
return printer.format(millis, buf);
}
@Override
public <B extends Appendable> B format(final Date date, final B buf) {
return printer.format(date, buf);
}
@Override
public <B extends Appendable> B format(final Calendar calendar, final B buf) {
return printer.format(calendar, buf);
}
// ----------------------------------------------------------------------- Parsing
@Override
public Date parse(final String source) throws ParseException {
return parser.parse(source);
}
@Override
public Date parse(final String source, final ParsePosition pos) {
return parser.parse(source, pos);
}
@Override
public boolean parse(final String source, final ParsePosition pos, final Calendar calendar) {
return parser.parse(source, pos, calendar);
}
@Override
public Object parseObject(final String source, final ParsePosition pos) {
return parser.parseObject(source, pos);
}
// ----------------------------------------------------------------------- Accessors
@Override
public String getPattern() {
return printer.getPattern();
}
@Override
public TimeZone getTimeZone() {
return printer.getTimeZone();
}
@Override
public Locale getLocale() {
return printer.getLocale();
}
/**
*估算生成的日期字符串长度<br>
* 实际生成的字符串长度小于或等于此值
*
* @return 日期字符串长度
*/
public int getMaxLengthEstimate() {
return printer.getMaxLengthEstimate();
}
// Basics
// -----------------------------------------------------------------------
@Override
public boolean equals(final Object obj) {
if (obj instanceof FastDateFormat == false) {
return false;
}
final FastDateFormat other = (FastDateFormat) obj;
// no need to check parser, as it has same invariants as printer
return printer.equals(other.printer);
}
@Override
public int hashCode() {
return printer.hashCode();
}
@Override
public String toString() {
return "FastDateFormat[" + printer.getPattern() + "," + printer.getLocale() + "," + printer.getTimeZone().getID() + "]";
}
}

View File

@ -1,822 +0,0 @@
package org.springblade.core.tool.date.format;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.text.DateFormatSymbols;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* {@link java.text.SimpleDateFormat} 的线程安全版本用于解析日期字符串并转换为 {@link Date} 对象<br>
* Thanks to Apache Commons Lang 3.5
*
* @since 2.16.2
* @see FastDatePrinter
*/
class FastDateParser extends AbstractDateBasic implements DateParser {
private static final long serialVersionUID = -3199383897950947498L;
static final Locale JAPANESE_IMPERIAL = new Locale("ja", "JP", "JP");
/** 世纪2000年前为19 之后为20 */
private final int century;
private final int startYear;
// derived fields
private transient List<StrategyAndWidth> patterns;
// comparator used to sort regex alternatives
// alternatives should be ordered longer first, and shorter last. ('february' before 'feb')
// all entries must be lowercase by locale.
private static final Comparator<String> LONGER_FIRST_LOWERCASE = new Comparator<String>(){
@Override
public int compare(final String left, final String right) {
return right.compareTo(left);
}
};
/**
* <p>
* Constructs a new FastDateParser.
* </p>
*
* Use {@link FastDateFormat#getInstance(String, TimeZone, Locale)} or another variation of the factory methods of {@link FastDateFormat} to get a cached FastDateParser instance.
*
* @param pattern non-null {@link java.text.SimpleDateFormat} compatible pattern
* @param timeZone non-null time zone to use
* @param locale non-null locale
*/
protected FastDateParser(final String pattern, final TimeZone timeZone, final Locale locale) {
this(pattern, timeZone, locale, null);
}
/**
* <p>
* Constructs a new FastDateParser.
* </p>
*
* @param pattern non-null {@link java.text.SimpleDateFormat} compatible pattern
* @param timeZone non-null time zone to use
* @param locale non-null locale
* @param centuryStart The start of the century for 2 digit year parsing
*/
protected FastDateParser(final String pattern, final TimeZone timeZone, final Locale locale, final Date centuryStart) {
super(pattern, timeZone, locale);
final Calendar definingCalendar = Calendar.getInstance(timeZone, locale);
int centuryStartYear;
if (centuryStart != null) {
definingCalendar.setTime(centuryStart);
centuryStartYear = definingCalendar.get(Calendar.YEAR);
} else if (locale.equals(JAPANESE_IMPERIAL)) {
centuryStartYear = 0;
} else {
// from 80 years ago to 20 years from now
definingCalendar.setTime(new Date());
centuryStartYear = definingCalendar.get(Calendar.YEAR) - 80;
}
century = centuryStartYear / 100 * 100;
startYear = centuryStartYear - century;
init(definingCalendar);
}
/**
* Initialize derived fields from defining fields. This is called from constructor and from readObject (de-serialization)
*
* @param definingCalendar the {@link Calendar} instance used to initialize this FastDateParser
*/
private void init(final Calendar definingCalendar) {
patterns = new ArrayList<>();
final StrategyParser fm = new StrategyParser(definingCalendar);
for (;;) {
final StrategyAndWidth field = fm.getNextStrategy();
if (field == null) {
break;
}
patterns.add(field);
}
}
// helper classes to parse the format string
// -----------------------------------------------------------------------
/**
* Holds strategy and field width
*/
private static class StrategyAndWidth {
final Strategy strategy;
final int width;
StrategyAndWidth(final Strategy strategy, final int width) {
this.strategy = strategy;
this.width = width;
}
int getMaxWidth(final ListIterator<StrategyAndWidth> lt) {
if (!strategy.isNumber() || !lt.hasNext()) {
return 0;
}
final Strategy nextStrategy = lt.next().strategy;
lt.previous();
return nextStrategy.isNumber() ? width : 0;
}
}
/**
* Parse format into Strategies
*/
private class StrategyParser {
final private Calendar definingCalendar;
private int currentIdx;
StrategyParser(final Calendar definingCalendar) {
this.definingCalendar = definingCalendar;
}
StrategyAndWidth getNextStrategy() {
if (currentIdx >= pattern.length()) {
return null;
}
final char c = pattern.charAt(currentIdx);
if (isFormatLetter(c)) {
return letterPattern(c);
}
return literal();
}
private StrategyAndWidth letterPattern(final char c) {
final int begin = currentIdx;
while (++currentIdx < pattern.length()) {
if (pattern.charAt(currentIdx) != c) {
break;
}
}
final int width = currentIdx - begin;
return new StrategyAndWidth(getStrategy(c, width, definingCalendar), width);
}
private StrategyAndWidth literal() {
boolean activeQuote = false;
final StringBuilder sb = new StringBuilder();
while (currentIdx < pattern.length()) {
final char c = pattern.charAt(currentIdx);
if (!activeQuote && isFormatLetter(c)) {
break;
} else if (c == '\'' && (++currentIdx == pattern.length() || pattern.charAt(currentIdx) != '\'')) {
activeQuote = !activeQuote;
continue;
}
++currentIdx;
sb.append(c);
}
if (activeQuote) {
throw new IllegalArgumentException("Unterminated quote");
}
final String formatField = sb.toString();
return new StrategyAndWidth(new CopyQuotedStrategy(formatField), formatField.length());
}
}
private static boolean isFormatLetter(final char c) {
return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z';
}
// Serializing
// -----------------------------------------------------------------------
/**
* Create the object after serialization. This implementation reinitializes the transient properties.
*
* @param in ObjectInputStream from which the object is being deserialized.
* @throws IOException if there is an IO issue.
* @throws ClassNotFoundException if a class cannot be found.
*/
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
final Calendar definingCalendar = Calendar.getInstance(timeZone, locale);
init(definingCalendar);
}
@Override
public Object parseObject(final String source) throws ParseException {
return parse(source);
}
@Override
public Date parse(final String source) throws ParseException {
final ParsePosition pp = new ParsePosition(0);
final Date date = parse(source, pp);
if (date == null) {
// Add a note re supported date range
if (locale.equals(JAPANESE_IMPERIAL)) {
throw new ParseException("(The " + locale + " locale does not support dates before 1868 AD)\n" + "Unparseable date: \"" + source, pp.getErrorIndex());
}
throw new ParseException("Unparseable date: " + source, pp.getErrorIndex());
}
return date;
}
@Override
public Object parseObject(final String source, final ParsePosition pos) {
return parse(source, pos);
}
@Override
public Date parse(final String source, final ParsePosition pos) {
// timing tests indicate getting new instance is 19% faster than cloning
final Calendar cal = Calendar.getInstance(timeZone, locale);
cal.clear();
return parse(source, pos, cal) ? cal.getTime() : null;
}
@Override
public boolean parse(final String source, final ParsePosition pos, final Calendar calendar) {
final ListIterator<StrategyAndWidth> lt = patterns.listIterator();
while (lt.hasNext()) {
final StrategyAndWidth strategyAndWidth = lt.next();
final int maxWidth = strategyAndWidth.getMaxWidth(lt);
if (!strategyAndWidth.strategy.parse(this, calendar, source, pos, maxWidth)) {
return false;
}
}
return true;
}
// Support for strategies
// -----------------------------------------------------------------------
private static StringBuilder simpleQuote(final StringBuilder sb, final String value) {
for (int i = 0; i < value.length(); ++i) {
final char c = value.charAt(i);
switch (c) {
case '\\':
case '^':
case '$':
case '.':
case '|':
case '?':
case '*':
case '+':
case '(':
case ')':
case '[':
case '{':
sb.append('\\');
default:
sb.append(c);
}
}
return sb;
}
/**
* Get the short and long values displayed for a field
*
* @param cal The calendar to obtain the short and long values
* @param locale The locale of display names
* @param field The field of interest
* @param regex The regular expression to build
* @return The map of string display names to field values
*/
private static Map<String, Integer> appendDisplayNames(final Calendar cal, final Locale locale, final int field, final StringBuilder regex) {
final Map<String, Integer> values = new HashMap<>();
final Map<String, Integer> displayNames = cal.getDisplayNames(field, Calendar.ALL_STYLES, locale);
final TreeSet<String> sorted = new TreeSet<>(LONGER_FIRST_LOWERCASE);
for (final Map.Entry<String, Integer> displayName : displayNames.entrySet()) {
final String key = displayName.getKey().toLowerCase(locale);
if (sorted.add(key)) {
values.put(key, displayName.getValue());
}
}
for (final String symbol : sorted) {
simpleQuote(regex, symbol).append('|');
}
return values;
}
/**
* 使用当前的世纪调整两位数年份为四位数年份
*
* @param twoDigitYear 两位数年份
* @return A value between centuryStart(inclusive) to centuryStart+100(exclusive)
*/
private int adjustYear(final int twoDigitYear) {
final int trial = century + twoDigitYear;
return twoDigitYear >= startYear ? trial : trial + 100;
}
/**
* 单个日期字段的分析策略
*/
private static abstract class Strategy {
/**
* Is this field a number? The default implementation returns false.
*
* @return true, if field is a number
*/
boolean isNumber() {
return false;
}
abstract boolean parse(FastDateParser parser, Calendar calendar, String source, ParsePosition pos, int maxWidth);
}
/**
* A strategy to parse a single field from the parsing pattern
*/
private static abstract class PatternStrategy extends Strategy {
private Pattern pattern;
void createPattern(final StringBuilder regex) {
createPattern(regex.toString());
}
void createPattern(final String regex) {
this.pattern = Pattern.compile(regex);
}
/**
* Is this field a number? The default implementation returns false.
*
* @return true, if field is a number
*/
@Override
boolean isNumber() {
return false;
}
@Override
boolean parse(final FastDateParser parser, final Calendar calendar, final String source, final ParsePosition pos, final int maxWidth) {
final Matcher matcher = pattern.matcher(source.substring(pos.getIndex()));
if (!matcher.lookingAt()) {
pos.setErrorIndex(pos.getIndex());
return false;
}
pos.setIndex(pos.getIndex() + matcher.end(1));
setCalendar(parser, calendar, matcher.group(1));
return true;
}
abstract void setCalendar(FastDateParser parser, Calendar cal, String value);
}
/**
* Obtain a Strategy given a field from a SimpleDateFormat pattern
*
* @param formatField A sub-sequence of the SimpleDateFormat pattern
* @param definingCalendar The calendar to obtain the short and long values
* @return The Strategy that will handle parsing for the field
*/
private Strategy getStrategy(final char f, final int width, final Calendar definingCalendar) {
switch (f) {
default:
throw new IllegalArgumentException("Format '" + f + "' not supported");
case 'D':
return DAY_OF_YEAR_STRATEGY;
case 'E':
return getLocaleSpecificStrategy(Calendar.DAY_OF_WEEK, definingCalendar);
case 'F':
return DAY_OF_WEEK_IN_MONTH_STRATEGY;
case 'G':
return getLocaleSpecificStrategy(Calendar.ERA, definingCalendar);
case 'H': // Hour in day (0-23)
return HOUR_OF_DAY_STRATEGY;
case 'K': // Hour in am/pm (0-11)
return HOUR_STRATEGY;
case 'M':
return width >= 3 ? getLocaleSpecificStrategy(Calendar.MONTH, definingCalendar) : NUMBER_MONTH_STRATEGY;
case 'S':
return MILLISECOND_STRATEGY;
case 'W':
return WEEK_OF_MONTH_STRATEGY;
case 'a':
return getLocaleSpecificStrategy(Calendar.AM_PM, definingCalendar);
case 'd':
return DAY_OF_MONTH_STRATEGY;
case 'h': // Hour in am/pm (1-12), i.e. midday/midnight is 12, not 0
return HOUR12_STRATEGY;
case 'k': // Hour in day (1-24), i.e. midnight is 24, not 0
return HOUR24_OF_DAY_STRATEGY;
case 'm':
return MINUTE_STRATEGY;
case 's':
return SECOND_STRATEGY;
case 'u':
return DAY_OF_WEEK_STRATEGY;
case 'w':
return WEEK_OF_YEAR_STRATEGY;
case 'y':
case 'Y':
return width > 2 ? LITERAL_YEAR_STRATEGY : ABBREVIATED_YEAR_STRATEGY;
case 'X':
return ISO8601TimeZoneStrategy.getStrategy(width);
case 'Z':
if (width == 2) {
return ISO8601TimeZoneStrategy.ISO_8601_3_STRATEGY;
}
//$FALL-THROUGH$
case 'z':
return getLocaleSpecificStrategy(Calendar.ZONE_OFFSET, definingCalendar);
}
}
@SuppressWarnings("unchecked") // OK because we are creating an array with no entries
private static final ConcurrentMap<Locale, Strategy>[] caches = new ConcurrentMap[Calendar.FIELD_COUNT];
/**
* Get a cache of Strategies for a particular field
*
* @param field The Calendar field
* @return a cache of Locale to Strategy
*/
private static ConcurrentMap<Locale, Strategy> getCache(final int field) {
synchronized (caches) {
if (caches[field] == null) {
caches[field] = new ConcurrentHashMap<>(3);
}
return caches[field];
}
}
/**
* Construct a Strategy that parses a Text field
*
* @param field The Calendar field
* @param definingCalendar The calendar to obtain the short and long values
* @return a TextStrategy for the field and Locale
*/
private Strategy getLocaleSpecificStrategy(final int field, final Calendar definingCalendar) {
final ConcurrentMap<Locale, Strategy> cache = getCache(field);
Strategy strategy = cache.get(locale);
if (strategy == null) {
strategy = field == Calendar.ZONE_OFFSET ? new TimeZoneStrategy(locale) : new CaseInsensitiveTextStrategy(field, definingCalendar, locale);
final Strategy inCache = cache.putIfAbsent(locale, strategy);
if (inCache != null) {
return inCache;
}
}
return strategy;
}
/**
* A strategy that copies the static or quoted field in the parsing pattern
*/
private static class CopyQuotedStrategy extends Strategy {
final private String formatField;
/**
* Construct a Strategy that ensures the formatField has literal text
*
* @param formatField The literal text to match
*/
CopyQuotedStrategy(final String formatField) {
this.formatField = formatField;
}
/**
* {@inheritDoc}
*/
@Override
boolean isNumber() {
return false;
}
@Override
boolean parse(final FastDateParser parser, final Calendar calendar, final String source, final ParsePosition pos, final int maxWidth) {
for (int idx = 0; idx < formatField.length(); ++idx) {
final int sIdx = idx + pos.getIndex();
if (sIdx == source.length()) {
pos.setErrorIndex(sIdx);
return false;
}
if (formatField.charAt(idx) != source.charAt(sIdx)) {
pos.setErrorIndex(sIdx);
return false;
}
}
pos.setIndex(formatField.length() + pos.getIndex());
return true;
}
}
/**
* A strategy that handles a text field in the parsing pattern
*/
private static class CaseInsensitiveTextStrategy extends PatternStrategy {
private final int field;
final Locale locale;
private final Map<String, Integer> lKeyValues;
/**
* Construct a Strategy that parses a Text field
*
* @param field The Calendar field
* @param definingCalendar The Calendar to use
* @param locale The Locale to use
*/
CaseInsensitiveTextStrategy(final int field, final Calendar definingCalendar, final Locale locale) {
this.field = field;
this.locale = locale;
final StringBuilder regex = new StringBuilder();
regex.append("((?iu)");
lKeyValues = appendDisplayNames(definingCalendar, locale, field, regex);
regex.setLength(regex.length() - 1);
regex.append(")");
createPattern(regex);
}
/**
* {@inheritDoc}
*/
@Override
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
final Integer iVal = lKeyValues.get(value.toLowerCase(locale));
cal.set(field, iVal.intValue());
}
}
/**
* A strategy that handles a number field in the parsing pattern
*/
private static class NumberStrategy extends Strategy {
private final int field;
/**
* Construct a Strategy that parses a Number field
*
* @param field The Calendar field
*/
NumberStrategy(final int field) {
this.field = field;
}
/**
* {@inheritDoc}
*/
@Override
boolean isNumber() {
return true;
}
@Override
boolean parse(final FastDateParser parser, final Calendar calendar, final String source, final ParsePosition pos, final int maxWidth) {
int idx = pos.getIndex();
int last = source.length();
if (maxWidth == 0) {
// if no maxWidth, strip leading white space
for (; idx < last; ++idx) {
final char c = source.charAt(idx);
if (!Character.isWhitespace(c)) {
break;
}
}
pos.setIndex(idx);
} else {
final int end = idx + maxWidth;
if (last > end) {
last = end;
}
}
for (; idx < last; ++idx) {
final char c = source.charAt(idx);
if (!Character.isDigit(c)) {
break;
}
}
if (pos.getIndex() == idx) {
pos.setErrorIndex(idx);
return false;
}
final int value = Integer.parseInt(source.substring(pos.getIndex(), idx));
pos.setIndex(idx);
calendar.set(field, modify(parser, value));
return true;
}
/**
* Make any modifications to parsed integer
*
* @param parser The parser
* @param iValue The parsed integer
* @return The modified value
*/
int modify(final FastDateParser parser, final int iValue) {
return iValue;
}
}
private static final Strategy ABBREVIATED_YEAR_STRATEGY = new NumberStrategy(Calendar.YEAR){
/**
* {@inheritDoc}
*/
@Override
int modify(final FastDateParser parser, final int iValue) {
return iValue < 100 ? parser.adjustYear(iValue) : iValue;
}
};
/**
* A strategy that handles a timezone field in the parsing pattern
*/
static class TimeZoneStrategy extends PatternStrategy {
private static final String RFC_822_TIME_ZONE = "[+-]\\d{4}";
private static final String GMT_OPTION = "GMT[+-]\\d{1,2}:\\d{2}";
private final Locale locale;
private final Map<String, TzInfo> tzNames = new HashMap<>();
private static class TzInfo {
TimeZone zone;
int dstOffset;
TzInfo(final TimeZone tz, final boolean useDst) {
zone = tz;
dstOffset = useDst ? tz.getDSTSavings() : 0;
}
}
/**
* Index of zone id
*/
private static final int ID = 0;
/**
* Construct a Strategy that parses a TimeZone
*
* @param locale The Locale
*/
TimeZoneStrategy(final Locale locale) {
this.locale = locale;
final StringBuilder sb = new StringBuilder();
sb.append("((?iu)" + RFC_822_TIME_ZONE + "|" + GMT_OPTION);
final Set<String> sorted = new TreeSet<>(LONGER_FIRST_LOWERCASE);
final String[][] zones = DateFormatSymbols.getInstance(locale).getZoneStrings();
for (final String[] zoneNames : zones) {
// offset 0 is the time zone ID and is not localized
final String tzId = zoneNames[ID];
if (tzId.equalsIgnoreCase("GMT")) {
continue;
}
final TimeZone tz = TimeZone.getTimeZone(tzId);
// offset 1 is long standard name
// offset 2 is short standard name
final TzInfo standard = new TzInfo(tz, false);
TzInfo tzInfo = standard;
for (int i = 1; i < zoneNames.length; ++i) {
switch (i) {
case 3: // offset 3 is long daylight savings (or summertime) name
// offset 4 is the short summertime name
tzInfo = new TzInfo(tz, true);
break;
case 5: // offset 5 starts additional names, probably standard time
tzInfo = standard;
break;
}
if (zoneNames[i] != null) {
final String key = zoneNames[i].toLowerCase(locale);
// ignore the data associated with duplicates supplied in
// the additional names
if (sorted.add(key)) {
tzNames.put(key, tzInfo);
}
}
}
}
// order the regex alternatives with longer strings first, greedy
// match will ensure longest string will be consumed
for (final String zoneName : sorted) {
simpleQuote(sb.append('|'), zoneName);
}
sb.append(")");
createPattern(sb);
}
/**
* {@inheritDoc}
*/
@Override
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
if (value.charAt(0) == '+' || value.charAt(0) == '-') {
final TimeZone tz = TimeZone.getTimeZone("GMT" + value);
cal.setTimeZone(tz);
} else if (value.regionMatches(true, 0, "GMT", 0, 3)) {
final TimeZone tz = TimeZone.getTimeZone(value.toUpperCase());
cal.setTimeZone(tz);
} else {
final TzInfo tzInfo = tzNames.get(value.toLowerCase(locale));
cal.set(Calendar.DST_OFFSET, tzInfo.dstOffset);
cal.set(Calendar.ZONE_OFFSET, tzInfo.zone.getRawOffset());
}
}
}
private static class ISO8601TimeZoneStrategy extends PatternStrategy {
// Z, +hh, -hh, +hhmm, -hhmm, +hh:mm or -hh:mm
/**
* Construct a Strategy that parses a TimeZone
*
* @param pattern The Pattern
*/
ISO8601TimeZoneStrategy(final String pattern) {
createPattern(pattern);
}
/**
* {@inheritDoc}
*/
@Override
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
if (value.equals("Z")) {
cal.setTimeZone(TimeZone.getTimeZone("UTC"));
} else {
cal.setTimeZone(TimeZone.getTimeZone("GMT" + value));
}
}
private static final Strategy ISO_8601_1_STRATEGY = new ISO8601TimeZoneStrategy("(Z|(?:[+-]\\d{2}))");
private static final Strategy ISO_8601_2_STRATEGY = new ISO8601TimeZoneStrategy("(Z|(?:[+-]\\d{2}\\d{2}))");
private static final Strategy ISO_8601_3_STRATEGY = new ISO8601TimeZoneStrategy("(Z|(?:[+-]\\d{2}(?::)\\d{2}))");
/**
* Factory method for ISO8601TimeZoneStrategies.
*
* @param tokenLen a token indicating the length of the TimeZone String to be formatted.
* @return a ISO8601TimeZoneStrategy that can format TimeZone String of length {@code tokenLen}. If no such strategy exists, an IllegalArgumentException will be thrown.
*/
static Strategy getStrategy(final int tokenLen) {
switch (tokenLen) {
case 1:
return ISO_8601_1_STRATEGY;
case 2:
return ISO_8601_2_STRATEGY;
case 3:
return ISO_8601_3_STRATEGY;
default:
throw new IllegalArgumentException("invalid number of X");
}
}
}
private static final Strategy NUMBER_MONTH_STRATEGY = new NumberStrategy(Calendar.MONTH){
@Override
int modify(final FastDateParser parser, final int iValue) {
return iValue - 1;
}
};
private static final Strategy LITERAL_YEAR_STRATEGY = new NumberStrategy(Calendar.YEAR);
private static final Strategy WEEK_OF_YEAR_STRATEGY = new NumberStrategy(Calendar.WEEK_OF_YEAR);
private static final Strategy WEEK_OF_MONTH_STRATEGY = new NumberStrategy(Calendar.WEEK_OF_MONTH);
private static final Strategy DAY_OF_YEAR_STRATEGY = new NumberStrategy(Calendar.DAY_OF_YEAR);
private static final Strategy DAY_OF_MONTH_STRATEGY = new NumberStrategy(Calendar.DAY_OF_MONTH);
private static final Strategy DAY_OF_WEEK_STRATEGY = new NumberStrategy(Calendar.DAY_OF_WEEK){
@Override
int modify(final FastDateParser parser, final int iValue) {
return iValue != 7 ? iValue + 1 : Calendar.SUNDAY;
}
};
private static final Strategy DAY_OF_WEEK_IN_MONTH_STRATEGY = new NumberStrategy(Calendar.DAY_OF_WEEK_IN_MONTH);
private static final Strategy HOUR_OF_DAY_STRATEGY = new NumberStrategy(Calendar.HOUR_OF_DAY);
private static final Strategy HOUR24_OF_DAY_STRATEGY = new NumberStrategy(Calendar.HOUR_OF_DAY){
@Override
int modify(final FastDateParser parser, final int iValue) {
return iValue == 24 ? 0 : iValue;
}
};
private static final Strategy HOUR12_STRATEGY = new NumberStrategy(Calendar.HOUR){
@Override
int modify(final FastDateParser parser, final int iValue) {
return iValue == 12 ? 0 : iValue;
}
};
private static final Strategy HOUR_STRATEGY = new NumberStrategy(Calendar.HOUR);
private static final Strategy MINUTE_STRATEGY = new NumberStrategy(Calendar.MINUTE);
private static final Strategy SECOND_STRATEGY = new NumberStrategy(Calendar.SECOND);
private static final Strategy MILLISECOND_STRATEGY = new NumberStrategy(Calendar.MILLISECOND);
}

View File

@ -1,252 +0,0 @@
package org.springblade.core.tool.date.format;
import java.text.DateFormat;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* 日期格式化器缓存<br>
* Thanks to Apache Commons Lang 3.5
*
* @since 2.16.2
*/
abstract class FormatCache<F extends Format> {
/**
* No date or no time. Used in same parameters as DateFormat.SHORT or DateFormat.LONG
*/
static final int NONE = -1;
private final ConcurrentMap<MultipartKey, F> cInstanceCache = new ConcurrentHashMap<>(7);
private static final ConcurrentMap<MultipartKey, String> cDateTimeInstanceCache = new ConcurrentHashMap<>(7);
/**
* 使用默认的patterntimezone和locale获得缓存中的实例
* @return a date/time formatter
*/
public F getInstance() {
return getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, TimeZone.getDefault(), Locale.getDefault());
}
/**
* 使用 pattern, time zone and locale 获得对应的 格式化器
*
* @param pattern 非空日期格式使用与 {@link SimpleDateFormat}相同格式
* @param timeZone 时区默认当前时区
* @param locale 地区默认使用当前地区
* @return 格式化器
* @throws IllegalArgumentException pattern 无效或<code>null</code>
*/
public F getInstance(final String pattern, TimeZone timeZone, Locale locale) {
if (pattern == null) {
throw new NullPointerException("pattern must not be null");
}
if (timeZone == null) {
timeZone = TimeZone.getDefault();
}
if (locale == null) {
locale = Locale.getDefault();
}
final MultipartKey key = new MultipartKey(pattern, timeZone, locale);
F format = cInstanceCache.get(key);
if (format == null) {
format = createInstance(pattern, timeZone, locale);
final F previousValue = cInstanceCache.putIfAbsent(key, format);
if (previousValue != null) {
// another thread snuck in and did the same work
// we should return the instance that is in ConcurrentMap
format = previousValue;
}
}
return format;
}
/**
* 创建格式化器
*
* @param pattern 非空日期格式使用与 {@link SimpleDateFormat}相同格式
* @param timeZone 时区默认当前时区
* @param locale 地区默认使用当前地区
* @return 格式化器
* @throws IllegalArgumentException pattern 无效或<code>null</code>
*/
abstract protected F createInstance(String pattern, TimeZone timeZone, Locale locale);
/**
* <p>
* Gets a date/time formatter instance using the specified style, time zone and locale.
* </p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT, null indicates no date in format
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT, null indicates no time in format
* @param timeZone optional time zone, overrides time zone of formatted date, null means use default Locale
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time pattern defined
*/
// This must remain private, see LANG-884
private F getDateTimeInstance(final Integer dateStyle, final Integer timeStyle, final TimeZone timeZone, Locale locale) {
if (locale == null) {
locale = Locale.getDefault();
}
final String pattern = getPatternForStyle(dateStyle, timeStyle, locale);
return getInstance(pattern, timeZone, locale);
}
/**
* <p>
* Gets a date/time formatter instance using the specified style, time zone and locale.
* </p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of formatted date, null means use default Locale
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time pattern defined
*/
// package protected, for access from FastDateFormat; do not make public or protected
F getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) {
return getDateTimeInstance(Integer.valueOf(dateStyle), Integer.valueOf(timeStyle), timeZone, locale);
}
/**
* <p>
* Gets a date formatter instance using the specified style, time zone and locale.
* </p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of formatted date, null means use default Locale
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time pattern defined
*/
// package protected, for access from FastDateFormat; do not make public or protected
F getDateInstance(final int dateStyle, final TimeZone timeZone, final Locale locale) {
return getDateTimeInstance(Integer.valueOf(dateStyle), null, timeZone, locale);
}
/**
* <p>
* Gets a time formatter instance using the specified style, time zone and locale.
* </p>
*
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of formatted date, null means use default Locale
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time pattern defined
*/
// package protected, for access from FastDateFormat; do not make public or protected
F getTimeInstance(final int timeStyle, final TimeZone timeZone, final Locale locale) {
return getDateTimeInstance(null, Integer.valueOf(timeStyle), timeZone, locale);
}
/**
* <p>
* Gets a date/time format for the specified styles and locale.
* </p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT, null indicates no date in format
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT, null indicates no time in format
* @param locale The non-null locale of the desired format
* @return a localized standard date/time format
* @throws IllegalArgumentException if the Locale has no date/time pattern defined
*/
// package protected, for access from test code; do not make public or protected
static String getPatternForStyle(final Integer dateStyle, final Integer timeStyle, final Locale locale) {
final MultipartKey key = new MultipartKey(dateStyle, timeStyle, locale);
String pattern = cDateTimeInstanceCache.get(key);
if (pattern == null) {
try {
DateFormat formatter;
if (dateStyle == null) {
formatter = DateFormat.getTimeInstance(timeStyle.intValue(), locale);
} else if (timeStyle == null) {
formatter = DateFormat.getDateInstance(dateStyle.intValue(), locale);
} else {
formatter = DateFormat.getDateTimeInstance(dateStyle.intValue(), timeStyle.intValue(), locale);
}
pattern = ((SimpleDateFormat) formatter).toPattern();
final String previous = cDateTimeInstanceCache.putIfAbsent(key, pattern);
if (previous != null) {
// even though it doesn't matter if another thread put the pattern
// it's still good practice to return the String instance that is
// actually in the ConcurrentMap
pattern = previous;
}
} catch (final ClassCastException ex) {
throw new IllegalArgumentException("No date time pattern for locale: " + locale);
}
}
return pattern;
}
// ----------------------------------------------------------------------
/**
* <p>
* Helper class to hold multi-part Map keys
* </p>
*/
private static class MultipartKey {
private final Object[] keys;
private int hashCode;
/**
* Constructs an instance of <code>MultipartKey</code> to hold the specified objects.
*
* @param keys the set of objects that make up the key. Each key may be null.
*/
public MultipartKey(final Object... keys) {
this.keys = keys;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final MultipartKey other = (MultipartKey) obj;
if (false == Arrays.equals(keys, other.keys)) {
return false;
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
if (hashCode == 0) {
int rc = 0;
for (final Object key : keys) {
if (key != null) {
rc = rc * 7 + key.hashCode();
}
}
hashCode = rc;
}
return hashCode;
}
}
}

View File

@ -23,8 +23,9 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springblade.core.tool.date.DatePattern; import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.utils.Exceptions; import org.springblade.core.tool.utils.Exceptions;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.StringUtil; import org.springblade.core.tool.utils.StringUtil;
import java.io.IOException; import java.io.IOException;
@ -35,6 +36,8 @@ import java.util.*;
/** /**
* Jackson工具类 * Jackson工具类
*
* @author smallchill
*/ */
@Slf4j @Slf4j
public class JsonUtil { public class JsonUtil {
@ -177,8 +180,8 @@ public class JsonUtil {
public static <T> List<T> parseArray(String content, Class<T> valueTypeRef) { public static <T> List<T> parseArray(String content, Class<T> valueTypeRef) {
try { try {
if (!StringUtil.startsWithIgnoreCase(content, "[")) { if (!StringUtil.startsWithIgnoreCase(content, StringPool.LEFT_SQ_BRACKET)) {
content = "[" + content + "]"; content = StringPool.LEFT_SQ_BRACKET + content + StringPool.RIGHT_SQ_BRACKET;
} }
List<Map<String, Object>> list = getInstance().readValue(content, new TypeReference<List<T>>() { List<Map<String, Object>> list = getInstance().readValue(content, new TypeReference<List<T>>() {
@ -207,7 +210,7 @@ public class JsonUtil {
try { try {
Map<String, Map<String, Object>> map = getInstance().readValue(content, new TypeReference<Map<String, T>>() { Map<String, Map<String, Object>> map = getInstance().readValue(content, new TypeReference<Map<String, T>>() {
}); });
Map<String, T> result = new HashMap<>(); Map<String, T> result = new HashMap<>(16);
for (Map.Entry<String, Map<String, Object>> entry : map.entrySet()) { for (Map.Entry<String, Map<String, Object>> entry : map.entrySet()) {
result.put(entry.getKey(), toPojo(entry.getValue(), valueTypeRef)); result.put(entry.getKey(), toPojo(entry.getValue(), valueTypeRef));
} }
@ -300,7 +303,7 @@ public class JsonUtil {
//设置为中国上海时区 //设置为中国上海时区
super.setTimeZone(TimeZone.getTimeZone(ZoneId.systemDefault())); super.setTimeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
//序列化时日期的统一格式 //序列化时日期的统一格式
super.setDateFormat(new SimpleDateFormat(DatePattern.NORM_DATETIME_PATTERN, Locale.CHINA)); super.setDateFormat(new SimpleDateFormat(DateUtil.PATTERN_DATETIME, Locale.CHINA));
//序列化处理 //序列化处理
super.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); super.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
super.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true); super.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);

View File

@ -24,10 +24,22 @@ import java.util.List;
*/ */
public interface INode { public interface INode {
/**
* 主键
* @return
*/
Integer getId(); Integer getId();
/**
* 父主键
* @return
*/
Integer getParentId(); Integer getParentId();
/**
* 子孙节点
* @return
*/
List<INode> getChildren(); List<INode> getChildren();
} }

View File

@ -157,11 +157,11 @@ public class StrSpliter {
final ArrayList<String> list = new ArrayList<>(limit > 0 ? limit : 16); final ArrayList<String> list = new ArrayList<>(limit > 0 ? limit : 16);
int len = str.length(); int len = str.length();
int start = 0;//切分后每个部分的起始 int start = 0;
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
if (Func.equals(separator, str.charAt(i))) { if (Func.equals(separator, str.charAt(i))) {
addToList(list, str.substring(start, i), isTrim, ignoreEmpty); addToList(list, str.substring(start, i), isTrim, ignoreEmpty);
start = i + 1;//i+1同时将start与i保持一致 start = i + 1;
//检查是否超出范围最大允许limit-1个剩下一个留给末尾字符串 //检查是否超出范围最大允许limit-1个剩下一个留给末尾字符串
if (limit > 0 && list.size() > limit - 2) { if (limit > 0 && list.size() > limit - 2) {
@ -169,7 +169,7 @@ public class StrSpliter {
} }
} }
} }
return addToList(list, str.substring(start, len), isTrim, ignoreEmpty);//收尾 return addToList(list, str.substring(start, len), isTrim, ignoreEmpty);
} }
/** /**
@ -294,9 +294,9 @@ public class StrSpliter {
return addToList(new ArrayList<String>(1), str, isTrim, ignoreEmpty); return addToList(new ArrayList<String>(1), str, isTrim, ignoreEmpty);
} }
if (StringUtil.isEmpty(separator)) {//分隔符为空时按照空白符切分 if (StringUtil.isEmpty(separator)) {
return split(str, limit); return split(str, limit);
} else if (separator.length() == 1) {//分隔符只有一个字符长度时按照单分隔符切分 } else if (separator.length() == 1) {
return split(str, separator.charAt(0), limit, isTrim, ignoreEmpty, ignoreCase); return split(str, separator.charAt(0), limit, isTrim, ignoreEmpty, ignoreCase);
} }
@ -358,19 +358,17 @@ public class StrSpliter {
final ArrayList<String> list = new ArrayList<>(); final ArrayList<String> list = new ArrayList<>();
int len = str.length(); int len = str.length();
int start = 0;//切分后每个部分的起始 int start = 0;
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
if (Func.isEmpty(str.charAt(i))) { if (Func.isEmpty(str.charAt(i))) {
addToList(list, str.substring(start, i), true, true); addToList(list, str.substring(start, i), true, true);
start = i + 1;//i+1同时将start与i保持一致 start = i + 1;
//检查是否超出范围最大允许limit-1个剩下一个留给末尾字符串
if (limit > 0 && list.size() > limit - 2) { if (limit > 0 && list.size() > limit - 2) {
break; break;
} }
} }
} }
return addToList(list, str.substring(start, len), true, true);//收尾 return addToList(list, str.substring(start, len), true, true);
} }
/** /**
@ -406,7 +404,7 @@ public class StrSpliter {
return addToList(new ArrayList<String>(1), str, isTrim, ignoreEmpty); return addToList(new ArrayList<String>(1), str, isTrim, ignoreEmpty);
} }
if (null == separatorPattern) {//分隔符为空时按照空白符切分 if (null == separatorPattern) {
return split(str, limit); return split(str, limit);
} }
@ -418,7 +416,6 @@ public class StrSpliter {
addToList(list, str.substring(start, matcher.start()), isTrim, ignoreEmpty); addToList(list, str.substring(start, matcher.start()), isTrim, ignoreEmpty);
start = matcher.end(); start = matcher.end();
//检查是否超出范围最大允许limit-1个剩下一个留给末尾字符串
if (limit > 0 && list.size() > limit - 2) { if (limit > 0 && list.size() > limit - 2) {
break; break;
} }

View File

@ -26,6 +26,12 @@ public class Try {
@FunctionalInterface @FunctionalInterface
public interface UncheckedFunction<T, R> { public interface UncheckedFunction<T, R> {
/**
* 调用
* @param t
* @return
* @throws Exception
*/
R apply(T t) throws Exception; R apply(T t) throws Exception;
} }
} }

View File

@ -28,9 +28,9 @@ import java.util.Arrays;
* *
* @author L.cm * @author L.cm
*/ */
public class AESUtil { public class AesUtil {
private AESUtil() { private AesUtil() {
} }
public static String genAesKey() { public static String genAesKey() {
@ -68,7 +68,7 @@ public class AESUtil {
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16); IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
return cipher.doFinal(PKCS7Encoder.encode(content)); return cipher.doFinal(Pkcs7Encoder.encode(content));
} catch (Exception e) { } catch (Exception e) {
throw Exceptions.unchecked(e); throw Exceptions.unchecked(e);
} }
@ -81,7 +81,7 @@ public class AESUtil {
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16)); IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
cipher.init(Cipher.DECRYPT_MODE, keySpec, iv); cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
return PKCS7Encoder.decode(cipher.doFinal(encrypted)); return Pkcs7Encoder.decode(cipher.doFinal(encrypted));
} catch (Exception e) { } catch (Exception e) {
throw Exceptions.unchecked(e); throw Exceptions.unchecked(e);
} }
@ -90,7 +90,7 @@ public class AESUtil {
/** /**
* 提供基于PKCS7算法的加解密接口. * 提供基于PKCS7算法的加解密接口.
*/ */
static class PKCS7Encoder { static class Pkcs7Encoder {
static int BLOCK_SIZE = 32; static int BLOCK_SIZE = 32;
static byte[] encode(byte[] src) { static byte[] encode(byte[] src) {
@ -115,7 +115,7 @@ public class AESUtil {
static byte[] decode(byte[] decrypted) { static byte[] decode(byte[] decrypted) {
int pad = (int) decrypted[decrypted.length - 1]; int pad = (int) decrypted[decrypted.length - 1];
if (pad < 1 || pad > 32) { if (pad < 1 || pad > BLOCK_SIZE) {
pad = 0; pad = 0;
} }
if (pad > 0) { if (pad > 0) {

View File

@ -15,8 +15,6 @@
*/ */
package org.springblade.core.tool.utils; package org.springblade.core.tool.utils;
import org.springblade.core.tool.date.DatePattern;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
@ -29,9 +27,9 @@ import java.time.temporal.TemporalAccessor;
* @author L.cm * @author L.cm
*/ */
public class DateTimeUtil { public class DateTimeUtil {
public static final DateTimeFormatter DATETIME_FORMAT = DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN); public static final DateTimeFormatter DATETIME_FORMAT = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATETIME);
public static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN); public static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATE);
public static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN); public static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern(DateUtil.PATTERN_TIME);
/** /**
* 日期时间格式化 * 日期时间格式化

View File

@ -23,6 +23,8 @@ import java.lang.reflect.UndeclaredThrowableException;
/** /**
* 异常处理工具类 * 异常处理工具类
*
* @author L.cm
*/ */
public class Exceptions { public class Exceptions {

View File

@ -18,8 +18,6 @@ package org.springblade.core.tool.utils;
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import org.springblade.core.tool.date.ConcurrentDateFormat;
import org.springblade.core.tool.date.DateUtil;
import org.springblade.core.tool.jackson.JsonUtil; import org.springblade.core.tool.jackson.JsonUtil;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
@ -608,12 +606,7 @@ public class Func {
if (value != null) { if (value != null) {
String val = String.valueOf(value); String val = String.valueOf(value);
val = val.toLowerCase().trim(); val = val.toLowerCase().trim();
if ("true".equalsIgnoreCase(val)) { return Boolean.parseBoolean(val);
return true;
} else if ("false".equalsIgnoreCase(val)) {
return false;
}
throw new RuntimeException("The value can not parse to Boolean : " + val);
} }
return defaultValue; return defaultValue;
} }
@ -905,7 +898,7 @@ public class Func {
* @param closeable 自动关闭 * @param closeable 自动关闭
*/ */
public static void closeQuietly(@Nullable Closeable closeable) { public static void closeQuietly(@Nullable Closeable closeable) {
IOUtil.closeQuietly(closeable); IoUtil.closeQuietly(closeable);
} }
/** /**
@ -916,7 +909,7 @@ public class Func {
* @throws NullPointerException if the input is null * @throws NullPointerException if the input is null
*/ */
public static String toString(InputStream input) { public static String toString(InputStream input) {
return IOUtil.toString(input); return IoUtil.toString(input);
} }
/** /**
@ -928,11 +921,11 @@ public class Func {
* @throws NullPointerException if the input is null * @throws NullPointerException if the input is null
*/ */
public static String toString(@Nullable InputStream input, Charset charset) { public static String toString(@Nullable InputStream input, Charset charset) {
return IOUtil.toString(input, charset); return IoUtil.toString(input, charset);
} }
public static byte[] toByteArray(@Nullable InputStream input) { public static byte[] toByteArray(@Nullable InputStream input) {
return IOUtil.toByteArray(input); return IoUtil.toByteArray(input);
} }
/** /**
@ -1078,7 +1071,7 @@ public class Func {
* @return the encoded String * @return the encoded String
*/ */
public static String encode(String source) { public static String encode(String source) {
return URLUtil.encode(source, Charsets.UTF_8); return UrlUtil.encode(source, Charsets.UTF_8);
} }
/** /**
@ -1093,7 +1086,7 @@ public class Func {
* @return the encoded String * @return the encoded String
*/ */
public static String encode(String source, Charset charset) { public static String encode(String source, Charset charset) {
return URLUtil.encode(source, charset); return UrlUtil.encode(source, charset);
} }
/** /**

View File

@ -1,4 +1,18 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.tool.utils; package org.springblade.core.tool.utils;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -14,6 +28,11 @@ import java.awt.image.*;
import java.io.*; import java.io.*;
import java.net.URL; import java.net.URL;
/**
* 图片工具类
*
* @author smallchill
*/
public final class ImageUtil { public final class ImageUtil {
/** /**
@ -130,12 +149,12 @@ public final class ImageUtil {
Image image = src.getScaledInstance(width, height, Image.SCALE_DEFAULT); Image image = src.getScaledInstance(width, height, Image.SCALE_DEFAULT);
BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics(); Graphics g = tag.getGraphics();
// 绘制缩小后的图
g.drawImage(image, 0, 0, null); g.drawImage(image, 0, 0, null);
g.dispose(); g.dispose();
// 输出为文件
ImageIO.write(tag, defaultString(type, DEFAULT_IMG_TYPE), output); ImageIO.write(tag, defaultString(type, DEFAULT_IMG_TYPE), output);
// 关闭流
output.close(); output.close();
} catch (IOException e) { } catch (IOException e) {
LOGGER.error("Error in zoom image", e); LOGGER.error("Error in zoom image", e);
@ -154,7 +173,7 @@ public final class ImageUtil {
*/ */
public final static void zoomFixed(BufferedImage src, OutputStream output, String type, int height, int width, boolean bb, Color fillColor) { public final static void zoomFixed(BufferedImage src, OutputStream output, String type, int height, int width, boolean bb, Color fillColor) {
try { try {
double ratio = 0.0; // 缩放比例 double ratio = 0.0;
Image itemp = src.getScaledInstance(width, height, BufferedImage.SCALE_SMOOTH); Image itemp = src.getScaledInstance(width, height, BufferedImage.SCALE_SMOOTH);
// 计算比例 // 计算比例
if (src.getHeight() > src.getWidth()) { if (src.getHeight() > src.getWidth()) {
@ -212,7 +231,7 @@ public final class ImageUtil {
Image img = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), cropFilter)); Image img = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), cropFilter));
BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics(); Graphics g = tag.getGraphics();
g.drawImage(img, 0, 0, width, height, null); // 绘制切割后的图 g.drawImage(img, 0, 0, width, height, null);
g.dispose(); g.dispose();
// 输出为文件 // 输出为文件
ImageIO.write(tag, defaultString(type, DEFAULT_IMG_TYPE), output); ImageIO.write(tag, defaultString(type, DEFAULT_IMG_TYPE), output);

View File

@ -23,11 +23,11 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
/** /**
* IOUtil * IoUtil
* *
* @author L.cm * @author L.cm
*/ */
public class IOUtil extends org.springframework.util.StreamUtils { public class IoUtil extends org.springframework.util.StreamUtils {
/** /**
* closeQuietly * closeQuietly
@ -63,21 +63,21 @@ public class IOUtil extends org.springframework.util.StreamUtils {
*/ */
public static String toString(@Nullable InputStream input, java.nio.charset.Charset charset) { public static String toString(@Nullable InputStream input, java.nio.charset.Charset charset) {
try { try {
return IOUtil.copyToString(input, charset); return IoUtil.copyToString(input, charset);
} catch (IOException e) { } catch (IOException e) {
throw Exceptions.unchecked(e); throw Exceptions.unchecked(e);
} finally { } finally {
IOUtil.closeQuietly(input); IoUtil.closeQuietly(input);
} }
} }
public static byte[] toByteArray(@Nullable InputStream input) { public static byte[] toByteArray(@Nullable InputStream input) {
try { try {
return IOUtil.copyToByteArray(input); return IoUtil.copyToByteArray(input);
} catch (IOException e) { } catch (IOException e) {
throw Exceptions.unchecked(e); throw Exceptions.unchecked(e);
} finally { } finally {
IOUtil.closeQuietly(input); IoUtil.closeQuietly(input);
} }
} }

View File

@ -26,7 +26,7 @@ import java.util.Map;
* @author smallchill * @author smallchill
*/ */
@Slf4j @Slf4j
public class OKHttpUtil { public class OkHttpUtil {
public static MediaType JSON = MediaType.parse("application/json; charset=utf-8"); public static MediaType JSON = MediaType.parse("application/json; charset=utf-8");
public static MediaType XML = MediaType.parse("application/xml; charset=utf-8"); public static MediaType XML = MediaType.parse("application/xml; charset=utf-8");

View File

@ -78,5 +78,9 @@ public interface StringPool {
String ONE = "1"; String ONE = "1";
String ZERO = "0"; String ZERO = "0";
String DOLLAR_LEFT_BRACE= "${"; String DOLLAR_LEFT_BRACE= "${";
char U_A = 'A';
char L_A = 'a';
char U_Z = 'Z';
char L_Z = 'z';
} }

View File

@ -199,10 +199,10 @@ public class StringUtil extends org.springframework.util.StringUtils {
return txt.replaceAll("[  `·•<C2B7>\\f\\t\\v\\s]", ""); return txt.replaceAll("[  `·•<C2B7>\\f\\t\\v\\s]", "");
} }
// 随机字符串
private static final String _INT = "0123456789"; private static final String S_INT = "0123456789";
private static final String _STR = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; private static final String S_STR = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
private static final String _ALL = _INT + _STR; private static final String S_ALL = S_INT + S_STR;
/** /**
* 随机数生成 * 随机数生成
@ -230,11 +230,11 @@ public class StringUtil extends org.springframework.util.StringUtils {
char[] buffer = new char[count]; char[] buffer = new char[count];
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
if (RandomType.INT == randomType) { if (RandomType.INT == randomType) {
buffer[i] = _INT.charAt(random.nextInt(_INT.length())); buffer[i] = S_INT.charAt(random.nextInt(S_INT.length()));
} else if (RandomType.STRING == randomType) { } else if (RandomType.STRING == randomType) {
buffer[i] = _STR.charAt(random.nextInt(_STR.length())); buffer[i] = S_STR.charAt(random.nextInt(S_STR.length()));
} else { } else {
buffer[i] = _ALL.charAt(random.nextInt(_ALL.length())); buffer[i] = S_ALL.charAt(random.nextInt(S_ALL.length()));
} }
} }
return new String(buffer); return new String(buffer);
@ -749,7 +749,7 @@ public class StringUtil extends org.springframework.util.StringUtils {
final String str2 = str.toString(); final String str2 = str.toString();
if (str2.startsWith(prefix.toString())) { if (str2.startsWith(prefix.toString())) {
return subSuf(str2, prefix.length());// 截取后半段 return subSuf(str2, prefix.length());
} }
return str2; return str2;
} }
@ -768,7 +768,7 @@ public class StringUtil extends org.springframework.util.StringUtils {
final String str2 = str.toString(); final String str2 = str.toString();
if (str2.toLowerCase().startsWith(prefix.toString().toLowerCase())) { if (str2.toLowerCase().startsWith(prefix.toString().toLowerCase())) {
return subSuf(str2, prefix.length());// 截取后半段 return subSuf(str2, prefix.length());
} }
return str2; return str2;
} }
@ -787,7 +787,7 @@ public class StringUtil extends org.springframework.util.StringUtils {
final String str2 = str.toString(); final String str2 = str.toString();
if (str2.endsWith(suffix.toString())) { if (str2.endsWith(suffix.toString())) {
return subPre(str2, str2.length() - suffix.length());// 截取前半段 return subPre(str2, str2.length() - suffix.length());
} }
return str2; return str2;
} }
@ -830,9 +830,9 @@ public class StringUtil extends org.springframework.util.StringUtils {
*/ */
public static String lowerFirst(String str) { public static String lowerFirst(String str) {
char firstChar = str.charAt(0); char firstChar = str.charAt(0);
if (firstChar >= 'A' && firstChar <= 'Z') { if (firstChar >= StringPool.U_A && firstChar <= StringPool.U_Z) {
char[] arr = str.toCharArray(); char[] arr = str.toCharArray();
arr[0] += ('a' - 'A'); arr[0] += (StringPool.L_A - StringPool.U_A);
return new String(arr); return new String(arr);
} }
return str; return str;
@ -846,9 +846,9 @@ public class StringUtil extends org.springframework.util.StringUtils {
*/ */
public static String upperFirst(String str) { public static String upperFirst(String str) {
char firstChar = str.charAt(0); char firstChar = str.charAt(0);
if (firstChar >= 'a' && firstChar <= 'z') { if (firstChar >= StringPool.L_A && firstChar <= StringPool.L_Z) {
char[] arr = str.toCharArray(); char[] arr = str.toCharArray();
arr[0] -= ('a' - 'A'); arr[0] -= (StringPool.L_A - StringPool.U_A);
return new String(arr); return new String(arr);
} }
return str; return str;

View File

@ -23,6 +23,8 @@ import java.io.Serializable;
/** /**
* 文件后缀过滤器 * 文件后缀过滤器
*
* @author smalchill
*/ */
public class SuffixFileFilter implements FileFilter, Serializable { public class SuffixFileFilter implements FileFilter, Serializable {

View File

@ -24,7 +24,7 @@ import java.nio.charset.Charset;
* *
* @author L.cm * @author L.cm
*/ */
public class URLUtil extends org.springframework.web.util.UriUtils { public class UrlUtil extends org.springframework.web.util.UriUtils {
/** /**
* url 编码同js decodeURIComponent * url 编码同js decodeURIComponent
@ -34,7 +34,7 @@ public class URLUtil extends org.springframework.web.util.UriUtils {
* @return 编码后的url * @return 编码后的url
*/ */
public static String encodeURL(String source, Charset charset) { public static String encodeURL(String source, Charset charset) {
return URLUtil.encode(source, charset.name()); return UrlUtil.encode(source, charset.name());
} }
/** /**
@ -45,7 +45,7 @@ public class URLUtil extends org.springframework.web.util.UriUtils {
* @return 解码url * @return 解码url
*/ */
public static String decodeURL(String source, Charset charset) { public static String decodeURL(String source, Charset charset) {
return URLUtil.decode(source, charset.name()); return UrlUtil.decode(source, charset.name());
} }
/** /**

View File

@ -44,6 +44,8 @@ public class WebUtil extends org.springframework.web.util.WebUtils {
public static final String USER_AGENT_HEADER = "user-agent"; public static final String USER_AGENT_HEADER = "user-agent";
public static final String UN_KNOWN = "unknown";
/** /**
* 判断是否ajax请求 * 判断是否ajax请求
* spring ajax 返回含有 ResponseBody 或者 RestController注解 * spring ajax 返回含有 ResponseBody 或者 RestController注解
@ -164,22 +166,22 @@ public class WebUtil extends org.springframework.web.util.WebUtils {
public static String getIP(HttpServletRequest request) { public static String getIP(HttpServletRequest request) {
Assert.notNull(request, "HttpServletRequest is null"); Assert.notNull(request, "HttpServletRequest is null");
String ip = request.getHeader("X-Requested-For"); String ip = request.getHeader("X-Requested-For");
if (StringUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) { if (StringUtil.isBlank(ip) || UN_KNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Forwarded-For"); ip = request.getHeader("X-Forwarded-For");
} }
if (StringUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) { if (StringUtil.isBlank(ip) || UN_KNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP"); ip = request.getHeader("Proxy-Client-IP");
} }
if (StringUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) { if (StringUtil.isBlank(ip) || UN_KNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP"); ip = request.getHeader("WL-Proxy-Client-IP");
} }
if (StringUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) { if (StringUtil.isBlank(ip) || UN_KNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP"); ip = request.getHeader("HTTP_CLIENT_IP");
} }
if (StringUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) { if (StringUtil.isBlank(ip) || UN_KNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR"); ip = request.getHeader("HTTP_X_FORWARDED_FOR");
} }
if (StringUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) { if (StringUtil.isBlank(ip) || UN_KNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr(); ip = request.getRemoteAddr();
} }
return StringUtil.isBlank(ip) ? null : ip.split(",")[0]; return StringUtil.isBlank(ip) ? null : ip.split(",")[0];
@ -227,7 +229,7 @@ public class WebUtil extends org.springframework.web.util.WebUtils {
if (contentLength < 0) { if (contentLength < 0) {
return null; return null;
} }
byte buffer[] = new byte[contentLength]; byte[] buffer = new byte[contentLength];
for (int i = 0; i < contentLength; ) { for (int i = 0; i < contentLength; ) {
int readlen = request.getInputStream().read(buffer, i, contentLength - i); int readlen = request.getInputStream().read(buffer, i, contentLength - i);
@ -247,7 +249,7 @@ public class WebUtil extends org.springframework.web.util.WebUtils {
* @return * @return
* @throws IOException * @throws IOException
*/ */
public static String getRequestStr(HttpServletRequest request, byte buffer[]) throws IOException { public static String getRequestStr(HttpServletRequest request, byte[] buffer) throws IOException {
String charEncoding = request.getCharacterEncoding(); String charEncoding = request.getCharacterEncoding();
if (charEncoding == null) { if (charEncoding == null) {
charEncoding = StringPool.UTF_8; charEncoding = StringPool.UTF_8;

View File

@ -75,7 +75,7 @@ public class XmlUtil {
StringReader sr = new StringReader(xmlStr.trim()); StringReader sr = new StringReader(xmlStr.trim());
InputSource inputSource = new InputSource(sr); InputSource inputSource = new InputSource(sr);
XmlUtil xmlUtil = create(inputSource); XmlUtil xmlUtil = create(inputSource);
IOUtil.closeQuietly(sr); IoUtil.closeQuietly(sr);
return xmlUtil; return xmlUtil;
} }
@ -201,7 +201,7 @@ public class XmlUtil {
*/ */
public Map<String, String> toMap() { public Map<String, String> toMap() {
Element root = doc.getDocumentElement(); Element root = doc.getDocumentElement();
Map<String, String> params = new HashMap<String, String>(); Map<String, String> params = new HashMap<>(16);
// 将节点封装成map形式 // 将节点封装成map形式
NodeList list = root.getChildNodes(); NodeList list = root.getChildNodes();
@ -224,7 +224,11 @@ public class XmlUtil {
return dbf; return dbf;
} }
// https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=23_5 /**
* preventXXE
* @param dbf
* @throws ParserConfigurationException
*/
private static void preventXXE(DocumentBuilderFactory dbf) throws ParserConfigurationException { private static void preventXXE(DocumentBuilderFactory dbf) throws ParserConfigurationException {
// This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all XML entity attacks are prevented // This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all XML entity attacks are prevented
// Xerces 2 only - http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl // Xerces 2 only - http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl