这期总结了Java中常用的关于日期和时间的接口,代码在后面的 DateUtil 类中。顺便讨论了一下时区。文章尽量做到精简
在 Java 里,表示时间通常有3种形式
(不讨论LocalDateTime之类的)
例如
上述3种情况
时间戳是不带时区的
时间戳是绝对的值,是当前这一刻(无论你在什么时区) 距离零时区1970年1月1日的毫秒数,所以时间戳是没有时区的
new Date() (java.util.Date
) 也是没有时区概念的
Date其里面存放的值是 System.currentTimeMillis() ,但是 toString() 方法会根据本地时区(即操作系统的时区)转成相应的字串,其本身是不带时区的!! 这点太容易混淆
SimpleDateFormat 是我们常用的类,用来把 Date 格式化成时间字符串,这个是可以设定时区的,Date 本身没有时区,要得到指定时区的时间字符串,需要对 SimpleDateFormat 设置时区 sdf.setTimeZone(TimeZone zone);
日期格式的字符串
是有时区的,比如 2015-04-04 16:22:01,或者 2015-04-04,这些表示方法都是没有时区概念的
附 DateUtil.java
/**
* @author Stone
* @version V1.0.0
* @date 2021/3/18
*/
public class DateUtil {
/**
* 常用时间格式
*/
public static final String Y_M_D_H_M_S = "yyyy-MM-dd HH:mm:ss";
public static final String Y_M_D = "yyyy-MM-dd";
public static final String YMD = "yyyyMMdd";
public static final String Y_M_D_H_M_S_SSS = "yyyy-MM-dd HH:mm:ss.SSS";
//public static final String DATE_FORMAT_UTC_TEMPLATE = "yyyy-MM-dd'T'HH:mm:ss'Z'";
//public static final String DATE_FORMAT_UTC_TEMPLATE = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
/**
* 常用时区
*/
public static class TZ {
public static final TimeZone UTC = TimeZone.getTimeZone("GMT+00:00");
public static final TimeZone BEIJING = TimeZone.getTimeZone("GMT+08:00");
public static final TimeZone TOKYO = TimeZone.getTimeZone("GMT+09:00");
public static final TimeZone LOCAL = localTimeZone();
}
/**
* 获取本地时区(指当前的时区,即Java程序所在JVM,JVM所在的操作系统的时区)
*
* @return 本地时区
*/
public static TimeZone localTimeZone() {
return TimeZone.getDefault();
}
/************************************ 时间类型互转,字串,Date,long BEGIN ************************************/
/**
* 时间戳转字串(本地时区)
*
* @param timestampWithMillis 时间戳,精确到毫秒,例如 System.currentTimeMillis() 的值
* @param destDateFormat 要转成什么样的字串格式
* @return 本地时区的日期时间的字串形式
*/
public static String long2Str(long timestampWithMillis, String destDateFormat) {
return long2StrTz(timestampWithMillis, destDateFormat, localTimeZone());
}
public static String long2StrUTC(long timestampWithMillis, String destDateFormat) {
return long2StrTz(timestampWithMillis, destDateFormat, TZ.UTC);
}
/**
* 时间戳转字串(指定时区)
*
* @param timestampWithMillis 时间戳,精确到毫秒,例如 System.currentTimeMillis() 的值
* @param destDateFormat 要转成什么样的字串格式
* @param destTimeZone 目的时区,即时间戳要转成什么时区。参考本类中定义的常用时区。
* @return 指定时区的日期时间的字串形式
*/
public static String long2StrTz(long timestampWithMillis, String destDateFormat, TimeZone destTimeZone) {
SimpleDateFormat sdf = new SimpleDateFormat(destDateFormat);
sdf.setTimeZone(destTimeZone);
return sdf.format(timestampWithMillis);
}
/**
* 时间戳转 Date
*
*
* 注意:Date 是没有时区概念的,其内部保存了时间戳,toString() 时根据本地时区转成字串而已
*
*
* @param timestampWithMillis 时间戳,到达毫秒级,如 System.currentTimeMillis()
* @return 返回 java.util.Date
*/
public static Date long2Date(long timestampWithMillis) {
Date date = new Date(timestampWithMillis);
return date;
}
/**
* Date 转字串(本地时区)
*
* @param date 日期
* @param destDateFormat 输出的字串格式
* @return 本地时区的字串形式
*/
public static String date2Str(Date date, String destDateFormat) {
return date2StrTz(date, destDateFormat, localTimeZone());
}
public static String date2StrUTC(Date date, String destDateFormat) {
return date2StrTz(date, destDateFormat, TZ.UTC);
}
/**
* Date 转字串(指定时区)
*
* @param date 日期
* @param destDateFormat 格式
* @param destTimeZone 转成什么时区的字串
* @return 指定时区的字串形式
*/
public static String date2StrTz(Date date, String destDateFormat, TimeZone destTimeZone) {
SimpleDateFormat sdf = new SimpleDateFormat(destDateFormat);
sdf.setTimeZone(destTimeZone);
return sdf.format(date);
}
/**
* Date 转时间戳
*
* @param date 日期时间
* @return 时间戳
*/
public static long date2long(Date date) {
return date.getTime();
}
/**
* 字串(本地时区)转 Date
*
*
* 注意:该方法把传入的日期字串当成是本地时区来进行转换的。
*
* 比如同样是 2017-04-02 15:33:08,在不同时区,
* 返回的Date里的时间戳的值是不一样的!!即 date.getTime() 得到不一样的值
*
* 北京时区(东八区)
* Sun Apr 02 15:33:08 CST 2017
* 1491118388000
* 东京时区(东九区)
* Sun Apr 02 15:33:08 JST 2017
* 1491114788000
*
*
* @param localDateStr 待转换的字串
* @param formatOfThisDateStr 字串的格式
* @return 返回 "把该字串认为是本地时区的 java.util.Date"
*/
public static Date str2Date(String localDateStr, String formatOfThisDateStr) {
return str2DateTz(localDateStr, formatOfThisDateStr, localTimeZone());
}
public static Date str2DateUTC(String utcDateStr, String formatOfThisDateStr) {
return str2DateTz(utcDateStr, formatOfThisDateStr, localTimeZone());
}
/**
* 字串(本地时区)转 Date
*
* @param dateStr 待转换的字串
* @param formatOfThisDateStr 字串的格式
* @param timeZoneOfThisDateStr 字串的时区
* @return 返回 "把该字串认为是本地时区的 java.util.Date"
*/
public static Date str2DateTz(String dateStr, String formatOfThisDateStr, TimeZone timeZoneOfThisDateStr) {
try {
SimpleDateFormat sdf = new SimpleDateFormat(formatOfThisDateStr);
sdf.setTimeZone(timeZoneOfThisDateStr);
return sdf.parse(dateStr);
} catch (ParseException e) {
throw new IllegalArgumentException(e);
}
}
/**
* 字串(本地时区)转时间戳
*
* @param dateStr 待转换的字串
* @param formatOfThisDateStr 字串的时区
* @return 返回 "把该字串认为是本地时区的时间戳"
*/
public static long str2long(String dateStr, String formatOfThisDateStr) {
return str2longTz(dateStr, formatOfThisDateStr, localTimeZone());
}
public static long str2longUTC(String dateStr, String formatOfThisDateStr) {
return str2longTz(dateStr, formatOfThisDateStr, TZ.UTC);
}
/**
* 字串(指定时区)转时间戳
*
* @param dateStr 待转换的字串
* @param formatOfThisDateStr 字串的时区
* @param timeZoneOfThisDateStr
* @return 返回 "把该字串认为是指定时区的时间戳"
*/
public static long str2longTz(String dateStr, String formatOfThisDateStr, TimeZone timeZoneOfThisDateStr) {
return date2long(str2DateTz(dateStr, formatOfThisDateStr, timeZoneOfThisDateStr));
}
/**
* 传入时间字串,指定格式和时区,转换成指定的时区的时间字串
*
* @param srcDateString 源字串
* @param srcDateFormat 源字串的格式
* @param srcTimeZone 源字串的时区
* @param destDateFormat 目的字串的格式
* @param destTimeZone 目的字串的时区
* @return 返回指定时区、指定格式的字串
*/
public static String str2str(String srcDateString,
String srcDateFormat,
TimeZone srcTimeZone,
String destDateFormat,
TimeZone destTimeZone
) {
SimpleDateFormat srcSdf = new SimpleDateFormat(srcDateFormat);
srcSdf.setTimeZone(srcTimeZone);
SimpleDateFormat destSdf = new SimpleDateFormat(destDateFormat);
destSdf.setTimeZone(destTimeZone);
Date sourceDate;
try {
sourceDate = srcSdf.parse(srcDateString);
return destSdf.format(sourceDate);
} catch (ParseException e) {
throw new RuntimeException("Can not parse date", e);
}
}
/************************************ 时间类型互转,字串,Date,long END ************************************/
/************************************ 获取时间 BEGIN ************************************/
/**
* 获取本地日期时间
*
* 这个 Date 其实是没有时区概念的,其里面存放的值是 System.currentTimeMillis() 的值
* 但是 toString() 方法会根据系统时区来转成相应的字串
*
*
* @return 返回 java.util.Date
*/
public static Date now() {
return new Date();
}
/**
* 获取 java.util.Date
*
* @return 获取日期部分(去掉了时分秒和毫秒)
*/
public static Date nowDate() {
return datePart(now());
}
/**
* 获取时间戳(到达毫秒,13位数字)
*
* @return 实际能不能精度达到毫秒,要看操作系统,详细参考 System.currentTimeMillis() 内的 javadoc
*/
private long currentTimeMillis() {
return System.currentTimeMillis();
}
/**
* 获取时间戳,精确到秒(也叫Unix时间戳,10位数字)
*
* @return 返回Unix时间戳(仅到达秒)
*/
private long currentTimeSecs() {
return System.currentTimeMillis() / 1000L;
}
/**
* 获取日期部分
*
* @param date 日期时间
* @return 返回只要日期部分的Date(注意 : 重新new了一个Date , 入参得到Date不变化)
*/
public static Date datePart(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
// 必须设置这个,否则毫秒数不同会导致compareDate方法在毫秒数上有差异
cal.set(Calendar.MILLISECOND, 0);
return cal.getTime();
}
/************************************ 获取时间 END ************************************/
/************************************ 时间顺序 BEGIN ************************************/
/**
* 获得日期是星期几
*
* @param date 日期时间
* @return 返回1~7分别代表周一到周日
*/
public static int getDayOfWeek(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
int index = cal.get(Calendar.DAY_OF_WEEK);
switch (index) {
case 1:
return 7;
case 2:
return 1;
case 3:
return 2;
case 4:
return 3;
case 5:
return 4;
case 6:
return 5;
default:
return 6;
}
}
/**
* 获得日期是当月第几天,从1开始
*
* @param date 日期时间
* @return 返回第几天, 从1开始
*/
public static int getDayOfMonth(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
int index = cal.get(Calendar.DAY_OF_MONTH);
return index;
}
/**
* 获得某个日期所在月份一共有多少天
*
* @param date 日期时间
* @return 日期所在月份有多少天
*/
public static int getHowManyDaysInMonth(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
return cal.getActualMaximum(Calendar.DATE);
}
/**
* 获得指定日期所在当月第一天(时间部分会保留)
*
* @param date 日期时间
* @return 日期所在月份的开始那天
*/
public static Date getStartDayOfMonth(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.DATE, 1);
return cal.getTime();
}
/**
* 获得指定日期所在当月最后一天(时间部分会保留), 能正确识别月份不同天数
*
* @param date 日期时间
* @return 日期所在月份最后一天
*/
public static Date getLastDayOfMonth(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.DATE, 1);
cal.add(Calendar.MONTH, 1);
cal.add(Calendar.DATE, -1);
return cal.getTime();
}
/************************************ 时间顺序 END ************************************/
/************************************ 时间比较 BEGIN ************************************/
/**
* 判断两个日期时间是否相等,连秒、毫秒都必须相等
*
* @param date1 日期时间1
* @param date2 日期时间2
* @return true则相等,反之不等
*/
public static boolean isEqualDateTime(Date date1, Date date2) {
return date1.getTime() == date2.getTime();
}
/**
* 仅仅比较日期部分是否相等, 不比较时间部分
* 相等: 2015-08-19 14:28:47 和 2015-08-19 14:28:51、2015-08-18 和 2015-08-18
* 不等: 2015-08-18 和 2015-08-19、2015-08-19 14:32:51和2015-08-18 14:32:57
*
* @param date1 日期1
* @param date2 日期2
* @return true则相等,反之不等
*/
public static boolean isEqualDate(Date date1, Date date2) {
date1 = datePart(date1);
date2 = datePart(date2);
return isEqualDateTime(date1, date2);
}
/**
* 比较日期时间先后顺序
* 辅助理解: 1.所谓时间大于,未来的时间比现在大
* 2.看着入参列出等式,如date1-date2,结果是1就表示大于0,即date1-date2>0亦即date1>date2
*
* @param date1
* @param date2
* @return date1>date2返回1,date1
*/
public static int compareDateTime(Date date1, Date date2) {
Calendar cal1 = Calendar.getInstance();
cal1.setTime(date1);
Calendar cal2 = Calendar.getInstance();
cal2.setTime(date2);
if (isEqualDateTime(date1, date2)) {
return 0;
}
if (cal1.after(cal2)) {
return 1;
} else {
return -1;
}
}
/**
* 比较日期的先后顺序
*
* @param date1
* @param date2
* @return date1>date2返回1,date1
*/
public static int compareDate(Date date1, Date date2) {
date1 = datePart(date1);
date2 = datePart(date2);
return compareDateTime(date1, date2);
}
/************************************ 时间比较 END ************************************/
/************************************ 时间加减 BEGIN ************************************/
/**
* 增加或减少年份数,注意入参本身是不改变的
*
* @param date 日期
* @param amount 可以传负整数
* @return 计算结果的新值, 入参本身不变!
*/
public static Date addYears(Date date, int amount) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.YEAR, amount);
return cal.getTime();
}
/**
* 增加或减少月份数,注意入参本身是不改变的
*
* @param date 日期
* @param amount 可以传负整数
* @return 计算结果的新值, 入参本身不变!
*/
public static Date addMonths(Date date, int amount) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.MONTH, amount);
return cal.getTime();
}
/**
* 增加或减少天数,注意入参本身是不改变的
*
* @param date 日期
* @param amount 可以传负整数
* @return 计算结果的新值, 入参本身不变!
*/
public static Date addDays(Date date, int amount) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.DATE, amount);
return cal.getTime();
}
/**
* 增加或减少小时数,注意入参本身是不改变的
*
* @param date
* @param amount 可以传负整数
* @return 计算结果的新值, 入参本身不变!
*/
public static Date addHours(Date date, int amount) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.HOUR_OF_DAY, amount);// 用HOUR和HOUR_OF_DAY结果一样
return cal.getTime();
}
/**
* 增加或减少分钟数,注意入参本身是不改变的
*
* @param date
* @param amount 可以传负整数
* @return 计算结果的新值, 入参本身不变!
*/
public static Date addMinutes(Date date, int amount) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.MINUTE, amount);
return cal.getTime();
}
/**
* 增加或减少秒数,注意入参本身是不改变的
*
* @param date
* @param amount 可以传负整数
* @return 计算结果的新值, 入参本身不变!
*/
public static Date addSeconds(Date date, int amount) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.SECOND, amount);
return cal.getTime();
}
/************************************ 时间加减 END ************************************/
/************************************ 时间间隔,其他 BEGIN ************************************/
/**
* 1. 计算两个日期之间有多少天(取了绝对值)
* 2. 只考虑日期,不考虑时间
* 3. 包括开始日期,不包括结束
* 4. 结果不会是负数
* 5. 举例: 2015-08-19 和 2015-08-18 结果是1 2015-08-19和2015-08-19结果是0
*
* @param endDate 第一个时间
* @param beginDate 第二个时间
* @return 天数, 正数!
*/
public static long getTwoDateInterval(Date endDate, Date beginDate) {
endDate = datePart(endDate);
beginDate = datePart(beginDate);
return Math.abs((endDate.getTime() - beginDate.getTime()) / (24 * 60 * 60 * 1000L));
}
/**
* 查询两个时间之间相差的天数,不足一天按一天计算
* 两个日期可以随便传按照任意顺序传入
*
* @param endDateTime 具体到时间,如2015-11-17 12:29:52
* @param beginDateTime 具体到时间,如2015-11-17 12:29:52
* @return
*/
public static long getTwoDateTimeInterval(Date endDateTime, Date beginDateTime) {
double d = ((double) (endDateTime.getTime() - beginDateTime.getTime())) / ((double) 24 * 60 * 60 * 1000L);
d = Math.abs(d);
Double day = Math.ceil(d);
return Math.abs(day.longValue());
}
/**
* 1. 计算两个日期之间有多少天
* 2. 只考虑日期,不考虑时间
* 3. 既包括开始日期,也包括结束
* 4. 举例: 2015-08-19 和 2015-08-18 结果是2 2015-08-19和2015-08-19结果是1
*
* @param endDate
* @param beginDate
* @return
*/
public static long getTwoDateIntervalContainEnd(Date endDate, Date beginDate) {
return getTwoDateInterval(endDate, beginDate) + 1;
}
/**
* 获得传入的日期时间,距离当天 24:00:00 的分钟数
*
* 注意: 某天的 24:00:00 就是下一天的 00:00:00, 例如 2015-04-04 24:00:00,其实是 2015-04-05 00:00:00
*
*
* @param date 日期时间
* @return 返回分钟数, 不足一分钟算0分钟, 例如 2015-04-04 23:59:01,距离今天结束还有59秒,接口返回0. 计算的结果只和时间的部分有关,和日期部分无关
*/
public static int howManyMinutesTillDayEnd(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 24);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date day24 = cal.getTime();
return (int) ((day24.getTime() - date.getTime()) / (1000 * 60L));
}
/**
* 获得传入的日期时间,距离当天 24:00:00 的秒数
*
* @param date 日期时间
* @return 返回秒数. 计算的结果只和时间的部分有关,和日期部分无关
*/
public static int howManySecondsTillDayEnd(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 24);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date day24 = cal.getTime();
return (int) ((day24.getTime() - date.getTime()) / 1000L);
}
/************************************ 时间间隔,其他 END ************************************/
}