Merge remote-tracking branch 'origin/v5-dev' into v5-dev

This commit is contained in:
duandazhi 2021-04-04 21:11:37 +08:00
commit 7e0c73485b
9 changed files with 327 additions and 48 deletions

View File

@ -3,12 +3,17 @@
-------------------------------------------------------------------------------------------------------------
# 5.6.3 (2021-03-31)
# 5.6.3 (2021-04-04)
### 新特性
* 【core 】 修改数字转换的实现增加按照指定端序转换pr#1492@Github
* 【core 】 修改拆分byte数组时最后一组长度的规则pr#1494@Github
* 【core 】 新增根据日期获取节气pr#1496@Github
* 【core 】 mapToBean()添加对布尔值is前缀的识别pr#294@Gitee
### Bug修复
* 【core 】 修复Validator.isUrl()传空返回trueissue#I3ETTY@Gitee
* 【db 】 修复数据库driver根据url的判断识别错误问题issue#I3EWBI@Gitee
-------------------------------------------------------------------------------------------------------------

View File

@ -10,7 +10,14 @@ import java.util.Map;
/**
* Map值提供者支持驼峰和下划线的key兼容<br>
* 假设目标属性为firstName则Map中为firstName或first_name都可以对应到值
* 假设目标属性为firstName则Map中以下形式的值都可以对应
* <ul>
* <li>firstName</li>
* <li>first_name</li>
* <li>isFirstName如果为Boolean或boolean类型的值</li>
* <li>is_first_name如果为Boolean或boolean类型的值</li>
* </ul>
* 为firstName或first_name都可以对应到值
*
* @author looly
*/
@ -50,23 +57,53 @@ public class MapValueProvider implements ValueProvider<String> {
@Override
public Object value(String key, Type valueType) {
Object value = map.get(key);
if (null == value) {
//检查下划线模式
value = map.get(StrUtil.toUnderlineCase(key));
final String key1 = getKey(key, valueType);
if (null == key1) {
return null;
}
final Object value = map.get(key1);
return Convert.convertWithCheck(valueType, value, null, this.ignoreError);
}
@Override
public boolean containsKey(String key) {
return null != getKey(key, null);
}
/**
* 获得map中可能包含的key,不包含返回null
*
* @param key map中可能包含的key
* @param valueType 值类型用于判断是否为Boolean可以为null
* @return map中可能包含的key
*/
private String getKey(String key, Type valueType) {
if (map.containsKey(key)) {
return true;
} else {
//检查下划线模式
return map.containsKey(StrUtil.toUnderlineCase(key));
return key;
}
//检查下划线模式
String customKey = StrUtil.toUnderlineCase(key);
if (map.containsKey(customKey)) {
return customKey;
}
//检查boolean类型
if (null == valueType || Boolean.class == valueType || boolean.class == valueType) {
//boolean类型字段字段名支持两种方式
customKey = StrUtil.upperFirstAndAddPre(key, "is");
if (map.containsKey(customKey)) {
return customKey;
}
//检查下划线模式
customKey = StrUtil.toUnderlineCase(customKey);
if (map.containsKey(customKey)) {
return customKey;
}
}
return null;
}
}

View File

@ -5,6 +5,7 @@ import cn.hutool.core.date.chinese.ChineseMonth;
import cn.hutool.core.date.chinese.GanZhi;
import cn.hutool.core.date.chinese.LunarFestival;
import cn.hutool.core.date.chinese.LunarInfo;
import cn.hutool.core.date.chinese.SolarTerms;
import cn.hutool.core.util.StrUtil;
import java.util.Calendar;
@ -327,6 +328,16 @@ public class ChineseDate {
return null;
}
/**
* 获得节气
* @return 获得节气
* @since 5.6.3
*/
public String getTerm() {
return SolarTerms.getTerm(gyear, gmonth, gday);
}
/**
* 转换为标准的日期格式来表示农历日期例如2020-01-13
*

View File

@ -1,45 +1,21 @@
package cn.hutool.core.date.chinese;
import cn.hutool.core.date.ChineseDate;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import java.time.LocalDate;
import java.util.Date;
/**
* 24节气相关信息
*
* @author looly
* @author looly, zak
* @since 5.4.1
*/
public class SolarTerms {
/**
* 根据节气修正干支月
*
* @param y
* @param n 节气
* @return 干支月
*/
public static int getTerm(int y, int n) {
if (y < 1900 || y > 2100) {
return -1;
}
if (n < 1 || n > 24) {
return -1;
}
String _table = S_TERM_INFO[y - 1900];
Integer[] _info = new Integer[6];
for (int i = 0; i < 6; i++) {
_info[i] = NumberUtil.parseInt("0x" + _table.substring(i * 5, 5 * (i + 1)));
}
String[] _calday = new String[24];
for (int i = 0; i < 6; i++) {
_calday[4 * i] = _info[i].toString().substring(0, 1);
_calday[4 * i + 1] = _info[i].toString().substring(1, 3);
_calday[4 * i + 2] = _info[i].toString().substring(3, 4);
_calday[4 * i + 3] = _info[i].toString().substring(4, 6);
}
return NumberUtil.parseInt(_calday[n - 1]);
}
/**
* 1900-2100各年的24节气日期速查表
* 此表来自https://github.com/jjonline/calendar.js/blob/master/calendar.js
@ -112,4 +88,121 @@ public class SolarTerms {
"7f0e36665b66a449801e9808297c35", "665f67f0e37f14898082b072297c35", "7ec967f0e37f14998082b0787b06bd",
"7f07e7f0e47f531b0723b0b6fb0721", "7f0e26665b66a449801e9808297c35", "665f67f0e37f1489801eb072297c35",
"7ec967f0e37f14998082b0787b06bd", "7f07e7f0e47f531b0723b0b6fb0721", "7f0e27f1487f531b0b0bb0b6fb0722"};
/**
* 24节气
*/
private static final String[] TERMS = {
"小寒", "大寒", "立春", "雨水", "惊蛰", "春分",
"清明", "谷雨", "立夏", "小满", "芒种", "夏至",
"小暑", "大暑", "立秋", "处暑", "白露", "秋分",
"寒露", "霜降", "立冬", "小雪", "大雪", "冬至"
};
/**
* 传入公历y年获得该年第n个节气的公历日期
*
* @param y 公历年(1900-2100)
* @param n 二十四节气中的第几个节气(1~24)从n=1(小寒)算起
* @return getTerm(1987,3) -4;意即1987年2月4日立春
*/
public static int getTerm(int y, int n) {
if (y < 1900 || y > 2100) {
return -1;
}
if (n < 1 || n > 24) {
return -1;
}
final String _table = S_TERM_INFO[y - 1900];
Integer[] _info = new Integer[6];
for (int i = 0; i < 6; i++) {
_info[i] = Integer.parseInt(_table.substring(i * 5, 5 * (i + 1)), 16);
}
String[] _calday = new String[24];
for (int i = 0; i < 6; i++) {
_calday[4 * i] = _info[i].toString().substring(0, 1);
_calday[4 * i + 1] = _info[i].toString().substring(1, 3);
_calday[4 * i + 2] = _info[i].toString().substring(3, 4);
_calday[4 * i + 3] = _info[i].toString().substring(4, 6);
}
return NumberUtil.parseInt(_calday[n - 1]);
}
/**
* 根据日期获取节气
* @param date 日期
* @return 返回指定日期所处的节气
*/
public static String getTerm(Date date) {
final DateTime dt = DateUtil.date(date);
return getTermInternal(dt.year(), dt.month() + 1, dt.dayOfMonth());
}
/**
* 根据农历日期获取节气
* @param chineseDate 农历日期
* @return 返回指定日期所处的节气
*/
public static String getTerm(ChineseDate chineseDate) {
return chineseDate.getTerm();
}
/**
* 根据日期获取节气
* @param date 日期
* @return 返回指定日期所处的节气
*/
public static String getTerm(LocalDate date) {
return getTermInternal(date.getYear(), date.getMonthValue(), date.getDayOfMonth());
}
/**
* 根据年月日获取节气
* @param year 公历年
* @param mouth 公历月从1开始
* @param day 公历日从1开始
* @return 返回指定年月日所处的节气
*/
public static String getTerm(int year, int mouth, int day) {
return getTerm(LocalDate.of(year, mouth, day));
}
/**
* 根据年月日获取节气, 内部方法不对月和日做有效校验
* @param year
* @param mouth 从1计数
* @param day 从1计数
* @return 返回指定年月日所处的节气
*/
private static String getTermInternal(int year, int mouth, int day) {
if (year < 1900 || year > 2100) {
throw new IllegalArgumentException("只支持1900-2100之间的日期获取节气");
}
final String termTable = S_TERM_INFO[year - 1900];
// 节气速查表中每5个字符含有4个节气通过月份直接计算偏移
final int segment = (mouth + 1) / 2 - 1;
final int termInfo = Integer.parseInt(termTable.substring(segment * 5, (segment + 1) * 5), 16);
final String termInfoStr = String.valueOf(termInfo);
final String[] segmentTable = new String[24];
segmentTable[0] = termInfoStr.substring(0, 1);
segmentTable[1] = termInfoStr.substring(1, 3);
segmentTable[2] = termInfoStr.substring(3, 4);
segmentTable[3] = termInfoStr.substring(4, 6);
// 奇数月份的节气在前2个偶数月份的节气在后两个
int segmentOffset = (mouth & 1) == 1 ? 0 : 2;
if (day < NumberUtil.parseInt(segmentTable[segmentOffset])) {
int idx = segment * 4 + segmentOffset - 1;
return TERMS[idx < 0 ? 23 : idx];
}
if(day >= NumberUtil.parseInt(segmentTable[segmentOffset + 1])) {
return TERMS[segment * 4 + segmentOffset + 1];
}
return TERMS[segment * 4 + segmentOffset];
}
}

View File

@ -951,6 +951,9 @@ public class Validator {
* @return 是否为URL
*/
public static boolean isUrl(CharSequence value) {
if(StrUtil.isBlank(value)){
return false;
}
try {
new java.net.URL(StrUtil.str(value));
} catch (MalformedURLException e) {

View File

@ -10,6 +10,7 @@ import cn.hutool.core.util.StrUtil;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.junit.Assert;
import org.junit.Test;
@ -18,13 +19,7 @@ import java.io.Serializable;
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.*;
/**
* Bean工具单元测试
@ -201,6 +196,8 @@ public class BeanUtilTest {
person.setName("测试A11");
person.setSubName("sub名字");
person.setSlow(true);
person.setBooleana(true);
person.setBooleanb(true);
Map<String, Object> map = BeanUtil.beanToMap(person);
Assert.assertEquals("sub名字", map.get("aliasSubName"));
@ -211,9 +208,13 @@ public class BeanUtilTest {
Map<String, Object> map = MapUtil.newHashMap();
map.put("aliasSubName", "sub名字");
map.put("slow", true);
map.put("is_booleana", "1");
map.put("is_booleanb", true);
final SubPersonWithAlias subPersonWithAlias = BeanUtil.toBean(map, SubPersonWithAlias.class);
Assert.assertEquals("sub名字", subPersonWithAlias.getSubName());
Assert.assertTrue(subPersonWithAlias.isBooleana());
Assert.assertEquals(true, subPersonWithAlias.getBooleanb());
}
@Test
@ -360,11 +361,14 @@ public class BeanUtilTest {
@Getter
@Setter
@ToString
public static class SubPersonWithAlias extends Person {
// boolean参数值非isXXX形式
@Alias("aliasSubName")
private String subName;
private Boolean slow;
private boolean booleana;
private Boolean booleanb;
}
@Getter

View File

@ -0,0 +1,105 @@
package cn.hutool.core.date.chinese;
import cn.hutool.core.date.ChineseDate;
import cn.hutool.core.date.DateUtil;
import org.junit.Assert;
import org.junit.Test;
public class SolarTermsTest {
@Test
public void getTermTest1(){
final int term = SolarTerms.getTerm(1987, 3);
Assert.assertEquals(4, term);
}
@Test
public void getTermTest() {
Assert.assertEquals("冬至", SolarTerms.getTerm(2021, 1, 4));
Assert.assertEquals("小寒", SolarTerms.getTerm(2021, 1, 5));
Assert.assertEquals("小寒", SolarTerms.getTerm(2021, 1, 19));
Assert.assertEquals("大寒", SolarTerms.getTerm(2021, 1, 20));
Assert.assertEquals("大寒", SolarTerms.getTerm(2021, 2, 2));
Assert.assertEquals("立春", SolarTerms.getTerm(2021, 2, 3));
Assert.assertEquals("立春", SolarTerms.getTerm(2021, 2, 17));
Assert.assertEquals("雨水", SolarTerms.getTerm(2021, 2, 18));
Assert.assertEquals("雨水", SolarTerms.getTerm(2021, 3, 4));
Assert.assertEquals("惊蛰", SolarTerms.getTerm(2021, 3, 5));
Assert.assertEquals("惊蛰", SolarTerms.getTerm(2021, 3, 19));
Assert.assertEquals("春分", SolarTerms.getTerm(2021, 3, 20));
Assert.assertEquals("春分", SolarTerms.getTerm(2021, 4, 3));
Assert.assertEquals("清明", SolarTerms.getTerm(2021, 4, 4));
Assert.assertEquals("清明", SolarTerms.getTerm(2021, 4, 10));
Assert.assertEquals("清明", SolarTerms.getTerm(2021, 4, 19));
Assert.assertEquals("谷雨", SolarTerms.getTerm(2021, 4, 20));
Assert.assertEquals("谷雨", SolarTerms.getTerm(2021, 4, 29));
Assert.assertEquals("谷雨", SolarTerms.getTerm(2021, 5, 4));
Assert.assertEquals("立夏", SolarTerms.getTerm(2021, 5, 5));
Assert.assertEquals("立夏", SolarTerms.getTerm(2021, 5, 9));
Assert.assertEquals("立夏", SolarTerms.getTerm(2021, 5, 20));
Assert.assertEquals("小满", SolarTerms.getTerm(2021, 5, 21));
Assert.assertEquals("小满", SolarTerms.getTerm(2021, 6, 4));
Assert.assertEquals("芒种", SolarTerms.getTerm(2021, 6, 5));
Assert.assertEquals("芒种", SolarTerms.getTerm(2021, 6, 20));
Assert.assertEquals("夏至", SolarTerms.getTerm(2021, 6, 21));
Assert.assertEquals("夏至", SolarTerms.getTerm(2021, 7, 6));
Assert.assertEquals("小暑", SolarTerms.getTerm(2021, 7, 7));
Assert.assertEquals("小暑", SolarTerms.getTerm(2021, 7, 21));
Assert.assertEquals("大暑", SolarTerms.getTerm(2021, 7, 22));
Assert.assertEquals("大暑", SolarTerms.getTerm(2021, 8, 6));
Assert.assertEquals("立秋", SolarTerms.getTerm(2021, 8, 7));
Assert.assertEquals("处暑", SolarTerms.getTerm(2021, 8, 23));
Assert.assertEquals("处暑", SolarTerms.getTerm(2021, 9, 6));
Assert.assertEquals("白露", SolarTerms.getTerm(2021, 9, 7));
Assert.assertEquals("白露", SolarTerms.getTerm(2021, 9, 22));
Assert.assertEquals("秋分", SolarTerms.getTerm(2021, 9, 23));
Assert.assertEquals("秋分", SolarTerms.getTerm(2021, 10, 7));
Assert.assertEquals("寒露", SolarTerms.getTerm(2021, 10, 8));
Assert.assertEquals("寒露", SolarTerms.getTerm(2021, 10, 22));
Assert.assertEquals("霜降", SolarTerms.getTerm(2021, 10, 23));
Assert.assertEquals("霜降", SolarTerms.getTerm(2021, 11, 6));
Assert.assertEquals("立冬", SolarTerms.getTerm(2021, 11, 7));
Assert.assertEquals("立冬", SolarTerms.getTerm(2021, 11, 21));
Assert.assertEquals("小雪", SolarTerms.getTerm(2021, 11, 22));
Assert.assertEquals("小雪", SolarTerms.getTerm(2021, 12, 6));
Assert.assertEquals("大雪", SolarTerms.getTerm(2021, 12, 7));
Assert.assertEquals("大雪", SolarTerms.getTerm(2021, 12, 20));
Assert.assertEquals("冬至", SolarTerms.getTerm(2021, 12, 21));
}
@Test
public void getTermByDateTest() {
Assert.assertEquals("春分", SolarTerms.getTerm(DateUtil.parseDate("2021-04-02")));
}
@Test
public void getTermByChineseDateTest() {
Assert.assertEquals("清明", SolarTerms.getTerm(new ChineseDate(2021, 2, 25)));
}
}

View File

@ -1,6 +1,7 @@
package cn.hutool.db.dialect;
import cn.hutool.core.util.ClassLoaderUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.dialect.impl.AnsiSqlDialect;
import cn.hutool.db.dialect.impl.H2Dialect;
@ -115,6 +116,12 @@ public class DialectFactory {
// 全部转为小写忽略大小写
nameContainsProductInfo = StrUtil.cleanBlank(nameContainsProductInfo.toLowerCase());
// 首先判断是否为标准的JDBC URL截取jdbc:xxxx:中间部分
final String name = ReUtil.getGroup1("jdbc:(.*?):", nameContainsProductInfo);
if(StrUtil.isNotBlank(name)){
nameContainsProductInfo = name;
}
String driver = null;
if (nameContainsProductInfo.contains("mysql")) {
driver = ClassLoaderUtil.isPresent(DRIVER_MYSQL_V6) ? DRIVER_MYSQL_V6 : DRIVER_MYSQL;

View File

@ -0,0 +1,14 @@
package cn.hutool.db.dialect;
import org.junit.Assert;
import org.junit.Test;
public class DriverUtilTest {
@Test
public void identifyDriverTest(){
String url = "jdbc:h2:file:./db/test;AUTO_SERVER=TRUE;DB_CLOSE_ON_EXIT=FALSE;MODE=MYSQL";
String driver = DriverUtil.identifyDriver(url); // driver 返回 mysql driver
Assert.assertEquals("org.h2.Driver", driver);
}
}