package util.common.time;
import org.springframework.util.Assert;
import util.common.assertt.AssertUtils;
import util.response.response.tuple.TupleFactory;
import util.response.response.tuple.TwoTuPle;
import javax.validation.constraints.NotNull;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
/**
* 日期处理
* 工具类不作业务验证,抛异常给上层
*
* @author chenshun
* @date 2016年12月21日 下午12:53:33
*/
public final class DateUtils {
/**
* 时间格式(yyyy-MM-dd)
*/
public final static String LOCAL_DATE_PATTERN = "yyyy-MM-dd";
/**
* 时间格式(yyyy-MM-dd HH:mm:ss)
*/
public final static String LOCAL_DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
/**
* 将Date转换为字符串
*
* @param date 要转换的日期
* @param pattern 日期格式
* @return 返回字符串
*/
@Deprecated
public static String format(@NotNull Date date, String pattern) {
Assert.notNull(date, "date is null");
pattern=AssertUtils.isBlank(pattern, () -> LOCAL_DATE_TIME_PATTERN);
return new SimpleDateFormat(pattern).format(date);
}
/**
* 将字符串转换为Date
*
* @param dateStr 要转换的字符串
* @param pattern 转换格式
* @return 日期Date
*/
@Deprecated
public static Date parseDate(@NotNull String dateStr, String pattern) throws ParseException {
AssertUtils.notBlank(dateStr, "date string is blank");
pattern= AssertUtils.isBlank(pattern, () -> LOCAL_DATE_TIME_PATTERN);
return new SimpleDateFormat(pattern).parse(dateStr);
}
/**
* 将日期转换成十位时间戳
* 被dateToTimestamp2代替
*
* @param date 要转换的日期
* @return 十位时间戳即秒
*/
@Deprecated
public static Long dateToTimestamp(@NotNull Date date) {
Assert.notNull(date, "date is null");
return date.getTime() / 1000;
}
/**
* 将字符串转换为十位时间戳即秒
*
* @param dateStr 字符串
* @param pattern 时间格式
* @return 秒级时间戳
*/
@Deprecated
public static Long dateToTimestamp(@NotNull String dateStr, String pattern) throws ParseException {
Assert.notNull(dateStr, "date string is null");
pattern= AssertUtils.isBlank(pattern, () -> LOCAL_DATE_TIME_PATTERN);
return parseDate(dateStr, pattern).getTime() / 1000;
}
/**
* 将十位时间戳(秒)转换为日期
*
* @param second 时间戳
* @return 日期Date
*/
@Deprecated
public static Date stampToDate(long second) {
return new Date(second * 1000);
}
/**
* 将十位时间戳(秒)转换为字符串
*
* @param second 时间戳
* @return 时间字符串
*/
@Deprecated
public static String stampToDate(int second, String pattern) {
pattern= AssertUtils.isBlank(pattern, () -> LOCAL_DATE_TIME_PATTERN);
return format(stampToDate(second), pattern);
}
/**
* 通过Date日期计算年龄
*
* @param date 生日日期
* @return 年龄
*/
@Deprecated
public static Integer getAges(@NotNull Date date) {
Assert.notNull(date, "date is null");
Calendar cal = Calendar.getInstance();
if (Calendar.getInstance().before(date)) {
throw new IllegalArgumentException("date is before current date");
}
int yearNow = cal.get(Calendar.YEAR);
cal.setTime(date);
int yearBirth = cal.get(Calendar.YEAR);
return yearNow - yearBirth;
}
/**
* 获取本年开始时间
*
* @return 获取当前年份开始日期
*/
@Deprecated
public static Date getStartYearTime() {
Calendar cal = Calendar.getInstance();
cal.set(cal.get(Calendar.YEAR), Calendar.JANUARY, 0, 0, 0, 0);
cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.YEAR));
return cal.getTime();
}
/**
* 获取本年结束时间
*
* @return 本年结束日期
*/
@Deprecated
public static Date getEndYearTime() {
Calendar cal = Calendar.getInstance();
cal.setTime(getStartYearTime());
cal.add(Calendar.MONTH, Calendar.DECEMBER);
cal.add(Calendar.DATE, 31);
return cal.getTime();
}
/**
* 获取指定月份的天数
*
* @param year 年
* @param month 月
*/
@Deprecated
public static int getDaysByYearMonth(int year, int month) {
Calendar a = Calendar.getInstance();
a.set(Calendar.YEAR, year);
a.set(Calendar.MONTH, month - 1);
a.set(Calendar.DATE, 1);
a.roll(Calendar.DATE, -1);
return a.get(Calendar.DATE);
}
/**
* 获取月份的所有日期字符串
*
* @param date 指定月份的任意日期
*/
@Deprecated
public static List<String> getDayMonth(@NotNull Date date) {
Assert.notNull(date, "date is null");
List<String> list = new ArrayList<>(31);
Calendar cal = Calendar.getInstance(Locale.CHINA);
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH) + 1;
int day = cal.getActualMaximum(Calendar.DATE);
int capacity = 19;
StringBuilder builder = new StringBuilder(capacity);
for (int i = 1; i <= day; i++) {
builder.append(year);
builder.append("-");
if (month < 10) {
builder.append(0);
}
builder.append(month);
builder.append("-");
if (i < 10) {
builder.append(0);
}
builder.append(i);
list.add(builder.toString());
builder.delete(0, capacity);
}
return list;
}
/**
* 星期枚举
*/
private enum WeekEnum {
/**
* 星期
*/
SUNDAY(0, "星期日"),
MONDAY(1, "星期一"),
TUESDAY(2, "星期二"),
WEDNESDAY(3, "星期三"),
THURSDAY(4, "星期四"),
FRIDAY(5, "星期五"),
SATURDAY(6, "星期六");
int day;
String weekDay;
WeekEnum(int day, String weekDay) {
this.day = day;
this.weekDay = weekDay;
}
/**
* 通过天数获取对应的星期几
*
* @param day 天数0-6
* @return 星期字符串
*/
public static String getWeekDayByDay(int day) {
for (WeekEnum value : values()) {
if (value.day == day) {
return value.weekDay;
}
}
throw new IllegalArgumentException("day only be 0 to 7");
}
}
/**
* 获取当前日期是星期几
*
* @param dateStr 字符串日期
*/
@Deprecated
public static String getWeekDay(@NotNull String dateStr) throws ParseException {
AssertUtils.notBlank(dateStr, "date string is blank");
Calendar cal = Calendar.getInstance();
Date d = parseDate(dateStr, LOCAL_DATE_PATTERN);
cal.setTime(d);
return WeekEnum.getWeekDayByDay(cal.get(Calendar.DAY_OF_WEEK) - 1);
}
/**
* 获取制定两个日期的所有月份的1号
* 例如传入3月5号 -5月6日 返回3-1 4-1 5-1
*
* @param startDate 开始日期
* @param endDate 结束日期
* @return 日期列表
*/
public static List<LocalDate> getAllLocalDate(@NotNull LocalDate startDate, @NotNull LocalDate endDate) {
validateTwoDate(startDate, endDate);
List<LocalDate> result = new ArrayList<>(16);
startDate = getFirstDayMonth(startDate);
endDate = getFirstDayMonth(endDate);
result.add(startDate);
while (startDate.isBefore(endDate)) {
startDate = startDate.plusMonths(1);
result.add(startDate);
}
return result;
}
/**
* 获取起止日期的所有月份1号的字符串
*
* @param startDate 开始日期
* @param endDate 结束日期
* @return 月份列表
*/
public static List<String> getAllLocalDateStr(@NotNull LocalDate startDate, @NotNull LocalDate endDate) {
validateTwoDate(startDate, endDate);
List<String> result = new ArrayList<>(16);
result.add(startDate.format(DateTimeFormatter.ofPattern("yyyy-MM")));
while (startDate.isBefore(endDate)) {
startDate = startDate.plusMonths(1);
result.add(startDate.format(DateTimeFormatter.ofPattern("yyyy-MM")));
}
return result;
}
/**
* 获取起止日期所有月份的每月的起始毫秒级时间戳和结束毫秒级时间戳
*
* @param startDate 开始日期
* @param endDate 结束日期
* @return 月份起止毫秒级时间戳元组
*/
public static List<TwoTuPle<Long, Long>> getAllMonthMillisPeriod(@NotNull LocalDate startDate, @NotNull LocalDate endDate) {
//获取两个日期的所有月份
List<LocalDate> allLocalDate = getAllLocalDate(startDate, endDate);
//结果容器
List<TwoTuPle<Long, Long>> datas = new ArrayList<>(16);
for (LocalDate eachMouth : allLocalDate) {
//月初0点起始时间戳
long begin = localDateToEpochMilli(eachMouth.with(TemporalAdjusters.firstDayOfMonth()));
//下月月初0点时间戳
long end = localDateToEpochMilli(eachMouth.plusMonths(1).with(TemporalAdjusters.firstDayOfMonth()));
TwoTuPle<Long, Long> tuple = TupleFactory.tuple(begin, end);
datas.add(tuple);
}
return datas;
}
/**
* 获取制定两个日期的下一月中间所有日期的1号
* 例如传入3月5号 -5月6日 那么返回 4月1号 5月1号和6月1号
*
* @param startDate 开始日期
* @param endDate 结束日期
* @return 日期列表
*/
public static List<LocalDate> getAllNextMonthLocalDate(@NotNull LocalDate startDate, @NotNull LocalDate endDate) {
validateTwoDate(startDate, endDate);
List<LocalDate> result = new ArrayList<>(16);
startDate = getFirstDayMonth(startDate);
endDate = getFirstDayMonth(endDate);
while (startDate.isBefore(endDate)) {
startDate = startDate.plusMonths(1);
result.add(startDate);
}
result.add(endDate.plusMonths(1));
return result;
}
/**
* 日期LocalDate字符串获取时间戳秒级
*
* @param dateStr 日期字符串
* @param pattern 日期格式
* @return 秒级时间戳
*/
public static Long dateToTimestamp2(@NotNull String dateStr, String pattern) {
AssertUtils.notBlank(dateStr, "date string is blank");
pattern= AssertUtils.isBlank(pattern, () -> LOCAL_DATE_PATTERN);
LocalDate localDate = LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(pattern));
return LocalDateTime.of(localDate, LocalTime.of(0, 0, 0)).toInstant(ZoneOffset.of("+8")).toEpochMilli();
}
/**
* LocalDateTime转毫秒时间戳
*
* @param dateTime 时间
* @return 毫秒级时间戳
*/
public static long localDateTimeToEpochMilli(@NotNull LocalDateTime dateTime) {
Assert.notNull(dateTime, "date time is null");
return dateTime.toInstant(ZoneOffset.of("+8")).toEpochMilli();
}
/**
* LocalDate转毫秒时间戳
*
* @param date 日期
* @return 毫秒级时间戳
*/
public static long localDateToEpochMilli(@NotNull LocalDate date) {
Assert.notNull(date, "date is null");
return LocalDateTime.of(date, LocalTime.of(0, 0, 0)).toInstant(ZoneOffset.of("+8")).toEpochMilli();
}
/**
* 获取日期所在月份的第一天
*
* @param date 日期
* @return 第一天的LocalDate日期
*/
public static LocalDate getFirstDayMonth(@NotNull LocalDate date) {
Assert.notNull(date, "date is null");
return date.withDayOfMonth(1);
}
/**
* 获取日期所在月份的第一天
*
* @param date 日期
* @return 第一天的LocalDate日期
*/
public static LocalDate getFirstDayMonth2(@NotNull LocalDate date) {
Assert.notNull(date, "date is null");
return LocalDate.of(date.getYear(), date.getMonth(), 1);
}
/**
* 获取当前日期月份最后一天
*
* @param date 日期
* @return 最后一天的LocalDate日期
*/
public static LocalDate getLastDayMonth(@NotNull LocalDate date) {
Assert.notNull(date, "date is null");
return date.withDayOfMonth(date.lengthOfMonth());
}
/**
* 获取当前日期月份最后一天
*
* @param date 日期
* @return 最后一天的LocalDate日期
*/
public static LocalDate getLastDayMonth2(@NotNull LocalDate date) {
Assert.notNull(date, "date is null");
return LocalDate.of(date.getYear(), date.getMonth(), date.lengthOfMonth());
}
private DateUtils() {
throw new IllegalArgumentException("DateUtils不可实例化");
}
private static void validateTwoDate(LocalDate startDate, LocalDate endDate) {
Assert.notNull(startDate, "startDate is null");
Assert.notNull(endDate, "endDate is null");
startDate = LocalDate.of(startDate.getYear(), startDate.getMonth(), 1);
endDate = LocalDate.of(endDate.getYear(), endDate.getMonth(), 1);
if (startDate.isAfter(endDate)) {
throw new IllegalArgumentException("startDate should before endDate");
}
}
}