!12 完成时间工具与随机字符串工具的改造

Merge pull request !12 from 彭宇琦/featrue/20210115-工具简单改造
This commit is contained in:
彭宇琦 2021-01-23 19:25:26 +08:00 committed by Gitee
commit b00868ebd7
10 changed files with 1357 additions and 931 deletions

View File

@ -194,10 +194,7 @@ public class Functions {
* @return 返回处理后的时间
*/
private static String disposeTime(String timeText, String pattern) {
Time time = new Time(timeText);
time.addTime(pattern);
return time.getFormatTime();
return Time.parse(timeText).addTime(pattern).getFormatTime();
}
/**

View File

@ -70,7 +70,7 @@ public class TestNGDataDriver {
/**
* 用于对日期格式或特殊字段输入的日期进行转换
*/
private Time time = new Time();
private Time time = Time.parse();
/**
* 构造对象初始化数据
@ -332,7 +332,7 @@ public class TestNGDataDriver {
* @return {@link Time}类型的数据
*/
public Time getTime(int index) {
return new Time(getString(index));
return Time.parse(getString(index));
}
/**
@ -342,7 +342,7 @@ public class TestNGDataDriver {
* @return {@link Time}类型的数据
*/
public Time getTime(String listName) {
return new Time(getString(listName));
return Time.parse(getString(listName));
}
/**

View File

@ -272,7 +272,7 @@ public class ExcelRecord extends AbstractWriteExcel<ExcelRecord> {
switchSheet("运行记录")
.addContent("state", isError ? "2" : "1")
.addContent("bug_number", String.valueOf(resultBugList.size()))
.addContent("active_time", new Time().getFormatTime())
.addContent("active_time", Time.parse().getFormatTime())
.addContent("use_time", startTime == 0L ? "" : (String.valueOf((System.currentTimeMillis() - startTime) / 1000.0) + "s"));
//获取父类的end方法结束当前的记录

View File

@ -1,46 +1,49 @@
package com.auxiliary.tool.data;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Optional;
import java.util.Random;
import java.util.stream.IntStream;
/**
* <p><b>文件名</b>RandomString.java</p>
* <p><b>用途</b>
* 提供根据字符串池产生随机字符串的范围中添加模型返回相应的随机字符串的方法
* <p>
* <b>文件名</b>RandomString.java
* </p>
* <p><b>编码时间</b>2019年2月13日下午19:53:01</p>
* <p><b>修改时间</b>2021年1月13日下午12:53:01</p>
* <p>
* <b>用途</b> 提供根据字符串池产生随机字符串的范围中添加模型返回相应的随机字符串的方法
* </p>
* <p>
* <b>编码时间</b>2019年2月13日下午19:53:01
* </p>
* <p>
* <b>修改时间</b>2021年1月16日下午12:53:01
* </p>
*
* @author 彭宇琦
* @version Ver1.9
* @version Ver1.10
* @since JDK 1.8
*
*/
public class RandomString {
// 用于定义随机字符串产生的范围
/**
* 字符串池
*/
private StringBuilder stringSeed = new StringBuilder("");
// 用于控制产生的随机字符串是否允许有重复字符的开关
/**
* 控制产生的随机字符串是否允许有重复字符的开关
*/
private boolean repeat = true;
/**
* 忽略只生成一个与随机字符串范围长度相同的随机字符串
* 指定通过{@link #toString()}方法默认输出的字符串长度
*/
public final int DISPOSE_IGNORE = 0;
/**
* 抛出异常中断生成字符串的操作
*/
public final int DISPOSE_THROW_EXCEPTION = 1;
/**
* 重复将随机字符串的生成条件改为允许字符串重复
*/
public final int DISPOSE_REPEAT = 2;
// 用于控制生成允许重复字符的随机字符串时其错误的处理方式默认为重复
private int dispose = DISPOSE_REPEAT;
public static int defaultLength = 6;
/**
* 指向StringMode枚举类可通过该属性向字符串池中添加模型
* 控制在需要生成的字符串大于字符串池时的处理方式
*/
public StringMode mode;
private RepeatDisposeType dispose = RepeatDisposeType.DISPOSE_REPEAT;
/**
* 初始化字符串池使其不包含任何元素
@ -51,511 +54,481 @@ public class RandomString {
/**
* 通过预设模型模型对字符串池进行初始化
*
* @param modes
* 需要加入字符串池中的模型为StringMode枚举对象
* @param stringModes 需要加入字符串池中的模型{@link StringMode}枚举对象
*/
public RandomString(StringMode... modes) {
addMode(modes);
public RandomString(StringMode... stringModes) {
addMode(stringModes);
}
/**
* 通过自定义的字符串对字符串池进行初始化
*
* @param mode
* 需要加入字符串池中的模型为StringMode枚举对象
* @param mode 需要加入字符串池中的模型
*/
public RandomString(String mode) {
addMode(mode);
}
/**
* 返回是否允许产生的随机字符串字符重复结果
* 设置产生的随机字符串中是否允许存在重复的字符默认为允许重复
* <p>
* <b>注意</b>随机字符串字符的重复是相对于字符串池而言的若字符串池中本身存在重复的字符
* 即便设置了不允许出现字符在生成的随机字符串中也可能包含重复的字符若要生成不包含重复字符的
* 字符串可调用{@link #removeRepetition()}方法去除字符串池中重复的字符后再调用返回字符串 的方法
* </p>
*
* @return 是否允许随机字符串字符串字符重复
*/
public boolean isRepeat() {
return repeat;
}
/**
* 设置产生的随机字符串中的字符是否允许重复默认允许重复当设置不允许重复时若需要生成随机字符串的
* 长度大于随机字符串生成范围的长度时需要配合{@link #setDispose(int)}方法联合使用有三种状态<br/>
* {@link #DISPOSE_IGNORE}忽略只生成一个与随机字符串范围长度相同的随机字符串<br/>
* {@link #DISPOSE_THROW_EXCEPTION}抛出异常中断生成字符串的操作<br/>
* {@link #DISPOSE_REPEAT}重复生成的随机字符串可带重复的字符<br/>
*
* @param repeat
* 是否允许重复
* @param repeat 是否允许重复
*/
public void setRepeat(boolean repeat) {
this.repeat = repeat;
}
/**
* 用于返回产生的随机字符串大于字符串生成范围时的处理方式其值对应如下<br/>
* 0 = {@link #DISPOSE_IGNORE}<br/>
* 1 = {@link #DISPOSE_THROW_EXCEPTION}<br/>
* 2 = {@link #DISPOSE_REPEAT}<br/>
* 用于设置需要产生的随机字符串长度大于字符串池长度且设置了不允许出现重复字符时的处理方式
* <p>
* 若需要产生的随机字符串长度大于字符串池长度则必定会出现重复的字符若同时调用
* {@link #setRepeat(boolean)}方法设置不允许存在重复字符时则需要对字符串的生成进行一定的处理
* 具体的处理规则可参照{@link RepeatDisposeType}枚举中的说明
* </p>
* <p>
* <b>注意</b>
* <ol>
* <li>若未设置则按照默认的{@link #DISPOSE_REPEAT}方式进行处理</li>
* <li>处理方式为{@link #DISPOSE_REPEAT}在处理后不影响{@link #setRepeat(boolean)}的设置</li>
* </ol>
* </p>
*
* @return 处理方式
* @param dispose 处理方式{@link RepeatDisposeType}枚举
*/
public int getDispose() {
return dispose;
public void setDispose(RepeatDisposeType repeatDisposeType) {
this.dispose = repeatDisposeType;
}
/**
* 用于设置产生的随机字符串大于字符串生成范围时的处理方式有三种操作方式<br/>
* {@link #DISPOSE_IGNORE}忽略只生成一个与随机字符串范围长度相同的随机字符串<br/>
* {@link #DISPOSE_THROW_EXCEPTION}抛出异常中断生成字符串的操作<br/>
* {@link #DISPOSE_REPEAT}重复将随机字符串的生成条件改为允许字符串重复<br/>
* 注意<br/>
* 1.若设置不正确则按照重{@link #DISPOSE_REPEAT}方式进行处理<br/>
* 2.处理方式为{@link #DISPOSE_REPEAT}会改临时改变变允许重复的控制开关不会永久生效<br/>
* 3.处理方式为{@link #DISPOSE_REPEAT}会生成一串与随机字符串范围长度相同字符串之后重复的字符在其后增加
* 用于返回字符串池
*
* @param dispose
* 处理方式见类中的三种处理方式{@link #DISPOSE_IGNORE}{@link #DISPOSE_THROW_EXCEPTION}{@link #DISPOSE_REPEAT}
*/
public void setDispose(int dispose) {
this.dispose = dispose;
}
/**
* 该方法用于返回字符串池
*
* @return 用于返回随机字符串生成的范围
* @return 字符串池
*/
public String getStringSeed() {
return stringSeed.toString();
}
/**
* 该方法用于向字符串池中添加模型通过该方法添加的模型将不检查字符串池中是否存在与之重复的元素
* 用于向字符串池中添加模型通过该方法添加的模型将不检查字符串池中是否存在与之重复的元素
*
* @param modes
* 需要加入到字符串池中的模型
* @param stringModes 字符串模型枚举{@link StringMode}
* @return 类本身
*/
/**
* 该方法用于向字符串池中添加模型通过该方法添加的模型将不检查字符串池中是否存在与之重复的元素
*
* @param modes
* 需要加入到字符串池中的模型
* @return 返回类本身以便于链式操作
*/
public RandomString addMode(StringMode... modes) {
for (StringMode m : modes) {
stringSeed.append(m.getSeed());
}
return this;
public RandomString addMode(StringMode... stringModes) {
return addMode(true, stringModes);
}
/**
* 该方法用于向字符串池中添加模型且通过isRepeat进行判断是否允许添加可重复的字符串进入字符串池
* 用于向字符串池中添加模型且通过isRepeat进行判断是否允许添加可重复的字符串进入字符串池
*
* @param isRepeat
* 字符串池中的元素是否可重复
* @param modes
* 需要加入到字符串池中的模型
*
* @return 返回类本身以便于链式操作
* @param isRepeat 字符串池中的元素是否可重复
* @param stringModes 字符串模型枚举{@link StringMode}
*
* @return 类本身
*/
public RandomString addMode(boolean isRepeat, StringMode... modes) {
// 判断传入的参数是否允许字符串池中元素重复若为true则等同于调用addMode(StringMode... modes)
if (!isRepeat) {
// 循环读取所有传入的模型
for (StringMode m : modes) {
// 判断模型整体是否已经存在若存在则不进行添加
if (stringSeed.indexOf(m.getSeed()) > -1) {
continue;
}
public RandomString addMode(boolean isRepeat, StringMode... stringModes) {
StringBuilder joinText = new StringBuilder("");
Optional.ofNullable(stringModes)
// 过滤掉无元素的数组
.filter(arr -> arr.length != 0)
// 将数组转换为集合若数组有误则返回空集合
.map(Arrays::asList).orElse(new ArrayList<>())
// 读取集合每一个元素并返回相应的字符串
.stream().map(StringMode::getSeed).forEach(joinText::append);
packageString(m.getSeed());
}
} else {
addMode(modes);
}
return this;
return addMode(isRepeat, joinText.toString());
}
/**
* 该方法用于向字符串池中添加自定义的字符串通过该方法添加的字符串将不检查字符串池中是否存在与之重复的元素
* 用于向字符串池中添加自定义的字符串通过该方法添加的字符串将不检查字符串池中是否存在与之重复的元素
*
* @param mode
* 需要加入到字符串池中的模型
*
* @return 返回类本身以便于链式操作
* @param mode 需要加入到字符串池中的模型
*
* @return 类本身
*/
public RandomString addMode(String mode) {
stringSeed.append(mode);
return this;
return addMode(true, mode);
}
/**
* 该方法用于向字符串池中添加自定义的字符串且通过isRepeat进行判断是否允许添加可重复的字符串进入字符串池
* 用于向字符串池中添加自定义的字符串且通过isRepeat进行判断是否允许添加可重复的字符串进入字符串池
*
* @param isRepeat
* 字符串池中的元素是否可重复
* @param mode
* 需要加入到字符串池中的模型
* @param isRepeat 字符串池中的元素是否可重复
* @param mode 需要加入到字符串池中的模型
*
* @return 返回类本身以便于链式操作
* @return 类本身
*/
public RandomString addMode(boolean isRepeat, String mode) {
// 判断传入的参数是否允许字符串池中元素重复若为true则等同于调用addMode(StringMode... modes)
if (!isRepeat) {
// 判断模型整体是否已经存在若存在则不进行添加
if (stringSeed.indexOf(mode) > -1) {
return this;
}
packageString(mode);
} else {
addMode(mode);
}
joinStringSeed(isRepeat, mode);
return this;
}
/**
* 该方法用于清空StringSeed中存储的随机字符串生成范围
* 用于清空字符串池的内容
*/
public void clear() {
stringSeed.replace(0, (stringSeed.length()), ""); // 将随机字符串生成范围清空
if (stringSeed.length() != 0) {
stringSeed.delete(0, stringSeed.length());
}
}
/**
* 该方法用于返回StringSeed的长度随机字符串生成范围的长度
* 用于返回字符串池的长度
*
* @return 返回随机字符串生成范围StringSeed的长度
* @return 字符串池的长度
*/
public int length() {
return stringSeed.length();
}
/**
* 该类用于在随机字符串生成范围中删除由用户指定的所有符合条件的字符串
* 用于删除字符串池中指定的字符串
*
* @param str
* 用户指定需要从随机字符串生成范围中删除的字符串
* @return 返回的字符串若输入的字符串错误则返回null
* @see #remove(int)
* @see #remove(int, int)
* @param str 需要删除的字符串
*/
public String remove(String str) {
public void remove(String str) {
// 判断StringSeed是否为空为空则返回false
if (stringSeed.length() == 0) {
return null;
return;
}
// 判断用户指定需要移除的字符串是否为空如果为空则返回false
if (str.equals("")) {
return null;
}
// 用于存储在StringSeed中查找到包含用户指定指定字符串str的位置
int StartPos = stringSeed.indexOf(str);
// 判断整个StringSeed中是否包含指定的字符串没有找到则返回false
if (StartPos < 0) // 如果无法找到与Char相匹配的字符串
{
return null; // 删除失败返回false
}
// 循环在StringSeed中移除指定的字符串
while (StartPos > 0) {
stringSeed.delete(StartPos, StartPos + str.length());// 在StringSeed中移除指定的字符串
StartPos = stringSeed.indexOf(str, StartPos);// 重新查找StringSeed是否还包含str
}
return str;// 删除结束返回true
// 对待删除的字符串进行过滤
Optional.ofNullable(str).filter(text -> !text.isEmpty())
.filter(text -> stringSeed.indexOf(text) > -1)
.ifPresent(text -> {
int index = -1;
// 循环直到字符串被完全删除为止
while ((index = stringSeed.indexOf(text)) > -1) {
remove(index, index + text.length());
}
});
}
/**
* 用于在随机字符串生成范围中删除由用户指定范围内字符串的位置是从0开始的字符串
* 用于删除字符串池中指定范围的字符串若两个下标相等则表示删除指定下标的字符串
* <p>
* <b>注意</b>指定的范围中删除的元素包括起始位置的元素但不包括结束位置的元素例如
* <code><pre>
* StringSeed.append("0123456789");
* remove(0, 3);
* System.out.println(StringSeed.toString());
* </pre></code>
* 输出结果为3456789
* <b>注意</b>
* <ol>
* <li>指定的范围中删除的元素包括起始位置的元素但不包括结束位置的元素</li>
* <li>在指定的范围不正确时将有如下的处理方式
* <ul>
* <li>若起始下标大于结束下标时则调换下标位置</li>
* <li>若起始下标小于0时
* <ul>
* <li>结束下标大于0则设置起始下标为0</li>
* <li>结束下标小于等于0则返回空串并不做任何删除</li>
* </ul>
* </li>
* <li>结束下标大于字符串池长度时
* <ul>
* <li>起始下标小于字符串池长度则设置结束下标为字符串池长度</li>
* <li>起始下标大于等于字符串池长度则返回空串并不做任何删除</li>
* </ul>
* </li>
* </ul>
* </li>
* </ol>
* </p>
* <p>
*
* @param startPos
* 表示用户指定范围的起始位置包括该位置的元素
* @param endPos
* 表示用户指定范围的结束位置不包括该位置的元素
* @param startIndex 表示用户指定范围的起始位置包括该位置的元素
* @param endIndex 表示用户指定范围的结束位置不包括该位置的元素
* @return 返回的字符串若输入的位置不正确则返回null
* @see #remove(int)
* @see #remove(String)
*/
public String remove(int startPos, int endPos) {
public String remove(int startIndex, int endIndex) {
// 判断StringSeed是否为空为空则返回false
if (stringSeed.length() == 0) {
return null;
}
// 判断用户指定开始位置是否大于结束位置大于则返回false
if (startPos > endPos) {
int temp = startPos;
startPos = endPos;
endPos = temp;
}
// 判断用户指定位置是否符合StringSeed的范围不符合则返回false
if (startPos < 0 || endPos > stringSeed.length()) {
return null;
}
String s = stringSeed.substring(startPos, endPos);
stringSeed.delete(startPos, endPos);// 删除指定位置的字符串
// 返回被删除的字符串
return s;
}
/**
* 该类用于在随机字符串生成范围中删除由用户指定位置字符串的位置是从0开始的字符串<br/>
* 例如
* <pre><code>
* StringSeed.append("0123456789");
* remove(2);
* System.out.println(StringSeed.toString());
* </code></pre>
* 输出结果为<br/>
* 013456789
*
* @param pos 表示用户指定位置
* @return 返回的字符串若输入的位置不正确则返回null
* @see #remove(String)
* @see #remove(int, int)
*/
public String remove(int pos) {
return remove(pos, pos + 1);// 删除成功返回true
}
/**
* 该方法用于移除随机字符串生成范围中重复的字符以达到范围中字符串不会重复的情况
*
* @return 返回移除字符串的状态true表示移除成功false表示移除失败
*/
public boolean removeRepetition() {
// 判断StringSeed是否为空为空则返回false
if (stringSeed.length() == 0) {
return false;
}
// 用于保存删除重复元素后的字符串
StringBuilder s = new StringBuilder();
// 循环拆分并删除字符串重复元素
for (int i = 0; i < stringSeed.length(); i++) {
// 判断元素是否在s中存在存在则不添加
if (s.indexOf(getStringSeed().substring(i, i + 1)) > -1) {
continue;
}
// 不存在的字符串则添加进s中
s.append(getStringSeed().substring(i, i + 1));
}
// 判断新字符串与原字符串是否相同相同则说明未进行改动返回false
if (s.toString().equals(getStringSeed())) {
return false;
}
clear();// 调用clear()清空StringSeed
stringSeed.append(s.toString());// 将s中的元素放入StringSeed中
return true;//
}
/**
* 乱序随机字符串产生的范围
*/
public void shuffle() {
// 定义临时存储乱序后的字符串
StringBuilder s = new StringBuilder();
// 循环使字符串产生范围乱序
while (true) {
// 判断字符串产生范围剩余量若其余量为0则表示随机字符串范围已被清空则结束循环
if (stringSeed.length() == 0) {
break;
}
// 以删除的方式来逐渐清空stringSeed并将删除的字符拼接到s
s.append(remove(new Random().nextInt(stringSeed.length())));
}
// 将生成的乱序范围重新赋予至stringSeed中
stringSeed = s;
}
/**
* 以默认方式返回生成的随机字符串默认长度为6个字符
*
* @return 生成的6个字符长度的字符串
* @see #toString(int)
* @see #toString(int, int)
* @throws IllegalDataException 产生字符串不允许重复且字符串产生范围长度小于6位处理方式为抛出异常时抛出
*/
@Override
public String toString() {
// 判断随机字符串生成范围是否为空为空则直接返回空字符串
if (stringSeed.length() == 0) {
return "";
}
return joinString(6);
// 判断最大生成长度与最小生成长度的关系
if (startIndex > endIndex) {
int temp = startIndex;
startIndex = endIndex;
endIndex = temp;
}
// 判断最小下标是否小于0
if (startIndex < 0) {
// 再判断最大下标是否大于0大于0则将最小下标为1
if (endIndex >= 0) {
startIndex = 0;
} else {
return "";
}
}
// 判断最大下标是否大于字符串池长度
if (endIndex >= stringSeed.length()) {
// 再判断最小下标是否小于字符串池长度
if (startIndex < stringSeed.length()) {
endIndex = stringSeed.length();
} else {
return "";
}
}
//判断两个下标是否一致
if (startIndex == endIndex) {
endIndex += 1;
}
String delectStr = stringSeed.substring(startIndex, endIndex);
// 删除指定位置的字符串
stringSeed.delete(startIndex, endIndex);
// 返回被删除的字符串
return delectStr;
}
/**
* 用于删除字符串池中指定下标的字符串
*
* @param index 表示用户指定位置
* @return 返回被删除的字符串
* @see #remove(String)
* @see #remove(int, int)
*/
public String remove(int index) {
return remove(index, index);
}
/**
* 用于移除随机字符串生成范围中重复的字符以达到范围中字符串不会重复的情况
*/
public void removeRepetition() {
// 判断StringSeed是否为空为空则返回false
if (stringSeed.length() == 0) {
return;
}
// 用于保存删除重复元素后的字符串
StringBuilder newStringSeed = new StringBuilder();
// 读取stringSeed中的内容过滤掉已存在的字符串
stringSeed.chars().mapToObj(ch -> String.valueOf((char) ch))
.filter(text -> newStringSeed.indexOf(text) < 0)
.forEach(newStringSeed::append);
// 清空StringSeed
clear();
// 将去重后的字符串放入stringSeed中
stringSeed.append(newStringSeed);
}
/**
* 以默认方式返回生成的随机字符串
* <p>
* 默认长度为6个字符可通过{@link #defaultLength}属性进行修改
* </p>
*
* @return 默认长度的随机字符串
* @throws IllegalDataException 当产生字符串不允许重复且字符串产生范围长度小于默认长度
* 处理方式为{@link RepeatDisposeType#DISPOSE_THROW_EXCEPTION}时抛出的异常
*/
@Override
public String toString() {
return toString(defaultLength);
}
/**
* 生成随机长度随机字符串其长度范围由用户指定
*
* @param stringLengthMin
* 随机长度的最小值
* @param stringLengthMax
* 随机长度的最大值
* @param minLength 随机长度的最小值
* @param maxLength 随机长度的最大值
* @return 返回生成的随机字符串
* @see #toString()
* @see #toString(int)
* @throws IllegalDataException 产生字符串不允许重复且传入的参数大于字符串产生范围长度处理方式为抛出异常时抛出
* @throws IllegalDataException 当产生字符串不允许重复且字符串产生范围长度小于默认长度
* 处理方式为{@link RepeatDisposeType#DISPOSE_THROW_EXCEPTION}时抛出的异常
*/
public String toString(int stringLengthMin, int stringLengthMax) {
// 判断随机字符串生成范围是否为空为空则直接返回空字符串
if (stringSeed.length() == 0) {
return "";
public String toString(int minLength, int maxLength) {
// 判断最大生成长度与最小生成长度的关系
if (minLength > maxLength) {
int temp = minLength;
minLength = maxLength;
maxLength = temp;
}
// 判断指定的字符串长度是否小于0如果是则返回空字符串
if (stringLengthMin < 0) {
return "";
}
// 判断指定的字符串长度最小值是否大于最大值如果是则返回空字符串
if (stringLengthMax - stringLengthMin < 0) {
return "";
// 判断最少个数是否小于1
if (minLength < 1) {
// 再判断最多个数是否大于1大于1则将最少个数设为1
if (maxLength > 1) {
minLength = 1;
} else {
// 若最少最多的数字都小于1则返回空集合
return "";
}
}
if ( isRepeat() && stringLengthMax > stringSeed.length() && dispose == DISPOSE_THROW_EXCEPTION ) {
throw new IllegalDataException("最大生成长度大于字符串产生范围的长度");
//判断两个数值是否一致
if (minLength == maxLength) {
return toString(minLength);
} else {
// 返回生成的字符串
return toString(new Random().nextInt((maxLength - minLength + 1)) + minLength);
}
return joinString(new Random().nextInt((stringLengthMax - stringLengthMin + 1)) + stringLengthMin); // 返回生成的字符串
}
/**
* 生成固定长度的随机字符串其长度由用户指定
*
* @param stringLength
* 随机字符串的长度
* @param stringLength 随机字符串的长度
* @return 返回生成的随机字符串
* @see #toString()
* @see #toString(int, int)
* @throws IllegalDataException 产生字符串不允许重复且传入的参数大于字符串产生范围长度处理方式为抛出异常时抛出
* @throws IllegalDataException 当产生字符串不允许重复且字符串产生范围长度小于默认长度
* 处理方式为{@link RepeatDisposeType#DISPOSE_THROW_EXCEPTION}时抛出的异常
*/
public String toString(int stringLength) {
// 判断随机字符串生成范围是否为空为空则直接返回空字符串
if (stringSeed.length() == 0) {
return "";
}
// 判断指定的字符串长度是否小于0如果是则返回空字符串
if (stringLength < 0) {
return "";
}
return joinString(stringLength); // 返回生成的字符串
return createRandomString(stringLength, stringSeed.toString());
}
/**
* 组装字符串生成范围用以在不允许出现重复的情况下对StringSeed进行修改 在调用该方法时会先将传参中的字符串拆分为单个字符串分别进行判断
* 达到不添加重复字符串的目的
*
* @param mode
* 待添加入StringSeed的字符串
* @return 返回字符串添加状态true为添加成功false为添加失败
*/
private void packageString(String mode) {
// 循环将Mode拆分为单个字符串并分别与StringSeed进行对比如果某个字符存在则不添加该字符以保证该字符不会重复添加
for (int i = 0; i < mode.length(); i++) {
// 判断当前字符是否存在与StringSeed中如果存在则继续循环判断下一个字符
if (stringSeed.indexOf(mode.substring(i, i + 1)) > -1) {
continue;
}
// 如果不存在拆分的字符则将字符存入StringSeed中并将flag定义为true
stringSeed.append(mode.substring(i, i + 1));
}
}
/**
* 用于生成随机字符串的核心算法
* 用于简单返回随机字符串下标规则可参考{@link #toString(int, int)}方法
*
* @param stringLength
* 指定生成的随机字符串长度
* @param minLength 随机长度的最小值
* @param maxLength 随机长度的最大值
* @param seed 字符串产生范围
* @return 返回生成的随机字符串
* @see #toString()
* @see #toString(int)
* @see #toString(int, int)
* @throws IllegalDataException 产生字符串不允许重复且传入的参数大于字符串产生范围长度处理方式为抛出异常时抛出
*/
private String joinString(int stringLength) {
// 判断字符串字符是否允许重复不允许重复时其处理方式是否为抛异常为抛异常时其stringLength是否大于字符串产生范围的长度若是则直接抛出异常
if (!isRepeat() && dispose == DISPOSE_THROW_EXCEPTION && stringLength > stringSeed.length()) {
throw new IllegalDataException("需要生成的随机字符串长度大于字符串产生范围的最大长度");
public static String randomString(int minLength, int maxLength, String seed) {
RandomString rs = new RandomString(seed);
return rs.toString(minLength, maxLength);
}
/**
* 用于简单返回随机字符串下标规则可参考{@link #toString(int, int)}方法
*
* @param minLength 随机长度的最小值
* @param maxLength 随机长度的最大值
* @param stringModes 字符串产生范围,{@link StringMode}枚举对象
* @return 返回生成的随机字符串
*/
public static String randomString(int minLength, int maxLength, StringMode... stringModes) {
RandomString rs = new RandomString(stringModes);
return rs.toString(minLength, maxLength);
}
/**
* 用于向字符串池中添加字符串模型
*
* @param mode 字符串模型
*/
private void joinStringSeed(boolean isRepeat, String mode) {
// 过滤掉null与空串后若存在数据则根据条件向字符串池中添加数据
Optional.ofNullable(mode).filter(text -> !text.isEmpty()).ifPresent(text -> {
// 判断传入的参数是否允许字符串池中元素重复若为true则等同于调用addMode(StringMode... modes)
if (!isRepeat) {
// 判断字符串整串是否都在stringSeed中
if (stringSeed.indexOf(text) > -1) {
return;
}
// 将字符串拆成单个字符串分别在stringSeed中进行判断
IntStream.range(0, text.length()).mapToObj(text::charAt)
// 将char类型转为字符串
.map(String::valueOf)
// 过滤掉存在于stringSeed中的字符
.filter(ch -> stringSeed.indexOf(ch) < 0)
.forEach(stringSeed::append);
} else {
stringSeed.append(text);
}
});
}
/**
* 用于根据字符串池生成随机字符串
*
* @param length 生成的字符串长度
* @param stringSeed 字符串池
* @return 生成的随机字符串
* @throws IllegalDataException 当产生字符串不允许重复且字符串产生范围长度小于默认长度
* 处理方式为{@link RepeatDisposeType#DISPOSE_THROW_EXCEPTION}时抛出的异常
*/
private String createRandomString(int length, String stringSeed) {
// 判断需要生成的字符串长度是否小于1位长度
if (length < 1) {
return "";
}
String RandomString = ""; // 定义一个字符串变量用于存储选择的字符串
int ChooiseNumber = -1; // 定义一个整形变量用于随机的生成数字并用于从StringSeed中选择对应的字符
Random RandomNumber = new Random(); // 定义一个Random变量用于生成随机数字
// 记录当前是否允许重复
boolean isRepeat = repeat;
//定义循环条件
int i = 0;
//判断是否允许产生的字符串中存在重复的字符
if (!isRepeat()) {
//定义临时字符串存储原字符串产生的范围
String s = stringSeed.toString();
//乱序字符串产生的范围
shuffle();
//判断传入的字符串生成长度是否小于字符串范围的总长度小于则按照字符串乱序后截取的方式获取随机字符串否则 则读取处理方式
if ( stringLength <= stringSeed.length() ) {
//截取字符串
RandomString = stringSeed.substring(0, stringLength);
//还原字符串产生范围
clear();
stringSeed.append(s);
return RandomString;
} else {
//将乱序后的范围赋给RandomString
RandomString = stringSeed.toString();
//还原字符串产生范围
clear();
stringSeed.append(s);
//判断操作方式是否为忽略若为忽略则直接返回乱序后的随机字符串
if (dispose == DISPOSE_IGNORE) {
return RandomString;
} else {
//若处理方式为重复则将循环的次数直接加上字符串的长度等同于循环了N次
i = RandomString.length();
}
// 判断当前字符串长度是否大于字符串池的最大长度
if (length > stringSeed.length() && !isRepeat) {
// 若传入的长度大于字符串且不能重复时则根据不同的设置做出相应的处理
switch (dispose) {
case DISPOSE_IGNORE:
length = stringSeed.length();
break;
case DISPOSE_THROW_EXCEPTION:
throw new IllegalDataException("需要生成的随机字符串长度大于字符串产生范围的最大长度");
case DISPOSE_REPEAT:
default:
isRepeat = true;
break;
}
}
// 循环在RandomString中放入随机字符其循环次数决定字符串的长度
for (; i < stringLength; i++) {
ChooiseNumber = RandomNumber.nextInt(stringSeed.length()); // 从0到StringSeed的总长中随机生成一个数字
RandomString = RandomString + stringSeed.charAt(ChooiseNumber); // 将该数字在StringSeed中对应的字符赋给RandomString
// 存储生成的随机字符串
StringBuilder randomString = new StringBuilder();
StringBuilder seed = new StringBuilder(stringSeed);
Random random = new Random();
// 循环生成随机字符串
while (randomString.length() < length) {
// 根据字符串池的长度生成随机数
int randomNum = random.nextInt(seed.length());
randomString.append(seed.charAt(randomNum));
// 若当前不允许重复则删除被获取的字符
if (!isRepeat) {
seed.deleteCharAt(randomNum);
}
}
return RandomString; // 返回生成的字符串
return randomString.toString();
}
/**
* <p>
* <b>文件名</b>RandomString.java
* </p>
* <p>
* <b>用途</b> 定义在设置随机字符串不允许出现重复但需要生成的字符串长度大于随机字符串池的长度时的处理方式
* </p>
* <p>
* <b>编码时间</b>2021年1月16日下午2:11:36
* </p>
* <p>
* <b>修改时间</b>2021年1月16日下午2:11:36
* </p>
*
* @author 彭宇琦
* @version Ver1.0
* @since JDK 1.8
*
*/
public enum RepeatDisposeType {
/**
* 忽略只生成一个与随机字符串范围长度相同的随机字符串
*/
DISPOSE_IGNORE,
/**
* 抛出异常中断生成字符串的操作
*/
DISPOSE_THROW_EXCEPTION,
/**
* 重复将随机字符串的生成条件改为允许字符串重复
*/
DISPOSE_REPEAT;
}
}

View File

@ -1,135 +0,0 @@
package com.auxiliary.tool.data;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
public class RandomWord {
/**
* 用于存储需要随机返回的词语
*/
private ArrayList<String> returnWordList = new ArrayList<>();
/**
* 用于存储默认返回的词语
*/
private ArrayList<String> conditionWordList = new ArrayList<>();
/**
* 指向生成的词语是否能重复
*/
private boolean isRepeat = true;
/**
* 用于向无需条件亦可直接返回的词语组中添加词语
* @param words 词语组
*/
public void addRetuenWord(String...words) {
returnWordList.addAll(Arrays.asList(words));
}
/**
* 用于向无需条件亦可直接返回的词语组中添加词语
* @param wordList 词语集合
*/
public void addRetuenWord(ArrayList<String> wordList) {
returnWordList.addAll(wordList);
}
/**
* 用于向符合条件后返回的词语组中添加词语
* @param words 词语组
*/
public void addConditionWord(String...words) {
conditionWordList.addAll(Arrays.asList(words));
}
/**
* 用于向符合条件后返回的词语组中添加词语
* @param wordList 词语集合
*/
public void addConditionWord(ArrayList<String> wordList) {
conditionWordList.addAll(wordList);
}
/**
* 用于设置词语是否可以重复生成
* @param isRepeat 词语是否可重复
*/
public void setRepeat(boolean isRepeat) {
this.isRepeat = isRepeat;
}
/**
* 用于随机生成指定随机个数的词语
* @param minLength 最少词语个数
* @param maxLength 最多词语个数
* @return 生成的词语集合
*/
public ArrayList<String> toWord(int minLength, int maxLength) {
return toWord(getLength(minLength, maxLength));
}
/**
* 用于随机生成指定个数的词语
* @param length 输出的词语个数
* @return 生成的词语集合
*/
public ArrayList<String> toWord(int length) {
//克隆returnWordList便于对不能生成重复词语的情况下移除集合中的元素达到快速返回的目的
@SuppressWarnings("unchecked")
ArrayList<String> wordList = (ArrayList<String>) returnWordList.clone();
//存储生成的词语
ArrayList<String> randomWordList = new ArrayList<>();
//循环生成相应的词语
for (int i = 0; i < length && wordList.size() > 0; i++) {
//获取随机下标
int index = getRandomIndex(wordList);
//存储下标对应的词语
randomWordList.add(wordList.get(index));
//若生成的词语不可重复则直接移除wordList中对应的词语
if (!isRepeat) {
wordList.remove(index);
}
}
return randomWordList;
}
/**
* 用于根据词语是否允许重复返回实际的最大生成词语个数
* @param maxLength 设置的最大词语生成个数
* @return 实际最大词语生成个数
*/
private int getLength(int minLength, int maxLength) {
//若词语不允许重复生成且minLength大于returnWordList.size()则将minLength改为returnWordList.size()
minLength = (!isRepeat && minLength > returnWordList.size()) ? returnWordList.size() : minLength;
//若词语不允许重复生成且maxLength大于returnWordList.size()则将maxLength改为returnWordList.size()
maxLength = (!isRepeat && maxLength > returnWordList.size()) ? returnWordList.size() : maxLength;
//若两个数字一致则返回任意一个数字
if (minLength == maxLength) {
return maxLength;
}
//为避免最小长度与最大长度传参相反故此处进行一个调换
int max = Math.max(minLength, maxLength);
int min = Math.min(minLength, maxLength);
//返回指定长度下的一个随机长度
return (new Random().nextInt(max - min + 1) + min);
}
/**
* 根据指定的集合返回随机的一个下标
* @param wordList 词语集合
* @return 集合元素的随机下标
*/
private int getRandomIndex(ArrayList<String> wordList) {
//根据条件获取集合的长度
int maxLength = wordList.size();
//生成最大长度范围内的随机数
return new Random().nextInt(maxLength);
}
}

View File

@ -0,0 +1,193 @@
package com.auxiliary.tool.date;
import java.time.LocalDateTime;
import java.util.Optional;
/**
* <p>
* <b>文件名</b>InitTimeUtil.java
* </p>
* <p>
* <b>用途</b> 提供特殊的更改日期/时间的方法
* </p>
* <p>
* <b>编码时间</b>2021年1月21日上午8:15:45
* </p>
* <p>
* <b>修改时间</b>2021年1月21日上午8:15:45
* </p>
*
* @author 彭宇琦
* @version Ver1.0
* @since JDK 1.8
*/
public class InitTimeUtil {
private InitTimeUtil(){
}
/**
* 更改日期为指定日期下当年的第一天
* <p>
* 例如指定时间为2019-12-21 15:21:10则调用方法后将返回时间为2019-01-01 00:00:00
* </p>
* <p>
* <b>注意</b>若日期未传入null则默认为当天的日期
* </p>
*
* @param date 指定的日期
* @return 更改的日期
*/
public static LocalDateTime firstDayOfYear(LocalDateTime date) {
date = Optional.ofNullable(date).orElse(LocalDateTime.now());
return LocalDateTime.of(date.getYear(), 1, 1, 0, 0, 0);
}
/**
* 更改日期为指定日期下当年的最后第一天
* <p>
* 方法允许指定是否将时间也指向最后的日期例如
* <ol>
* <li>指定时间为2019-12-21 15:21:10设置时间指向最后一刻则返回时间为2019-12-31 23:59:59</li>
* <li>指定时间为2019-12-21 15:21:10不设置时间则返回时间为2019-12-31 15:21:10</li>
* </ol>
* </p>
* <p>
* <b>注意</b>若日期未传入null则默认为当天的日期
* </p>
*
* @param date 指定的日期
* @return 更改的日期
*/
public static LocalDateTime lastDayOfYear(boolean isLastTime, LocalDateTime date) {
date = Optional.ofNullable(date).orElse(LocalDateTime.now());
return LocalDateTime.of(date.getYear(), 12, 31, isLastTime ? 23 : date.getHour(),
isLastTime ? 59 : date.getMinute(), isLastTime ? 59 : date.getSecond());
}
/**
* 更改日期为指定日期下当月的第一天
* <p>
* 例如指定时间为2019-12-21 15:21:10则调用方法后将返回时间为2019-12-01 00:00:00
* </p>
* <p>
* <b>注意</b>若日期未传入null则默认为当天的日期
* </p>
*
* @return 更改的日期
*/
public static LocalDateTime firstDayOfMonth(LocalDateTime date) {
date = Optional.ofNullable(date).orElse(LocalDateTime.now());
return LocalDateTime.of(date.getYear(), date.getMonth(), 1, 0, 0, 0);
}
/**
* 更改日期为指定日期下当月的最后一天
* <p>
* 方法允许指定是否将时间也指向最后的日期例如
* <ol>
* <li>指定时间为2019-12-21 15:21:10设置时间指向最后一刻则返回时间为2019-12-31 23:59:59</li>
* <li>指定时间为2019-12-21 15:21:10不设置时间则返回时间为2019-12-31 15:21:10</li>
* </ol>
* </p>
* <p>
* <b>注意</b>若日期未传入null则默认为当天的日期
* </p>
*
* @return 更改的日期
*/
public static LocalDateTime lastDayOfMonth(boolean isLastTime, LocalDateTime date) {
date = Optional.ofNullable(date).orElse(LocalDateTime.now());
// 设置月份为当前月份的下一月的第一天
// 当月份为12月时使用月份数值加1则会抛出异常故在异常时设置月份为下一年的第一天即可
try {
date = LocalDateTime.of(date.getYear(), date.getMonthValue() + 1, 1, isLastTime ? 23 : date.getHour(),
isLastTime ? 59 : date.getMinute(), isLastTime ? 59 : date.getSecond());
} catch (Exception e) {
date = LocalDateTime.of(date.getYear() + 1, 1, 1, isLastTime ? 23 : date.getHour(),
isLastTime ? 59 : date.getMinute(), isLastTime ? 59 : date.getSecond());
}
// 将日期减去一天即可得到本月的最后一天日期
return date.minusDays(1);
}
/**
* 更改日期为指定日期下当天的0点
* <p>
* 例如指定时间为2019-12-21 15:21:10则调用方法后将返回时间为2019-12-21 00:00:00
* </p>
* <p>
* <b>注意</b>若日期未传入null则默认为当天的日期
* </p>
*
* @return 更改的日期
*/
public static LocalDateTime startTimeOfDay(LocalDateTime date) {
date = Optional.ofNullable(date).orElse(LocalDateTime.now());
return LocalDateTime.of(date.getYear(), date.getMonth(), date.getDayOfMonth(), 0, 0, 0);
}
/**
* 更改日期为指定日期下当天的最后一刻
* <p>
* 例如指定时间为2019-12-21 15:21:10则调用方法后将返回时间为2019-12-21 23:59:59
* </p>
* <p>
* <b>注意</b>若日期未传入null则默认为当天的日期
* </p>
*
* @return 更改的日期
*/
public static LocalDateTime lastTimeOfDay(LocalDateTime date) {
date = Optional.ofNullable(date).orElse(LocalDateTime.now());
return LocalDateTime.of(date.getYear(), date.getMonth(), date.getDayOfMonth(), 23, 59, 59);
}
/**
* 更改日期为指定日期下当天的指定的小时数的起始时间
* <p>
* 例如指定时间为2019-12-21 15:21:10则调用方法后将返回时间为2019-12-21 15:00:00
* </p>
* <p>
* <b>注意</b>
* <ol>
* <li>若日期未传入null则默认为当天的日期</li>
* <li>若传入的小时数大于23或者小于0则将小时数初始化为指定时间的小时数</li>
* </ol>
* </p>
*
* @param hour 指定的小时数
* @return 更改的日期
*/
public static LocalDateTime startHour(int hour, LocalDateTime date) {
date = Optional.ofNullable(date).orElse(LocalDateTime.now());
if (hour > 23 || hour < 0) {
hour = date.getHour();
}
return LocalDateTime.of(date.getYear(), date.getMonth(), date.getDayOfMonth(), hour, 0, 0);
}
/**
* 更改日期为指定日期下当天的指定的分钟数的起始时间
* <p>
* 例如指定时间为2019-12-21 15:21:10则调用方法后将返回时间为2019-12-21 15:21:00
* </p>
* <p>
* <b>注意</b>
* <ol>
* <li>若日期未传入null则默认为当天的日期</li>
* <li>若传入的分钟数大于59或者小于0则将数值初始化为指定时间的分钟数</li>
* </ol>
* </p>
*
* @param hour 指定的分钟数
* @return 更改的日期
*/
public static LocalDateTime startMinute(int minute, LocalDateTime date) {
date = Optional.ofNullable(date).orElse(LocalDateTime.now());
if (minute > 59 || minute < 0) {
minute = date.getMinute();
}
return LocalDateTime.of(date.getYear(), date.getMonth(), date.getDayOfMonth(), date.getHour(), minute, 0);
}
}

View File

@ -1,10 +1,16 @@
package com.auxiliary.tool.date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Date;
import java.util.regex.Pattern;
import java.util.Optional;
/**
* <p>
@ -17,128 +23,183 @@ import java.util.regex.Pattern;
* <b>编码时间</b>2019年12月2日下午5:15:55
* </p>
* <p>
* <b>修改时间</b>2019年12月2日下午5:15:55
* <b>修改时间</b>2021年1月20日下午12:43:01
* </p>
*
* @author 彭宇琦
* @version Ver1.0
* @since JDK 12
* @since JDK 1.8
*
*/
public class Time {
public class Time implements Comparable<Time> {
/**
* 用于指向指定的时间
* 定义默认时区
*/
private Date date;
public static ZoneId defaultZoneId = ZoneId.systemDefault();
/**
* 用于设置的时间以保证在增加或减少时间后能还原回初始设置的时间
* 指向初始化时设置的时间
*/
private Date oldDate;
private LocalDateTime initTime;
/**
* 指向根据初始化时间计算后得到的时间
*/
private LocalDateTime calculateTime;
/**
* 用于存储日期的格式默认格式为yyyy-MM-dd HH:mm:ss
*/
private String dateFormat = "yyyy-MM-dd HH:mm:ss";
private static DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 定义日期约束类型的传入格式
*/
private final String REGEX_DATE = "(\\d{4}[-\\.年\\\\/][01]?\\d[-\\.月\\\\/][0123]?\\d日?"
+ "( [012]?\\d[:时][0123456]?\\d分?([:分][0123456]?\\d秒?)?)?)|"
+ "([012]?\\d[:时][0123456]?\\d分?([:分][0123456]?\\d秒?))";
private final static String REGEX_DATE = "(\\D*((\\d{1,2})|(\\d{4}))\\D+\\d{1,2}\\D+\\d{1,2})((\\D+\\d{1,2}){3})?\\D*";
/**
* 构造当前时间
* 初始化日期/时间
*
* @param initTime 日期/时间
*/
public Time() {
setNowTime();
private Time(LocalDateTime initTime) {
this.initTime = initTime;
this.calculateTime = initTime;
}
/**
* 通过Date类对象进行构造
* 用于根据{@link Date}类对象初始化时间
*
* @param date Date类对象
*/
public Time(Date date) {
setTime(date);
public static Time parse(Date date) {
return parse(Optional.ofNullable(date).orElse(new Date()).getTime());
}
/**
* 通过时间戳进行构造
* 用于根据毫秒数初始化时间
*
* @param ms 时间戳毫秒值
*/
public Time(long ms) {
setTime(ms);
public static Time parse(long ms) {
// 转换时间戳
return new Time(LocalDateTime.ofInstant(Instant.ofEpochMilli(ms), defaultZoneId));
}
/**
* 通过格式化过的时间进行构造
* 用于根据已格式化的时间初始化时间
*
* @param formatTime 已格式化的时间
* @throws IncorrectConditionException 时间转换错误时抛出的异常
*/
public Time(String formatTime) {
setTime(formatTime);
public static Time parse(String formatTime) {
// 判断传入的格式化时间是否符合要求并将其转换为格式化字符串
return parse(formatTime,
Optional.ofNullable(formatTime).filter(text -> !text.isEmpty()).filter(text -> text.matches(REGEX_DATE))
.map(Time::judgeDateFormatText)
.orElseThrow(() -> new IncorrectConditionException("时间“" + formatTime + "”不符合格式的规则")));
}
/**
* 用于Date类对象设置指定的时间
* 用于根据格式化的日期/时间及相应的时间格式初始化日期/时间
* <p>
* 该方法允许只传入格式化的日期或者时间 <code><pre>
* Time time1 = Time.parse("2020-12-12", "yyyy-MM-dd");//初始化为2020年12月12日的0点
* Time time2 = Time.parse("15:15:15", "HH:mm:ss");//初始化为当天的15时15分15秒
* </pre></code>
* </p>
*
* @param date Date类对象
* @param formatTime 格式化的日期/时间
* @param formatText 时间格式
* @return 初始化的类
* @throws DateTimeParseException 日期/时间无法转换时抛出的异常
*/
public void setTime(Date date) {
this.date = date;
oldDate = this.date;
}
/**
* 用于根据毫秒数设置指定的时间
*
* @param ms 时间戳毫秒值
*/
public void setTime(long ms) {
date = new Date(ms);
oldDate = this.date;
}
/**
* 用于根据已格式化的时间设置指定的时间
*
* @param formatTime 已格式化的时间
* @throws IncorrectConditionException 时间转换错误时抛出的异常
*/
public void setTime(String formatTime) {
//若formatTime传入为Null或为空串则设置为当前时间
if (formatTime == null || formatTime.isEmpty()) {
setNowTime();
return;
}
if (formatTime.matches(REGEX_DATE)) {
try {
date = new SimpleDateFormat(getDateFormat(formatTime)).parse(formatTime);
oldDate = this.date;
} catch (ParseException e) {
public static Time parse(String formatTime, String formatText) {
// 定义相应的时间格式并用于解析传入的时间
dateFormat = DateTimeFormatter.ofPattern(formatText);
try {
return new Time(LocalDateTime.parse(formatTime, dateFormat));
} catch (DateTimeParseException e) {
if (formatText.matches(".*M+.*")) {
return new Time(LocalDate.parse(formatTime, dateFormat).atStartOfDay());
} else {
return new Time(LocalTime.parse(formatTime, dateFormat).atDate(LocalDate.now()));
}
} else {
throw new IncorrectConditionException("时间“" + formatTime + "”不符合格式的规则");
}
}
/**
* 用于将时间设置为当前时间
* 用于根据{@link LocalDateTime}对象初始化日期/时间若未传入时间或时间写入有误则初始化为当前时间
*
* @param dateTime 指定的{@link LocalDateTime}对象
*/
public void setNowTime() {
date = new Date();
oldDate = this.date;
public static Time parse(LocalDateTime dateTime) {
return new Time(Optional.ofNullable(dateTime).orElse(LocalDateTime.now()));
}
/**
* 用于将时间初始化为当前时间
*/
public static Time parse() {
return new Time(LocalDateTime.now());
}
/**
* 用于对日期/时间中的指定单位进行赋值
* @param timeNum 指定的数值
* @param timeUnit 时间单位枚举{@link TimeUnit}
* @return 类本身
* @throws IncorrectConditionException 数值无法被赋入相应的单位下时抛出的异常
*/
public Time setTime(int timeNum, TimeUnit timeUnit) {
//记录每个时间下的数值
int year = calculateTime.getYear();
int month = calculateTime.getMonthValue();
int day = calculateTime.getDayOfMonth();
int hour = calculateTime.getHour();
int minute = calculateTime.getMinute();
int second = calculateTime.getSecond();
//根据枚举对相应的日期进行赋值
switch (timeUnit) {
case YEAR:
year = timeNum;
break;
case MONTH:
month = timeNum;
break;
case DAY:
day = timeNum;
break;
case HOUR:
hour = timeNum;
break;
case MINUTE:
minute = timeNum;
break;
case SECOND:
second = timeNum;
break;
default:
break;
}
//格式化时间若时间无法被写入则抛出IncorrectConditionException异常
try {
calculateTime = LocalDateTime.of(year, month, day, hour, minute, second);
} catch (Exception e) {
throw new IncorrectConditionException(
String.format("不存在的日期:%d-%d-%d %d:%d:%d", year, month, day, hour, minute, second), e);
}
return this;
}
/**
* 设置返回时间的格式该方法可传入时间格式亦可向该方法中传入时间格式的模板
* 通过识别模板得到日期的格式但作为模板的日期也必须满足时间格式例如<br>
* <pre><code>
*
* <pre>
* <code>
* Time time = new Time(1575387800000L);
*
* time.setTimeFormat("yyyy年MM月dd日 HH:mm:ss");
@ -146,20 +207,35 @@ public class Time {
*
* time.setTimeFormat("2019/12/04 03:03:20");
* getFormatTime();//输出2019/12/03 23:43:20
* </code></pre>
* 注意传入已格式化的时间时其不会改变当前存储的时间
* </code>
* </pre>
*
* <p>
* <b>注意</b>
* <ol>
* <li>传入已格式化的时间时其不会改变当前存储的时间</li>
* <li>已格式化的时间中其分隔符不能包含字母否则转译将出错在格式化时间的方法中也不允许存在字母</li>
* </ol>
* </p>
*
* @param pattern 指定的格式或已格式化的时间
*/
public void setTimeFormat(String pattern) {
public Time setTimeFormat(String pattern) {
pattern = Optional.ofNullable(pattern).filter(text -> !text.isEmpty())
.orElseThrow(() -> new IncorrectConditionException("未指定时格式"));
if (pattern.matches(REGEX_DATE)) {
dateFormat = getDateFormat(pattern);
} else {
try {
dateFormat = pattern;
} catch (IllegalArgumentException e) {
dateFormat = DateTimeFormatter.ofPattern(judgeDateFormatText(pattern));
} catch (IncorrectConditionException e) {
// 若转换时出现异常则按照基本的方式进行转换
dateFormat = DateTimeFormatter.ofPattern(pattern);
}
} else {
dateFormat = DateTimeFormatter.ofPattern(pattern);
}
return this;
}
/**
@ -168,18 +244,7 @@ public class Time {
* @return Date类对象
*/
public Date getDate() {
return date;
}
/**
* 用于返回Calendar类对象
* @return Calendar类对象
*/
public Calendar getCalendar() {
Calendar c = Calendar.getInstance();
c.setTime(date);
return c;
return Date.from(calculateTime.atZone(defaultZoneId).toInstant());
}
/**
@ -187,321 +252,349 @@ public class Time {
*
* @return 时间戳
*/
public long getTime() {
return date.getTime();
public long getMilliSecond() {
return calculateTime.atZone(defaultZoneId).toInstant().toEpochMilli();
}
/**
* 用于返回设置时间的格式化后的时间若通过{@link #Time(String)}构造
* {@link #setTime(String)}方法创建的时间则按照原格式进行返回 通过其他方法 创建的时间则按照默认的yyyy-MM-dd
* HH:mm:ss格式进行返回
* 用于以指定的格式返回格式化后的时间
* <p>
* 若未设置时间格式则默认按照yyyy-MM-dd HH:mm:ss的格式进行返回
* </p>
*
* @return 格式化后的时间
*/
public String getFormatTime() {
return new SimpleDateFormat(dateFormat).format(date);
}
/**
* 用于还原最后一次设置的时间
*/
public void initTime() {
date = oldDate;
}
/**
* <p>
* 用于根据传入的增减时间的规则对时间进行增减传入需要修改的时间单位
* 根据单位前的数值对单位进行增减例如需要对当前设置的时间增加1年3个月又5天并较少2小时30分钟45秒
* 此时可以传入1年3月5日-2时-30分-45秒亦可以传入1y3m5d-2h-30min-45s
* </p>
* <p>
* 注意
* <ol>
* <li>单位必须准确允许传入以下单位
* <ul>
* <li>年单位yY</li>
* <li>月单位mM</li>
* <li>周单位wW</li>
* <li>日单位dD</li>
* <li>小时单位hH</li>
* <li>分钟单位minMIN</li>
* <li>秒单位sS</li>
* </ul>
* </li>
* <li>允许传入小数但年月传入小数时按照自然年及自然月计算即1年365天不考虑闰年
* 1月30天不考虑大月与二月例如传入5.3y2.5h则5.3年转换为5.3 * 365天计算
* 2.5小时将转化为增加2.5 * 60分钟计算建议不要在年月中传入小数否则可能导致计算失真
* </li>
* </ol>
* </p>
*
*
* @param regex 时间规则
* @return 返回修改后的时间戳
*/
public long addTime(String regex) {
//去空格
regex = regex.replaceAll(" ", "");
// 初始化Calendar类用于修改日期
Calendar cTime = Calendar.getInstance();
cTime.setTime(date);
//封装传入的修改时间规则以便于删除已修改的规则
StringBuilder time = new StringBuilder(regex);
//用于指向规则中单位的位置
int index = -1;
// 判断是否包含分钟由于分钟比较特殊可能会与月份重复故放在第一位判断
//若存在小数点则按照毫秒进行转换
if ((index = time.indexOf("")) > -1 || (index = time.indexOf("min")) > -1
|| (index = time.indexOf("MIN")) > -1) {
//存储待判断单位前的字符串
String subTime = time.substring(0, index);
//存储待判断单位前数值位置
int numIndex = getIndex(time.substring(0, index));
//转换数值
int addTime = (int) (Double.valueOf(subTime.substring(numIndex)) * 60.0);
//cTime增加相应日期
cTime.add(Calendar.SECOND, addTime);
//删除已修改的单位以及其数值由于分钟单位可能为三位故需要根据具体传入的单位来定义
time = time.delete(numIndex,
time.substring(index, time.length()).indexOf("") == 0 ? index + 1 : index + 3);
}
//判断是否包含年份若存在小数点则忽略
if ((index = time.indexOf("")) > -1 || (index = time.indexOf("y")) > -1 || (index = time.indexOf("Y")) > -1) {
//存储待判断单位前的字符串
String subTime = time.substring(0, index);
//存储待判断单位前数值位置
int numIndex = getIndex(time.substring(0, index));
//转换数值
int addTime = 0;
String yearNum = subTime.substring(numIndex);
try {
//若传入的年份是整数则直接按照整数转换并对年份增加相应的数值
addTime = Integer.valueOf(yearNum);
cTime.add(Calendar.YEAR, addTime);
}catch (NumberFormatException e) {
//若年份包含小数则按照小数点进行切分先对年份的整数部分进行增加
String[] num = yearNum.split("\\.");
addTime = Integer.valueOf(num[0]);
cTime.add(Calendar.YEAR, addTime);
//之后对小数部分拼接0.转换为double之后将其转换成天数
int remainder = (int) (Double.valueOf((addTime > 0 ? "0.": "-0.") + num[1]) * 365.0);
cTime.add(Calendar.DATE, remainder);
}
//删除已修改的单位以及其数值
time = time.delete(numIndex, index + 1);
}
//判断是否包含月份若存在小数点则忽略
if ((index = time.indexOf("")) > -1 || (index = time.indexOf("m")) > -1 || (index = time.indexOf("M")) > -1) {
//存储待判断单位前的字符串
String subTime = time.substring(0, index);
//存储待判断单位前数值位置
int numIndex = getIndex(time.substring(0, index));
//转换数值
int addTime = 0;
String monthNum = subTime.substring(numIndex);
try {
//若传入的年份是整数则直接按照整数转换并对年份增加相应的数值
addTime = Integer.valueOf(monthNum);
cTime.add(Calendar.MONTH, addTime);
}catch (NumberFormatException e) {
//若年份包含小数则按照小数点进行切分先对年份的整数部分进行增加
String[] num = monthNum.split("\\.");
addTime = Integer.valueOf(num[0]);
cTime.add(Calendar.MONTH, addTime);
//之后对小数部分拼接0.转换为double之后将其转换成天数
addTime = (int) (Double.valueOf((addTime > 0 ? "0.": "-0.") + num[1]) * 30.0);
cTime.add(Calendar.DATE, addTime);
}
//删除已修改的单位以及其数值
time = time.delete(numIndex, index + 1);
}
//判断是否包含月份若存在小数点则忽略
if ((index = time.indexOf("")) > -1 || (index = time.indexOf("w")) > -1 || (index = time.indexOf("W")) > -1) {
//存储待判断单位前的字符串
String subTime = time.substring(0, index);
//存储待判断单位前数值位置
int numIndex = getIndex(time.substring(0, index));
//转换数值
int addTime = (int) (Double.valueOf(subTime.substring(numIndex)) * 7.0);
//cTime增加相应日期
cTime.add(Calendar.DATE, addTime);
//删除已修改的单位以及其数值
time = time.delete(numIndex, index + 1);
}
//判断是否包含天数若存在小数点则忽略
if ((index = time.indexOf("")) > -1 || (index = time.indexOf("d")) > -1 || (index = time.indexOf("D")) > -1) {
//存储待判断单位前的字符串
String subTime = time.substring(0, index);
//存储待判断单位前数值位置
int numIndex = getIndex(time.substring(0, index));
//转换数值
int addTime = (int) (Double.valueOf(subTime.substring(numIndex)) * 24.0 * 60.0 * 60.0);
//cTime增加相应日期
cTime.add(Calendar.SECOND, addTime);
//删除已修改的单位以及其数值
time = time.delete(numIndex, index + 1);
}
//判断是否包含小时若存在小数点则按照毫秒进行转换
if ((index = time.indexOf("")) > -1 || (index = time.indexOf("h")) > -1 || (index = time.indexOf("H")) > -1) {
//存储待判断单位前的字符串
String subTime = time.substring(0, index);
//存储待判断单位前数值位置
int numIndex = getIndex(time.substring(0, index));
//转换数值
int addTime = (int) (Double.valueOf(subTime.substring(numIndex)) * 60.0 * 60.0);
//cTime增加相应日期
cTime.add(Calendar.SECOND, addTime);
//删除已修改的单位以及其数值
time = time.delete(numIndex, index + 1);
}
//判断是否包含秒若存在小数点则按照毫秒进行转换
if ((index = time.indexOf("")) > -1 || (index = time.indexOf("s")) > -1 || (index = time.indexOf("S")) > -1) {
//存储待判断单位前的字符串
String subTime = time.substring(0, index);
//存储待判断单位前数值位置
int numIndex = getIndex(time.substring(0, index));
//转换数值
int addTime = (int) (Double.valueOf(subTime.substring(numIndex)) * 1.0);
//cTime增加相应日期
cTime.add(Calendar.SECOND, addTime);
//删除已修改的单位以及其数值
time = time.delete(numIndex, index + 1);
}
//将转换后的时间存储至date中
date = cTime.getTime();
return getTime();
return calculateTime.format(dateFormat);
}
/**
* 修改原始存储的时间返回修改后的时间戳且不影响原存储的时间具体修改规则可以参见{@link #addTime(String)}
* 用于以{@link LocalDateTime}类对象的形式返回计算后的日期/时间
* @return {@link LocalDateTime}类对象
*/
public LocalDateTime getLocalDateTime() {
return LocalDateTime.of(calculateTime.toLocalDate(), calculateTime.toLocalTime());
}
/**
* 用于以{@link Time}的形式返回初始化时设置的时间
* @return 始化时设置的时间
*/
public Time getInitTime() {
return Time.parse(initTime);
}
/**
* 用于以{@link Time}的形式将设置后的时间作为初始时间进行返回
* @return 始化时设置的时间
*/
public Time getCalculateTime() {
return Time.parse(calculateTime);
}
/**
* 用于还原初始化时设置的日期/时间
*/
public Time initTime() {
calculateTime = initTime;
return this;
}
/**
* 用于根据条件计算日期/时间方法允许传入小数与负数进行计算
* <p>
* <b>注意</b>在计算年月时若传入的数值是小数在转换毫秒值时其会按照
* <ul>
* <li>1年 = 365天</li>
* <li>1月 = 30天</li>
* </ul>
* 进行计算在跨度大的计算中其会存在精度的丢失
* </p>
*
* @param num 日期/时间增减的数量
* @param timeUnit 日期计算的单位
*/
public Time addTime(double num, TimeUnit timeUnit) {
calculateTime = calcuLocalTime(Double.valueOf(num), timeUnit, calculateTime);
return this;
}
/**
* 用于根据传入的增减时间的规则对时间进行增减
* <p>
* 根据单位前的数值对指定的单位进行增减例如需要对当前设置的时间增加1年3个月又5天并较少2小时30分钟45秒
* 此时可以传入1年3月5日-2时-30分-45秒亦可以传入1y3m5d-2h-30min-45s
* <ul>
* 可传入的单位有
* <li>年单位yY</li>
* <li>月单位mM</li>
* <li>周单位wW</li>
* <li>日单位dD</li>
* <li>小时单位hH</li>
* <li>分钟单位minMIN</li>
* <li>秒单位sS</li>
* </ul>
* 具体的计算规则与{@link #addTime(double, TimeUnit)}方法一致
* </p>
* @param regex 时间规则
* @return 返回修改后的时间戳
* @see #addTime(String)
*/
public long addOldTime(String regex) {
//TODO 此处逻辑需要修改
long time = addTime(regex);
initTime();
public Time addTime(String calculateTimeText) {
// 将字符串转换为char[]数组
char[] chars = Optional.ofNullable(calculateTimeText).filter(text -> !text.isEmpty())
// 为保证最后一位能进行计算在字符串末尾拼接一个-符号
.map(text -> text + "-").map(String::toCharArray)
.orElseThrow(() -> new IncorrectConditionException("必须指定修改时间的参数"));
// 记录当前计算的时间
LocalDateTime nowTime = calculateTime;
/*
* 判断单位思路 1.遍历通过calculateTimeText得到的每一个字符 2.判断当前字符是否为数字
* a.若为数字则判断上一次读取的内容是否为字符 I.若为字符则表示上一个单位及计算数值已读取完毕则先对上一次的数值对日期时间进行一次计算
* II.若为数字则表示当前正在读取计算的数值则不进行操作 判断结束后记录isUnit为false表示当前字符为数字并拼接到numText中
* b.若为非数字则将isUnit设置为true并拼接计算单位
*/
// 遍历所有的字符区别存储单位与增减的数值
StringBuilder numText = new StringBuilder();
StringBuilder unitText = new StringBuilder();
boolean isUnit = false;
for (char ch : chars) {
// 判断当前字符是否为数字
if (Character.isDigit(ch) || ch == '.' || ch == '-') {
// 判断上一次读取的内容是否为字符
if (isUnit) {
nowTime = calcuLocalTime(disposeDoubleText(numText.toString()),
Arrays.stream(TimeUnit.values()).filter(unit -> unit.isTimeUnit(unitText.toString()))
.findFirst().orElseThrow(
() -> new IncorrectConditionException("无法识别的计算公式:" + numText + unitText)),
nowTime);
numText.delete(0, numText.length());
unitText.delete(0, unitText.length());
}
numText.append(ch);
isUnit = false;
} else {
isUnit = true;
unitText.append(ch);
}
}
calculateTime = nowTime;
return this;
}
/**
* 用于对计算的double数值进行处理不全小数点前后缺失的内容
*
* @param doubleText 数值文本
* @return 转换后的double类型
*/
private Double disposeDoubleText(String doubleText) {
int index = doubleText.indexOf(".");
if (index == doubleText.length() - 1) {
return Double.valueOf(doubleText + "0");
} else if (index == 0) {
return Double.valueOf("0" + doubleText);
} else {
return Double.valueOf(doubleText);
}
}
/**
* 用于对传入的时间进行计算并返回计算结果
*
* @param num 计算数值
* @param timeUnit 计算单位
* @param time 指定的日期
* @return 计算后得到的日期
*/
private LocalDateTime calcuLocalTime(Double num, TimeUnit timeUnit, LocalDateTime time) {
// 为避免出现数字过大导致计算出错的问题先计算整数部分再将小数部分转换为时间戳后计算毫秒值
time = time.plus(num.intValue(), timeUnit.getChronoUnit());
num = num - num.intValue();
time = time.plus((long) (num * timeUnit.getToMillisNum()), ChronoUnit.MILLIS);
return time;
}
/**
* 用于识别传入的日期文本并将日期文本转换为相应的日期格式化字符串
* <p>
* <b>注意</b>
* <ol>
* <li>日期字符串必须是三位完整的日期X年X月X日或时间X时X分X秒或者是完整的日期+时间X年X月X日X时X分X秒</li>
* <li>日期字符串前后允许添加非数字字符</li>
* <li>无法识别纯数字的日期格式</li>
* </ol>
* </p>
*
* @param dateText 日期文本
* @return 相应的日期格式化字符串
*/
private static String judgeDateFormatText(String dateText) {
// 判断格式化日期时间中是否存在字母
if (dateText.matches(".*[a-zA-Z]+.*")) {
throw new IncorrectConditionException("格式化的日期/时间中存在字母:" + dateText);
}
// 将传入的日期文本转换为字符数组
char[] chars = dateText.toCharArray();
/*
* 转换思路 1.遍历通过dateText得到的每一个字符 2.判断当前字符是否为数字
* a.若为数字则记录isSign为false表示当前字符为数字并拼接index指向的位数
* b.若为非数字则记录isSign为true,表示当前字符为字符则需要再次判断上一个字符是 否也是非数字即isSign是否本身为false:
* I.若上一个字符不为非数字isSign原为true则设置index指向的位数加1即第一次读取到分隔符 表示上一位的日期以存储完毕
* II.若上一位为非数字isSign原为false则不做改动即该字符仅为分隔符的一部分 判断结束后将isSign设置为true并拼接分隔符
* 3.结束循环后得到一个待转译的中间字符串
*
* 举例传入2020-12-25 14:12:12最终会转换为1111-22-33 44:55:66
*/
int index = 1;
boolean isSign = false;
StringBuilder formatTextBuilder = new StringBuilder();
for (char ch : chars) {
if (Character.isDigit(ch)) {
isSign = false;
formatTextBuilder.append(index);
} else {
if (!isSign) {
index++;
}
isSign = true;
formatTextBuilder.append(ch);
}
}
// 判断中间字符串最后一位是否为非数字字符若为非数字字符表示位数多移动了1位需要减1后得到真实的位数
index -= (formatTextBuilder.substring(formatTextBuilder.length() - 1).matches("\\d") ? 0 : 1);
// 判断位数若位数为3则表示只传入了日期或者时间
if (index == 3) {
// 若第一位包含4个字符则按日期转换否则按时间转换
if (formatTextBuilder.substring(formatTextBuilder.indexOf("1"), formatTextBuilder.lastIndexOf("1") + 1)
.length() == 4) {
return formatTextBuilder.toString().replaceAll("1", "y").replaceAll("2", "M").replaceAll("3", "d");
} else {
return formatTextBuilder.toString().replaceAll("1", "H").replaceAll("2", "m").replaceAll("3", "s");
}
} else if (index == 6) {
// 若位数为6表示既传入了日期也传入了时间
return formatTextBuilder.toString().replaceAll("1", "y").replaceAll("2", "M").replaceAll("3", "d")
.replaceAll("4", "H").replaceAll("5", "m").replaceAll("6", "s");
} else {
throw new IncorrectConditionException("时间“" + dateText + "”不符合格式的规则");
}
}
@Override
public int compareTo(Time compateTime) {
return Optional.ofNullable(compateTime)
.map(Time::getLocalDateTime)
.map(calculateTime::compareTo)
.orElseThrow(() -> new IncorrectConditionException("需要比较的时间存在异常"));
}
@Override
public String toString() {
return getFormatTime();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((calculateTime == null) ? 0 : calculateTime.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Time other = (Time) obj;
if (calculateTime == null) {
if (other.calculateTime != null)
return false;
} else if (!calculateTime.equals(other.calculateTime))
return false;
return true;
}
/**
* 用于判断相应单位前的数字在整个字符串中所存在的位置
* @param text 传入的除当前判断单位前的字符串例如有规则5H31s判断秒数则传入5H31
* @return 返回待判断单位前的数值如5H31s判断秒数则返回2
* 用于根据最小比较单位对时间进行比较
* <p>
* 通过指定最小比较单位从而返回该单位及以上单位的对比结果例如
* <ul>
* 假定有如下两个日期
* <code><pre>
* Time t1 = Time.parse("2020-12-10 17:22:12");
* Time t2 = Time.parse("2020-12-10 20:27:12");
* </pre></code>
* <li>当调用{@code t1.equalsForUnit(t2, TimeUnit.SECOND)}则返回结果为false</li>
* <li>当调用{@code t1.equalsForUnit(t2, TimeUnit.DAY)}则返回结果为true</li>
* </ul>
* </p>
* <p>
* <b>注意</b>{@link TimeUnit#YEAR}{@link TimeUnit#MONTH}{@link TimeUnit#DAY}
* {@link TimeUnit#HOUR}{@link TimeUnit#MINUTE}{@link TimeUnit#SECOND}单位外其他的
* 单位传入进行判断时会抛出异常
* </p>
* @param compareTime 需要比对的时间
* @param timeUnit 最小判断单位
* @return 对比结果
* @throws IncorrectConditionException 单位传入有误时抛出的异常
*/
private static int getIndex(String text) {
// 定义规则
String regex = "(\\.\\d+)|(-?\\d+(\\.\\d+)?)";
// 如果其本身符合正则则返回0
if (text.matches(regex)) {
return 0;
}
// 若本身不符合正则则从后向前对字符串逐个增加直到找到下一个单位为止返回其在字符串中相应的位置
for (int i = 1; i < text.length(); i++) {
int index = text.length() - i;
// 若切分到的字符串不再符合正则即此时已找到下一个单位则返回其下标+1
// 例如有字符串5H31s传入到方法中的字符串将为5H31
// 逐个累加字符串时将读取到H31此时正则返回false则记录其下标+1即为3的位置
if (!text.substring(index).matches(regex)) {
return index + 1;
}
public boolean equalsForUnit(Time compareTime, TimeUnit timeUnit) {
if (compareTime == null || timeUnit == null) {
return false;
}
//若判断失败则返回-1理论上不存在该返回
return -1;
}
/**
* 识别传入的时间格式
*
* @param time 时间
* @return 时间格式
*/
private String getDateFormat(String time) {
boolean year = false;
Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");
if (pattern.matcher(time.substring(0, 4)).matches()) {
year = true;
boolean result = equals(compareTime);
//若两时间一致则直接返回true
if (result) {
return result;
}
StringBuilder sb = new StringBuilder();
int index = 0;
if (!year) {
if (time.contains("") || time.contains("-") || time.contains("/")) {
if (Character.isDigit(time.charAt(0))) {
index = 1;
}
} else {
index = 3;
switch (timeUnit) {
case SECOND:
result = (compareTime.getLocalDateTime().getSecond() == calculateTime.getSecond());
if (!result) {
return result;
}
}
for (int i = 0; i < time.length(); i++) {
char chr = time.charAt(i);
if (Character.isDigit(chr)) {
if (index == 0) {
sb.append("y");
}
if (index == 1) {
sb.append("M");
}
if (index == 2) {
sb.append("d");
}
if (index == 3) {
sb.append("H");
}
if (index == 4) {
sb.append("m");
}
if (index == 5) {
sb.append("s");
}
if (index == 6) {
sb.append("S");
}
} else {
if (i > 0) {
char lastChar = time.charAt(i - 1);
if (Character.isDigit(lastChar)) {
index++;
}
}
sb.append(chr);
case MINUTE:
result = (compareTime.getLocalDateTime().getMinute() == calculateTime.getMinute());
if (!result) {
return result;
}
case HOUR:
result = (compareTime.getLocalDateTime().getHour() == calculateTime.getHour());
if (!result) {
return result;
}
case DAY:
result = (compareTime.getLocalDateTime().getDayOfMonth() == calculateTime.getDayOfMonth());
if (!result) {
return result;
}
case MONTH:
result = (compareTime.getLocalDateTime().getMonth() == calculateTime.getMonth());
if (!result) {
return result;
}
case YEAR:
result = (compareTime.getLocalDateTime().getYear() == calculateTime.getYear());
return result;
default:
throw new IncorrectConditionException("无法比较的单位:" + timeUnit);
}
// 存储转换后的格式
dateFormat = sb.toString();
return sb.toString();
}
}

View File

@ -0,0 +1,100 @@
package com.auxiliary.tool.date;
import java.time.temporal.ChronoUnit;
/**
* <p><b>文件名</b>TimeUnit.java</p>
* <p><b>用途</b>
* 指定允许使用的时间单位
* </p>
* <p><b>编码时间</b>2021年1月20日上午7:54:09</p>
* <p><b>修改时间</b>2021年1月20日上午7:54:09</p>
* @author
* @version Ver1.0
* @since JDK 1.8
*
*/
public enum TimeUnit {
/**
* 指向计算单位<b></b>对应的时间单位为yY
*/
YEAR("[年yY]", ChronoUnit.YEARS, (long)(365.25 * 1000L * 24L * 60L * 60L)),
/**
* 指向计算单位<b></b>对应的时间单位为mM
*/
MONTH("[月mM]", ChronoUnit.MONTHS, (long)(30.4375 * 1000L * 24L * 60L * 60L)),
/**
* 指向计算单位<b></b>对应的时间单位为wW
*/
WEEK("[周wW]", ChronoUnit.WEEKS, (7L * 24L * 60L * 60L * 1000L)),
/**
* 指向计算单位<b></b>对应的时间单位为dD
*/
DAY("[日dD]", ChronoUnit.DAYS, (24L * 60L * 60L * 1000L)),
/**
* 指向计算单位<b></b>对应的时间单位为hH
*/
HOUR("[时hH]", ChronoUnit.HOURS, (60L * 60L * 1000L)),
/**
* 指向计算单位<b></b>对应的时间单位为min所有字母不区分大小写
*/
MINUTE("分|((m|M)(i|I)(n|N))", ChronoUnit.MINUTES, (60L * 1000L)),
/**
* 指向计算单位<b></b>对应的时间单位为sS
*/
SECOND("[秒sS]", ChronoUnit.SECONDS, (1000L)),
;
/**
* 指定判断当前单位的正则
*/
private String unitRegex;
/**
* 存储转换为毫秒值所需的乘积
*/
private long toMillisNum;
/**
* 指向当前的单位在{@link ChronoUnit}中的映射
*/
private ChronoUnit chronoUnit;
/**
* 初始化枚举值
* @param unitRegex 单位判断正则
* @param chronoUnit {@link ChronoUnit}的映射
* @param toMillisNum 转换为毫秒值所需的乘积
*/
private TimeUnit(String unitRegex, ChronoUnit chronoUnit, long toMillisNum) {
this.unitRegex = unitRegex;
this.toMillisNum = toMillisNum;
this.chronoUnit = chronoUnit;
}
/**
* 用于返回单位转换为毫秒值所需的乘积
* <p>
* <b>注意</b>在返回年月单位的毫秒值乘积时由于无法精确知道具体经过的闰年数与31天的月份数
* 月的毫秒值乘积返回取的是平均值在精确计算时会存在误差
* </p>
* @return 单位转换为毫秒值所需的乘积
*/
public long getToMillisNum() {
return toMillisNum;
}
/**
* 用于返回单位在{@link ChronoUnit}的映射
* @return {@link ChronoUnit}的映射
*/
public ChronoUnit getChronoUnit() {
return chronoUnit;
}
/**
* 用于判断传入的单位是否符合当前枚举值
* @param unit 单位
* @return 是否符合当前枚举
*/
public boolean isTimeUnit(String unit) {
return unit.matches(unitRegex);
}
}

View File

@ -0,0 +1,205 @@
package com.auxiliary.tool.date;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Random;
/**
* <p>
* <b>文件名</b>TimeUtil.java
* </p>
* <p>
* <b>用途</b> 提供对{@link Time}类进行扩展的方法
* </p>
* <p>
* <b>编码时间</b>2021年1月22日下午7:52:10
* </p>
* <p>
* <b>修改时间</b>2021年1月22日下午7:52:10
* </p>
*
* @author 彭宇琦
* @version Ver1.0
* @since JDK 1.8
*
*/
public class TimeUtil {
private TimeUtil() {
}
/**
* 用于随机生成一个起始时间与结束时间范围内的一个时间
* <p>
* 方法可通过传入的单位来限制随机产生的时间最小的变动单位例如 <code><pre>
* Time startTime = Time.parse("2020-12-20 00:00:00");
* Time endTime = Time.parse("2020-12-22 00:00:00");
*
* TimeUtil.randomTime(startTime, endTime, TimeUnit.SECONDS)
* TimeUtil.randomTime(startTime, endTime, TimeUnit.DAYS)
* </pre></code> 则在以上代码中第一次调用方法可能得到一个2020-12-21 17:31:22的时间或其他的可能但第二次调用方法
* 仅得到2020-12-20 00:00:002020-12-21 00:00:00两种结果
* </p>
* <p>
* <b>注意</b>
* <ol>
* <li>在生成随机时间的范围中允许生成起始时间但不会生成结束时间</li>
* <li>在随机时间中不变的时间与起始时间一致例如最小的变动单位为则在生成的时间中其时间的 秒的数值都与起始时间保持一致</li>
* <li>若起始时间大于结束时间则会自动对时间进行调换若起始时间与结束时间一致则直接返回起始时间</li>
* </ol>
* </p>
*
* @param startTime 随机时间的起始时间
* @param endTime 随机时间的结束时间
* @param timeUnit 随机时间最小变动单位
* @return 随机的时间
*/
public static Time randomTime(Time startTime, Time endTime, TimeUnit timeUnit) {
// 判断传入的对象是否为空
if (startTime == null || endTime == null || timeUnit == null) {
throw new IncorrectConditionException(String
.format("必须指定时间范围与最小变动单位:[startTime:%s, endTime:%s, timeUnit:%s]", startTime, endTime, timeUnit));
}
//若开始时间大于结束时间则交换两个时间若一致则直接返回起始时间
int compareResult = startTime.compareTo(endTime);
if (compareResult > 0) {
Time tempTime = startTime;
startTime = endTime;
endTime = tempTime;
} else if (compareResult == 0) {
return startTime;
} else {
}
// 获取开始时间与结束时间的毫秒值之后通过指定的单位对毫秒值进行转换
long startTime2Unit = startTime.getMilliSecond() / timeUnit.getToMillisNum();
//若结束时间与起始时间相同则在结束时间的毫秒值上加上1
long endTime2Unit = endTime.getMilliSecond() / timeUnit.getToMillisNum();
//判断当前转换单位后得到的结果是否一致若结果一致则仍无需计算随机时间
Time randomTime;
if (startTime2Unit != endTime2Unit) {
// 生成随机的经转换的数值并乘以单位乘积转换成毫秒值后再将毫秒值转换为时间
randomTime = Time.parse((new Random().longs(startTime2Unit, endTime2Unit).findAny().orElse(startTime2Unit))
* timeUnit.getToMillisNum());
// 为保证精度则根据最小变动的单位将起始时间的下级单位的数值赋给生成的时间
LocalDateTime startTimeLocal = startTime.getLocalDateTime();
switch (timeUnit) {
case YEAR:
randomTime.setTime(startTimeLocal.getMonthValue(), TimeUnit.MONTH);
case MONTH:
// 若当前月份无开始时间指向的日期一般为本月最后一天则将日期改为当月的最后一天
try {
randomTime.setTime(startTimeLocal.getDayOfMonth(), TimeUnit.DAY);
} catch (IncorrectConditionException e) {
randomTime = Time.parse(InitTimeUtil.lastDayOfMonth(false, randomTime.getLocalDateTime()));
}
case WEEK:
case DAY:
randomTime.setTime(startTimeLocal.getHour(), TimeUnit.HOUR);
case HOUR:
randomTime.setTime(startTimeLocal.getMinute(), TimeUnit.MINUTE);
case MINUTE:
randomTime.setTime(startTimeLocal.getSecond(), TimeUnit.SECOND);
case SECOND:
default:
break;
}
} else {
randomTime = startTime;
}
// 根据毫秒值转换为Time对象
return randomTime;
}
/**
* 用于以当前时间和指定的时间为随机时间范围生成一个随机的时间
* <p>
* 若当前时间大于指定的时间则将当前时间作为结束时间进行处理
* 若当前时间小于指定的时间则将当前时间作为起始时间进行处理
* 详细参数说明可参考{@link #randomTime(Time, Time, TimeUnit)}
* </p>
* @param time 指定的时间
* @param timeUnit 随机时间最小变动单位
* @return 随机的时间
*/
public static Time randomTime(Time time, TimeUnit timeUnit) {
return randomTime(Time.parse(), time, timeUnit);
}
/**
* 用于计算指定时间与待比较时间之间差值并以{@link Duration}的形式对计算结果进行返回
* @param time 指定时间
* @param compareTime 待比较时间
* @return 两时间的差值
*/
public static Duration timeDifference(Time time, Time compareTime) {
// 判断传入的对象是否为空
if (time == null || compareTime == null) {
throw new IncorrectConditionException(String
.format("必须指定时间范围与最小变动单位:[time:%s, compareTime:%s]", time, compareTime));
}
return Duration.between(time.getLocalDateTime(), compareTime.getLocalDateTime());
}
/**
* 用于计算指定时间与待比较时间之间各个单位的差值并以{@link Map}的形式进行返回
* <p>
* 在返回值中其键为时间单位值为该单位的差值
* </p>
* @param time 指定时间
* @param compareTime 待比较时间
* @return 两时间的差值
*/
/* TODO 暂时搁置 未想到解决借位或精度的解决办法
public static Map<TimeUnit, Integer> timeDifferenceToMap(Time time, Time compareTime) {
// 判断传入的对象是否为空
if (time == null || compareTime == null) {
throw new IncorrectConditionException(String
.format("必须指定时间范围与最小变动单位:[time:%s, compareTime:%s]", time, compareTime));
}
//存储转换单位后的数据
Map<TimeUnit, Integer> UnitMap = new HashMap<>();
TimeUnit unit = TimeUnit.YEAR;
//循环直到毫秒值小于1000时单位已计算至毫秒结束
while(unit != null) {
int value = 0;
switch (unit) {
case YEAR:
value = time.getLocalDateTime().getYear() - compareTime.getLocalDateTime().getYear();
unit = TimeUnit.MONTH;
break;
case MONTH:
value = time.getLocalDateTime().getMonthValue() - compareTime.getLocalDateTime().getMonthValue();
if (value < 0) {
UnitMap.put(TimeUnit.YEAR, UnitMap.get(TimeUnit.YEAR) - 1);
value = 12 + value;
}
unit = TimeUnit.DAY;
break;
case DAY:
unit = TimeUnit.HOUR;
break;
case HOUR:
unit = TimeUnit.MINUTE;
break;
case MINUTE:
unit = TimeUnit.SECOND;
break;
case SECOND:
default:
unit = null;
break;
}
UnitMap.put(unit, value);
}
return UnitMap;
}
*/
}

View File

@ -307,7 +307,7 @@ public class TableFileReadUtil {
// 判断单元格内的数据是否为日期
if (DateUtil.isCellDateFormatted(cell)) {
// 将单元格内容转换为Date后在time中进行存储
Time time = new Time(cell.getDateCellValue());
Time time = Time.parse(cell.getDateCellValue());
// 根据存储的时间格式对时间进行转换输出格式化后的时间
return time.getFormatTime();
} else {