diff --git a/CHANGELOG.md b/CHANGELOG.md
index 58a5de0e6..3b93dc404 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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()传空返回true(issue#I3ETTY@Gitee)
+* 【db 】 修复数据库driver根据url的判断识别错误问题(issue#I3EWBI@Gitee)
-------------------------------------------------------------------------------------------------------------
diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/MapValueProvider.java b/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/MapValueProvider.java
index 16c1f3d74..7c84e4074 100644
--- a/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/MapValueProvider.java
+++ b/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/MapValueProvider.java
@@ -10,7 +10,14 @@ import java.util.Map;
/**
* Map值提供者,支持驼峰和下划线的key兼容。
- * 假设目标属性为firstName,则Map中为firstName或first_name都可以对应到值。
+ * 假设目标属性为firstName,则Map中以下形式的值都可以对应:
+ *
+ * - firstName
+ * - first_name
+ * - isFirstName(如果为Boolean或boolean类型的值)
+ * - is_first_name(如果为Boolean或boolean类型的值)
+ *
+ * 为firstName或first_name都可以对应到值。
*
* @author looly
*/
@@ -50,23 +57,53 @@ public class MapValueProvider implements ValueProvider {
@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;
}
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/ChineseDate.java b/hutool-core/src/main/java/cn/hutool/core/date/ChineseDate.java
index b9b08cd92..5d4ea75b7 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/ChineseDate.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/ChineseDate.java
@@ -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
*
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/chinese/SolarTerms.java b/hutool-core/src/main/java/cn/hutool/core/date/chinese/SolarTerms.java
index 597e60bc2..13c7c3b83 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/chinese/SolarTerms.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/chinese/SolarTerms.java
@@ -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];
+ }
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Validator.java b/hutool-core/src/main/java/cn/hutool/core/lang/Validator.java
index bca1d0ae5..cde7cb4ab 100644
--- a/hutool-core/src/main/java/cn/hutool/core/lang/Validator.java
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/Validator.java
@@ -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) {
diff --git a/hutool-core/src/test/java/cn/hutool/core/bean/BeanUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/bean/BeanUtilTest.java
index 209e4dba5..2c3621871 100644
--- a/hutool-core/src/test/java/cn/hutool/core/bean/BeanUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/bean/BeanUtilTest.java
@@ -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 map = BeanUtil.beanToMap(person);
Assert.assertEquals("sub名字", map.get("aliasSubName"));
@@ -211,9 +208,13 @@ public class BeanUtilTest {
Map 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
diff --git a/hutool-core/src/test/java/cn/hutool/core/date/chinese/SolarTermsTest.java b/hutool-core/src/test/java/cn/hutool/core/date/chinese/SolarTermsTest.java
new file mode 100644
index 000000000..909af0fb2
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/date/chinese/SolarTermsTest.java
@@ -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)));
+ }
+}
diff --git a/hutool-db/src/main/java/cn/hutool/db/dialect/DialectFactory.java b/hutool-db/src/main/java/cn/hutool/db/dialect/DialectFactory.java
index 0140ac43c..78e35fa2a 100644
--- a/hutool-db/src/main/java/cn/hutool/db/dialect/DialectFactory.java
+++ b/hutool-db/src/main/java/cn/hutool/db/dialect/DialectFactory.java
@@ -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;
diff --git a/hutool-db/src/test/java/cn/hutool/db/dialect/DriverUtilTest.java b/hutool-db/src/test/java/cn/hutool/db/dialect/DriverUtilTest.java
new file mode 100644
index 000000000..7dacba36a
--- /dev/null
+++ b/hutool-db/src/test/java/cn/hutool/db/dialect/DriverUtilTest.java
@@ -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);
+ }
+}