DateUtil,时区,常用的日期接口

DateUtil,时区,常用的日期接口

背景

这期总结了Java中常用的关于日期和时间的接口,代码在后面的 DateUtil 类中。顺便讨论了一下时区。文章尽量做到精简

表示时间的形式

在 Java 里,表示时间通常有3种形式

  • 时间戳
  • Date (java.util.Date)
  • 字符串

(不讨论LocalDateTime之类的)

例如

  • 时间戳: System.currentTimeMillis() 的值
  • Date:你经常 new Date()
  • 字符串: 比如 2015-04-04 16:22:01,或者 2015-04-04

关于时区

上述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 ************************************/ }

你可能感兴趣的:(Java基础/JUC/JVM,DateUtil,时区)