mirror of
https://gitee.com/dromara/hutool.git
synced 2025-04-24 18:04:54 +08:00
Merge remote-tracking branch 'origin/v6-dev' into v6-dev
This commit is contained in:
commit
373996fcbb
@ -15,9 +15,9 @@
|
||||
<artifactId>hutool-core</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>Hutool核心,包括集合、字符串、Bean等工具</description>
|
||||
|
||||
|
||||
<properties>
|
||||
<Automatic-Module-Name>cn.hutool.core</Automatic-Module-Name>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
</project>
|
@ -363,7 +363,7 @@ public class CollUtil {
|
||||
* 例如:集合1:[a, b, c, c, c],集合2:[a, b, c, c]<br>
|
||||
* 结果:[a, b, c],此结果中只保留了一个c
|
||||
*
|
||||
* @param <T> 集合元素类型
|
||||
* @param <T> 集合元素类型
|
||||
* @param colls 集合列表
|
||||
* @return 交集的集合,返回 {@link LinkedHashSet}
|
||||
* @since 5.3.9
|
||||
@ -1735,21 +1735,7 @@ public class CollUtil {
|
||||
list.sort(comparator);
|
||||
}
|
||||
|
||||
return page(pageNo, pageSize, list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对指定List分页取值
|
||||
*
|
||||
* @param <T> 集合元素类型
|
||||
* @param pageNo 页码,从0开始计数,0表示第一页
|
||||
* @param pageSize 每页的条目数
|
||||
* @param list 列表
|
||||
* @return 分页后的段落内容
|
||||
* @since 4.1.20
|
||||
*/
|
||||
public static <T> List<T> page(final int pageNo, final int pageSize, final List<T> list) {
|
||||
return ListUtil.page(pageNo, pageSize, list);
|
||||
return ListUtil.page(list, pageNo, pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2015,7 +2001,7 @@ public class CollUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据元素的指定字段名分组,非Bean都放在第一个分组中
|
||||
* 根据元素的指定字段值分组,非Bean都放在第一个分组中
|
||||
*
|
||||
* @param <T> 元素类型
|
||||
* @param collection 集合
|
||||
@ -2023,8 +2009,24 @@ public class CollUtil {
|
||||
* @return 分组列表
|
||||
*/
|
||||
public static <T> List<List<T>> groupByField(final Collection<T> collection, final String fieldName) {
|
||||
return groupByFunc(collection, t -> BeanUtil.getFieldValue(t, fieldName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据元素的指定字段值分组,非Bean都放在第一个分组中<br>
|
||||
* 例如:{@code
|
||||
* CollUtil.groupByFunc(list, TestBean::getAge)
|
||||
* }
|
||||
*
|
||||
* @param <T> 元素类型
|
||||
* @param collection 集合
|
||||
* @param getter getter方法引用
|
||||
* @return 分组列表
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static <T> List<List<T>> groupByFunc(final Collection<T> collection, final Function<T, ?> getter) {
|
||||
return group(collection, new Hash32<T>() {
|
||||
private final List<Object> fieldNameList = new ArrayList<>();
|
||||
private final List<Object> hashValList = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public int hash32(final T t) {
|
||||
@ -2032,14 +2034,13 @@ public class CollUtil {
|
||||
// 非Bean放在同一子分组中
|
||||
return 0;
|
||||
}
|
||||
final Object value = FieldUtil.getFieldValue(t, fieldName);
|
||||
final int hash = fieldNameList.indexOf(value);
|
||||
final Object value = getter.apply(t);
|
||||
int hash = hashValList.indexOf(value);
|
||||
if (hash < 0) {
|
||||
fieldNameList.add(value);
|
||||
return fieldNameList.size() - 1;
|
||||
} else {
|
||||
return hash;
|
||||
hashValList.add(value);
|
||||
hash = hashValList.size() - 1;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -8,20 +8,11 @@ import cn.hutool.core.collection.partition.RandomAccessPartition;
|
||||
import cn.hutool.core.comparator.PinyinComparator;
|
||||
import cn.hutool.core.comparator.PropertyComparator;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.math.PageInfo;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.PageUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.RandomAccess;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
@ -254,42 +245,53 @@ public class ListUtil {
|
||||
* 对指定List分页取值
|
||||
*
|
||||
* @param <T> 集合元素类型
|
||||
* @param pageNo 页码,第一页的页码取决于{@link PageUtil#getFirstPageNo()},默认0
|
||||
* @param pageNo 页码,第一页的页码,从0开始
|
||||
* @param pageSize 每页的条目数
|
||||
* @param list 列表
|
||||
* @return 分页后的段落内容
|
||||
* @since 4.1.20
|
||||
*/
|
||||
public static <T> List<T> page(final int pageNo, final int pageSize, final List<T> list) {
|
||||
public static <T> List<T> page(final List<T> list, final int pageNo, final int pageSize) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
return page(list, PageInfo.of(list.size(), pageSize)
|
||||
.setFirstPageNo(0).setPageNo(pageNo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 对指定List分页取值
|
||||
*
|
||||
* @param <T> 集合元素类型
|
||||
* @param pageInfo 分页信息
|
||||
* @param list 列表
|
||||
* @return 分页后的段落内容
|
||||
* @since 4.1.20
|
||||
*/
|
||||
public static <T> List<T> page(final List<T> list, final PageInfo pageInfo) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
|
||||
final int resultSize = list.size();
|
||||
// 每页条目数大于总数直接返回所有
|
||||
if (resultSize <= pageSize) {
|
||||
if (pageNo < (PageUtil.getFirstPageNo() + 1)) {
|
||||
final int total = list.size();
|
||||
final int pageSize = pageInfo.getPageSize();
|
||||
// 不满一页
|
||||
if (total <= pageSize) {
|
||||
if (pageInfo.isFirstPage()) {
|
||||
// 页码为1,返回所有
|
||||
return view(list);
|
||||
} else {
|
||||
// 越界直接返回空
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
}
|
||||
// 相乘可能会导致越界 临时用long
|
||||
if (((long) (pageNo - PageUtil.getFirstPageNo()) * pageSize) > resultSize) {
|
||||
|
||||
if (pageInfo.getBeginIndex() > total) {
|
||||
// 越界直接返回空
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
|
||||
final int[] startEnd = PageUtil.transToStartEnd(pageNo, pageSize);
|
||||
if (startEnd[1] > resultSize) {
|
||||
startEnd[1] = resultSize;
|
||||
if (startEnd[0] > startEnd[1]) {
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
}
|
||||
|
||||
return sub(list, startEnd[0], startEnd[1]);
|
||||
return sub(list, pageInfo.getBeginIndex(), pageInfo.getEndIndexExclude());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -307,16 +309,11 @@ public class ListUtil {
|
||||
}
|
||||
|
||||
final int total = list.size();
|
||||
final int totalPage = PageUtil.totalPage(total, pageSize);
|
||||
for (int pageNo = PageUtil.getFirstPageNo(); pageNo < totalPage + PageUtil.getFirstPageNo(); pageNo++) {
|
||||
// 获取当前页在列表中对应的起止序号
|
||||
final int[] startEnd = PageUtil.transToStartEnd(pageNo, pageSize);
|
||||
if (startEnd[1] > total) {
|
||||
startEnd[1] = total;
|
||||
}
|
||||
|
||||
final PageInfo pageInfo = PageInfo.of(total, pageSize);
|
||||
while(pageInfo.isValidPage()){
|
||||
// 返回数据
|
||||
pageListConsumer.accept(sub(list, startEnd[0], startEnd[1]));
|
||||
pageListConsumer.accept(sub(list, pageInfo.getBeginIndex(), pageInfo.getEndIndexExclude()));
|
||||
pageInfo.nextPage();
|
||||
}
|
||||
}
|
||||
|
||||
@ -346,7 +343,7 @@ public class ListUtil {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return list;
|
||||
}
|
||||
if(null == c){
|
||||
if (null == c) {
|
||||
c = Comparator.nullsFirst((Comparator<? super T>) Comparator.naturalOrder());
|
||||
}
|
||||
list.sort(c);
|
||||
@ -446,10 +443,10 @@ public class ListUtil {
|
||||
/**
|
||||
* 在指定位置设置元素。当index小于List的长度时,替换指定位置的值,否则追加{@code paddingElement}直到到达index后,设置值
|
||||
*
|
||||
* @param <T> 元素类型
|
||||
* @param list List列表
|
||||
* @param index 位置
|
||||
* @param element 新元素
|
||||
* @param <T> 元素类型
|
||||
* @param list List列表
|
||||
* @param index 位置
|
||||
* @param element 新元素
|
||||
* @param paddingElement 填充的值
|
||||
* @return 原List
|
||||
* @since 5.8.4
|
||||
@ -471,29 +468,29 @@ public class ListUtil {
|
||||
/**
|
||||
* 截取集合的部分
|
||||
*
|
||||
* @param <T> 集合元素类型
|
||||
* @param list 被截取的数组
|
||||
* @param start 开始位置(包含)
|
||||
* @param end 结束位置(不包含)
|
||||
* @param <T> 集合元素类型
|
||||
* @param list 被截取的数组
|
||||
* @param begionInclude 开始位置(包含)
|
||||
* @param endExclude 结束位置(不包含)
|
||||
* @return 截取后的数组,当开始位置超过最大时,返回空的List
|
||||
*/
|
||||
public static <T> List<T> sub(final List<T> list, final int start, final int end) {
|
||||
return sub(list, start, end, 1);
|
||||
public static <T> List<T> sub(final List<T> list, final int begionInclude, final int endExclude) {
|
||||
return sub(list, begionInclude, endExclude, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取集合的部分<br>
|
||||
* 此方法与{@link List#subList(int, int)} 不同在于子列表是新的副本,操作子列表不会影响原列表。
|
||||
*
|
||||
* @param <T> 集合元素类型
|
||||
* @param list 被截取的数组
|
||||
* @param start 开始位置(包含)
|
||||
* @param end 结束位置(不包含)
|
||||
* @param step 步进
|
||||
* @param <T> 集合元素类型
|
||||
* @param list 被截取的数组
|
||||
* @param begionInclude 开始位置(包含)
|
||||
* @param endExclude 结束位置(不包含)
|
||||
* @param step 步进
|
||||
* @return 截取后的数组,当开始位置超过最大时,返回空的List
|
||||
* @since 4.0.6
|
||||
*/
|
||||
public static <T> List<T> sub(final List<T> list, int start, int end, int step) {
|
||||
public static <T> List<T> sub(final List<T> list, int begionInclude, int endExclude, int step) {
|
||||
if (list == null) {
|
||||
return null;
|
||||
}
|
||||
@ -503,25 +500,25 @@ public class ListUtil {
|
||||
}
|
||||
|
||||
final int size = list.size();
|
||||
if (start < 0) {
|
||||
start += size;
|
||||
if (begionInclude < 0) {
|
||||
begionInclude += size;
|
||||
}
|
||||
if (end < 0) {
|
||||
end += size;
|
||||
if (endExclude < 0) {
|
||||
endExclude += size;
|
||||
}
|
||||
if (start == size) {
|
||||
if (begionInclude == size) {
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
if (start > end) {
|
||||
final int tmp = start;
|
||||
start = end;
|
||||
end = tmp;
|
||||
if (begionInclude > endExclude) {
|
||||
final int tmp = begionInclude;
|
||||
begionInclude = endExclude;
|
||||
endExclude = tmp;
|
||||
}
|
||||
if (end > size) {
|
||||
if (start >= size) {
|
||||
if (endExclude > size) {
|
||||
if (begionInclude >= size) {
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
end = size;
|
||||
endExclude = size;
|
||||
}
|
||||
|
||||
if (step < 1) {
|
||||
@ -529,7 +526,7 @@ public class ListUtil {
|
||||
}
|
||||
|
||||
final List<T> result = new ArrayList<>();
|
||||
for (int i = start; i < end; i += step) {
|
||||
for (int i = begionInclude; i < endExclude; i += step) {
|
||||
result.add(list.get(i));
|
||||
}
|
||||
return result;
|
||||
@ -719,8 +716,8 @@ public class ListUtil {
|
||||
* 通过删除或替换现有元素或者原地添加新的元素来修改列表,并以列表形式返回被修改的内容。此方法不会改变原列表。
|
||||
* 类似js的<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/splice">splice</a>函数
|
||||
*
|
||||
* @param <T> 元素类型
|
||||
* @param list 列表
|
||||
* @param <T> 元素类型
|
||||
* @param list 列表
|
||||
* @param start 指定修改的开始位置(从 0 计数), 可以为负数, -1代表最后一个元素
|
||||
* @param deleteCount 删除个数,必须是正整数
|
||||
* @param items 放入的元素
|
||||
|
@ -2,12 +2,12 @@ package cn.hutool.core.date;
|
||||
|
||||
import cn.hutool.core.comparator.CompareUtil;
|
||||
import cn.hutool.core.convert.NumberChineseFormatter;
|
||||
import cn.hutool.core.date.format.GlobalCustomFormat;
|
||||
import cn.hutool.core.date.format.parser.DateParser;
|
||||
import cn.hutool.core.date.format.parser.FastDateParser;
|
||||
import cn.hutool.core.date.format.GlobalCustomFormat;
|
||||
import cn.hutool.core.date.format.parser.PositionDateParser;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.text.StrUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
|
||||
import javax.xml.datatype.XMLGregorianCalendar;
|
||||
import java.text.ParsePosition;
|
||||
@ -15,7 +15,6 @@ import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
@ -74,7 +73,7 @@ public class CalendarUtil {
|
||||
/**
|
||||
* 转换为Calendar对象
|
||||
*
|
||||
* @param millis 时间戳
|
||||
* @param millis 时间戳
|
||||
* @param timeZone 时区
|
||||
* @return Calendar对象
|
||||
* @since 5.7.22
|
||||
@ -105,6 +104,8 @@ public class CalendarUtil {
|
||||
return Calendar.PM == calendar.get(Calendar.AM_PM);
|
||||
}
|
||||
|
||||
// region ----- modify 时间修改
|
||||
|
||||
/**
|
||||
* 修改日期为某个时间字段起始时间
|
||||
*
|
||||
@ -355,6 +356,7 @@ public class CalendarUtil {
|
||||
public static Calendar endOfYear(final Calendar calendar) {
|
||||
return ceiling(calendar, DateField.YEAR);
|
||||
}
|
||||
// endregion
|
||||
|
||||
/**
|
||||
* 比较两个日期是否为同一天
|
||||
@ -375,8 +377,8 @@ public class CalendarUtil {
|
||||
/**
|
||||
* 比较两个日期是否为同一周
|
||||
*
|
||||
* @param cal1 日期1
|
||||
* @param cal2 日期2
|
||||
* @param cal1 日期1
|
||||
* @param cal2 日期2
|
||||
* @param isMon 是否为周一。国内第一天为星期一,国外第一天为星期日
|
||||
* @return 是否为同一周
|
||||
* @since 5.7.21
|
||||
@ -443,28 +445,6 @@ public class CalendarUtil {
|
||||
return date1.getTimeInMillis() == date2.getTimeInMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定日期区间内的年份和季度<br>
|
||||
*
|
||||
* @param startDate 起始日期(包含)
|
||||
* @param endDate 结束日期(包含)
|
||||
* @return 季度列表 ,元素类似于 20132
|
||||
* @since 4.1.15
|
||||
*/
|
||||
public static LinkedHashSet<String> yearAndQuarter(long startDate, final long endDate) {
|
||||
final LinkedHashSet<String> quarters = new LinkedHashSet<>();
|
||||
final Calendar cal = calendar(startDate);
|
||||
while (startDate <= endDate) {
|
||||
// 如果开始时间超出结束时间,让结束时间为开始时间,处理完后结束循环
|
||||
quarters.add(yearAndQuarter(cal));
|
||||
|
||||
cal.add(Calendar.MONTH, 3);
|
||||
startDate = cal.getTimeInMillis();
|
||||
}
|
||||
|
||||
return quarters;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定日期年份和季度<br>
|
||||
* 格式:[20131]表示2013年第一季度
|
||||
@ -637,51 +617,7 @@ public class CalendarUtil {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算相对于dateToCompare的年龄,长用于计算指定生日在某年的年龄
|
||||
*
|
||||
* @param birthday 生日
|
||||
* @param dateToCompare 需要对比的日期
|
||||
* @return 年龄
|
||||
*/
|
||||
protected static int age(final long birthday, final long dateToCompare) {
|
||||
if (birthday > dateToCompare) {
|
||||
throw new IllegalArgumentException("Birthday is after dateToCompare!");
|
||||
}
|
||||
|
||||
final Calendar cal = Calendar.getInstance();
|
||||
cal.setTimeInMillis(dateToCompare);
|
||||
|
||||
final int year = cal.get(Calendar.YEAR);
|
||||
final int month = cal.get(Calendar.MONTH);
|
||||
final int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
|
||||
final boolean isLastDayOfMonth = dayOfMonth == cal.getActualMaximum(Calendar.DAY_OF_MONTH);
|
||||
|
||||
cal.setTimeInMillis(birthday);
|
||||
int age = year - cal.get(Calendar.YEAR);
|
||||
//当前日期,则为0岁
|
||||
if (age == 0){
|
||||
return 0;
|
||||
}
|
||||
|
||||
final int monthBirth = cal.get(Calendar.MONTH);
|
||||
if (month == monthBirth) {
|
||||
|
||||
final int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH);
|
||||
final boolean isLastDayOfMonthBirth = dayOfMonthBirth == cal.getActualMaximum(Calendar.DAY_OF_MONTH);
|
||||
// issue#I6E6ZG,法定生日当天不算年龄,从第二天开始计算
|
||||
if ((false == isLastDayOfMonth || false == isLastDayOfMonthBirth) && dayOfMonth <= dayOfMonthBirth) {
|
||||
// 如果生日在当月,但是未达到生日当天的日期,年龄减一
|
||||
age--;
|
||||
}
|
||||
} else if (month < monthBirth) {
|
||||
// 如果当前月份未达到生日的月份,年龄计算减一
|
||||
age--;
|
||||
}
|
||||
|
||||
return age;
|
||||
}
|
||||
|
||||
// region ----- parse
|
||||
/**
|
||||
* 通过给定的日期格式解析日期时间字符串。<br>
|
||||
* 传入的日期格式会逐个尝试,直到解析成功,返回{@link Calendar}对象,否则抛出{@link DateException}异常。
|
||||
@ -782,4 +718,50 @@ public class CalendarUtil {
|
||||
|
||||
return parser.parse(StrUtil.str(str), new ParsePosition(0), calendar) ? calendar : null;
|
||||
}
|
||||
// endregion
|
||||
|
||||
/**
|
||||
* 计算相对于dateToCompare的年龄,长用于计算指定生日在某年的年龄
|
||||
*
|
||||
* @param birthday 生日
|
||||
* @param dateToCompare 需要对比的日期
|
||||
* @return 年龄
|
||||
*/
|
||||
protected static int age(final long birthday, final long dateToCompare) {
|
||||
if (birthday > dateToCompare) {
|
||||
throw new IllegalArgumentException("Birthday is after dateToCompare!");
|
||||
}
|
||||
|
||||
final Calendar cal = Calendar.getInstance();
|
||||
cal.setTimeInMillis(dateToCompare);
|
||||
|
||||
final int year = cal.get(Calendar.YEAR);
|
||||
final int month = cal.get(Calendar.MONTH);
|
||||
final int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
|
||||
final boolean isLastDayOfMonth = dayOfMonth == cal.getActualMaximum(Calendar.DAY_OF_MONTH);
|
||||
|
||||
cal.setTimeInMillis(birthday);
|
||||
int age = year - cal.get(Calendar.YEAR);
|
||||
//当前日期,则为0岁
|
||||
if (age == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
final int monthBirth = cal.get(Calendar.MONTH);
|
||||
if (month == monthBirth) {
|
||||
|
||||
final int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH);
|
||||
final boolean isLastDayOfMonthBirth = dayOfMonthBirth == cal.getActualMaximum(Calendar.DAY_OF_MONTH);
|
||||
// issue#I6E6ZG,法定生日当天不算年龄,从第二天开始计算
|
||||
if ((false == isLastDayOfMonth || false == isLastDayOfMonthBirth) && dayOfMonth <= dayOfMonthBirth) {
|
||||
// 如果生日在当月,但是未达到生日当天的日期,年龄减一
|
||||
age--;
|
||||
}
|
||||
} else if (month < monthBirth) {
|
||||
// 如果当前月份未达到生日的月份,年龄计算减一
|
||||
age--;
|
||||
}
|
||||
|
||||
return age;
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ import java.util.regex.Pattern;
|
||||
* <li>yyyy-MM-dd HH:mm:ss.SSS 示例:2022-08-05 12:59:59.559</li>
|
||||
* <li>yyyy-MM-dd HH:mm:ss.SSSZ 示例:2022-08-05 12:59:59.559+0800【东八区中国时区】、2022-08-05 04:59:59.559+0000【冰岛0时区】, 年月日 时分秒 毫秒 时区</li>
|
||||
* <li>yyyy-MM-dd HH:mm:ss.SSSz 示例:2022-08-05 12:59:59.559UTC【世界标准时间=0时区】、2022-08-05T12:59:59.599GMT【冰岛0时区】、2022-08-05T12:59:59.599CST【东八区中国时区】、2022-08-23T03:45:00.599EDT【美国东北纽约时间,-0400】 ,年月日 时分秒 毫秒 时区</li>
|
||||
* <li>yyyy-MM-dd'T'HH:mm:ss.SSS'Z' 示例:2022-08-05T12:59:59.559Z, 其中:''单引号表示转义字符,T:分隔符,Z:一般值UTC,0时区的时间含义</li>
|
||||
* <li>yyyy-MM-dd'T'HH:mm:ss.SSS'Z' 示例:2022-08-05T12:59:59.559Z, 其中:''单引号表示转义字符,T:分隔符,Z:一般指UTC,0时区的时间含义</li>
|
||||
* <li>yyyy-MM-dd'T'HH:mm:ss.SSSZ 示例:2022-08-05T11:59:59.559+0800, 其中:Z,表示时区</li>
|
||||
* <li>yyyy-MM-dd'T'HH:mm:ss.SSSX 示例:2022-08-05T12:59:59.559+08, 其中:X:两位时区,+08表示:东8区,中国时区</li>
|
||||
* <li>yyyy-MM-dd'T'HH:mm:ss.SSSXX 示例:2022-08-05T12:59:59.559+0800, 其中:XX:四位时区</li>
|
||||
@ -61,7 +61,7 @@ import java.util.regex.Pattern;
|
||||
* 如:“09:30 UTC”表示为“09:30Z”或“T0930Z”,其中:Z 是 +00:00 的缩写,意思是 UTC(零时分秒的偏移量).
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>yyyy-MM-dd'T'HH:mm:ssZ</li>
|
||||
* <li>yyyy-MM-dd'T'HH:mm:ss'Z'</li>
|
||||
* <li>2022-08-23T15:20:46UTC</li>
|
||||
* <li>2022-08-23T15:20:46 UTC</li>
|
||||
* <li>2022-08-23T15:20:46+0000</li>
|
||||
@ -189,15 +189,15 @@ public class DatePattern {
|
||||
/**
|
||||
* ISO8601日期时间格式,精确到毫秒:yyyy-MM-dd HH:mm:ss,SSS
|
||||
*/
|
||||
public static final String ISO8601_PATTERN = "yyyy-MM-dd HH:mm:ss,SSS";
|
||||
public static final String NORM_DATETIME_COMMA_MS_PATTERN = "yyyy-MM-dd HH:mm:ss,SSS";
|
||||
/**
|
||||
* ISO8601日期时间格式,精确到毫秒 {@link FastDateFormat}:yyyy-MM-dd HH:mm:ss,SSS
|
||||
*/
|
||||
public static final FastDateFormat ISO8601_FORMAT = FastDateFormat.getInstance(ISO8601_PATTERN);
|
||||
public static final FastDateFormat NORM_DATETIME_COMMA_MS_FORMAT = FastDateFormat.getInstance(NORM_DATETIME_COMMA_MS_PATTERN);
|
||||
/**
|
||||
* 标准日期格式 {@link DateTimeFormatter}:yyyy-MM-dd HH:mm:ss,SSS
|
||||
*/
|
||||
public static final DateTimeFormatter ISO8601_FORMATTER = createFormatter(ISO8601_PATTERN);
|
||||
public static final DateTimeFormatter NORM_DATETIME_COMMA_MS_FORMATTER = createFormatter(NORM_DATETIME_COMMA_MS_PATTERN);
|
||||
|
||||
/**
|
||||
* 标准日期格式:yyyy年MM月dd日
|
||||
@ -286,8 +286,7 @@ public class DatePattern {
|
||||
.toFormatter();
|
||||
// endregion
|
||||
|
||||
// region Others
|
||||
//================================================== Others ==================================================
|
||||
// region ----- Others
|
||||
/**
|
||||
* HTTP头中日期时间格式:EEE, dd MMM yyyy HH:mm:ss z
|
||||
*/
|
||||
@ -307,76 +306,78 @@ public class DatePattern {
|
||||
public static final FastDateFormat JDK_DATETIME_FORMAT = FastDateFormat.getInstance(JDK_DATETIME_PATTERN, Locale.US);
|
||||
|
||||
/**
|
||||
* UTC时间:yyyy-MM-dd'T'HH:mm:ss
|
||||
* ISO8601日期时间:yyyy-MM-dd'T'HH:mm:ss<br>
|
||||
* 按照ISO8601规范,默认使用T分隔日期和时间,末尾不加Z表示当地时区
|
||||
*/
|
||||
public static final String UTC_SIMPLE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss";
|
||||
public static final String ISO8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss";
|
||||
/**
|
||||
* UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss
|
||||
* ISO8601日期时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss
|
||||
*/
|
||||
public static final FastDateFormat UTC_SIMPLE_FORMAT = FastDateFormat.getInstance(UTC_SIMPLE_PATTERN, TimeZone.getTimeZone("UTC"));
|
||||
public static final FastDateFormat ISO8601_FORMAT = FastDateFormat.getInstance(ISO8601_PATTERN);
|
||||
|
||||
/**
|
||||
* UTC时间:yyyy-MM-dd'T'HH:mm:ss.SSS
|
||||
*/
|
||||
public static final String UTC_SIMPLE_MS_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS";
|
||||
public static final String ISO8601_MS_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS";
|
||||
/**
|
||||
* UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss.SSS
|
||||
*/
|
||||
public static final FastDateFormat UTC_SIMPLE_MS_FORMAT = FastDateFormat.getInstance(UTC_SIMPLE_MS_PATTERN, TimeZone.getTimeZone("UTC"));
|
||||
public static final FastDateFormat ISO8601_MS_FORMAT = FastDateFormat.getInstance(ISO8601_MS_PATTERN);
|
||||
|
||||
/**
|
||||
* UTC时间:yyyy-MM-dd'T'HH:mm:ss'Z'
|
||||
* UTC时间:yyyy-MM-dd'T'HH:mm:ss'Z'<br>
|
||||
* 按照ISO8601规范,后缀加Z表示UTC时间
|
||||
*/
|
||||
public static final String UTC_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||
/**
|
||||
* UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss'Z'
|
||||
* ISO8601时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss'Z'
|
||||
*/
|
||||
public static final FastDateFormat UTC_FORMAT = FastDateFormat.getInstance(UTC_PATTERN, TimeZone.getTimeZone("UTC"));
|
||||
public static final FastDateFormat UTC_FORMAT = FastDateFormat.getInstance(UTC_PATTERN, ZoneUtil.ZONE_UTC);
|
||||
|
||||
/**
|
||||
* UTC时间:yyyy-MM-dd'T'HH:mm:ssZ
|
||||
* ISO8601时间:yyyy-MM-dd'T'HH:mm:ssZ,Z表示一个时间偏移,如+0800
|
||||
*/
|
||||
public static final String UTC_WITH_ZONE_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ssZ";
|
||||
public static final String ISO8601_WITH_ZONE_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ssZ";
|
||||
/**
|
||||
* UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ssZ
|
||||
* ISO8601时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ssZ,Z表示一个时间偏移,如+0800
|
||||
*/
|
||||
public static final FastDateFormat UTC_WITH_ZONE_OFFSET_FORMAT = FastDateFormat.getInstance(UTC_WITH_ZONE_OFFSET_PATTERN, TimeZone.getTimeZone("UTC"));
|
||||
public static final FastDateFormat ISO8601_WITH_ZONE_OFFSET_FORMAT = FastDateFormat.getInstance(ISO8601_WITH_ZONE_OFFSET_PATTERN);
|
||||
|
||||
/**
|
||||
* UTC时间:yyyy-MM-dd'T'HH:mm:ssXXX
|
||||
* ISO8601时间:yyyy-MM-dd'T'HH:mm:ssXXX
|
||||
*/
|
||||
public static final String UTC_WITH_XXX_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ssXXX";
|
||||
public static final String ISO8601_WITH_XXX_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ssXXX";
|
||||
/**
|
||||
* UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ssXXX
|
||||
* ISO8601时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ssXXX
|
||||
*/
|
||||
public static final FastDateFormat UTC_WITH_XXX_OFFSET_FORMAT = FastDateFormat.getInstance(UTC_WITH_XXX_OFFSET_PATTERN);
|
||||
public static final FastDateFormat ISO8601_WITH_XXX_OFFSET_FORMAT = FastDateFormat.getInstance(ISO8601_WITH_XXX_OFFSET_PATTERN);
|
||||
|
||||
/**
|
||||
* UTC时间:yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
|
||||
* ISO8601时间:yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
|
||||
*/
|
||||
public static final String UTC_MS_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
|
||||
/**
|
||||
* UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
|
||||
* ISO8601时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
|
||||
*/
|
||||
public static final FastDateFormat UTC_MS_FORMAT = FastDateFormat.getInstance(UTC_MS_PATTERN, TimeZone.getTimeZone("UTC"));
|
||||
public static final FastDateFormat UTC_MS_FORMAT = FastDateFormat.getInstance(UTC_MS_PATTERN, ZoneUtil.ZONE_UTC);
|
||||
|
||||
/**
|
||||
* UTC时间:yyyy-MM-dd'T'HH:mm:ss.SSSZ
|
||||
* ISO8601时间:yyyy-MM-dd'T'HH:mm:ss.SSSZ
|
||||
*/
|
||||
public static final String UTC_MS_WITH_ZONE_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
|
||||
public static final String ISO8601_MS_WITH_ZONE_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
|
||||
/**
|
||||
* UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss.SSSZ
|
||||
* ISO8601时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss.SSSZ
|
||||
*/
|
||||
public static final FastDateFormat UTC_MS_WITH_ZONE_OFFSET_FORMAT = FastDateFormat.getInstance(UTC_MS_WITH_ZONE_OFFSET_PATTERN, TimeZone.getTimeZone("UTC"));
|
||||
public static final FastDateFormat ISO8601_MS_WITH_ZONE_OFFSET_FORMAT = FastDateFormat.getInstance(ISO8601_MS_WITH_ZONE_OFFSET_PATTERN);
|
||||
|
||||
/**
|
||||
* UTC时间:yyyy-MM-dd'T'HH:mm:ss.SSSXXX
|
||||
* ISO8601时间:yyyy-MM-dd'T'HH:mm:ss.SSSXXX
|
||||
*/
|
||||
public static final String UTC_MS_WITH_XXX_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
|
||||
public static final String ISO8601_MS_WITH_XXX_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
|
||||
/**
|
||||
* UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss.SSSXXX
|
||||
*/
|
||||
public static final FastDateFormat UTC_MS_WITH_XXX_OFFSET_FORMAT = FastDateFormat.getInstance(UTC_MS_WITH_XXX_OFFSET_PATTERN);
|
||||
public static final FastDateFormat ISO8601_MS_WITH_XXX_OFFSET_FORMAT = FastDateFormat.getInstance(ISO8601_MS_WITH_XXX_OFFSET_PATTERN);
|
||||
// endregion
|
||||
|
||||
/**
|
||||
|
@ -5,12 +5,7 @@ import cn.hutool.core.comparator.CompareUtil;
|
||||
import cn.hutool.core.date.format.DatePrinter;
|
||||
import cn.hutool.core.date.format.FastDateFormat;
|
||||
import cn.hutool.core.date.format.GlobalCustomFormat;
|
||||
import cn.hutool.core.date.format.parser.CSTDateParser;
|
||||
import cn.hutool.core.date.format.parser.NormalDateParser;
|
||||
import cn.hutool.core.date.format.parser.PositionDateParser;
|
||||
import cn.hutool.core.date.format.parser.PureDateParser;
|
||||
import cn.hutool.core.date.format.parser.TimeParser;
|
||||
import cn.hutool.core.date.format.parser.UTCDateParser;
|
||||
import cn.hutool.core.date.format.parser.*;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.math.NumberUtil;
|
||||
import cn.hutool.core.regex.PatternPool;
|
||||
@ -26,14 +21,7 @@ import java.time.LocalDateTime;
|
||||
import java.time.Year;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
@ -513,21 +501,7 @@ public class DateUtil extends CalendarUtil {
|
||||
return yearAndQuarter(calendar(date));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定日期区间内的年份和季节<br>
|
||||
*
|
||||
* @param startDate 起始日期(包含)
|
||||
* @param endDate 结束日期(包含)
|
||||
* @return 季度列表 ,元素类似于 20132
|
||||
*/
|
||||
public static LinkedHashSet<String> yearAndQuarter(final Date startDate, final Date endDate) {
|
||||
if (startDate == null || endDate == null) {
|
||||
return new LinkedHashSet<>(0);
|
||||
}
|
||||
return yearAndQuarter(startDate.getTime(), endDate.getTime());
|
||||
}
|
||||
// ------------------------------------ Format start ----------------------------------------------
|
||||
|
||||
// region ----- format
|
||||
/**
|
||||
* 格式化日期时间<br>
|
||||
* 格式 yyyy-MM-dd HH:mm:ss
|
||||
@ -696,10 +670,9 @@ public class DateUtil extends CalendarUtil {
|
||||
|
||||
return CalendarUtil.formatChineseDate(CalendarUtil.calendar(date), withTime);
|
||||
}
|
||||
// ------------------------------------ Format end ----------------------------------------------
|
||||
|
||||
// ------------------------------------ Parse start ----------------------------------------------
|
||||
// endregion
|
||||
|
||||
// region ----- parse
|
||||
/**
|
||||
* 构建DateTime对象
|
||||
*
|
||||
@ -840,8 +813,8 @@ public class DateUtil extends CalendarUtil {
|
||||
// Wed Aug 01 00:00:00 CST 2012
|
||||
return CSTDateParser.INSTANCE.parse(dateStr);
|
||||
} else if (StrUtil.contains(dateStr, 'T')) {
|
||||
// UTC时间
|
||||
return UTCDateParser.INSTANCE.parse(dateStr);
|
||||
// ISO8601标准时间
|
||||
return ISO8601DateParser.INSTANCE.parse(dateStr);
|
||||
}
|
||||
|
||||
//标准日期格式(包括单个数字的日期时间)
|
||||
@ -853,10 +826,9 @@ public class DateUtil extends CalendarUtil {
|
||||
// 没有更多匹配的时间格式
|
||||
throw new DateException("No format fit for date String [{}] !", dateStr);
|
||||
}
|
||||
// ------------------------------------ Parse end ----------------------------------------------
|
||||
|
||||
// ------------------------------------ Offset start ----------------------------------------------
|
||||
// endregion
|
||||
|
||||
// region ----- offset
|
||||
/**
|
||||
* 修改日期为某个时间字段起始时间
|
||||
*
|
||||
@ -1096,7 +1068,6 @@ public class DateUtil extends CalendarUtil {
|
||||
public static DateTime endOfYear(final Date date) {
|
||||
return new DateTime(endOfYear(calendar(date)));
|
||||
}
|
||||
// --------------------------------------------------- Offset for now
|
||||
|
||||
/**
|
||||
* 昨天
|
||||
@ -1243,9 +1214,9 @@ public class DateUtil extends CalendarUtil {
|
||||
public static DateTime offset(final Date date, final DateField dateField, final int offset) {
|
||||
return dateNew(date).offset(dateField, offset);
|
||||
}
|
||||
// endregion
|
||||
|
||||
// ------------------------------------ Offset end ----------------------------------------------
|
||||
|
||||
// region ----- between
|
||||
/**
|
||||
* 判断两个日期相差的时长,只保留绝对值
|
||||
*
|
||||
@ -1351,7 +1322,9 @@ public class DateUtil extends CalendarUtil {
|
||||
public static long betweenYear(final Date beginDate, final Date endDate, final boolean isReset) {
|
||||
return new DateBetween(beginDate, endDate).betweenYear(isReset);
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region ----- formatBetween
|
||||
/**
|
||||
* 格式化日期间隔输出
|
||||
*
|
||||
@ -1397,6 +1370,7 @@ public class DateUtil extends CalendarUtil {
|
||||
public static String formatBetween(final long betweenMs) {
|
||||
return new BetweenFormatter(betweenMs, BetweenFormatter.Level.MILLISECOND).format();
|
||||
}
|
||||
// endregion
|
||||
|
||||
/**
|
||||
* 当前日期是否在日期指定范围内<br>
|
||||
@ -1992,7 +1966,7 @@ public class DateUtil extends CalendarUtil {
|
||||
* @return 单位简写名称
|
||||
* @since 5.7.16
|
||||
*/
|
||||
public static String getShotName(final TimeUnit unit) {
|
||||
public static String getShortName(final TimeUnit unit) {
|
||||
switch (unit) {
|
||||
case NANOSECONDS:
|
||||
return "ns";
|
||||
|
@ -352,7 +352,7 @@ public class StopWatch {
|
||||
unit = TimeUnit.NANOSECONDS;
|
||||
}
|
||||
return StrUtil.format("StopWatch '{}': running time = {} {}",
|
||||
this.id, getTotal(unit), DateUtil.getShotName(unit));
|
||||
this.id, getTotal(unit), DateUtil.getShortName(unit));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -382,7 +382,7 @@ public class StopWatch {
|
||||
sb.append("No task info kept");
|
||||
} else {
|
||||
sb.append("---------------------------------------------").append(FileUtil.getLineSeparator());
|
||||
sb.append(DateUtil.getShotName(unit)).append(" % Task name").append(FileUtil.getLineSeparator());
|
||||
sb.append(DateUtil.getShortName(unit)).append(" % Task name").append(FileUtil.getLineSeparator());
|
||||
sb.append("---------------------------------------------").append(FileUtil.getLineSeparator());
|
||||
|
||||
final NumberFormat nf = NumberFormat.getNumberInstance();
|
||||
|
@ -23,11 +23,6 @@ import java.util.function.Function;
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class TimeUtil extends TemporalAccessorUtil {
|
||||
/**
|
||||
* UTC 的 ZoneID
|
||||
*/
|
||||
public static final ZoneId ZONE_ID_UTC = ZoneId.of("UTC");
|
||||
|
||||
/**
|
||||
* 当前时间,默认时区
|
||||
*
|
||||
@ -53,7 +48,7 @@ public class TimeUtil extends TemporalAccessorUtil {
|
||||
* @return {@link LocalDateTime}
|
||||
*/
|
||||
public static LocalDateTime ofUTC(final Instant instant) {
|
||||
return of(instant, ZONE_ID_UTC);
|
||||
return of(instant, ZoneUtil.ZONE_ID_UTC);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -222,7 +217,7 @@ public class TimeUtil extends TemporalAccessorUtil {
|
||||
|
||||
/**
|
||||
* 解析日期时间字符串为{@link LocalDateTime},格式支持日期时间、日期、时间<br>
|
||||
* 如果formatter为{code null},则使用{@link DateTimeFormatter#ISO_LOCAL_DATE_TIME}
|
||||
* 如果formatter为{@code null},则使用{@link DateTimeFormatter#ISO_LOCAL_DATE_TIME}
|
||||
*
|
||||
* @param text 日期时间字符串
|
||||
* @param formatter 日期格式化器,预定义的格式见:{@link DateTimeFormatter}
|
||||
|
@ -11,6 +11,15 @@ import java.util.TimeZone;
|
||||
*/
|
||||
public class ZoneUtil {
|
||||
|
||||
/**
|
||||
* UTC 的 ZoneID
|
||||
*/
|
||||
public static final TimeZone ZONE_UTC = TimeZone.getTimeZone("UTC");
|
||||
/**
|
||||
* UTC 的 TimeZone
|
||||
*/
|
||||
public static final ZoneId ZONE_ID_UTC = ZONE_UTC.toZoneId();
|
||||
|
||||
/**
|
||||
* {@link ZoneId}转换为{@link TimeZone},{@code null}则返回系统默认值
|
||||
*
|
||||
|
@ -9,7 +9,7 @@ import cn.hutool.core.text.StrUtil;
|
||||
import cn.hutool.core.util.CharUtil;
|
||||
|
||||
/**
|
||||
* UTC日期字符串(JDK的Date对象toString默认格式)解析,支持格式;
|
||||
* ISO8601日期字符串(JDK的Date对象toString默认格式)解析,支持格式;
|
||||
* <ol>
|
||||
* <li>yyyy-MM-dd'T'HH:mm:ss'Z'</li>
|
||||
* <li>yyyy-MM-dd'T'HH:mm:ss.SSS'Z'</li>
|
||||
@ -22,13 +22,13 @@ import cn.hutool.core.util.CharUtil;
|
||||
* @author looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class UTCDateParser extends DefaultDateBasic implements DateParser {
|
||||
public class ISO8601DateParser extends DefaultDateBasic implements DateParser {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 单例对象
|
||||
*/
|
||||
public static UTCDateParser INSTANCE = new UTCDateParser();
|
||||
public static ISO8601DateParser INSTANCE = new ISO8601DateParser();
|
||||
|
||||
@Override
|
||||
public DateTime parse(String source) {
|
||||
@ -61,10 +61,10 @@ public class UTCDateParser extends DefaultDateBasic implements DateParser {
|
||||
if (StrUtil.contains(source, CharUtil.DOT)) {
|
||||
// 带毫秒,格式类似:2018-09-13T05:34:31.999+08:00
|
||||
source = normalizeMillSeconds(source, ".", "+");
|
||||
return new DateTime(source, DatePattern.UTC_MS_WITH_XXX_OFFSET_FORMAT);
|
||||
return new DateTime(source, DatePattern.ISO8601_MS_WITH_XXX_OFFSET_FORMAT);
|
||||
} else {
|
||||
// 格式类似:2018-09-13T05:34:31+08:00
|
||||
return new DateTime(source, DatePattern.UTC_WITH_XXX_OFFSET_FORMAT);
|
||||
return new DateTime(source, DatePattern.ISO8601_WITH_XXX_OFFSET_FORMAT);
|
||||
}
|
||||
} else if(ReUtil.contains("-\\d{2}:?00", source)){
|
||||
// Issue#2612,类似 2022-09-14T23:59:00-08:00 或者 2022-09-14T23:59:00-0800
|
||||
@ -78,22 +78,22 @@ public class UTCDateParser extends DefaultDateBasic implements DateParser {
|
||||
if (StrUtil.contains(source, CharUtil.DOT)) {
|
||||
// 带毫秒,格式类似:2018-09-13T05:34:31.999-08:00
|
||||
source = normalizeMillSeconds(source, ".", "-");
|
||||
return new DateTime(source, DatePattern.UTC_MS_WITH_XXX_OFFSET_FORMAT);
|
||||
return new DateTime(source, DatePattern.ISO8601_MS_WITH_XXX_OFFSET_FORMAT);
|
||||
} else {
|
||||
// 格式类似:2018-09-13T05:34:31-08:00
|
||||
return new DateTime(source, DatePattern.UTC_WITH_XXX_OFFSET_FORMAT);
|
||||
return new DateTime(source, DatePattern.ISO8601_WITH_XXX_OFFSET_FORMAT);
|
||||
}
|
||||
} else {
|
||||
if (length == DatePattern.UTC_SIMPLE_PATTERN.length() - 2) {
|
||||
if (length == DatePattern.ISO8601_PATTERN.length() - 2) {
|
||||
// 格式类似:2018-09-13T05:34:31
|
||||
return new DateTime(source, DatePattern.UTC_SIMPLE_FORMAT);
|
||||
} else if (length == DatePattern.UTC_SIMPLE_PATTERN.length() - 5) {
|
||||
return new DateTime(source, DatePattern.ISO8601_FORMAT);
|
||||
} else if (length == DatePattern.ISO8601_PATTERN.length() - 5) {
|
||||
// 格式类似:2018-09-13T05:34
|
||||
return new DateTime(source + ":00", DatePattern.UTC_SIMPLE_FORMAT);
|
||||
return new DateTime(source + ":00", DatePattern.ISO8601_FORMAT);
|
||||
} else if (StrUtil.contains(source, CharUtil.DOT)) {
|
||||
// 可能为: 2021-03-17T06:31:33.99
|
||||
source = normalizeMillSeconds(source, ".", null);
|
||||
return new DateTime(source, DatePattern.UTC_SIMPLE_MS_FORMAT);
|
||||
return new DateTime(source, DatePattern.ISO8601_MS_FORMAT);
|
||||
}
|
||||
}
|
||||
// 没有更多匹配的时间格式
|
@ -1,5 +1,7 @@
|
||||
package cn.hutool.core.lang;
|
||||
|
||||
import cn.hutool.core.text.StrUtil;
|
||||
|
||||
/**
|
||||
* 片段默认实现
|
||||
*
|
||||
@ -9,26 +11,32 @@ package cn.hutool.core.lang;
|
||||
*/
|
||||
public class DefaultSegment<T extends Number> implements Segment<T> {
|
||||
|
||||
protected T startIndex;
|
||||
protected T beginIndex;
|
||||
protected T endIndex;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
* @param startIndex 起始位置
|
||||
* @param endIndex 结束位置
|
||||
*
|
||||
* @param beginIndex 起始位置
|
||||
* @param endIndex 结束位置
|
||||
*/
|
||||
public DefaultSegment(final T startIndex, final T endIndex) {
|
||||
this.startIndex = startIndex;
|
||||
public DefaultSegment(final T beginIndex, final T endIndex) {
|
||||
this.beginIndex = beginIndex;
|
||||
this.endIndex = endIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getStartIndex() {
|
||||
return this.startIndex;
|
||||
public T getBeginIndex() {
|
||||
return this.beginIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getEndIndex() {
|
||||
return this.endIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return StrUtil.format("[{}, {}]", beginIndex, endIndex);
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ public interface Segment<T extends Number> {
|
||||
*
|
||||
* @return 起始位置
|
||||
*/
|
||||
T getStartIndex();
|
||||
T getBeginIndex();
|
||||
|
||||
/**
|
||||
* 获取结束位置
|
||||
@ -34,7 +34,7 @@ public interface Segment<T extends Number> {
|
||||
* @return 片段长度
|
||||
*/
|
||||
default T length(){
|
||||
final T start = Assert.notNull(getStartIndex(), "Start index must be not null!");
|
||||
final T start = Assert.notNull(getBeginIndex(), "Start index must be not null!");
|
||||
final T end = Assert.notNull(getEndIndex(), "End index must be not null!");
|
||||
return Convert.convert((Type) start.getClass(), NumberUtil.sub(end, start).abs());
|
||||
}
|
||||
|
@ -538,7 +538,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
|
||||
* 从当前节点开始,按广度优先向下遍历当前节点的所有子节点
|
||||
*
|
||||
* @param includeCurrent 是否包含当前节点
|
||||
* @param consumer 对节点与节点和当前节点的距离的操作,当{code includeCurrent}为false时下标从1开始,否则从0开始
|
||||
* @param consumer 对节点与节点和当前节点的距离的操作,当{@code includeCurrent}为false时下标从1开始,否则从0开始
|
||||
* @param breakTraverse 是否终止遍历,为null时默认总是返回{@code true}
|
||||
* @return 遍历到的最后一个节点
|
||||
*/
|
||||
|
@ -0,0 +1,71 @@
|
||||
package cn.hutool.core.math;
|
||||
|
||||
public class NavigatePageInfo extends PageInfo{
|
||||
|
||||
private final int navigatePages = 8; //导航页码数
|
||||
private int[] navigatePageNumbers; //所有导航页号
|
||||
|
||||
public NavigatePageInfo(final int total, final int pageSize) {
|
||||
super(total, pageSize);
|
||||
|
||||
//基本参数设定之后进行导航页面的计算
|
||||
calcNavigatePageNumbers();
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到所有导航页号
|
||||
*
|
||||
* @return {int[]}
|
||||
*/
|
||||
public int[] getNavigatePageNumbers() {
|
||||
return navigatePageNumbers;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
final StringBuilder str = new StringBuilder(super.toString());
|
||||
str.append(", {navigatePageNumbers=");
|
||||
final int len = navigatePageNumbers.length;
|
||||
if (len > 0) str.append(navigatePageNumbers[0]);
|
||||
for (int i = 1; i < len; i++) {
|
||||
str.append(" ").append(navigatePageNumbers[i]);
|
||||
}
|
||||
str.append("}");
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算导航页
|
||||
*/
|
||||
private void calcNavigatePageNumbers() {
|
||||
//当总页数小于或等于导航页码数时
|
||||
if (pages <= navigatePages) {
|
||||
navigatePageNumbers = new int[pages];
|
||||
for (int i = 0; i < pages; i++) {
|
||||
navigatePageNumbers[i] = i + 1;
|
||||
}
|
||||
} else { //当总页数大于导航页码数时
|
||||
navigatePageNumbers = new int[navigatePages];
|
||||
int startNum = pageNo - navigatePages / 2;
|
||||
int endNum = pageNo + navigatePages / 2;
|
||||
|
||||
if (startNum < 1) {
|
||||
startNum = 1;
|
||||
//(最前navPageCount页
|
||||
for (int i = 0; i < navigatePages; i++) {
|
||||
navigatePageNumbers[i] = startNum++;
|
||||
}
|
||||
} else if (endNum > pages) {
|
||||
endNum = pages;
|
||||
//最后navPageCount页
|
||||
for (int i = navigatePages - 1; i >= 0; i--) {
|
||||
navigatePageNumbers[i] = endNum--;
|
||||
}
|
||||
} else {
|
||||
//所有中间页
|
||||
for (int i = 0; i < navigatePages; i++) {
|
||||
navigatePageNumbers[i] = startNum++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
291
hutool-core/src/main/java/cn/hutool/core/math/PageInfo.java
Normal file
291
hutool-core/src/main/java/cn/hutool/core/math/PageInfo.java
Normal file
@ -0,0 +1,291 @@
|
||||
package cn.hutool.core.math;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.DefaultSegment;
|
||||
import cn.hutool.core.lang.Segment;
|
||||
|
||||
/**
|
||||
* 分页信息,通过提供的总数、页码、每页记录数等信息,计算总页数等信息<br>
|
||||
* 来自:https://bbs.csdn.net/topics/360010907
|
||||
*
|
||||
* @author 莫取网名
|
||||
*/
|
||||
public class PageInfo {
|
||||
|
||||
private static final int DEFAULT_PAGE_SIZE = 10;
|
||||
|
||||
/**
|
||||
* 创建{@code PageInfo},默认当前页是1
|
||||
*
|
||||
* @param total 总记录数
|
||||
* @param pageSize 每页显示记录数
|
||||
* @return {@code PageInfo}
|
||||
*/
|
||||
public static PageInfo of(final int total, final int pageSize) {
|
||||
return new PageInfo(total, pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 总记录数
|
||||
*/
|
||||
int total;
|
||||
/**
|
||||
* 每页显示记录数
|
||||
*/
|
||||
int pageSize;
|
||||
/**
|
||||
* 总页数
|
||||
*/
|
||||
int pages;
|
||||
/**
|
||||
* 首页标识
|
||||
*/
|
||||
int firstPageNo = 1;
|
||||
/**
|
||||
* 当前页
|
||||
*/
|
||||
int pageNo = firstPageNo;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param total 总记录数
|
||||
* @param pageSize 每页显示记录数
|
||||
*/
|
||||
public PageInfo(final int total, final int pageSize) {
|
||||
init(total, pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @param total 总记录数
|
||||
* @param pageSize 每页显示记录数
|
||||
*/
|
||||
private void init(final int total, int pageSize) {
|
||||
Assert.isTrue(total >= 0, "Total must >= 0");
|
||||
|
||||
//设置基本参数
|
||||
this.total = total;
|
||||
|
||||
if (pageSize < 1) {
|
||||
pageSize = DEFAULT_PAGE_SIZE;
|
||||
}
|
||||
this.pageSize = pageSize;
|
||||
// 因为总条数除以页大小的最大余数是页大小数-1,
|
||||
// 因此加一个最大余数,保证舍弃的余数与最大余数凑1.x,就是一旦有余数则+1页
|
||||
this.pages = (total + pageSize - 1) / pageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到记录总数
|
||||
*
|
||||
* @return {int}
|
||||
*/
|
||||
public int getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到每页显示多少条记录
|
||||
*
|
||||
* @return {int}
|
||||
*/
|
||||
public int getPageSize() {
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到页面总数
|
||||
*
|
||||
* @return {int}
|
||||
*/
|
||||
public int getPages() {
|
||||
return pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到当前页号
|
||||
*
|
||||
* @return {int}
|
||||
*/
|
||||
public int getPageNo() {
|
||||
return pageNo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 是否首页
|
||||
*
|
||||
* @return 是否首页
|
||||
*/
|
||||
public boolean isFirstPage() {
|
||||
return getPageIndexBase1() == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否尾页
|
||||
*
|
||||
* @return 是否尾页
|
||||
*/
|
||||
public boolean isLastPage() {
|
||||
return getPageIndexBase1() == this.pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有前一页
|
||||
*
|
||||
* @return 是否有前一页
|
||||
*/
|
||||
public boolean hasPreviousPage() {
|
||||
return getPageIndexBase1() > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有下一页
|
||||
*
|
||||
* @return 是否有下一页
|
||||
*/
|
||||
public boolean hasNextPage() {
|
||||
return getBeginIndex() < this.pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前页是否可用(是否大于firstPageNum且小于总页数)
|
||||
* @return 是否可用
|
||||
*/
|
||||
public boolean isValidPage(){
|
||||
return this.getPageIndexBase1() <= this.pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前页的开始记录index(包含)
|
||||
*
|
||||
* @return 开始记录index(包含)
|
||||
*/
|
||||
public int getBeginIndex() {
|
||||
return (getPageIndexBase1() -1) * this.pageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前页的结束记录index(不包含)
|
||||
* <ul>
|
||||
* <li>当开始index超出total时,返回正常值</li>
|
||||
* <li>当开始index未超出total但是计算的end超出时,返回total + 1</li>
|
||||
* <li>当开始index和end都未超出时,返回正常值</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return 结束记录index(不包含)
|
||||
*/
|
||||
public int getEndIndexExclude() {
|
||||
return getEndIndex() + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前页的结束记录index(包含)
|
||||
* <ul>
|
||||
* <li>当开始index超出total时,返回正常值</li>
|
||||
* <li>当开始index未超出total但是计算的end超出时,返回total</li>
|
||||
* <li>当开始index和end都未超出时,返回正常值</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return 结束记录index(包含)
|
||||
*/
|
||||
public int getEndIndex() {
|
||||
final int begin = getBeginIndex();
|
||||
int end = begin + this.pageSize - 1;
|
||||
if (begin <= this.total && end > this.total) {
|
||||
end = this.total;
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将页数和每页条目数转换为开始位置和结束位置<br>
|
||||
* 此方法用于包括结束位置的分页方法<br>
|
||||
* 例如:
|
||||
* <pre>
|
||||
* 页码:1,每页10 =》 [0, 9]
|
||||
* 页码:2,每页10 =》 [10, 19]
|
||||
* ……
|
||||
* </pre>
|
||||
*
|
||||
* @return {@link Segment}
|
||||
*/
|
||||
public Segment<Integer> getSegment() {
|
||||
return new DefaultSegment<>(getBeginIndex(), getEndIndex());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设置首页编号,即以数字几为第一页标志
|
||||
*
|
||||
* @return 设置首页编号
|
||||
*/
|
||||
public int getFirstPageNo() {
|
||||
return this.firstPageNo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置首页编号,即以数字几为第一页标志<br>
|
||||
* 如设置0,则0表示第一页,1表示第二页
|
||||
*
|
||||
* @param firstPageNo 首页编号
|
||||
* @return this
|
||||
*/
|
||||
public PageInfo setFirstPageNo(final int firstPageNo) {
|
||||
this.firstPageNo = firstPageNo;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前页码
|
||||
*
|
||||
* @param pageNo 当前页码
|
||||
* @return this
|
||||
*/
|
||||
public PageInfo setPageNo(final int pageNo) {
|
||||
//根据输入可能错误的当前号码进行自动纠正
|
||||
// 不判断后边界,因为当页码超出边界,应该返回一个空区间的数据,而非始终保持在最后一页
|
||||
this.pageNo = Math.max(pageNo, firstPageNo);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下一页,即当前页码+1
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public PageInfo nextPage() {
|
||||
return setPageNo(this.pageNo + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上一页,即当前页码-1,直到第一页则始终为第一页
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public PageInfo PreviousPage() {
|
||||
return setPageNo(this.pageNo - 1);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "{" +
|
||||
"total=" + total +
|
||||
",pages=" + pages +
|
||||
",pageNumber=" + pageNo +
|
||||
",limit=" + pageSize +
|
||||
",isFirstPage=" + isFirstPage() +
|
||||
",isLastPage=" + isLastPage() +
|
||||
",hasPreviousPage=" + hasPreviousPage() +
|
||||
",hasNextPage=" + hasNextPage() +
|
||||
"}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取页码序号,第一个序号就是1
|
||||
* @return 页码序号
|
||||
*/
|
||||
private int getPageIndexBase1(){
|
||||
return this.pageNo - this.firstPageNo + 1;
|
||||
}
|
||||
}
|
@ -121,7 +121,6 @@ public class MethodHandleUtil {
|
||||
* return "Quack";
|
||||
* }
|
||||
* }
|
||||
* <p>
|
||||
* Duck duck = (Duck) Proxy.newProxyInstance(
|
||||
* ClassLoaderUtil.getClassLoader(),
|
||||
* new Class[] { Duck.class },
|
||||
@ -167,7 +166,6 @@ public class MethodHandleUtil {
|
||||
* return "Quack";
|
||||
* }
|
||||
* }
|
||||
* <p>
|
||||
* Duck duck = (Duck) Proxy.newProxyInstance(
|
||||
* ClassLoaderUtil.getClassLoader(),
|
||||
* new Class[] { Duck.class },
|
||||
@ -193,7 +191,6 @@ public class MethodHandleUtil {
|
||||
* return "Quack";
|
||||
* }
|
||||
* }
|
||||
* <p>
|
||||
* Duck duck = (Duck) Proxy.newProxyInstance(
|
||||
* ClassLoaderUtil.getClassLoader(),
|
||||
* new Class[] { Duck.class },
|
||||
|
@ -1,12 +1,12 @@
|
||||
package cn.hutool.core.reflect;
|
||||
|
||||
import cn.hutool.core.text.StrUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.text.StrUtil;
|
||||
|
||||
/**
|
||||
* {@link ParameterizedType} 接口实现,用于重新定义泛型类型
|
||||
*
|
||||
@ -70,13 +70,14 @@ public class ParameterizedTypeImpl implements ParameterizedType, Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加 {@code types} 到 @{code buf},使用 {@code sep} 分隔
|
||||
* 追加 {@code types} 到 {@code buf},使用 {@code sep} 分隔
|
||||
*
|
||||
* @param buf 目标
|
||||
* @param sep 分隔符
|
||||
* @param types 加入的类型
|
||||
* @return {@code buf}
|
||||
*/
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private static StringBuilder appendAllTo(final StringBuilder buf, final String sep, final Type... types) {
|
||||
if (ArrayUtil.isNotEmpty(types)) {
|
||||
boolean isFirst = true;
|
||||
|
@ -197,7 +197,7 @@ public class ReUtil {
|
||||
* @return 匹配后得到的字符串数组,按照分组顺序依次列出,未匹配到返回空列表,任何一个参数为null返回null
|
||||
* @since 4.0.13
|
||||
*/
|
||||
public static List<String> getAllGroups(Pattern pattern, CharSequence content, boolean withGroup0) {
|
||||
public static List<String> getAllGroups(final Pattern pattern, final CharSequence content, final boolean withGroup0) {
|
||||
return getAllGroups(pattern, content, withGroup0, false);
|
||||
}
|
||||
|
||||
@ -211,12 +211,12 @@ public class ReUtil {
|
||||
* @return 匹配后得到的字符串数组,按照分组顺序依次列出,未匹配到返回空列表,任何一个参数为null返回null
|
||||
* @since 4.0.13
|
||||
*/
|
||||
public static List<String> getAllGroups(Pattern pattern, CharSequence content, boolean withGroup0, boolean findAll) {
|
||||
public static List<String> getAllGroups(final Pattern pattern, final CharSequence content, final boolean withGroup0, final boolean findAll) {
|
||||
if (null == content || null == pattern) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ArrayList<String> result = new ArrayList<>();
|
||||
final ArrayList<String> result = new ArrayList<>();
|
||||
final Matcher matcher = pattern.matcher(content);
|
||||
while (matcher.find()) {
|
||||
final int startGroup = withGroup0 ? 0 : 1;
|
||||
@ -893,7 +893,6 @@ public class ReUtil {
|
||||
/**
|
||||
* 替换所有正则匹配的文本,并使用自定义函数决定如何替换<br>
|
||||
* replaceFun可以通过{@link Matcher}提取出匹配到的内容的不同部分,然后经过重新处理、组装变成新的内容放回原位。
|
||||
* <p>
|
||||
* <pre class="code">
|
||||
* replaceAll(this.content, "(\\d+)", parameters -> "-" + parameters.group(1) + "-")
|
||||
* // 结果为:"ZZZaaabbbccc中文-1234-"
|
||||
@ -912,7 +911,6 @@ public class ReUtil {
|
||||
/**
|
||||
* 替换所有正则匹配的文本,并使用自定义函数决定如何替换<br>
|
||||
* replaceFun可以通过{@link Matcher}提取出匹配到的内容的不同部分,然后经过重新处理、组装变成新的内容放回原位。
|
||||
* <p>
|
||||
* <pre class="code">
|
||||
* replaceAll(this.content, "(\\d+)", parameters -> "-" + parameters.group(1) + "-")
|
||||
* // 结果为:"ZZZaaabbbccc中文-1234-"
|
||||
|
145
hutool-core/src/main/java/cn/hutool/core/text/dfa/NFA.java
Normal file
145
hutool-core/src/main/java/cn/hutool/core/text/dfa/NFA.java
Normal file
@ -0,0 +1,145 @@
|
||||
package cn.hutool.core.text.dfa;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
*
|
||||
* 基于非确定性有穷自动机(NFA) 实现的多模匹配工具
|
||||
*
|
||||
* @author renyp
|
||||
*/
|
||||
public class NFA {
|
||||
private final Node root;
|
||||
|
||||
/**
|
||||
* 默认构造
|
||||
*/
|
||||
public NFA() {
|
||||
this.root = new Node();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造函数 并 初始化词库
|
||||
*
|
||||
* @param words 添加的新词
|
||||
*/
|
||||
public NFA(final String... words) {
|
||||
this();
|
||||
this.insert(words);
|
||||
}
|
||||
|
||||
/**
|
||||
* 词库添加新词,初始化查找树
|
||||
*
|
||||
* @param word 添加的新词
|
||||
*/
|
||||
public void insert(final String word) {
|
||||
Node p = root;
|
||||
for (final char curr : word.toCharArray()) {
|
||||
if (p.next.get((int) curr) == null) {
|
||||
p.next.put((int) curr, new Node());
|
||||
}
|
||||
p = p.next.get((int) curr);
|
||||
}
|
||||
p.flag = true;
|
||||
p.str = word;
|
||||
}
|
||||
|
||||
/**
|
||||
* 词库批量添加新词,初始化查找树
|
||||
*
|
||||
* @param words 添加的新词
|
||||
*/
|
||||
public void insert(final String... words) {
|
||||
for (final String word : words) {
|
||||
this.insert(word);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建基于NFA模型的 AC自动机
|
||||
*/
|
||||
public void buildAc() {
|
||||
final Queue<Node> queue = new LinkedList<>();
|
||||
final Node p = root;
|
||||
for (final Integer key : p.next.keySet()) {
|
||||
p.next.get(key).fail = root;
|
||||
queue.offer(p.next.get(key));
|
||||
}
|
||||
while (!queue.isEmpty()) {
|
||||
final Node curr = queue.poll();
|
||||
for (final Integer key : curr.next.keySet()) {
|
||||
Node fail = curr.fail;
|
||||
// 查找当前节点匹配失败,他对应等效匹配的节点是哪个
|
||||
while (fail != null && fail.next.get(key) == null) {
|
||||
fail = fail.fail;
|
||||
}
|
||||
// 代码到这,有两种可能,fail不为null,说明找到了fail;fail为null,没有找到,那么就把fail指向root节点(当到该节点匹配失败,那么从root节点开始重新匹配)
|
||||
if (fail != null) {
|
||||
fail = fail.next.get(key);
|
||||
} else {
|
||||
fail = root;
|
||||
}
|
||||
curr.next.get(key).fail = fail;
|
||||
queue.offer(curr.next.get(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param text 查询的文本(母串)
|
||||
* @return 关键字列表
|
||||
*/
|
||||
public List<FoundWord> find(final String text) {
|
||||
return this.find(text, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param text 查找的文本(母串)
|
||||
* @param isDensityMatch 是否密集匹配
|
||||
* @return 关键字列表
|
||||
*/
|
||||
public List<FoundWord> find(final String text, final boolean isDensityMatch) {
|
||||
final List<FoundWord> ans = new ArrayList<>();
|
||||
Node p = root, k;
|
||||
for (int i = 0, len = text.length(); i < len; i++) {
|
||||
final int ind = text.charAt(i);
|
||||
// 状态转移(沿着fail指针链接的链表,此处区别于DFA模型)
|
||||
while (p != null && p.next.get(ind) == null) {
|
||||
p = p.fail;
|
||||
}
|
||||
if (p == null) {
|
||||
p = root;
|
||||
} else {
|
||||
p = p.next.get(ind);
|
||||
}
|
||||
// 提取结果(沿着fail指针链接的链表,此处区别于DFA模型)
|
||||
k = p;
|
||||
while (k != null) {
|
||||
if (k.flag) {
|
||||
ans.add(new FoundWord(k.str, k.str, i - k.str.length() + 1, i));
|
||||
if (!isDensityMatch) {
|
||||
p = root;
|
||||
break;
|
||||
}
|
||||
}
|
||||
k = k.fail;
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
private static class Node {
|
||||
|
||||
boolean flag;
|
||||
Node fail;
|
||||
String str;
|
||||
Map<Integer, Node> next;
|
||||
|
||||
public Node() {
|
||||
this.flag = false;
|
||||
next = new HashMap<>();
|
||||
}
|
||||
}
|
||||
}
|
@ -171,7 +171,7 @@ public final class SensitiveUtil {
|
||||
} : sensitiveProcessor;
|
||||
|
||||
final Map<Integer, FoundWord> foundWordMap = new HashMap<>(foundWordList.size(), 1);
|
||||
foundWordList.forEach(foundWord -> foundWordMap.put(foundWord.getStartIndex(), foundWord));
|
||||
foundWordList.forEach(foundWord -> foundWordMap.put(foundWord.getBeginIndex(), foundWord));
|
||||
final int length = text.length();
|
||||
final StringBuilder textStringBuilder = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cn.hutool.core.text.split;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.regex.PatternPool;
|
||||
import cn.hutool.core.text.StrUtil;
|
||||
import cn.hutool.core.text.finder.CharFinder;
|
||||
@ -302,7 +303,8 @@ public class SplitUtil {
|
||||
|
||||
/**
|
||||
* 切分字符串<br>
|
||||
* 如果为空字符串或者null 则返回空集合
|
||||
* 如果提供的字符串为{@code null},则返回一个空的{@link ArrayList}<br>
|
||||
* 如果提供的字符串为"",则当ignoreEmpty时返回空的{@link ArrayList},否则返回只有一个""元素的{@link ArrayList}
|
||||
*
|
||||
* @param text 被切分的字符串
|
||||
* @param separator 分隔符字符串
|
||||
@ -314,8 +316,10 @@ public class SplitUtil {
|
||||
* @since 3.2.1
|
||||
*/
|
||||
public static List<String> split(final CharSequence text, final String separator, final int limit, final boolean isTrim, final boolean ignoreEmpty, final boolean ignoreCase) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
if(null == text){
|
||||
return new ArrayList<>(0);
|
||||
} else if (0 == text.length()) {
|
||||
return ignoreEmpty ? new ArrayList<>(0) : ListUtil.of(StrUtil.EMPTY);
|
||||
}
|
||||
final SplitIter splitIter = new SplitIter(text, new StrFinder(separator, ignoreCase), limit, ignoreEmpty);
|
||||
return splitIter.toList(isTrim);
|
||||
|
@ -140,7 +140,7 @@ public class ThreadUtil {
|
||||
* <pre>
|
||||
* 1. 核心线程数与最大线程数为nThreads指定的大小
|
||||
* 2. 默认使用LinkedBlockingQueue,默认队列大小为1024
|
||||
* 3. 如果isBlocked为{code true},当执行拒绝策略的时候会处于阻塞状态,直到能添加到队列中或者被{@link Thread#interrupt()}中断
|
||||
* 3. 如果isBlocked为{@code true},当执行拒绝策略的时候会处于阻塞状态,直到能添加到队列中或者被{@link Thread#interrupt()}中断
|
||||
* </pre>
|
||||
*
|
||||
* @param nThreads 线程池大小
|
||||
@ -159,7 +159,7 @@ public class ThreadUtil {
|
||||
* <pre>
|
||||
* 1. 核心线程数与最大线程数为nThreads指定的大小
|
||||
* 2. 默认使用LinkedBlockingQueue
|
||||
* 3. 如果isBlocked为{code true},当执行拒绝策略的时候会处于阻塞状态,直到能添加到队列中或者被{@link Thread#interrupt()}中断
|
||||
* 3. 如果isBlocked为{@code true},当执行拒绝策略的时候会处于阻塞状态,直到能添加到队列中或者被{@link Thread#interrupt()}中断
|
||||
* </pre>
|
||||
*
|
||||
* @param nThreads 线程池大小
|
||||
|
@ -28,7 +28,7 @@ public class JNDIUtil {
|
||||
/**
|
||||
* 创建{@link InitialDirContext}
|
||||
*
|
||||
* @param environment 环境参数,{code null}表示无参数
|
||||
* @param environment 环境参数,{@code null}表示无参数
|
||||
* @return {@link InitialDirContext}
|
||||
*/
|
||||
public static InitialDirContext createInitialDirContext(final Map<String, String> environment) {
|
||||
@ -45,7 +45,7 @@ public class JNDIUtil {
|
||||
/**
|
||||
* 创建{@link InitialContext}
|
||||
*
|
||||
* @param environment 环境参数,{code null}表示无参数
|
||||
* @param environment 环境参数,{@code null}表示无参数
|
||||
* @return {@link InitialContext}
|
||||
*/
|
||||
public static InitialContext createInitialContext(final Map<String, String> environment) {
|
||||
|
@ -1,273 +0,0 @@
|
||||
package cn.hutool.core.util;
|
||||
|
||||
import cn.hutool.core.lang.DefaultSegment;
|
||||
import cn.hutool.core.lang.Segment;
|
||||
|
||||
/**
|
||||
* 分页工具类
|
||||
*
|
||||
* @author xiaoleilu
|
||||
*/
|
||||
public class PageUtil {
|
||||
|
||||
private static int firstPageNo = 0;
|
||||
|
||||
/**
|
||||
* 获得首页的页码,可以为0或者1
|
||||
*
|
||||
* @return 首页页码
|
||||
*/
|
||||
public static int getFirstPageNo() {
|
||||
return firstPageNo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置首页页码,可以为0或者1
|
||||
*
|
||||
* <pre>
|
||||
* 当设置为0时,页码0表示第一页,开始位置为0
|
||||
* 当设置为1时,页码1表示第一页,开始位置为0
|
||||
* </pre>
|
||||
*
|
||||
* @param customFirstPageNo 自定义的首页页码,为0或者1
|
||||
*/
|
||||
synchronized public static void setFirstPageNo(final int customFirstPageNo) {
|
||||
firstPageNo = customFirstPageNo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置首页页码为1
|
||||
*
|
||||
* <pre>
|
||||
* 当设置为1时,页码1表示第一页,开始位置为0
|
||||
* </pre>
|
||||
*/
|
||||
public static void setOneAsFirstPageNo() {
|
||||
setFirstPageNo(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将页数和每页条目数转换为开始位置<br>
|
||||
* 此方法用于不包括结束位置的分页方法<br>
|
||||
* 例如:
|
||||
*
|
||||
* <pre>
|
||||
* 页码:0,每页10 =》 0
|
||||
* 页码:1,每页10 =》 10
|
||||
* ……
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* 当{@link #setFirstPageNo(int)}设置为1时:
|
||||
* <pre>
|
||||
* 页码:1,每页10 =》 0
|
||||
* 页码:2,每页10 =》 10
|
||||
* ……
|
||||
* </pre>
|
||||
*
|
||||
* @param pageNo 页码(从0计数)
|
||||
* @param pageSize 每页条目数
|
||||
* @return 开始位置
|
||||
*/
|
||||
public static int getStart(int pageNo, int pageSize) {
|
||||
if (pageNo < firstPageNo) {
|
||||
pageNo = firstPageNo;
|
||||
}
|
||||
|
||||
if (pageSize < 1) {
|
||||
pageSize = 0;
|
||||
}
|
||||
|
||||
return (pageNo - firstPageNo) * pageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将页数和每页条目数转换为结束位置<br>
|
||||
* 此方法用于不包括结束位置的分页方法<br>
|
||||
* 例如:
|
||||
*
|
||||
* <pre>
|
||||
* 页码:0,每页10 =》 9
|
||||
* 页码:1,每页10 =》 19
|
||||
* ……
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* 当{@link #setFirstPageNo(int)}设置为1时:
|
||||
* <pre>
|
||||
* 页码:1,每页10 =》 9
|
||||
* 页码:2,每页10 =》 19
|
||||
* ……
|
||||
* </pre>
|
||||
*
|
||||
* @param pageNo 页码(从0计数)
|
||||
* @param pageSize 每页条目数
|
||||
* @return 开始位置
|
||||
* @since 5.2.5
|
||||
*/
|
||||
public static int getEnd(final int pageNo, final int pageSize) {
|
||||
final int start = getStart(pageNo, pageSize);
|
||||
return getEndByStart(start, pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将页数和每页条目数转换为开始位置和结束位置<br>
|
||||
* 此方法用于包括结束位置的分页方法<br>
|
||||
* 例如:
|
||||
*
|
||||
* <pre>
|
||||
* 页码:0,每页10 =》 [0, 10]
|
||||
* 页码:1,每页10 =》 [10, 20]
|
||||
* ……
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* 当{@link #setFirstPageNo(int)}设置为1时:
|
||||
* <pre>
|
||||
* 页码:1,每页10 =》 [0, 10]
|
||||
* 页码:2,每页10 =》 [10, 20]
|
||||
* ……
|
||||
* </pre>
|
||||
*
|
||||
* @param pageNo 页码(从0计数)
|
||||
* @param pageSize 每页条目数
|
||||
* @return 第一个数为开始位置,第二个数为结束位置
|
||||
*/
|
||||
public static int[] transToStartEnd(final int pageNo, final int pageSize) {
|
||||
final int start = getStart(pageNo, pageSize);
|
||||
return new int[]{start, getEndByStart(start, pageSize)};
|
||||
}
|
||||
|
||||
/**
|
||||
* 将页数和每页条目数转换为开始位置和结束位置<br>
|
||||
* 此方法用于包括结束位置的分页方法<br>
|
||||
* 例如:
|
||||
*
|
||||
* <pre>
|
||||
* 页码:0,每页10 =》 [0, 10]
|
||||
* 页码:1,每页10 =》 [10, 20]
|
||||
* ……
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* 当{@link #setFirstPageNo(int)}设置为1时:
|
||||
* <pre>
|
||||
* 页码:1,每页10 =》 [0, 10]
|
||||
* 页码:2,每页10 =》 [10, 20]
|
||||
* ……
|
||||
* </pre>
|
||||
*
|
||||
* @param pageNo 页码(从0计数)
|
||||
* @param pageSize 每页条目数
|
||||
* @return {@link Segment}
|
||||
* @since 5.5.3
|
||||
*/
|
||||
public static Segment<Integer> toSegment(final int pageNo, final int pageSize) {
|
||||
final int[] startEnd = transToStartEnd(pageNo, pageSize);
|
||||
return new DefaultSegment<>(startEnd[0], startEnd[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据总数计算总页数
|
||||
*
|
||||
* @param totalCount 总数
|
||||
* @param pageSize 每页数
|
||||
* @return 总页数
|
||||
*/
|
||||
public static int totalPage(final int totalCount, final int pageSize) {
|
||||
return totalPage((long) totalCount, pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据总数计算总页数
|
||||
*
|
||||
* @param totalCount 总数
|
||||
* @param pageSize 每页数
|
||||
* @return 总页数
|
||||
* @since 5.8.5
|
||||
*/
|
||||
public static int totalPage(final long totalCount, final int pageSize) {
|
||||
if (pageSize == 0) {
|
||||
return 0;
|
||||
}
|
||||
return Math.toIntExact(totalCount % pageSize == 0
|
||||
? (totalCount / pageSize) : (totalCount / pageSize + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页彩虹算法<br>
|
||||
* 来自:<a href="https://github.com/iceroot/iceroot/blob/master/src/main/java/com/icexxx/util/IceUtil.java">https://github.com/iceroot/iceroot/blob/master/src/main/java/com/icexxx/util/IceUtil.java</a><br>
|
||||
* 通过传入的信息,生成一个分页列表显示
|
||||
*
|
||||
* @param pageNo 当前页
|
||||
* @param totalPage 总页数
|
||||
* @param displayCount 每屏展示的页数
|
||||
* @return 分页条
|
||||
*/
|
||||
public static int[] rainbow(final int pageNo, final int totalPage, final int displayCount) {
|
||||
// displayCount % 2
|
||||
final boolean isEven = (displayCount & 1) == 0;
|
||||
final int left = displayCount >> 1;
|
||||
int right = displayCount >> 1;
|
||||
|
||||
int length = displayCount;
|
||||
if (isEven) {
|
||||
right++;
|
||||
}
|
||||
if (totalPage < displayCount) {
|
||||
length = totalPage;
|
||||
}
|
||||
final int[] result = new int[length];
|
||||
if (totalPage >= displayCount) {
|
||||
if (pageNo <= left) {
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = i + 1;
|
||||
}
|
||||
} else if (pageNo > totalPage - right) {
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = i + totalPage - displayCount + 1;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = i + pageNo - left + (isEven ? 1 : 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = i + 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页彩虹算法(默认展示10页)<br>
|
||||
* 来自:<a href="https://github.com/iceroot/iceroot/blob/master/src/main/java/com/icexxx/util/IceUtil.java">https://github.com/iceroot/iceroot/blob/master/src/main/java/com/icexxx/util/IceUtil.java</a>
|
||||
*
|
||||
* @param currentPage 当前页
|
||||
* @param pageCount 总页数
|
||||
* @return 分页条
|
||||
*/
|
||||
public static int[] rainbow(final int currentPage, final int pageCount) {
|
||||
return rainbow(currentPage, pageCount, 10);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------- Private method start
|
||||
|
||||
/**
|
||||
* 根据起始位置获取结束位置
|
||||
*
|
||||
* @param start 起始位置
|
||||
* @param pageSize 每页条目数
|
||||
* @return 结束位置
|
||||
*/
|
||||
private static int getEndByStart(final int start, int pageSize) {
|
||||
if (pageSize < 1) {
|
||||
pageSize = 0;
|
||||
}
|
||||
return start + pageSize;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------- Private method end
|
||||
}
|
@ -427,6 +427,26 @@ public class CollUtilTest {
|
||||
Assert.assertEquals("李四", groupByField.get(1).get(0).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupByFuncTest() {
|
||||
final List<TestBean> list = ListUtil.of(new TestBean("张三", 12), new TestBean("李四", 13), new TestBean("王五", 12));
|
||||
final List<List<TestBean>> groupByField = CollUtil.groupByFunc(list, TestBean::getAge);
|
||||
Assert.assertEquals("张三", groupByField.get(0).get(0).getName());
|
||||
Assert.assertEquals("王五", groupByField.get(0).get(1).getName());
|
||||
|
||||
Assert.assertEquals("李四", groupByField.get(1).get(0).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupByFunc2Test() {
|
||||
final List<TestBean> list = ListUtil.of(new TestBean("张三", 12), new TestBean("李四", 13), new TestBean("王五", 12));
|
||||
final List<List<TestBean>> groupByField = CollUtil.groupByFunc(list, a -> a.getAge() > 12);
|
||||
Assert.assertEquals("张三", groupByField.get(0).get(0).getName());
|
||||
Assert.assertEquals("王五", groupByField.get(0).get(1).getName());
|
||||
|
||||
Assert.assertEquals("李四", groupByField.get(1).get(0).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sortByPropertyTest() {
|
||||
final List<TestBean> list = ListUtil.of(
|
||||
@ -844,7 +864,7 @@ public class CollUtilTest {
|
||||
objects.add(Dict.of().set("name", "姓名:" + i));
|
||||
}
|
||||
|
||||
Assert.assertEquals(0, CollUtil.page(3, 5, objects).size());
|
||||
Assert.assertEquals(0, ListUtil.page(objects, 3, 5).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -854,7 +874,7 @@ public class CollUtilTest {
|
||||
|
||||
final List<Long> result = CollUtil.subtractToList(list1, list2);
|
||||
Assert.assertEquals(1, result.size());
|
||||
Assert.assertEquals(1L, (long)result.get(0));
|
||||
Assert.assertEquals(1L, (long) result.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -2,7 +2,7 @@ package cn.hutool.core.collection;
|
||||
|
||||
import cn.hutool.core.date.StopWatch;
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.core.util.PageUtil;
|
||||
import cn.hutool.core.math.PageInfo;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
@ -10,11 +10,7 @@ import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class ListUtilTest {
|
||||
@ -105,51 +101,32 @@ public class ListUtilTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pageTest() {
|
||||
public void pageTest1() {
|
||||
final List<Integer> a = ListUtil.ofLinked(1, 2, 3, 4, 5);
|
||||
|
||||
PageUtil.setFirstPageNo(1);
|
||||
final int[] a_1 = ListUtil.page(1, 2, a).stream().mapToInt(Integer::valueOf).toArray();
|
||||
final int[] a1 = ListUtil.page(1, 2, a).stream().mapToInt(Integer::valueOf).toArray();
|
||||
final int[] a2 = ListUtil.page(2, 2, a).stream().mapToInt(Integer::valueOf).toArray();
|
||||
final int[] a3 = ListUtil.page(3, 2, a).stream().mapToInt(Integer::valueOf).toArray();
|
||||
final int[] a4 = ListUtil.page(4, 2, a).stream().mapToInt(Integer::valueOf).toArray();
|
||||
final int[] a_1 = ListUtil.page(a, 0, 2).stream().mapToInt(Integer::valueOf).toArray();
|
||||
final int[] a1 = ListUtil.page(a, 0, 2).stream().mapToInt(Integer::valueOf).toArray();
|
||||
final int[] a2 = ListUtil.page(a, 1, 2).stream().mapToInt(Integer::valueOf).toArray();
|
||||
final int[] a3 = ListUtil.page(a, 2, 2).stream().mapToInt(Integer::valueOf).toArray();
|
||||
final int[] a4 = ListUtil.page(a, 3, 2).stream().mapToInt(Integer::valueOf).toArray();
|
||||
Assert.assertArrayEquals(new int[]{1, 2}, a_1);
|
||||
Assert.assertArrayEquals(new int[]{1, 2}, a1);
|
||||
Assert.assertArrayEquals(new int[]{3, 4}, a2);
|
||||
Assert.assertArrayEquals(new int[]{5}, a3);
|
||||
Assert.assertArrayEquals(new int[]{}, a4);
|
||||
}
|
||||
|
||||
|
||||
PageUtil.setFirstPageNo(2);
|
||||
final int[] b_1 = ListUtil.page(1, 2, a).stream().mapToInt(Integer::valueOf).toArray();
|
||||
final int[] b1 = ListUtil.page(2, 2, a).stream().mapToInt(Integer::valueOf).toArray();
|
||||
final int[] b2 = ListUtil.page(3, 2, a).stream().mapToInt(Integer::valueOf).toArray();
|
||||
final int[] b3 = ListUtil.page(4, 2, a).stream().mapToInt(Integer::valueOf).toArray();
|
||||
final int[] b4 = ListUtil.page(5, 2, a).stream().mapToInt(Integer::valueOf).toArray();
|
||||
Assert.assertArrayEquals(new int[]{1, 2}, b_1);
|
||||
Assert.assertArrayEquals(new int[]{1, 2}, b1);
|
||||
Assert.assertArrayEquals(new int[]{3, 4}, b2);
|
||||
Assert.assertArrayEquals(new int[]{5}, b3);
|
||||
Assert.assertArrayEquals(new int[]{}, b4);
|
||||
|
||||
PageUtil.setFirstPageNo(0);
|
||||
final int[] c_1 = ListUtil.page(-1, 2, a).stream().mapToInt(Integer::valueOf).toArray();
|
||||
final int[] c1 = ListUtil.page(0, 2, a).stream().mapToInt(Integer::valueOf).toArray();
|
||||
final int[] c2 = ListUtil.page(1, 2, a).stream().mapToInt(Integer::valueOf).toArray();
|
||||
final int[] c3 = ListUtil.page(2, 2, a).stream().mapToInt(Integer::valueOf).toArray();
|
||||
final int[] c4 = ListUtil.page(3, 2, a).stream().mapToInt(Integer::valueOf).toArray();
|
||||
Assert.assertArrayEquals(new int[]{1, 2}, c_1);
|
||||
Assert.assertArrayEquals(new int[]{1, 2}, c1);
|
||||
Assert.assertArrayEquals(new int[]{3, 4}, c2);
|
||||
Assert.assertArrayEquals(new int[]{5}, c3);
|
||||
Assert.assertArrayEquals(new int[]{}, c4);
|
||||
|
||||
|
||||
PageUtil.setFirstPageNo(1);
|
||||
final int[] d1 = ListUtil.page(0, 8, a).stream().mapToInt(Integer::valueOf).toArray();
|
||||
@Test
|
||||
public void pageTest2() {
|
||||
final List<Integer> a = ListUtil.ofLinked(1, 2, 3, 4, 5);
|
||||
final int[] d1 = ListUtil.page(a, PageInfo.of(a.size(), 8).setFirstPageNo(0).setPageNo(0))
|
||||
.stream().mapToInt(Integer::valueOf).toArray();
|
||||
Assert.assertArrayEquals(new int[]{1, 2, 3, 4, 5}, d1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pageTest3() {
|
||||
final List<Integer> a = ListUtil.ofLinked(1, 2, 3, 4, 5);
|
||||
// page with consumer
|
||||
final List<List<Integer>> pageListData = new ArrayList<>();
|
||||
ListUtil.page(a, 2, pageListData::add);
|
||||
@ -168,9 +145,6 @@ public class ListUtilTest {
|
||||
Assert.assertArrayEquals(new int[]{}, pageListData.get(0).stream().mapToInt(Integer::valueOf).toArray());
|
||||
Assert.assertArrayEquals(new int[]{3, 4}, pageListData.get(1).stream().mapToInt(Integer::valueOf).toArray());
|
||||
Assert.assertArrayEquals(new int[]{5}, pageListData.get(2).stream().mapToInt(Integer::valueOf).toArray());
|
||||
|
||||
// 恢复默认值,避免影响其他测试用例
|
||||
PageUtil.setFirstPageNo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -109,10 +109,10 @@ public class DateTimeTest {
|
||||
public void toStringTest2() {
|
||||
final DateTime dateTime = new DateTime("2017-01-05 12:34:23", DatePattern.NORM_DATETIME_FORMAT);
|
||||
|
||||
String dateStr = dateTime.toString(DatePattern.UTC_WITH_ZONE_OFFSET_PATTERN);
|
||||
String dateStr = dateTime.toString(DatePattern.ISO8601_WITH_ZONE_OFFSET_PATTERN);
|
||||
Assert.assertEquals("2017-01-05T12:34:23+0800", dateStr);
|
||||
|
||||
dateStr = dateTime.toString(DatePattern.UTC_WITH_XXX_OFFSET_PATTERN);
|
||||
dateStr = dateTime.toString(DatePattern.ISO8601_WITH_XXX_OFFSET_PATTERN);
|
||||
Assert.assertEquals("2017-01-05T12:34:23+08:00", dateStr);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
package cn.hutool.core.date;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.date.BetweenFormatter.Level;
|
||||
import cn.hutool.core.date.format.FastDateFormat;
|
||||
import cn.hutool.core.lang.Console;
|
||||
@ -14,15 +13,7 @@ import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.TimeZone;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 时间工具单元测试<br>
|
||||
@ -796,23 +787,6 @@ public class DateUtilTest {
|
||||
Assert.assertEquals(0, DateUtil.compare(date11, date22, DatePattern.NORM_MONTH_PATTERN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void yearAndQTest() {
|
||||
final String yearAndQuarter = DateUtil.yearAndQuarter(DateUtil.parse("2018-12-01"));
|
||||
Assert.assertEquals("20184", yearAndQuarter);
|
||||
|
||||
final LinkedHashSet<String> yearAndQuarters = DateUtil.yearAndQuarter(DateUtil.parse("2018-09-10"), DateUtil.parse("2018-12-20"));
|
||||
final List<String> list = ListUtil.of(false, yearAndQuarters);
|
||||
Assert.assertEquals(2, list.size());
|
||||
Assert.assertEquals("20183", list.get(0));
|
||||
Assert.assertEquals("20184", list.get(1));
|
||||
|
||||
final LinkedHashSet<String> yearAndQuarters2 = DateUtil.yearAndQuarter(DateUtil.parse("2018-10-10"), DateUtil.parse("2018-12-10"));
|
||||
final List<String> list2 = ListUtil.of(false, yearAndQuarters2);
|
||||
Assert.assertEquals(1, list2.size());
|
||||
Assert.assertEquals("20184", list2.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formatHttpDateTest() {
|
||||
final String formatHttpDate = DateUtil.formatHttpDate(DateUtil.parse("2019-01-02 22:32:01"));
|
||||
|
22
hutool-core/src/test/java/cn/hutool/core/date/Issue2981Test.java
Executable file
22
hutool-core/src/test/java/cn/hutool/core/date/Issue2981Test.java
Executable file
@ -0,0 +1,22 @@
|
||||
package cn.hutool.core.date;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class Issue2981Test {
|
||||
/**
|
||||
* https://github.com/dromara/hutool/issues/2981<br>
|
||||
* 按照ISO8601规范,以Z结尾表示UTC时间,否则为当地时间
|
||||
*/
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
@Test
|
||||
public void parseUTCTest() {
|
||||
final String str1 = "2019-01-01T00:00:00.000Z";
|
||||
final String str2 = "2019-01-01T00:00:00.000";
|
||||
final String str3 = "2019-01-01 00:00:00.000";
|
||||
|
||||
Assert.assertEquals(1546300800000L, DateUtil.parse(str1).getTime());
|
||||
Assert.assertEquals(1546272000000L, DateUtil.parse(str2).getTime());
|
||||
Assert.assertEquals(1546272000000L, DateUtil.parse(str3).getTime());
|
||||
}
|
||||
}
|
@ -27,12 +27,22 @@ public class TimeUtilTest {
|
||||
final String dateStr = "2020-01-23T12:23:56";
|
||||
final DateTime dt = DateUtil.parse(dateStr);
|
||||
|
||||
LocalDateTime of = TimeUtil.of(dt);
|
||||
final LocalDateTime of = TimeUtil.of(dt);
|
||||
Assert.assertNotNull(of);
|
||||
Assert.assertEquals(dateStr, of.toString());
|
||||
}
|
||||
|
||||
of = TimeUtil.ofUTC(dt.getTime());
|
||||
Assert.assertEquals(dateStr, of.toString());
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
@Test
|
||||
public void ofUTCTest() {
|
||||
final String dateStr = "2020-01-23T12:23:56Z";
|
||||
final DateTime dt = DateUtil.parse(dateStr);
|
||||
|
||||
final LocalDateTime of = TimeUtil.of(dt);
|
||||
final LocalDateTime of2 = TimeUtil.ofUTC(dt.getTime());
|
||||
Assert.assertNotNull(of);
|
||||
Assert.assertNotNull(of2);
|
||||
Assert.assertEquals(of, of2);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -0,0 +1,32 @@
|
||||
package cn.hutool.core.math;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PageInfoTest {
|
||||
@Test
|
||||
public void pagesTest() {
|
||||
PageInfo pageInfo = new PageInfo(20, 3);
|
||||
Assert.assertEquals(7, pageInfo.getPages());
|
||||
|
||||
pageInfo = new PageInfo(20, 4);
|
||||
Assert.assertEquals(5, pageInfo.getPages());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSegmentTest() {
|
||||
final PageInfo page = PageInfo.of(20, 10);
|
||||
Assert.assertEquals("[0, 9]", page.getSegment().toString());
|
||||
Assert.assertEquals("[10, 19]", page.nextPage().getSegment().toString());
|
||||
Assert.assertEquals("[20, 20]", page.nextPage().getSegment().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSegmentTest2() {
|
||||
final PageInfo page = PageInfo.of(20, 10);
|
||||
page.setFirstPageNo(0).setPageNo(0);
|
||||
Assert.assertEquals("[0, 9]", page.getSegment().toString());
|
||||
Assert.assertEquals("[10, 19]", page.nextPage().getSegment().toString());
|
||||
Assert.assertEquals("[20, 20]", page.nextPage().getSegment().toString());
|
||||
}
|
||||
}
|
@ -91,15 +91,15 @@ public class DfaTest {
|
||||
Assert.assertEquals(3, result.size());
|
||||
|
||||
Assert.assertEquals("赵", result.get(0).getWord());
|
||||
Assert.assertEquals(0, result.get(0).getStartIndex().intValue());
|
||||
Assert.assertEquals(0, result.get(0).getBeginIndex().intValue());
|
||||
Assert.assertEquals(0, result.get(0).getEndIndex().intValue());
|
||||
|
||||
Assert.assertEquals("赵阿", result.get(1).getWord());
|
||||
Assert.assertEquals(0, result.get(1).getStartIndex().intValue());
|
||||
Assert.assertEquals(0, result.get(1).getBeginIndex().intValue());
|
||||
Assert.assertEquals(1, result.get(1).getEndIndex().intValue());
|
||||
|
||||
Assert.assertEquals("赵阿三", result.get(2).getWord());
|
||||
Assert.assertEquals(0, result.get(2).getStartIndex().intValue());
|
||||
Assert.assertEquals(0, result.get(2).getBeginIndex().intValue());
|
||||
Assert.assertEquals(2, result.get(2).getEndIndex().intValue());
|
||||
}
|
||||
|
||||
|
228
hutool-core/src/test/java/cn/hutool/core/text/dfa/NFATest.java
Normal file
228
hutool-core/src/test/java/cn/hutool/core/text/dfa/NFATest.java
Normal file
@ -0,0 +1,228 @@
|
||||
package cn.hutool.core.text.dfa;
|
||||
|
||||
import cn.hutool.core.date.StopWatch;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class NFATest {
|
||||
|
||||
/**
|
||||
* 密集匹配 测试查找结果,并与WordTree对比效率
|
||||
*/
|
||||
@Test
|
||||
public void testFind() {
|
||||
final NFA NFA = new NFA();
|
||||
NFA.insert("say", "her", "he", "she", "shr");
|
||||
NFA.buildAc();
|
||||
|
||||
final WordTree wordTree = new WordTree();
|
||||
wordTree.addWords("say", "her", "he", "she", "shr");
|
||||
|
||||
final StopWatch stopWatch = new StopWatch();
|
||||
final String input = "sasherhsay";
|
||||
|
||||
stopWatch.start("automaton_char_find");
|
||||
final List<FoundWord> ans1 = NFA.find(input);
|
||||
stopWatch.stop();
|
||||
|
||||
Assert.assertEquals("she,he,her,say", ans1.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
|
||||
Assert.assertEquals(2, ans1.get(0).getBeginIndex().intValue());
|
||||
Assert.assertEquals(4, ans1.get(0).getEndIndex().intValue());
|
||||
Assert.assertEquals(3, ans1.get(1).getBeginIndex().intValue());
|
||||
Assert.assertEquals(4, ans1.get(1).getEndIndex().intValue());
|
||||
Assert.assertEquals(3, ans1.get(2).getBeginIndex().intValue());
|
||||
Assert.assertEquals(5, ans1.get(2).getEndIndex().intValue());
|
||||
Assert.assertEquals(7, ans1.get(3).getBeginIndex().intValue());
|
||||
Assert.assertEquals(9, ans1.get(3).getEndIndex().intValue());
|
||||
|
||||
stopWatch.start("wordtree_char_find");
|
||||
final List<String> ans2 = wordTree.matchAll(input, -1, true, true);
|
||||
stopWatch.stop();
|
||||
Assert.assertEquals("she,he,her,say", String.join(",", ans2));
|
||||
|
||||
//Console.log(stopWatch.prettyPrint());
|
||||
}
|
||||
|
||||
/**
|
||||
* 非密集匹配 测试查找结果,并与WordTree对比效率
|
||||
*/
|
||||
@Test
|
||||
public void testFindNotDensity() {
|
||||
final NFA NFA = new NFA();
|
||||
NFA.insert("say", "her", "he", "she", "shr");
|
||||
NFA.buildAc();
|
||||
|
||||
final WordTree wordTree = new WordTree();
|
||||
wordTree.addWords("say", "her", "he", "she", "shr");
|
||||
|
||||
final StopWatch stopWatch = new StopWatch();
|
||||
final String input = "sasherhsay";
|
||||
|
||||
stopWatch.start("automaton_char_find_not_density");
|
||||
final List<FoundWord> ans1 = NFA.find(input, false);
|
||||
stopWatch.stop();
|
||||
Assert.assertEquals("she,say", ans1.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
|
||||
Assert.assertEquals(2, ans1.get(0).getBeginIndex().intValue());
|
||||
Assert.assertEquals(4, ans1.get(0).getEndIndex().intValue());
|
||||
Assert.assertEquals(7, ans1.get(1).getBeginIndex().intValue());
|
||||
Assert.assertEquals(9, ans1.get(1).getEndIndex().intValue());
|
||||
|
||||
stopWatch.start("wordtree_char_find_not_density");
|
||||
final List<String> ans2 = wordTree.matchAll(input, -1, false, true);
|
||||
stopWatch.stop();
|
||||
Assert.assertEquals("she,say", String.join(",", ans2));
|
||||
|
||||
//Console.log(stopWatch.prettyPrint());
|
||||
}
|
||||
|
||||
/**
|
||||
* 密集匹配 测试建树和查找,并与WordTree对比效率
|
||||
*/
|
||||
@Test
|
||||
public void testBuildAndFind() {
|
||||
final StopWatch stopWatch = new StopWatch();
|
||||
final String input = "sasherhsay";
|
||||
|
||||
stopWatch.start("automaton_char_buid_find");
|
||||
final NFA NFALocal = new NFA();
|
||||
NFALocal.insert("say", "her", "he", "she", "shr");
|
||||
NFALocal.buildAc();
|
||||
final List<FoundWord> ans1 = NFALocal.find(input);
|
||||
stopWatch.stop();
|
||||
|
||||
Assert.assertEquals("she,he,her,say", ans1.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
|
||||
Assert.assertEquals(2, ans1.get(0).getBeginIndex().intValue());
|
||||
Assert.assertEquals(4, ans1.get(0).getEndIndex().intValue());
|
||||
Assert.assertEquals(3, ans1.get(1).getBeginIndex().intValue());
|
||||
Assert.assertEquals(4, ans1.get(1).getEndIndex().intValue());
|
||||
Assert.assertEquals(3, ans1.get(2).getBeginIndex().intValue());
|
||||
Assert.assertEquals(5, ans1.get(2).getEndIndex().intValue());
|
||||
Assert.assertEquals(7, ans1.get(3).getBeginIndex().intValue());
|
||||
Assert.assertEquals(9, ans1.get(3).getEndIndex().intValue());
|
||||
|
||||
stopWatch.start("wordtree_char_build_find");
|
||||
final WordTree wordTreeLocal = new WordTree();
|
||||
wordTreeLocal.addWords("say", "her", "he", "she", "shr");
|
||||
final List<String> ans2 = wordTreeLocal.matchAll(input, -1, true, true);
|
||||
stopWatch.stop();
|
||||
Assert.assertEquals("she,he,her,say", String.join(",", ans2));
|
||||
|
||||
//Console.log(stopWatch.prettyPrint());
|
||||
}
|
||||
|
||||
/**
|
||||
* 密集匹配 构建树和查找 测试中文字符,并与wordTree对比效率
|
||||
*/
|
||||
@Test
|
||||
public void buildFindCnCharTest() {
|
||||
final StopWatch stopWatch = new StopWatch();
|
||||
final String input = "赵啊三在做什么";
|
||||
|
||||
stopWatch.start("automaton_cn_build_find");
|
||||
final NFA NFALocal = new NFA();
|
||||
NFALocal.insert("赵", "赵啊", "赵啊三");
|
||||
NFALocal.buildAc();
|
||||
|
||||
final List<FoundWord> result = NFALocal.find(input);
|
||||
stopWatch.stop();
|
||||
|
||||
Assert.assertEquals(3, result.size());
|
||||
Assert.assertEquals("赵,赵啊,赵啊三", result.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
|
||||
Assert.assertEquals(Integer.valueOf(0), result.get(0).getBeginIndex());
|
||||
Assert.assertEquals(Integer.valueOf(0), result.get(0).getEndIndex());
|
||||
Assert.assertEquals(Integer.valueOf(0), result.get(1).getBeginIndex());
|
||||
Assert.assertEquals(Integer.valueOf(1), result.get(1).getEndIndex());
|
||||
Assert.assertEquals(Integer.valueOf(0), result.get(2).getBeginIndex());
|
||||
Assert.assertEquals(Integer.valueOf(2), result.get(2).getEndIndex());
|
||||
|
||||
stopWatch.start("wordtree_cn_build_find");
|
||||
final WordTree wordTreeLocal = new WordTree();
|
||||
wordTreeLocal.addWords("赵", "赵啊", "赵啊三");
|
||||
|
||||
final List<String> result1 = wordTreeLocal.matchAll(input, -1, true, true);
|
||||
stopWatch.stop();
|
||||
|
||||
Assert.assertEquals(3, result1.size());
|
||||
Assert.assertEquals("赵,赵啊,赵啊三", String.join(",", result1));
|
||||
|
||||
//Console.log(stopWatch.prettyPrint());
|
||||
}
|
||||
|
||||
/**
|
||||
* 密集匹配 测试构建树和查找 中文字符,并与wordTree对比效率
|
||||
*/
|
||||
@Test
|
||||
public void testFindCNChar() {
|
||||
final StopWatch stopWatch = new StopWatch();
|
||||
final String input = "赵啊三在做什么";
|
||||
|
||||
final NFA NFALocal = new NFA();
|
||||
NFALocal.insert("赵", "赵啊", "赵啊三");
|
||||
NFALocal.buildAc();
|
||||
|
||||
stopWatch.start("automaton_cn_find");
|
||||
final List<FoundWord> result = NFALocal.find(input);
|
||||
stopWatch.stop();
|
||||
|
||||
Assert.assertEquals(3, result.size());
|
||||
Assert.assertEquals("赵,赵啊,赵啊三", result.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
|
||||
Assert.assertEquals(Integer.valueOf(0), result.get(0).getBeginIndex());
|
||||
Assert.assertEquals(Integer.valueOf(0), result.get(0).getEndIndex());
|
||||
Assert.assertEquals(Integer.valueOf(0), result.get(1).getBeginIndex());
|
||||
Assert.assertEquals(Integer.valueOf(1), result.get(1).getEndIndex());
|
||||
Assert.assertEquals(Integer.valueOf(0), result.get(2).getBeginIndex());
|
||||
Assert.assertEquals(Integer.valueOf(2), result.get(2).getEndIndex());
|
||||
|
||||
final WordTree wordTreeLocal = new WordTree();
|
||||
wordTreeLocal.addWords("赵", "赵啊", "赵啊三");
|
||||
|
||||
stopWatch.start("wordtree_cn_find");
|
||||
final List<String> result1 = wordTreeLocal.matchAllWords(input, -1, true, true).stream().map(FoundWord::getWord)
|
||||
.collect(Collectors.toList());
|
||||
stopWatch.stop();
|
||||
|
||||
Assert.assertEquals(3, result1.size());
|
||||
Assert.assertEquals("赵,赵啊,赵啊三", String.join(",", result1));
|
||||
|
||||
//Console.log(stopWatch.prettyPrint());
|
||||
}
|
||||
|
||||
/**
|
||||
* 非密集匹配 测试构建树和查找 中文字符,并与wordTree对比效率,
|
||||
*/
|
||||
@Test
|
||||
public void testFindCNCharNotDensity() {
|
||||
final StopWatch stopWatch = new StopWatch();
|
||||
final String input = "赵啊三在做什么";
|
||||
|
||||
final NFA NFALocal = new NFA();
|
||||
NFALocal.insert("赵", "赵啊", "赵啊三");
|
||||
NFALocal.buildAc();
|
||||
|
||||
stopWatch.start("automaton_cn_find_not_density");
|
||||
final List<FoundWord> result = NFALocal.find(input, false);
|
||||
stopWatch.stop();
|
||||
|
||||
Assert.assertEquals(1, result.size());
|
||||
Assert.assertEquals("赵", result.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
|
||||
Assert.assertEquals(Integer.valueOf(0), result.get(0).getBeginIndex());
|
||||
Assert.assertEquals(Integer.valueOf(0), result.get(0).getEndIndex());
|
||||
|
||||
final WordTree wordTreeLocal = new WordTree();
|
||||
wordTreeLocal.addWords("赵", "赵啊", "赵啊三");
|
||||
|
||||
stopWatch.start("wordtree_cn_find_not_density");
|
||||
final List<String> result1 =
|
||||
wordTreeLocal.matchAllWords(input, -1, false, true).stream().map(FoundWord::getWord)
|
||||
.collect(Collectors.toList());
|
||||
stopWatch.stop();
|
||||
|
||||
Assert.assertEquals(1, result1.size());
|
||||
Assert.assertEquals("赵", String.join(",", result1));
|
||||
|
||||
//Console.log(stopWatch.prettyPrint());
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package cn.hutool.core.text.split;
|
||||
|
||||
import cn.hutool.core.lang.Console;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
@ -59,16 +60,25 @@ public class StrSplitterTest {
|
||||
final String str = "";
|
||||
final String[] split = str.split(",");
|
||||
final String[] strings = SplitUtil.splitToArray(str, ",", -1, false, false);
|
||||
|
||||
Assert.assertNotNull(strings);
|
||||
Assert.assertArrayEquals(split, strings);
|
||||
|
||||
final String[] strings2 = SplitUtil.splitToArray(str, ",", -1, false, true);
|
||||
Assert.assertEquals(0, strings2.length);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantValue")
|
||||
@Test
|
||||
public void splitNullTest(){
|
||||
final String str = null;
|
||||
final String[] strings = SplitUtil.splitToArray(str, ",", -1, false, false);
|
||||
Assert.assertNotNull(strings);
|
||||
Assert.assertEquals(0, strings.length);
|
||||
|
||||
final String[] strings2 = SplitUtil.splitToArray(str, ",", -1, false, true);
|
||||
Assert.assertNotNull(strings2);
|
||||
Assert.assertEquals(0, strings2.length);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,35 +0,0 @@
|
||||
package cn.hutool.core.util;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* 分页单元测试
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class PageUtilTest {
|
||||
|
||||
@Test
|
||||
public void transToStartEndTest() {
|
||||
final int[] startEnd1 = PageUtil.transToStartEnd(0, 10);
|
||||
Assert.assertEquals(0, startEnd1[0]);
|
||||
Assert.assertEquals(10, startEnd1[1]);
|
||||
|
||||
final int[] startEnd2 = PageUtil.transToStartEnd(1, 10);
|
||||
Assert.assertEquals(10, startEnd2[0]);
|
||||
Assert.assertEquals(20, startEnd2[1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void totalPage() {
|
||||
final int totalPage = PageUtil.totalPage(20, 3);
|
||||
Assert.assertEquals(7, totalPage);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rainbowTest() {
|
||||
final int[] rainbow = PageUtil.rainbow(5, 20, 6);
|
||||
Assert.assertArrayEquals(new int[]{3, 4, 5, 6, 7, 8}, rainbow);
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
package cn.hutool.db;
|
||||
|
||||
import cn.hutool.core.lang.Segment;
|
||||
import cn.hutool.core.math.PageInfo;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.PageUtil;
|
||||
import cn.hutool.db.sql.Order;
|
||||
|
||||
import java.io.Serializable;
|
||||
@ -16,6 +16,9 @@ import java.util.Arrays;
|
||||
public class Page implements Segment<Integer>, Serializable {
|
||||
private static final long serialVersionUID = 97792549823353462L;
|
||||
|
||||
/**
|
||||
* 默认
|
||||
*/
|
||||
public static final int DEFAULT_PAGE_SIZE = 20;
|
||||
|
||||
/**
|
||||
@ -140,15 +143,16 @@ public class Page implements Segment<Integer>, Serializable {
|
||||
|
||||
/**
|
||||
* @return 开始位置
|
||||
* @see #getStartIndex()
|
||||
* @see #getBeginIndex()
|
||||
*/
|
||||
public int getStartPosition() {
|
||||
return getStartIndex();
|
||||
return getBeginIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getStartIndex() {
|
||||
return PageUtil.getStart(this.pageNumber, this.pageSize);
|
||||
public Integer getBeginIndex() {
|
||||
return PageInfo.of(Integer.MAX_VALUE, this.pageSize)
|
||||
.setFirstPageNo(0).setPageNo(this.pageNumber).getBeginIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -161,7 +165,7 @@ public class Page implements Segment<Integer>, Serializable {
|
||||
|
||||
@Override
|
||||
public Integer getEndIndex() {
|
||||
return PageUtil.getEnd(this.pageNumber, this.pageSize);
|
||||
return PageInfo.of(Integer.MAX_VALUE, this.pageSize).setFirstPageNo(0).getEndIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -178,7 +182,9 @@ public class Page implements Segment<Integer>, Serializable {
|
||||
* @return 第一个数为开始位置,第二个数为结束位置
|
||||
*/
|
||||
public int[] getStartEnd() {
|
||||
return PageUtil.transToStartEnd(pageNumber, pageSize);
|
||||
final PageInfo pageInfo = PageInfo.of(Integer.MAX_VALUE, this.pageSize)
|
||||
.setFirstPageNo(0).setPageNo(this.pageNumber);
|
||||
return new int[]{pageInfo.getBeginIndex(), pageInfo.getEndIndexExclude()};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.hutool.db;
|
||||
|
||||
import cn.hutool.core.util.PageUtil;
|
||||
import cn.hutool.core.math.PageInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@ -16,7 +16,7 @@ public class PageResult<T> extends ArrayList<T> {
|
||||
public static final int DEFAULT_PAGE_SIZE = Page.DEFAULT_PAGE_SIZE;
|
||||
|
||||
/**
|
||||
* 页码,{@link PageUtil#getFirstPageNo()}表示第一页
|
||||
* 页码
|
||||
*/
|
||||
private int page;
|
||||
/**
|
||||
@ -65,7 +65,7 @@ public class PageResult<T> extends ArrayList<T> {
|
||||
this(page, pageSize);
|
||||
|
||||
this.total = total;
|
||||
this.totalPage = PageUtil.totalPage(total, pageSize);
|
||||
this.totalPage = PageInfo.of(total, pageSize).getPages();
|
||||
}
|
||||
//---------------------------------------------------------- Constructor end
|
||||
|
||||
@ -142,7 +142,7 @@ public class PageResult<T> extends ArrayList<T> {
|
||||
* @return 是否第一页
|
||||
*/
|
||||
public boolean isFirst() {
|
||||
return this.page == PageUtil.getFirstPageNo();
|
||||
return this.page == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -34,7 +34,8 @@ public class DbTest {
|
||||
@Test
|
||||
public void pageTest() {
|
||||
// 测试数据库中一共4条数据,第0页有3条,第1页有1条
|
||||
final List<Entity> page0 = Db.of().page(Entity.of("user"), Page.of(0, 3));
|
||||
final List<Entity> page0 = Db.of().page(Entity.of("user"),
|
||||
Page.of(0, 3));
|
||||
Assert.assertEquals(3, page0.size());
|
||||
|
||||
final List<Entity> page1 = Db.of().page(Entity.of("user"), Page.of(1, 3));
|
||||
|
@ -35,12 +35,12 @@ public class JSONUtilTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字解析为JSONObject忽略
|
||||
* 数字解析为JSONArray报错
|
||||
*/
|
||||
@Test
|
||||
@Test(expected = JSONException.class)
|
||||
public void parseNumberTest2() {
|
||||
final JSONObject json = JSONUtil.parseObj(123L);
|
||||
Assert.assertEquals(new JSONObject(), json);
|
||||
Assert.assertNotNull(json);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -85,7 +85,7 @@ public class JSONUtilTest {
|
||||
@Test
|
||||
public void toJsonStrTest3() {
|
||||
// 验证某个字段为JSON字符串时转义是否规范
|
||||
final JSONObject object = new JSONObject(true);
|
||||
final JSONObject object = new JSONObject(JSONConfig.of().setIgnoreError(true));
|
||||
object.set("name", "123123");
|
||||
object.set("value", "\\");
|
||||
object.set("value2", "</");
|
||||
|
Loading…
Reference in New Issue
Block a user