
com.moon.core.time.CalendarUtil Maven / Gradle / Ivy
package com.moon.core.time;
import com.moon.core.enums.IntTesters;
import com.moon.core.enums.Testers;
import com.moon.core.lang.LongUtil;
import com.moon.core.util.TestUtil;
import com.moon.core.util.validator.ResidentID18Validator;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.function.Function;
import java.util.function.IntPredicate;
import java.util.function.LongFunction;
import java.util.function.Supplier;
import static com.moon.core.lang.ThrowUtil.noInstanceError;
import static java.lang.Integer.parseInt;
import static java.util.Calendar.*;
/**
* 1. 本类所有返回{@link Calendar}的方法均是返回新对象,而不是在原有对象上操作
*
* 2. 本类针对 MONTH 字段做了人性化处理,在标准 Calendar 中, MONTH 字段的 5,实际上对应的是 6 月,这里作人性化
* 处理之后,你只需要考虑想要设置的值,比如:“我想给 Calendar 设置为 5 月” ==> {@code CalendarUtil.setMonth(5);}即可;
*
* 如:
*
* Calendar calendar = Calendar.getInstance();
* Calendar resultCalendar = CalendarUtil.nextYear(calendar);
* assertTrue(calendar != resultCalendar); // 传入对象和返回对象是不同的
*
*
* @author moonsky
*/
public class CalendarUtil {
private final static class ImportedJoda {
final static boolean IMPORTED_JODA;
static {
boolean importedJoda;
try {
org.joda.time.DateTime.now();
importedJoda = true;
} catch (Throwable t) {
importedJoda = false;
}
IMPORTED_JODA = importedJoda;
}
}
protected CalendarUtil() { noInstanceError(); }
/**
* 最小毫秒数:1970-01-01 08:00:00 000
*/
public final static long MIN_TIME_IN_MILLIS = 0;
/**
* 最大毫秒数:9999-12-31 23:59:59 999
*/
public final static long MAX_TIME_IN_MILLIS = 253402271999999L;
public final static String yyyy_MM_dd_HH_mm_ss_SSS = "yyyy-MM-dd HH:mm:ss SSS";
public final static String yyyy_MM_dd_hh_mm_ss_SSS = "yyyy-MM-dd hh:mm:ss SSS";
public final static String yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss";
public final static String yyyy_MM_dd_hh_mm_ss = "yyyy-MM-dd hh:mm:ss";
public final static String yyyy_MM_dd_HH_mm = "yyyy-MM-dd HH:mm";
public final static String yyyy_MM_dd_hh_mm = "yyyy-MM-dd hh:mm";
public final static String yyyy_MM_dd_HH = "yyyy-MM-dd HH";
public final static String yyyy_MM_dd_hh = "yyyy-MM-dd hh";
public final static String yyyy_MM_dd = "yyyy-MM-dd";
public final static String yyyy_MM = "yyyy-MM";
public final static String yyyy = "yyyy";
public final static String HH_mm_ss = "HH:mm:ss";
public final static String HH_mm = "HH:mm";
public final static String DATE_PATTERN = yyyy_MM_dd;
public final static String MONTH_PATTERN = yyyy_MM;
public final static String TIME_PATTERN = HH_mm_ss;
public final static String PATTERN = yyyy_MM_dd_HH_mm_ss;
private final static String[] PATTERNS = {
yyyy, yyyy_MM, yyyy_MM_dd, yyyy_MM_dd_HH, yyyy_MM_dd_HH_mm, yyyy_MM_dd_HH_mm_ss, yyyy_MM_dd_HH_mm_ss_SSS,
};
private final static int[] PARSE_FIELD_OF_CALENDAR = {
YEAR, MONTH, DAY_OF_MONTH, HOUR_OF_DAY, MINUTE, SECOND, MILLISECOND
};
private final static int[] PARSE_FIELD_INITIALIZE_VALUES = {-1, 1, 1, 0, 0, 0, 0};
final static Calendar getInstance() { return Calendar.getInstance(); }
/**
* 是否引入 joda 时间
*
* @return
*/
public final static boolean isImportedJodaTime() { return ImportedJoda.IMPORTED_JODA; }
/*
* -------------------------------------------------------------------------
* is
* -------------------------------------------------------------------------
*/
public final static boolean isToday(Calendar value) { return value != null && isSameDay(value, getInstance()); }
public final static boolean isSameDay(Calendar value, Calendar other) {
return value != null && getYear(value) == getYear(other) && getMonth(value) == getMonth(other) && getDayOfMonth(
value) == getDayOfMonth(other);
}
public final static boolean isSameTime(Calendar value, Calendar other) {
return value != null && getYear(value) == getYear(other) && getMonth(value) == getMonth(other) && getDayOfMonth(
value) == getDayOfMonth(other) && getHour(value) == getHour(other) && getMinute(value) == getMinute(other) && getSecond(
value) == getSecond(other);
}
public final static boolean isBefore(Calendar value, Calendar other) {
return value != null && value.getTimeInMillis() < other.getTimeInMillis();
}
public final static boolean isAfter(Calendar value, Calendar other) {
return value != null && value.getTimeInMillis() > other.getTimeInMillis();
}
public final static boolean isBeforeNow(Calendar value) { return isBefore(value, getInstance()); }
public final static boolean isAfterNow(Calendar value) { return isAfter(value, getInstance()); }
public final static boolean isEqualsMonthDay(Calendar calendar1, Calendar calendar2) {
return getMonth(calendar1) == getMonth(calendar2) && getDayOfMonth(calendar1) == getDayOfMonth(calendar2);
}
public final static boolean isBeforeMonthDay(Calendar calendar1, Calendar calendar2) {
return isBeforeMonthDay(calendar1, getMonth(calendar2), getDayOfMonth(calendar2));
}
public final static boolean isBeforeMonthDay(Calendar calendar1, int month, int dayOfMonth) {
int month1 = getMonth(calendar1);
return month1 > month || (month1 == month && getDayOfMonth(calendar1) > dayOfMonth);
}
public final static boolean isAfterMonthDay(Calendar calendar1, Calendar calendar2) {
return isAfterMonthDay(calendar1, getMonth(calendar2), getDayOfMonth(calendar2));
}
public final static boolean isAfterMonthDay(Calendar calendar1, int month, int dayOfMonth) {
int month1 = getMonth(calendar1);
return month1 < month || (month1 == month && getDayOfMonth(calendar1) < dayOfMonth);
}
/*
* -------------------------------------------------------------------------
* now
* -------------------------------------------------------------------------
*/
public final static int nowYear() { return getYear(getInstance()); }
public final static int nowMonth() { return getMonth(getInstance()); }
public final static int nowDayOfMonth() { return getDayOfMonth(getInstance()); }
public final static int nowDayOfYear() { return getDayOfYear(getInstance()); }
public final static int nowHour() { return getHour(getInstance()); }
public final static int nowMinute() { return getMinute(getInstance()); }
public final static int nowSecond() { return getSecond(getInstance()); }
public final static int nowMillisecond() { return getMillisecond(getInstance()); }
public final static Calendar getCalendar() { return getInstance(); }
/**
* 当前时间戳(毫秒数)
*
* @return 返回当前时刻时间戳
*/
public final static long nowTimeMillis() { return System.currentTimeMillis(); }
/**
* 当前时间戳(秒数)
*
* @return 时间戳(秒数)
*/
public final static long nowTimeSeconds() { return nowTimeMillis() / 1000; }
/**
* 默认当前时间,如果指定时间为 null 的话
*
* @param calendar 指定时间
*
* @return
*/
public final static Calendar nowIfNull(Calendar calendar) {
return calendar == null ? getInstance() : calendar;
}
/*
* -------------------------------------------------------------------------
* clears
* -------------------------------------------------------------------------
*/
public final static Calendar clearTime(Calendar value) {
Calendar current = copy(value);
current.set(HOUR_OF_DAY, 0);
current.set(MINUTE, 0);
current.set(SECOND, 0);
current.set(MILLISECOND, 0);
return current;
}
public final static Calendar startOfSecond(Calendar calendar) { return setMillisecond(calendar, 0); }
public final static Calendar startOfDay(Calendar calendar) {
return toCalendar(getYear(calendar), getMonth(calendar), getDayOfMonth(calendar));
}
public final static Calendar startOfMonth(Calendar calendar) {
return toCalendar(getYear(calendar), getMonth(calendar));
}
public final static Calendar startOfYear(Calendar calendar) { return toCalendar(getYear(calendar)); }
public final static Calendar endOfYear(Calendar calendar) {
return new DateTime(calendar).endOfYear().originCalendar();
}
/*
* -------------------------------------------------------------------------
* copies
* -------------------------------------------------------------------------
*/
public final static Calendar copy(Calendar value) { return toCalendar(value.getTimeInMillis()); }
/*
* -------------------------------------------------------------------------
* next and prev
* -------------------------------------------------------------------------
*/
public final static Calendar nextYear(Calendar value) { return plusYears(value, 1); }
public final static Calendar nextMonth(Calendar value) { return plusMonths(value, 1); }
public final static Calendar nextDay(Calendar value) { return plusDays(value, 1); }
public final static Calendar nextHour(Calendar value) { return plusHours(value, 1); }
public final static Calendar nextMinute(Calendar value) { return plusMinutes(value, 1); }
public final static Calendar nextSecond(Calendar value) { return plusSeconds(value, 1); }
public final static Calendar nextMillisecond(Calendar value) { return plusMilliSeconds(value, 1); }
public final static Calendar prevYear(Calendar value) { return minusYears(value, 1); }
public final static Calendar prevMonth(Calendar value) { return minusMonths(value, 1); }
public final static Calendar prevDay(Calendar value) { return minusDays(value, 1); }
public final static Calendar prevHour(Calendar value) { return minusHours(value, 1); }
public final static Calendar prevMinute(Calendar value) { return minusMinutes(value, 1); }
public final static Calendar prevSecond(Calendar value) { return minusSeconds(value, 1); }
public final static Calendar prevMillisecond(Calendar value) { return minusMilliSeconds(value, 1); }
/*
* -------------------------------------------------------------------------
* plus and minus
* -------------------------------------------------------------------------
*/
public final static Calendar plusYears(Calendar value, int amount) {
return setYear(value, getYear(value) + amount);
}
public final static Calendar plusMonths(Calendar value, int amount) {
return setMonth(value, getMonth(value) + amount);
}
public final static Calendar plusDays(Calendar value, int amount) {
return setDayOfMonth(value, getDayOfMonth(value) + amount);
}
public final static Calendar plusHours(Calendar value, int amount) {
return setHourOfDay(value, getHour(value) + amount);
}
public final static Calendar plusMinutes(Calendar value, int amount) {
return setMinute(value, getMinute(value) + amount);
}
public final static Calendar plusSeconds(Calendar value, int amount) {
return setSecond(value, getSecond(value) + amount);
}
public final static Calendar plusMilliSeconds(Calendar value, int amount) {
return setMillisecond(value, getMillisecond(value) + amount);
}
public final static Calendar minusYears(Calendar value, int amount) {
return setYear(value, getYear(value) - amount);
}
public final static Calendar minusMonths(Calendar value, int amount) {
return setMonth(value, getMonth(value) - amount);
}
public final static Calendar minusDays(Calendar value, int amount) {
return setDayOfMonth(value, getDayOfMonth(value) - amount);
}
public final static Calendar minusHours(Calendar value, int amount) {
return setHourOfDay(value, getHour(value) - amount);
}
public final static Calendar minusMinutes(Calendar value, int amount) {
return setMonth(value, getMinute(value) - amount);
}
public final static Calendar minusSeconds(Calendar value, int amount) {
return setSecond(value, getSecond(value) - amount);
}
public final static Calendar minusMilliSeconds(Calendar value, int amount) {
return setMillisecond(value, getMillisecond(value) - amount);
}
/*
* -------------------------------------------------------------------------
* getters and setters
* -------------------------------------------------------------------------
*/
public final static Calendar setYear(Calendar value, int amount) { return set(value, YEAR, amount); }
public final static Calendar setMonth(Calendar value, int amount) { return set(value, MONTH, amount); }
public final static Calendar setDayOfMonth(Calendar value, int amount) { return set(value, DAY_OF_MONTH, amount); }
public final static Calendar setHourOfDay(Calendar value, int amount) { return set(value, HOUR_OF_DAY, amount); }
public final static Calendar setMinute(Calendar value, int amount) { return set(value, MINUTE, amount); }
public final static Calendar setSecond(Calendar value, int amount) { return set(value, SECOND, amount); }
public final static Calendar setMillisecond(Calendar value, int amount) { return set(value, MILLISECOND, amount); }
/**
* 获取年份
*
* @param value Calendar
*
* @return 年份
*/
public final static int getYear(Calendar value) { return get(value, YEAR); }
/**
* 获取月份;
*
* 注在{@link Date}或{@link Calendar}中直接获取到的月份比实际的要少一月,即当前是 6 月获取到的实际是 5 月;
* 此工具类针对月份做了统一处理,是几月获取到的就是几月
*
* @param value Calendar 对象
*
* @return 月份
*/
public final static int getMonth(Calendar value) { return get(value, MONTH); }
/**
* 获取星期
*
* @param value Calendar 对象
*
* @return 星期枚举
*/
public final static int getDayOfWeek(Calendar value) { return get(value, DAY_OF_WEEK); }
/**
* 一月中的第 N 天。如一月一日 => 第 1 天;一月 31 日是第 31 天;二月 1 日是 2 月的第 1 天
*
* @param value Calendar 对象
*
* @return 月中的第几天
*/
public final static int getDayOfMonth(Calendar value) { return get(value, DAY_OF_MONTH); }
/**
* 一年中的第 N 天。如一月一日 => 第 1 天;一月 31 日是第 31 天;二月 1 日是第 32 天
*
* @param value Calendar 对象
*
* @return 年中的第几日
*/
public final static int getDayOfYear(Calendar value) { return get(value, DAY_OF_YEAR); }
/**
* 获取小时
*
* @param value Calendar 对象
*
* @return 小时数
*/
public final static int getHour(Calendar value) { return get(value, HOUR_OF_DAY); }
/**
* 获取分钟
*
* @param value Calendar 对象
*
* @return 分钟数
*/
public final static int getMinute(Calendar value) { return get(value, MINUTE); }
/**
* 获取秒数
*
* @param value Calendar 对象
*
* @return 秒数
*/
public final static int getSecond(Calendar value) { return get(value, SECOND); }
/**
* 获取毫秒数
*
* @param value Calendar 对象
*
* @return 毫秒数
*/
public final static int getMillisecond(Calendar value) { return get(value, MILLISECOND); }
/**
* 根据日期获取年龄(周岁)
*
* @param birthday 出生日期
*
* @return 周岁
*
* @see ResidentID18Validator#getAge()
*/
public final static int getAge(Calendar birthday) { return getAge(birthday, getInstance()); }
/**
* 根据日期获取年龄(虚岁)
*
* @param birthday 出生日期
*
* @return 虚岁
*
* @see ResidentID18Validator#getNominalAge()
*/
public final static int getNominalAge(Calendar birthday) { return getAge(birthday) + 1; }
/**
* 返回生日到指定日期的周岁数
*
* @param birthday 出生日期
* @param endDate 指定日期
*
* @return 周岁
*/
public final static int getAge(Calendar birthday, Calendar endDate) {
int age = getYear(endDate) - getYear(birthday);
return isBeforeMonthDay(birthday, endDate) ? age - 1 : age;
}
/**
* 返回生日到指定日期的周虚岁
*
* @param birthday 出生日期
* @param endDate 指定日期
*
* @return 虚岁
*/
public final static int getNominalAge(Calendar birthday, Calendar endDate) { return getAge(birthday, endDate) + 1; }
/**
* 设置日期指定字段值,总是返回一个新对象
*
* 注意 month 字段人性化处理:在 Calendar 标准处理中,设置的月份是实际月份加一,即如果设置 5 月,实际设置到日期的是 6 月;
* 这个方法对这进行了处理,设置的是实际月份,即:如果设置 5 月,实际设置到日期的就是 5 月
*
* @param value 日期
* @param field 字段
* @param amount 字段值
*
* @return 设置新值后的新对象
*/
public final static Calendar set(Calendar value, int field, int amount) {
return originSetField(copy(value), field, amount);
}
/**
* 非复制设置字段值,同样针对 month 字段做了人性化处理
*
* @param value calendar
* @param field field
* @param amount 差值
*
* @return calendar
*/
public final static Calendar originSetField(Calendar value, int field, int amount) {
value.set(field, field == MONTH ? amount - 1 : amount);
return value;
}
/**
* 获取 Calendar 指定字段值
*
* 注意 month 字段人性化处理:在 Calendar 标准处理中,获取到的月份是实际月份减一,即如果现在是 5 月,获取到的是 4 月;
* 这个方法对这进行了处理,获取到的是实际月份,即:即如果现在是 5 月,获取到的就是 5 月
*
* @param cal 日期
* @param field 字段
*
* @return 字段值
*/
public final static int get(Calendar cal, int field) {
return field == MONTH ? cal.get(field) + 1 : cal.get(field);
}
/*
* -------------------------------------------------------------------------
* formatter
* -------------------------------------------------------------------------
*/
/**
* 返回指定模式的 DateFormat
*
* @param pattern 模式
*
* @return DateFormat
*/
public final static DateFormat toFormat(String pattern) { return new SimpleDateFormat(pattern); }
/**
* 按模式 yyyy-MM-dd HH:mm:ss 格式化当前日期
*
* @return 格式化后的字符串
*/
public final static String format() { return format(PATTERN); }
/**
* 按指定模式格式化当前日期
*
* @param pattern 日期格式
*
* @return 格式化后的日期字符串
*/
public final static String format(String pattern) { return pattern == null ? null : format(new Date(), pattern); }
/**
* 按指定模式格式化日期
*
* @param date 将要格式化的日期
* @param pattern 日期格式
*
* @return 格式化后的日期字符串
*/
public final static String format(Date date, String pattern) {
return date == null ? null : format(date, toFormat(pattern));
}
/**
* 按指定模式格式化日期
*
* @param date 将要格式化的日期
* @param formatter 日期格式
*
* @return 格式化后的日期字符串
*/
public final static String format(Date date, DateFormat formatter) {
return date == null ? null : formatter.format(date);
}
/**
* 按模式 yyyy-MM-dd HH:mm:ss 格式化指定日期
*
* @param date 将要格式化的日期
*
* @return 格式化后的日期字符串
*/
public final static String format(Date date) { return date == null ? null : format(date, PATTERN); }
/**
* 按模式 yyyy-MM-dd HH:mm:ss 格式化指定日期
*
* @param date 将要格式化的日期
*
* @return 格式化后的日期字符串
*/
public final static String format(Calendar date) { return date == null ? null : format(date.getTime()); }
/**
* 按指定模式格式化日期
*
* @param date 将要格式化的日期
* @param pattern 日期格式
*
* @return 格式化后的日期字符串
*/
public final static String format(Calendar date, String pattern) {
return date == null ? null : format(date.getTime(), pattern);
}
/**
* 按指定模式格式化日期
*
* @param date 将要格式化的日期
* @param formatter 日期格式
*
* @return 格式化后的日期字符串
*/
public final static String format(Calendar date, DateFormat formatter) {
return date == null ? null : format(date.getTime(), formatter);
}
/**
* 将指定日期格式化成年月字符串
*
* @param date 将要格式化的日期
*
* @return 格式化后的年月字符串
*/
public final static String toYearMonth(Date date) { return format(date, yyyy_MM); }
/**
* 将指定日期格式化成年月日字符串
*
* @param date 将要格式化的日期
*
* @return 格式化后的年月日字符串
*/
public final static String toYearMonthDay(Date date) { return format(date, yyyy_MM_dd); }
/*
* -------------------------------------------------------------------------
* parsers
* -------------------------------------------------------------------------
*/
/**
* 解析成 Calendar 日期
*
* 要求符合格式 "yyyy-MM-dd HH:mm:ss SSS" 的一个或多个字段;
* 1. 超出部分将忽略
* 2. 不足部分将用该字段的默认值填充,比如:月、日填充为 1,时间的各字段填充为 0
*
* @param datetimeString 符合格式 "yyyy-MM-dd HH:mm:ss SSS" 一个或多个字段的字符串
*
* @see #extractDateTimeFields(CharSequence)
*/
public static Calendar parseToCalendar(String datetimeString) {
return transformDateTimeString(datetimeString, CalendarUtil::toCalendar, CalendarUtil::toCalendar);
}
/**
* 提取日期各个字段值
*
* 要求符合格式:yyyy-MM-dd HH:mm:ss SSS 的一个或多个字段,并且每个字段的位数一致
*
* - 1. 超出部分将忽略
* - 2. 不足部分用该字段初始值填充,月日填充 1,时间各字段填充 0;
* - 3. 如果各字段之间有有效的非数字符号(汉字、- 号、/ 号、: 号、. 号等)间隔,间隔符号之间的数字就是字段值,格式无特殊要求
* - 4. 如果只有连续的数字则严格要求年份是 4 位,月日时分秒字段都是两位,最后紧接的最多三位连续数字是毫秒数
*
* 示例:
*
* |-----------------------------------------------------------------------------------|
* | Date String | Year | Month | Day | Hour | Minute | Second | Micro |
* |-----------------------------|------|-------|-----|------|--------|--------|-------|
* | 1980年02月03日08时09分59秒23 | 1980 | 02 | 03 | 08 | 09 | 59 | 23 |
* | 1980年2月3日8时9分59秒23 | 1980 | 02 | 03 | 08 | 09 | 59 | 23 |
* | 1980年02月03日08095923 | 1980 | 02 | 03 | 08 | 09 | 59 | 23 |
* | 1980-02-03 08:09:59.23 | 1980 | 02 | 03 | 08 | 09 | 59 | 23 |
* | 1980-02/03 08.09*59 23 | 1980 | 02 | 03 | 08 | 09 | 59 | 23 |
* | 1980 02 03 08 09 59 23 | 1980 | 02 | 03 | 08 | 09 | 59 | 23 |
* | 1980 023 08 09 59 23 | 1980 | 02 | 03 | 08 | 09 | 59 | 23 |
* | 19802 3 08 09 59 23 | 1980 | 02 | 03 | 08 | 09 | 59 | 23 |
* | 198023 08 09 59 23 | 1980 | 23 | 08 | 09 | 59 | 23 | 00 | 注
* | 1980 02 03 08 09 59 | 1980 | 02 | 03 | 08 | 09 | 59 | 00 |
* | 1980020308095923 | 1980 | 02 | 03 | 08 | 09 | 59 | 23 |
* | 1980 020308 095923 | 1980 | 02 | 03 | 08 | 09 | 59 | 23 |
* | 19800203 | 1980 | 02 | 03 | 00 | 00 | 00 | 00 | 注
* | 日期19800203时间080959毫秒23 | 1980 | 02 | 03 | 08 | 09 | 59 | 23 |
* |-----------------------------|------|-------|-----|------|--------|--------|-------|
* | 999 | 999 | 01 | 01 | 00 | 00 | 00 | 00 |
* | 1 | 1 | 01 | 01 | 00 | 00 | 00 | 00 |
* |-----------------------------------------------------------------------------------|
*
*
* @param datetimeString 输入的日期字符串
*
* @return 日期年、月、日、时、分、秒、毫秒各字段的值或如果字符串中不包含任何数字时返回 null
*/
public final static int[] extractDateTimeFields(CharSequence datetimeString) {
return doExtractDateTimeFields(datetimeString, DATETIME_BUILDER, FIELDS_LENGTH_DATE_TIME, 4);
}
private final static Supplier DATETIME_BUILDER = () -> new int[]{1970, 1, 1, 0, 0, 0, 0};
private final static byte[] FIELDS_LENGTH_DATE_TIME = {4, 2, 2, 2, 2, 2, 3};
private final static byte[] FIELDS_LENGTH_TIME = {2, 2, 2, 3};
/**
* 提取时间各个字段值,参考{@link #extractDateTimeFields(CharSequence)}
*
* 要求符合格式:HH:mm:ss SSS 的一个或多个字段,并且每个字段的位数一致
*
* @param timeString 时间字符串
*
* @return 时间时、分、秒、毫秒各字段的值或如果字符串中不包含任何数字时返回 null
*/
public final static int[] extractTimeFields(CharSequence timeString) {
return doExtractDateTimeFields(timeString, () -> new int[]{0, 0, 0, 0}, FIELDS_LENGTH_TIME, 3);
}
/**
* 提取时间日期各字段值
*
* @param datetimeString 日期时间字符串
* @param initResultFieldsCreator 返回值各字段初始化构造器
* @param maxLengthOfEachField 各个字段的最大长度(这个数组的长度要和{@code initResultFieldsCreator}返回值长度相同)
* @param maxLengthInAllFields 所有字段长度的最大值,这个是{@code maxLengthOfEachField}里的最大值,手动输入,避免一次循环判断
*
* @return 日期各字段值组成的数组
*/
protected static int[] doExtractDateTimeFields(
CharSequence datetimeString,
Supplier initResultFieldsCreator,
byte[] maxLengthOfEachField,
int maxLengthInAllFields
) {
return doExtractFields(datetimeString,
IntTesters.DIGIT,
initResultFieldsCreator,
maxLengthOfEachField,
maxLengthInAllFields);
}
/**
* 提取时间日期各字段值
*
* @param datetimeString 日期时间字符串
* @param tester 判断器,判断是否符合条件,符合条件的字符属于字段的组成部分
* @param initResultFieldsCreator 返回值各字段初始化构造器
* @param maxLengthOfEachField 各个字段的最大长度(这个数组的长度要和{@code initResultFieldsCreator}返回值长度相同)
* @param maxLengthInAllFields 所有字段长度的最大值,这个是{@code maxLengthOfEachField}里的最大值,手动输入,避免一次循环判断
*
* @return 日期各字段值组成的数组
*/
protected static int[] doExtractFields(
CharSequence datetimeString,
IntPredicate tester,
Supplier initResultFieldsCreator,
byte[] maxLengthOfEachField,
int maxLengthInAllFields
) {
if (datetimeString == null || datetimeString.length() == 0) {
return null;
}
char[] chars = datetimeString.toString().toCharArray(), field = new char[maxLengthInAllFields];
int fieldIdx = 0, valuesIdx = 0, strLen = chars.length, strIdx = skipNonNumeric(chars, 0, strLen);
if (strIdx == strLen) {
return null;
}
final int[] values = initResultFieldsCreator.get();
int last = Math.min(strLen, maxLengthOfEachField[0]), vLastIdx = values.length - 1;
for (char ch; strIdx < strLen; ) {
if (tester.test(ch = chars[strIdx++])) {
field[fieldIdx++] = ch;
if (fieldIdx < last) {
continue;
}
}
values[valuesIdx++] = toInt(field, fieldIdx);
if (valuesIdx > vLastIdx) {
return values;
}
strIdx = skipNonNumeric(chars, strIdx, strLen);
last = maxLengthOfEachField[valuesIdx];
fieldIdx = 0;
}
if (valuesIdx == 0) {
return null;
}
if (fieldIdx > 0) {
values[valuesIdx] = toInt(field, fieldIdx);
}
return values;
}
private final static int toInt(char[] chars, int lastIdx) {
return Integer.parseInt(new String(chars, 0, lastIdx));
}
/**
* 是否是合法的日期字段列表
*
* @param values 日期各字段值按顺序列表
*
* @return 日期各字段值符合要求范围值时返回 true,否则返回 false
*/
public final static boolean isValidDateTimeFields(int... values) {
return isValidDateTimeFields(0, 9999, false, values);
}
/**
* 是否是合法的日期字段列表
*
* @param minYear 最小年份
* @param maxYear 最大年份
* @param leapSecond 是否闰秒(通常都是 false)
* @param values 日期各字段值按顺序列表
*
* @return 日期各字段值符合要求范围值时返回 true,否则返回 false
*/
public final static boolean isValidDateTimeFields(int minYear, int maxYear, boolean leapSecond, int... values) {
int field, length = Math.max(values == null ? 0 : values.length, 7);
switch (length) {
case 0:
return false;
case 7:
// 毫秒
if ((field = values[6]) < 0 || field > 999) {
return false;
}
case 6:
// 秒
if ((field = values[5]) < 0 || field > (leapSecond ? 60 : 59)) {
return false;
}
case 5:
// 分
if ((field = values[4]) < 0 || field > 59) {
return false;
}
case 4:
// 时
if ((field = values[3]) < 0 || field > 23) {
return false;
}
case 3:
// 日
if ((field = values[1]) < 1 || field > 12) {
return false;
} else {
int lengthOfMonth = Month.of(field).length(Year.isLeap(values[0]));
if ((field = values[2]) < 1 || field > lengthOfMonth) {
return false;
}
}
case 2:
// 月
if ((field = values[1]) < 1 || field > 12) {
return false;
}
case 1:
// 年
if ((field = values[0]) < minYear || field > maxYear) {
return false;
}
}
return true;
}
/**
* 从 startIdx 开始跳过所有非数字字符,返回下一个数字字符的索引,如果超过 chars 长度,则返回 charsLen
*
* @param chars 原始 chars
* @param startIdx 其实索引
* @param charsLen chars 长度
*
* @return 下一个数字字符索引或 charsLen
*/
private final static int skipNonNumeric(char[] chars, int startIdx, int charsLen) {
for (char ch; startIdx < charsLen; startIdx++) {
if ((ch = chars[startIdx]) > 47 && ch < 58) {
return startIdx;
}
}
return charsLen;
}
/*
* toCalendar ============================================
*/
/**
* 解析成 Calendar 日期
*
* 要求用 String 表示的年、月、日、时、分、秒、毫秒等一个或多个字段按顺序传入
* 1. 超出部分将忽略
* 2. 不足部分将用该字段的默认值填充,比如:月、日填充为 1,时间的各字段填充为 0
*
* @param values 符合年、月、日、时、分、秒、毫秒顺序的字段
*/
public final static Calendar toCalendar(String... values) {
int valuesLen = values == null ? 0 : values.length;
if (valuesLen < 1) { return null; }
int length = PARSE_FIELD_OF_CALENDAR.length;
int size = Math.min(valuesLen, length);
Calendar calendar = Calendar.getInstance();
calendar.clear();
int i = 0;
for (int minLen = Math.min(size, PARSE_FIELD_OF_CALENDAR.length); i < minLen; i++) {
originSetField(calendar, PARSE_FIELD_OF_CALENDAR[i], parseInt(values[i]));
}
for (; i < length; i++) {
originSetField(calendar, PARSE_FIELD_OF_CALENDAR[i], PARSE_FIELD_INITIALIZE_VALUES[i]);
}
return calendar;
}
public final static Calendar toCalendar(int... values) {
int size = values == null ? 0 : values.length;
if (size < 1) { return null; }
Calendar calendar = Calendar.getInstance();
calendar.clear();
int i = 0;
for (int minLen = Math.min(size, PARSE_FIELD_OF_CALENDAR.length); i < minLen; i++) {
originSetField(calendar, PARSE_FIELD_OF_CALENDAR[i], values[i]);
}
int length = PARSE_FIELD_OF_CALENDAR.length;
for (; i < length; i++) {
originSetField(calendar, PARSE_FIELD_OF_CALENDAR[i], PARSE_FIELD_INITIALIZE_VALUES[i]);
}
return calendar;
}
public final static Calendar toCalendar(Number num) { return num == null ? null : toCalendar(num.longValue()); }
public final static Calendar toCalendar(long timeMillis) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(timeMillis);
return calendar;
}
public final static Calendar toCalendar(Date date) { return date == null ? null : toCalendar(date.getTime()); }
public final static Calendar toCalendar(LocalDate date, LocalTime time) {
return overrideCalendar(getInstance(), date, time);
}
public final static Calendar toCalendar(LocalTime time) { return toCalendar(LocalDate.now(), time); }
public final static Calendar toCalendar(LocalDate date) {
return overrideCalendar(getInstance(), date.getYear(), date.getMonthValue(), date.getDayOfMonth(), 0, 0, 0, 0);
}
public final static Calendar toCalendar(LocalDateTime datetime) {
return overrideCalendar(getInstance(), datetime);
}
/**
* 这里不需要检查 null:因为{@code extractDateTimeFields}、{@code isValidDateTimeFields}、
* {@code TestUtil.isDigit}、{@code StringUtil.isAllMatches}均有检查
*
* @param datetimeString
* @param fieldsValueTransformer
* @param timestampTransformer
* @param
*
* @return
*/
final static T transformDateTimeString(
CharSequence datetimeString, Function fieldsValueTransformer, LongFunction timestampTransformer
) {
int[] fields = extractDateTimeFields(datetimeString);
if (isValidDateTimeFields(fields)) {
return fieldsValueTransformer.apply(fields);
} else if (TestUtil.isDigit(datetimeString)) {
return timestampTransformer.apply(Long.parseLong(datetimeString.toString()));
}
throw new DateTimeParseException("不识别日期格式: " + datetimeString, datetimeString, 0);
}
/**
* 暂时没有使用这种方式,增加复杂度
* @param timestamp
* @param fieldsValueTransformer
* @param timestampTransformer
* @param
* @return
*/
@SuppressWarnings("all")
final static T transformLongTimestamp(
long timestamp, Function fieldsValueTransformer, LongFunction timestampTransformer
) {
if (timestamp < 10000) {
int[] values = {(int) timestamp};
return fieldsValueTransformer.apply(values);
}
return timestampTransformer.apply(timestamp);
}
/**
* 字符串解析为 Calendar
* 1. 首先认为是符合:yyyy-MM-dd HH:mm:ss SSS 一个或多个字段的日期字符串
* 2. 其次如果字符串是连续数字,则认为是时间戳毫秒数解析
*
* 3. 如果是连续数字,且是日期字符串类似 yyyyMMddHHmmssSSS 的一个或多个字段值,请手动调用方法{@link #parseToCalendar(String)}
*
* @param value 日期时间字符串
*
* @return Calendar 对象
*
* @see #transformDateTimeString(CharSequence, Function, LongFunction)
*/
public final static Calendar toCalendar(CharSequence value) {
return transformDateTimeString(value, CalendarUtil::toCalendar, CalendarUtil::toCalendar);
}
public final static Calendar toCalendar(Object value) {
if (value == null) { return null; }
if (value instanceof Calendar) { return ((Calendar) value); }
if (value instanceof Date) { return toCalendar((Date) value); }
if (value instanceof Number) { return toCalendar(((Number) value).longValue()); }
if (value instanceof LocalDate) { return toCalendar((LocalDate) value); }
if (value instanceof LocalDateTime) { return toCalendar((LocalDateTime) value); }
if (value instanceof LocalTime) { return toCalendar((LocalTime) value); }
if (value instanceof CharSequence) { return toCalendar((CharSequence) value); }
if (value instanceof int[]) { return toCalendar((int[]) value); }
if (value instanceof String[]) { return toCalendar((String[]) value); }
throw new IllegalArgumentException("can not converter to java.util.Calendar of value: " + value);
}
/*
* -----------------------------------------------------------------------------------
* 覆盖 Calendar 字段
* -----------------------------------------------------------------------------------
*/
public final static Calendar overrideCalendar(Calendar calendar, int... fieldsValue) {
if (fieldsValue == null) { return null; }
final int len = fieldsValue.length, max = 6;
int i = 0;
switch (len) {
case 0:
return calendar;
case 1:
calendar.set(YEAR, fieldsValue[i]);
break;
case 2:
calendar.set(YEAR, fieldsValue[i++]);
calendar.set(MONTH, fieldsValue[i] - 1);
break;
case 3:
calendar.set(fieldsValue[i++], fieldsValue[i++] - 1, fieldsValue[i]);
break;
case 4:
int minute = calendar.get(MINUTE);
calendar.set(fieldsValue[i++], fieldsValue[i++] - 1, fieldsValue[i++], fieldsValue[i], minute);
break;
case 5:
calendar.set(fieldsValue[i++],
fieldsValue[i++] - 1,
fieldsValue[i++],
fieldsValue[i++],
fieldsValue[i++]);
break;
default:
calendar.set(fieldsValue[i++],
fieldsValue[i++] - 1,
fieldsValue[i++],
fieldsValue[i++],
fieldsValue[i++],
fieldsValue[i++]);
if (len > max) {
calendar.set(MILLISECOND, fieldsValue[i]);
}
break;
}
return calendar;
}
public final static Calendar overrideCalendar(Calendar calendar, LocalDateTime date) {
return overrideCalendar(calendar, date.toLocalDate(), date.toLocalTime());
}
public final static Calendar overrideCalendar(Calendar calendar, LocalDate date, LocalTime time) {
return overrideCalendar(calendar,
date.getYear(),
date.getMonthValue(),
date.getDayOfMonth(),
time.getHour(),
time.getMinute(),
time.getSecond(),
time.getNano() / 1000000);
}
}