package com.apobates.forum.utils;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.TimeZone;
import java.util.TreeMap;
/**
* java.time包工具类
*
* @author xiaofanku
* @since 20190331
*/
public final class DateTimeUtils {
public final static String Y = "Year";
public final static String M = "Month";
public final static String D = "Day";
private final static String YMDHMS_L = "yyyy-MM-dd HH:mm:ss";
private final static String YMDHMS = "yyyyMMddHHmmssSSS";
private DateTimeUtils() throws Exception {
throw new Exception("不需要实化公共工具类");
}
/**
* 格式化当前日期为yyyy-MM-dd HH:mm:ss<14> https://tools.ietf.org/html/rfc3339 示例:
* 1985-04-12T23:20:50.52Z 1996-12-19T16:39:57-08:00
*
* @param date
* 要求不为null,反之返回空字符串
* @return
*/
public static String getRFC3339(LocalDateTime date) {
if (null == date) {
return "";
}
return date.format(DateTimeFormatter.ofPattern(YMDHMS_L).withZone(ZoneId.systemDefault()));
}
/**
* 使用指定的时区进行格式化
*
* @param date
* 要求不为null,反之返回空字符串
* @param zone
* 时区
* @return
*/
public static String getRFC3339(LocalDateTime date, ZoneId zone) {
if (null == date) {
return "";
}
return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").withZone(zone));
}
/**
* 格式化当前日期为yyyy-MM-ddTHH:mm:ss<15>
* https://www.iso.org/iso-8601-date-and-time-format.html 示例:
* 2015-12-14T12:00:29.3387979-05:00 2008-09-15T15:53:00+05:00
*
* @param date
* 要求不为null,反之返回空字符串
* @return
*/
public final static String getISO8601(LocalDateTime date) {
if (null == date) {
return "";
}
return date.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
/**
* 格式化当前日期为yyyyMMdd<16>
*
* @return
*/
public static String getYMD() {
return getYMDByLine().replaceAll("-", "");
}
/**
* 格式化日期
*
* @param date
* @return
*/
public static String formatClock(LocalDateTime date) {
if (null == date) {
return "-";
}
LocalDateTime n = LocalDateTime.now();
if (!isSameYear(date, n)) { // 不是同一年
return date.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日"));
}
if (!isSameMonth(date, n)) { // 是同一年 // 不是同一个月{return m-d}
return date.format(DateTimeFormatter.ofPattern("MM月dd日"));
}
if (!isSameDay(date, n)) { // 同一个月 // 不是同一天
long dd = diffDays(n, date);
return (dd <= 1) ? "昨天" : dd + "天前";
}
// 同一天
long diffMin = diffMinutes(n, date);
if (diffMin <= 1) { // 一分钟以内
return "刚刚";
}
if (diffMin > 1 && diffMin < 60) { // 一小时以内
return diffMin + "分钟前";
}
return Math.round(diffMin / 60) + "小时前";
}
/**
* 格式化指定日期为yyyyMMdd
*
* @param date
* 要求不为null,反之返回空字符串
* @return
*/
public static String getYMD(LocalDateTime date) {
if (null == date) {
return "";
}
return getYMDByLine(date.toLocalDate()).replaceAll("-", "");
}
/**
* 格式化当前日期为yyyy-MM-dd<17>
*
* @return
*/
public static String getYMDByLine() {
return getYMDByLine(LocalDate.now());
}
/**
* 格式化指定日期为yyyy-MM-dd
*
* @param date
* 要求不为null,反之返回空字符串
* @return
*/
public static String getYMDByLine(LocalDate date) {
if (null == date) {
return "";
}
return date.toString();
}
/**
* 计算昨天的日期
*
* @return
*/
public static LocalDateTime getYesterDay() {
return LocalDateTime.now().minusDays(1);
}
/**
* 格式化当前日期为HH:mm:ss<18>
*
* @return
*/
public static String getTimeString() {
return LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
}
/**
* 格式化当前日期为yyyyMMddHHmmssSSS<19>
*
* @return
*/
public static String getDateFullFormatString() {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(YMDHMS));
}
/**
* 指定的日期是否与当前日期是同一天<11>
*
* @param date
* 要求不为null,反之返回false
* @return
*/
public static boolean isSameDay(LocalDateTime date) {
if (null == date) {
return false;
}
return isSameDay(date, LocalDateTime.now());
}
/**
* 两个指定的日期是否是同一天
*
* @param date1
* 要求不为null,反之返回false
* @param date2
* 要求不为null,反之返回false
* @return
*/
public static boolean isSameDay(LocalDateTime date1, LocalDateTime date2) {
if (null == date1 || null == date2) {
return false;
}
//
LocalDate ld1 = date1.toLocalDate();
LocalDate ld2 = date2.toLocalDate();
return ld1.getYear() == ld2.getYear() && ld1.getMonthValue() == ld2.getMonthValue()
&& ld1.getDayOfMonth() == ld2.getDayOfMonth();
}
/**
* 指定的日期是否与当前日期是同一月<12>
*
* @param date
* 要求不为null,反之返回false
* @return
*/
public static boolean isSameMonth(LocalDateTime date) {
if (null == date) {
return false;
}
return isSameMonth(date, LocalDateTime.now());
}
/**
* 两个指定的日期是否是同一月
*
* @param date1
* 要求不为null,反之返回false
* @param date2
* 要求不为null,反之返回false
* @return
*/
public static boolean isSameMonth(LocalDateTime date1, LocalDateTime date2) {
if (null == date1 || null == date2) {
return false;
}
LocalDate ld1 = date1.toLocalDate();
LocalDate ld2 = date2.toLocalDate();
return ld1.getYear() == ld2.getYear() && ld1.getMonthValue() == ld2.getMonthValue();
}
/**
* 指定的日期是否与当前日期是同一年<13>
*
* @param date
* 要求不为null,反之返回false
* @return
*/
public static boolean isSameYear(LocalDateTime date) {
if (null == date) {
return false;
}
return isSameYear(date, LocalDateTime.now());
}
/**
* 两个指定的日期是否是同一年
*
* @param date1
* 要求不为null,反之返回false
* @param date2
* 要求不为null,反之返回false
* @return
*/
public static boolean isSameYear(LocalDateTime date1, LocalDateTime date2) {
if (null == date1 || null == date2) {
return false;
}
return date1.toLocalDate().getYear() == date2.toLocalDate().getYear();
}
/**
* 使用默认的时区格式化日期
*
* @param date
* 要求不为null,反之返回空字符串
* @param formatString
* 日期格式字符串
* @return
*/
public static String formatDateString(LocalDateTime date, String formatString) {
if (null == date) {
return "";
}
try {
return date.format(DateTimeFormatter.ofPattern(formatString));
} catch (Exception e) {
}
return "-";
}
/**
* 获取上月的第一天凌晨日期(00:00:00)<20>
*
* @return
*/
public static LocalDateTime getPreviousMonthFirstDayEarlyMorning() {
return LocalDate.now().minusMonths(1).atStartOfDay();
}
/**
* 获取上月的第后一天午夜日期(23:59:59)<21>
*
* @return
*/
public static LocalDateTime getPreviousMonthLastDayMidnight() {
LocalDateTime prevMonthFirstDay = getPreviousMonthFirstDayEarlyMorning();
return prevMonthFirstDay.withHour(23).withMinute(59).withSecond(29);
}
/**
* 获取本月的第一天凌晨日期(00:00:00)<2>
*
* @return
*/
public static LocalDateTime getCurrentMonthFirstDayEarlyMorning() {
return LocalDate.now().withDayOfMonth(1).atStartOfDay();
}
/**
* 获取今天的凌晨(00:00:00)<1> 适用的范围广
*
* @return
*/
public static LocalDateTime getTodayEarlyMorning() {
return LocalDate.now().atStartOfDay();
}
/**
* 获取今天的午夜日期(23:59:59)
*
* @return
*/
public static LocalDateTime getTodayMidnight() {
LocalDate date = LocalDate.now();
return LocalDateTime.of(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), 23, 59, 59);
}
/**
* 将指定日期设置为凌晨(00:00:00)<3> 用在几个作业(cron)类上
*
* @param date
* 要求不为null,否则抛出NPE
* @return
*/
public static LocalDateTime setDateEarlyMorning(LocalDateTime date) {
Objects.requireNonNull(date);
return LocalDateTime.of(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), 0, 0, 0);
}
/**
* 将指定日期设置为午夜(23:59:59)<4>
*
* @param date
* 要求不为null,否则抛出NPE
* @return
*/
public static LocalDateTime setDateMidnight(LocalDateTime date) {
Objects.requireNonNull(date);
return LocalDateTime.of(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), 23, 59, 59);
}
/**
* 昨天午夜的日期(23:59:59)<5>
*
* @return
*/
public static LocalDateTime getYesterdayMidnight() {
LocalDateTime yesDay = getYesterDay();
return LocalDateTime.of(yesDay.getYear(), yesDay.getMonthValue(), yesDay.getDayOfMonth(), 23, 59, 59);
}
/**
* 获得指定月份第一天凌晨(00:00:00)<6>
*
* @param year
* 年
* @param month
* 月
* @return
*/
public static LocalDateTime getFirstDayEarlyMorningOfMonth(int year, int month) {
LocalDate ld = LocalDate.of(year, month, 1);
return ld.atStartOfDay();
}
/**
* 获得指定月份最后一天午夜(23:59:59)<7>
*
* @param year
* 年
* @param month
* 月
* @return
*/
public static LocalDateTime getLastDayMidnightOfMonth(int year, int month) {
LocalDate monthOneDay = getFirstDayEarlyMorningOfMonth(year, month).toLocalDate();
int lastDay = monthOneDay.lengthOfMonth();
return LocalDateTime.of(year, month, lastDay, 23, 59, 59);
}
/**
* 两个日期之间天数差<8>
*
* @param date1
* 要求不为null,反之返回-1
* @param date2
* 要求不为null,反之返回-1
* @return 最近的为date1,返回正值;反之返回负值
*/
public static long diffDays(LocalDateTime date1, LocalDateTime date2) {
if (null == date1 || null == date2) {
return -1;
}
return date2.until(date1, ChronoUnit.DAYS);
}
/**
* 两个日期之间分钟数差<9>
*
* @param date1
* 要求不为null,反之返回-1
* @param date2
* 要求不为null,反之返回-1
* @return 最近的为date1,返回正值;反之返回负值
*/
public static long diffMinutes(LocalDateTime date1, LocalDateTime date2) {
if (null == date1 || null == date2) {
return -1;
}
return date2.until(date1, ChronoUnit.MINUTES);
}
/**
* 指定的日期在{limit}{calendarUnit}后,是否小于当前日期 例: c1在5小时后,是否小于当前日期<10>
*
* @param c1
* 要求不为null,反之返回false
* @param unit
* TemporalUnit单位,例:年:ChronoUnit.YEARS,月:ChronoUnit.MONTHS,日:ChronoUnit.DAYS,小时:ChronoUnit.HOURS,分钟:ChronoUnit.MINUTES,秒:ChronoUnit.SECONDS
* @param limit
* 单位值
* @return 如果当前日期大于c1累加后的值返回true
*/
public static boolean isFeatureDate(LocalDateTime c1, TemporalUnit unit, int limit) {
if (null == c1) {
return false;
}
LocalDateTime ldt = c1.plus(limit, unit);
return LocalDateTime.now().isBefore(ldt);
}
/**
* 判断指定的日期是否是未来时
*
* @param dateTime
* 要求不为null,反之返回false
* @return
*/
public static boolean isFeatureDate(LocalDateTime dateTime) {
if (null == dateTime) {
return false;
}
return LocalDateTime.now().isBefore(dateTime);
}
/**
* 使用yyyy-MM-dd HH:mm:ss格式解析日期
*
* @param timeString
* 日期字符串.要求不为null,否则抛出NPE
* @return
*/
public static LocalDateTime parseDate(String timeString) {
Objects.requireNonNull(timeString);
return parseDate(timeString, YMDHMS_L);
}
/**
* 使用指定的格式解析日期
*
* @param timeString
* 日期字符串.要求不为null,否则抛出NPE
* @param formatString
* 日期格式字符串
* @return
*/
public static LocalDateTime parseDate(String timeString, String formatString) {
Objects.requireNonNull(timeString);
return LocalDateTime.parse(timeString, DateTimeFormatter.ofPattern(formatString));
}
/**
* 当前日期在指定分钟后的日期
*
* @param minutelimit
* 分钟数
* @return
*/
public static LocalDateTime addMinuteForDate(int minutelimit) {
return LocalDateTime.now().plusMinutes(minutelimit);
}
/**
* 将指定的日期加上指定的分钟
*
* @param date
* 要求不为null,否则抛出NPE
* @param minutelimit
* 分钟数
* @return
*/
public static LocalDateTime addMinuteForDate(LocalDateTime date, int minutelimit) {
Objects.requireNonNull(date);
return date.plusMinutes(minutelimit);
}
/**
* 当前日期在指定小时后的日期
*
* @param hourlimit
* 小时数
* @return
*/
public static LocalDateTime addHourForDate(int hourlimit) {
return LocalDateTime.now().plusHours(hourlimit);
}
/**
* 将指定的日期加上指定的小时
*
* @param date
* 要求不为null,否则抛出NPE
* @param hourlimit
* 小时数
* @return
*/
public static LocalDateTime addHourForDate(LocalDateTime date, int hourlimit) {
Objects.requireNonNull(date);
return date.plusHours(hourlimit);
}
/**
* 当前日期在指定天后的日期
*
* @param daylimit
* 天数
* @return
*/
public static LocalDateTime addDayForDate(int daylimit) {
return LocalDateTime.now().plusDays(daylimit);
}
/**
* 将指定的日期加上指定的天
*
* @param date
* 要求不为null,否则抛出NPE
* @param daylimit
* 天数
* @return
*/
public static LocalDateTime addDayForDate(LocalDateTime date, int daylimit) {
Objects.requireNonNull(date);
return date.plusDays(daylimit);
}
/**
* 当前日期在指定月后的日期
*
* @param monthlimit
* 月数
* @return
*/
public static LocalDateTime addMonthForDate(int monthlimit) {
return LocalDateTime.now().plusMonths(monthlimit);
}
/**
* 将指定的日期加上指定的月
*
* @param date
* 要求不为null,否则抛出NPE
* @param monthlimit
* 月数
* @return
*/
public static LocalDateTime addMonthForDate(LocalDateTime date, int monthlimit) {
Objects.requireNonNull(date);
return date.plusMonths(monthlimit);
}
/**
* 当前日期在指定年后的日期
*
* @param yearlimit
* 年数
* @return
*/
public static LocalDateTime addYearForDate(int yearlimit) {
return LocalDateTime.now().plusYears(yearlimit);
}
/**
* 将指定的日期加上指定的年
*
* @param date
* 要求不为null,否则抛出NPE
* @param yearlimit
* 年数
* @return
*/
public static LocalDateTime addYearForDate(LocalDateTime date, int yearlimit) {
Objects.requireNonNull(date);
return date.plusYears(yearlimit);
}
/**
* 指定日期的前几天日期
*
* @param date
* 要求不为null,否则抛出NPE
* @param day
* 天数
* @return
*/
public static LocalDateTime beforeDayForDate(LocalDateTime date, int day) {
Objects.requireNonNull(date);
return date.minusDays(day);
}
/**
* 指定日期的前几分钟日期
*
* @param date
* 要求不为null,否则抛出NPE
* @param minute
* 分钟数
* @return
*/
public static LocalDateTime beforeMinuteForDate(LocalDateTime date, int minute) {
Objects.requireNonNull(date);
return date.minusMinutes(minute);
}
/**
* 将指定的日期转成unix timestamp
*
* @param date
* 要求不为null,反之返回0
* @return
*/
public static Integer covertTimestamp(LocalDateTime date) {
if (null == date) {
return 0;
}
Long d = Timestamp.valueOf(date).getTime() / 1000L;
return d.intValue();
}
/**
* 将指定的unix timestamp转成util.LocalDateTime
*
* @param timestamp
* 要求不为null,否则抛出NPE.unix 时间戳,例:1589316 时间戳是自 1970 年 1 月 1
* 日(00:00:00 GMT)以来的秒数。它也被称为 Unix 时间戳(Unix Timestamp)。
* @return
*/
public static LocalDateTime getDateTimeByUnixTimestamp(Integer timestamp) {
Objects.requireNonNull(timestamp);
return new Timestamp(timestamp * 1000L).toLocalDateTime();
}
/**
* 获得当前的unix timestamp
*
* @return
*/
public static Integer getUnixTimestamp() {
Long d = Timestamp.valueOf(LocalDateTime.now()).getTime() / 1000L;
return d.intValue();
}
/**
* 输出当前日期所在年(yyyy)和月(MM)
*
* @return key=CommonUtils.Y(ear)|M(onth), Y对应四位数字的年,M对应两位数的月
*/
public static Map getCurrentY4M2() {
return getYearAndMonth(LocalDate.now());
}
/**
* 输出指定日期所在年(yyyy)和月(MM)
*
* @param date
* java.time.LocalDate 日期,要求不为null,反之返回空集合
* @return key=DateTimeUtils.Y(ear)|M(onth), Y对应四位数字的年,M对应两位数的月
*/
public static Map getYearAndMonth(LocalDate date) {
if (null == date) {
return Collections.emptyMap();
}
String[] ymArray = date.toString().split("-");
Map data = new HashMap<>();
data.put(DateTimeUtils.Y, ymArray[0]);
data.put(DateTimeUtils.M, ymArray[1]);
return data;
}
/**
* 将java.util.Calendar 转为java.time.LocalDateTime
* 注意:Jdk8和以上的环境都不要使用java.util.Calendar,java.util.Date,java.text.SimpleDateFormat
*
* @param calendar
* 要求不为null,否则抛出NPE
* @return
*/
public static LocalDateTime toLocalDateTime(Calendar calendar) {
Objects.requireNonNull(calendar);
TimeZone tz = calendar.getTimeZone();
ZoneId zid = (null == tz) ? ZoneId.systemDefault() : tz.toZoneId();
return LocalDateTime.ofInstant(calendar.toInstant(), zid);
}
/**
* 将 java.time.LocalDateTime 转为java.util.Date
* 注意:Jdk8和以上的环境都不要使用java.util.Calendar,java.util.Date,java.text.SimpleDateFormat
*
* @param dateTime
* 要求不为null,否则抛出NPE
* @return
*/
public static Date toDate(LocalDateTime dateTime) {
Objects.requireNonNull(dateTime);
ZoneId systemZone = ZoneId.systemDefault();
ZoneOffset currentOffsetForMyZone = systemZone.getRules().getOffset(Instant.now());
Instant instant = dateTime.toInstant(currentOffsetForMyZone);
return Date.from(instant);
}
/**
* 以时区ID参照转化为java.util.Date
*
* @param dateTime
* 时间.要求不为null,否则抛出NPE
* @param zone
* 时区ID
* @return
*/
public static Date toZoneDate(LocalDateTime dateTime, ZoneId zone) {
Objects.requireNonNull(dateTime);
Instant instant = dateTime.atZone(zone).toInstant();
return Date.from(instant);
}
/**
* 填充数据源中时间的空值,主要用于连续日期的统计
*
* @param start 开始日期
* @param finish 结束日期
* @param rawdata 数据源,要求Key=YYYY-MM-DD
* @return
*/
public static TreeMap fillEmptyResult(LocalDateTime start, LocalDateTime finish, TreeMap rawdata){
TreeMap data = new TreeMap<>();
LocalDateTime loopDate=start;
do{
String loopKey = DateTimeUtils.getYMDByLine(loopDate.toLocalDate());
try{
Long size = rawdata.get(loopKey);
if(size==null){
size = 0L;
}
data.put(loopKey, size);
}catch(NullPointerException e){
data.put(loopKey, 0L);
}
loopDate = DateTimeUtils.addDayForDate(loopDate, 1);
}while(!DateTimeUtils.isSameDay(loopDate, finish));
return data;
}
}