• Date
  • Calendar
  • LocalDateTime

一、System.currentTimeMillis()

获取标准时间可以通过System.currentTimeMillis()方法获取,此方法不受时区影响,得到的结果是时间戳格式的。例如:

public static void main(String[] args) {
    long l = System.currentTimeMillis();
    System.out.println(l);
}
//print:1623983027297

我们可以将时间戳转化成我们易于理解的格式,获取当前系统的日期和时间:

public static void main(String[] args) {
    //HH为24小时制,hh为12小时制
    SimpleDateFormat formatter= new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z");
    Date date = new Date(System.currentTimeMillis());
    String dateTime = formatter.format(date);
    System.out.println(dateTime);
}
//print:2021-06-18 at 10:23:47 CST

注意:此方法会根据我们的系统时间返回当前值,因为世界各地的时区是不一样的。

字母 日期或时间元素 表示 示例

G

Era 标志符

Text

AD

y


Year

1996; 96

M

年中的月份

Month

July; Jul; 07

w

年中的周数

Number

27

W

月份中的周数

Number

2

D

年中的天数

Number

189

d

月份中的天数

Number

10

F

月份中的星期

Number

2

E

星期中的天数

Text

Tuesday; Tue

a

Am/pm 标记

Text

PM

H

一天中的小时数(0-23)

Number

0

k

一天中的小时数(1-24)

Number

24

K

am/pm 中的小时数(0-11)

Number

0

h

am/pm 中的小时数(1-12)

Number

12

m

小时中的分钟数

Number

30

s

分钟中的秒数

Number

55

S

毫秒数

Number

978

z

时区

General time zone

Pacific Standard Time; PST; GMT-08:00

Z

时区

RFC 822 time zone

-0800

二、java.util.Date

在Java中,获取当前日期最简单的方法之一就是直接实例化位于Java包java.util的Date类。

Date date = new Date(); // 该对象包含当前日期值

上面获取到的当前系统的日期和时间,也可以被format成我们需要的格式,例如:

public static void main(String[] args) {
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    System.out.println(formatter.format(new Date()));
}
//print:2021-06-18 10:47:38

三、Calendar API

Calendar类,专门用于转换特定时刻和日历字段之间的日期和时间。

获取当前日期和时间

Calendar calendar = Calendar.getInstance(); // 获取日历的当前实例

与date一样,我们也可以非常轻松地format这个日期成我们需要的格式

public static void main(String[] args) {
    //初始化日历对象
    Calendar calendar = Calendar.getInstance();
    //格式化输出日期时间
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    System.out.println(formatter.format(calendar.getTime()));
}
//print:2021-06-18 10:51:41
Calendar和Date的转化
public static void main(String[] args) {
	Calendar calendar = Calendar.getInstance();
    // 从一个 Calendar 对象中获取 Date 对象
    Date date = calendar.getTime();
    // 将 Date 对象反应到一个 Calendar 对象中,
    // Calendar/GregorianCalendar 没有构造函数可以接受 Date 对象
    // 所以我们必需先获得一个实例,然后设置 Date 对象
    calendar.setTime(new Date());
}
获取年、月、日
public static void main(String[] args) {
    Calendar calendar = Calendar.getInstance();
    //将日历定位到当前时间
    calendar.setTime(new Date());
    //获取年份
    String year = String.valueOf(calendar.get(Calendar.YEAR));
    //获取月份,月份从0开始所以实际月份需要+1
    String month = String.valueOf(calendar.get(Calendar.MONTH) + 1);
    //获取时间日期
    String day = String.valueOf(calendar.get(Calendar.DAY_OF_MONTH));
    //获取当前时间是一个星期的第几天   星期日为第一天 
    String week = String.valueOf(calendar.get(Calendar.DAY_OF_WEEK) - 1);
    System.out.println("现在时间是:" + year + "年" + month + "月" + day + "日,星期" + week);
}
//现在时间是:2021年6月21日,星期1

get(Calendar.Month) 这样的方法 0表示一月,1表示二月
get(Calendar.DAY_OF_MONTH) 获得这个月的第几天
get(Calendar.DAY_OF_WEEK) 获得这个星期的第几天
get(Calendar.DAY_OF_YEAR) 获得这个年的第几天
getTimeMillis() 获得当前时间的毫秒表示

将日期定位到任意一个时间

可以使用下面三个方法把日历定到任何一个时间

set(int year ,int month,int date)
set(int year ,int month,int date,int hour,int minute)
set(int year ,int month,int date,int hour,int minute,int second)

常用方法

计算某一月份的最大天数

public static void main(String[] args) {
    Calendar time = Calendar.getInstance();
    time.clear();
    time.set(Calendar.YEAR, 2021);
    //注意,Calendar对象默认一月为0
    time.set(Calendar.MONTH, 6);
    //本月份的天数
    int day = time.getActualMaximum(Calendar.DAY_OF_MONTH);
    System.out.println("六月有:" + day + "天");
}
//六月有:31天

计算一年中的第几星期

public static void main(String[] args) {
    //计算某一天是一年中的第几星期
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.YEAR, 2021);
    cal.set(Calendar.MONTH, 5);
    cal.set(Calendar.DAY_OF_MONTH, 20);
    int week = cal.get(Calendar.WEEK_OF_YEAR);
    System.out.println("2021-06-20,是今年第" + week + "个星期");
    //计算一年中的第几星期是几号
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
    cal.clear();
    cal.set(Calendar.YEAR, 2021);
    cal.set(Calendar.WEEK_OF_YEAR, 1);
    cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
    System.out.println(df.format(cal.getTime()));
}
//2021-06-20,是今年第26个星期
//2020-12-28

添加或减去指定的时间量

add()用法

public static void main(String[] args) {
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
    Calendar cal=Calendar.getInstance();
    cal.set(Calendar.YEAR, 2021);
    cal.set(Calendar.MONTH, 5);
    cal.set(Calendar.DAY_OF_MONTH, 20);
    //当前日期的前四天,减4天日期
    cal.add(Calendar.DATE, -4);
    Date date = cal.getTime();
    System.out.println(df.format(date));
    //当前日期前四天的后四天
    cal.add(Calendar.DATE, 4);
    date = cal.getTime();
    System.out.println(df.format(date));
}
//2021-06-16
//2021-06-20

roll()的用法

public static void main(String[] args) {
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.YEAR, 2021);
    cal.set(Calendar.MONTH, 5);
    cal.set(Calendar.DAY_OF_MONTH, 2);
    cal.roll(Calendar.DATE, -4);
    Date date = cal.getTime();
    System.out.println(df.format(date));
    cal.roll(Calendar.DATE, 4);
    date = cal.getTime();
    System.out.println(df.format(date));
}

roll()方法只能在本月内循环,一般使用add()方法;

计算两个任意时间中间的间隔天数

/**
 * 计算两个时间之间相隔天数
 *
 * @param startday 开始时间
 * @param endday   结束时间
 * @return
 */
public static int getCalendarIntervalDays(Calendar startday, Calendar endday) {
    //确保startday在endday之前
    if (startday.after(endday)) {
        Calendar cal = startday;
        startday = endday;
        endday = cal;
    }
    //分别得到两个时间的毫秒数
    long sl = startday.getTimeInMillis();
    long el = endday.getTimeInMillis();
    long ei = el-sl;
    //根据毫秒数计算间隔天数
    return (int)(ei/(1000*60*60*24));
}

/**
 * 计算两个时间之间相隔天数
 *
 * @param startday 开始时间
 * @param endday   结束时间
 * @return
 */
public static int getDateIntervalDays(Date startday, Date endday) {
    //确保startday在endday之前
    if (startday.after(endday)) {
        Date cal = startday;
        startday = endday;
        endday = cal;
    }
    //分别得到两个时间的毫秒数
    long sl = startday.getTime();
    long el = endday.getTime();
    long ei=el-sl;
    //根据毫秒数计算间隔天数
    return (int)(ei/(1000*60*60*24));
}

可以用相同的方法计算出任意两个时间相隔的小时数,分钟数,秒钟数等

public static void main(String[] args) {
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Calendar start = Calendar.getInstance();
    start.set(2021, 5, 2, 20, 10, 0);
    Calendar now = Calendar.getInstance();
    now.set(2021, 5, 3, 8, 0, 0);
    int intervalDays = getCalendarIntervalDays(start, now);
    System.out.println(intervalDays);
    System.out.println(df.format(start.getTime()));
    System.out.println(df.format(now.getTime()));
}
//0
//2021-06-02 20:10:00
//2021-06-03 08:00:00

计算结果为0,但是我们也许相让计算结果变为1

设定endday的时间为最后的时间

public static void main(String[] args) {
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Calendar start = Calendar.getInstance();
    start.set(2021, 5, 2, 20, 10, 0);
    Calendar now = Calendar.getInstance();
    now.set(2021, 5, 3, 8, 0, 0);
    now.set(Calendar.HOUR_OF_DAY, 23);
    now.set(Calendar.MINUTE, 59);
    now.set(Calendar.SECOND, 59);
    now.set(Calendar.MILLISECOND, 59);
    int intervalDays = getCalendarIntervalDays(start, now);
    System.out.println(intervalDays);
    System.out.println(df.format(start.getTime()));
    System.out.println(df.format(now.getTime()));
}
//1
//2021-06-02 20:10:00
//2021-06-03 23:59:59

使用以下方法不会出现这个问题

public static int getDaysBetween(Calendar d1, Calendar d2) {
    if (d1.after(d2)) {
        // 交换日期,使d1为开始,d2为结束
        Calendar temp = d1;
        d1 = d2;
        d2 = temp;
    }
    int days = d2.get(Calendar.DAY_OF_YEAR) - d1.get(Calendar.DAY_OF_YEAR);
    int y2 = d2.get(Calendar.YEAR);
    if (d1.get(Calendar.YEAR) != y2) {
        d1 = (Calendar) d1.clone();
        do {
            //得到当年的实际天数
            days += d1.getActualMaximum(Calendar.DAY_OF_YEAR);
            d1.add(Calendar.YEAR, 1);
        } while (d1.get(Calendar.YEAR) != y2);
    }
    return days;
}

public static void main(String[] args) {
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Calendar start = Calendar.getInstance();
    start.set(2021, 5, 2, 20, 10, 0);
    Calendar now = Calendar.getInstance();
    now.set(2021, 5, 3, 8, 0, 0);
    int intervalDays = getDaysBetween(start, now);
    System.out.println(intervalDays);
    System.out.println(df.format(start.getTime()));
    System.out.println(df.format(now.getTime()));
}
//1
//2021-06-02 20:10:00
//2021-06-03 08:00:00
工具类
public class DateHandleUtil {

    /**
     * 判断当前时间在时间区间内
     *
     * @param nowTime   当前时间
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return boolean
     */
    public static boolean isEffectiveDate(Date nowTime, Date startTime, Date endTime) {
        if (nowTime.getTime() == startTime.getTime()
                || nowTime.getTime() == endTime.getTime()) {
            return true;
        }

        Calendar date = Calendar.getInstance();
        date.setTime(nowTime);

        Calendar begin = Calendar.getInstance();
        begin.setTime(startTime);

        Calendar end = Calendar.getInstance();
        end.setTime(endTime);

        return date.after(begin) && date.before(end);
    }

    /**
     * 判断时间大小
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return boolean 开始时间>结束时间 2020-08-09 > 2020-08-08
     */
    public static boolean isMaxDate(Date startTime, Date endTime) {
        if (startTime.getTime() == endTime.getTime()) {
            return false;
        }

        Calendar begin = Calendar.getInstance();
        begin.setTime(startTime);

        Calendar end = Calendar.getInstance();
        end.setTime(endTime);
        return begin.after(end);
    }

    /**
     * 判断时间大小
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return boolean 开始时间>=结束时间
     */
    public static boolean isMaxOrEqualDate(Date startTime, Date endTime) {
        if (startTime.getTime() == endTime.getTime()) {
            return true;
        }

        Calendar begin = Calendar.getInstance();
        begin.setTime(startTime);

        Calendar end = Calendar.getInstance();
        end.setTime(endTime);
        return begin.after(end);
    }

    /**
     * Date时间格式转换成String
     *
     * @param date    日期时间
     * @param pattern 时间格式
     * @return 字符串时间
     */
    public static String getStringDate(Date date, String pattern) {
        if (date == null) {
            return "";
        }
        try {
            SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
            return dateFormat.format(date);
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * String 转 DATE
     *
     * @param date    字符串时间
     * @param pattern 时间格式
     * @return DATE
     */
    public static Date getDateTime(String date, String pattern) {
        SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
        try {
            return dateFormat.parse(date);
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 日期转化为cron表达式
     * @param date 日期
     * @return cron表达式 eg."0 06 10 15 1 ? 2020"
     */
    public static String getCron(Date  date){
        String dateFormat="ss mm HH dd MM ? yyyy";
        return getStringDate(date, dateFormat);
    }

    /**
     * cron表达式转为日期
     * @param cron cron表达式 eg."0 06 10 15 1 ? 2020"
     * @return 日期
     */
    public static Date getCronToDate(String cron) {
        String dateFormat="ss mm HH dd MM ? yyyy";
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
        Date date = null;
        try {
            date = sdf.parse(cron);
        } catch (ParseException e) {
            return null;
        }
        return date;
    }
}
Api方法简介
abstract void add(int field, int amount) 根据日历的规则,为给定的日历字段添加或减去指定的时间量。  
boolean after(Object when) 判断此 Calendar 表示的时间是否在指定 Object 表示的时间之后,返回判断结果。  
boolean before(Object when) 判断此 Calendar 表示的时间是否在指定 Object 表示的时间之前,返回判断结果。  
void clear()将此 Calendar 的所日历字段值和时间值(从历元至现在的毫秒偏移量)设置成未定义。  
void clear(int field) 将此 Calendar 的给定日历字段值和时间值(从历元至现在的毫秒偏移量)设置成未定义。  
Object clone()创建并返回此对象的一个副本。  
int compareTo(Calendar anotherCalendar) 比较两个 Calendar 对象表示的时间值(从历元至现在的毫秒偏移量)。  
protected void complete()填充日历字段中所有未设置的字段。  
protected abstract void computeFields()将当前毫秒时间值 time 转换为 fields[] 中的日历字段值。  
protected abstract void computeTime()将 fields[] 中的当前日历字段值转换为毫秒时间值 time。  
boolean equals(Object obj) 将此 Calendar 与指定 Object 比较。  
int get(int field)返回给定日历字段的值。  
int getActualMaximum(int field)给定此 Calendar 的时间值,返回指定日历字段可能拥有的最大值。  
int getActualMinimum(int field)给定此 Calendar 的时间值,返回指定日历字段可能拥有的最小值。  
static Locale[] getAvailableLocales()返回所有语言环境的数组,此类的 getInstance 方法可以为其返回本地化的实例。  
String getDisplayName(int field, int style, Locale locale) 返回给定 style 和 locale 下的日历 field 值的字符串表示形式。  
Map<String,Integer> getDisplayNames(int field, int style, Locale locale) 返回给定 style 和 locale 下包含日历 field 所有名称的 Map 及其相应字段值。  
int getFirstDayOfWeek()获取一星期的第一天;例如,在美国,这一天是 SUNDAY,而在法国,这一天是 MONDAY。  
abstract int getGreatestMinimum(int field)返回此 Calendar 实例给定日历字段的最高的最小值。  
static Calendar getInstance() 使用默认时区和语言环境获得一个日历。  
static Calendar getInstance(Locale aLocale) 使用默认时区和指定语言环境获得一个日历。  
static Calendar getInstance(TimeZone zone) 使用指定时区和默认语言环境获得一个日历。  
static Calendar getInstance(TimeZone zone, Locale aLocale) 使用指定时区和语言环境获得一个日历。  
abstract int getLeastMaximum(int field) 返回此 Calendar 实例给定日历字段的最低的最大值。  
abstract int getMaximum(int field) 返回此 Calendar 实例给定日历字段的最大值。  
int getMinimalDaysInFirstWeek()获取一年中第一个星期所需的最少天数,例如,如果定义第一个星期包含一年第一个月的第一天,则此方法将返回 1。  
abstract int getMinimum(int field) 返回此 Calendar 实例给定日历字段的最小值。  
Date getTime()返回一个表示此 Calendar 时间值(从历元至现在的毫秒偏移量)的 Date 对象。  
long getTimeInMillis()返回此 Calendar 的时间值,以毫秒为单位。  
TimeZone getTimeZone()获得时区。  
int hashCode()返回该此日历的哈希码。  
protected int internalGet(int field)返回给定日历字段的值。  
boolean isLenient()判断日期/时间的解释是否为宽松的。  
boolean isSet(int field) 确定给定日历字段是否已经设置了一个值,其中包括因为调用 get 方法触发内部字段计算而导致已经设置该值的情况。  
abstract void roll(int field, boolean up) 在给定的时间字段上添加或减去(上/下)单个时间单元,不更改更大的字段。  
void roll(int field, int amount) 向指定日历字段添加指定(有符号的)时间量,不更改更大的字段。  
void set(int field, int value) 将给定的日历字段设置为给定值。  
void set(int year, int month, int date) 设置日历字段 YEAR、MONTH 和 DAY_OF_MONTH 的值。  
void set(int year, int month, int date, int hourOfDay, int minute) 设置日历字段 YEAR、MONTH、DAY_OF_MONTH、HOUR_OF_DAY 和 MINUTE 的值。  
void set(int year, int month, int date, int hourOfDay, int minute, int second) 设置字段 YEAR、MONTH、DAY_OF_MONTH、HOUR、MINUTE 和 SECOND 的值。  
void setFirstDayOfWeek(int value) 设置一星期的第一天是哪一天;例如,在美国,这一天是 SUNDAY,而在法国,这一天是 MONDAY。  
void setLenient(boolean lenient) 指定日期/时间解释是否是宽松的。  
void setMinimalDaysInFirstWeek(int value) 设置一年中第一个星期所需的最少天数,例如,如果定义第一个星期包含一年第一个月的第一天,则使用值 1 调用此方法。  
void setTime(Date date) 使用给定的 Date 设置此 Calendar 的时间。  
void setTimeInMillis(long millis) 用给定的 long 值设置此 Calendar 的当前时间值。  
void setTimeZone(TimeZone value) 使用给定的时区值来设置时区。  
String toString() 返回此日历的字符串表示形式

四、Date/Time API

Java 8提供了一个全新的API,用以替换java.util.Date和java.util.Calendar。Date / Time API提供了多个类,帮助我们来完成工作,包括:

  • LocalDate
  • LocalTime
  • LocalDateTime
  • ZonedDateTime

1、LocalDate

LocalDate只是一个日期,没有时间。 这意味着我们只能获得当前日期,但没有一天的具体时间

LocalDate date = LocalDate.now(); // 获取当前日期

使用DateTimeFormatter格式化,得到结果只显示年月日,例如:

public static void main(String[] args) {
    LocalDate date = LocalDate.now();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    System.out.println(date.format(formatter));
}
//print:2021-06-18

2、LocalTime

LocalTime与LocalDate相反,它只代表一个时间,没有日期。 这意味着我们只能获得当天的当前时间,而不是实际日期:

LocalTime time = LocalTime.now(); // 获取当前时间

如下方式格式化,只有时分秒:

public static void main(String[] args) {
    LocalTime time = LocalTime.now();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
    System.out.println(time.format(formatter));
}
//print:10:59:50

3、LocalDateTime

LocalDateTime,也是Java中最常用的Date / Time类,代表前两个类的组合 - 即日期和时间的值:

LocalDateTime dateTime = LocalDateTime.now(); // 获取当前日期和时间

格式化的方式也一样,如下:

public static void main(String[] args) {
    LocalDateTime dateTime = LocalDateTime.now();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss E");
    System.out.println(dateTime.format(formatter));
}
//print:2021-06-18 11:04:44 星期五
时间之间的转换
LocalDateTime dt = LocalDateTime.now(); // 当前日期和时间
LocalDate d = dt.toLocalDate(); // 转换到当前日期
LocalTime t = dt.toLocalTime(); // 转换到当前时间

通过指定的日期和时间创建LocalDateTime,用.of

public static void main(String[] args) {
    LocalDate ld = LocalDate.of(2021, 6, 18);
    LocalTime lt = LocalTime.of(15, 30, 10);
    LocalDateTime dt1 = LocalDateTime.of(2021, 6, 18, 15, 30, 10);
    LocalDateTime dt2 = LocalDateTime.of(ld, lt);
    System.out.println(ld);
    System.out.println(lt);
    System.out.println(dt1);
    System.out.println(dt2);
}
// 2021-06-18
// 15:30:10
// 2021-06-18T15:30:10
// 2021-06-18T15:30:10
指定零点的写法
public static void main(String[] args) {
    //当天零点
    LocalDateTime today_start = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
    // 获取当天结束时间
    String td_st_str = today_start.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    //当天最后一刻
    LocalDateTime today_end = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
    System.out.println(today_start); //2021-06-18T00:00
    System.out.println(td_st_str); //2021-06-18 00:00:00
    System.out.println(today_end); //2021-06-18T23:59:59.999999999
}
日期字符串转LocalDateTime
public static void main(String[] args) {
    LocalDateTime dt = LocalDateTime.parse("2021-06-18T15:30:20");
    LocalDate d = LocalDate.parse("2021-06-18");
    LocalTime t = LocalTime.parse("15:30:20");
    System.out.println(dt);
    System.out.println(d);
    System.out.println(t);
}
// 2021-06-18T15:30:20
// 2021-06-18
// 15:30:20

注意:ISO 8601规定的日期和时间分隔符是T。标准格式如下:

  1. 日期:yyyy-MM-dd
  2. 时间:HH:mm:ss
  3. 带毫秒的时间:HH:mm:ss.SSS
  4. 日期和时间:yyyy-MM-dd’T’HH:mm:ss
  5. 带毫秒的日期和时间:yyyy-MM-dd’T’HH:mm:ss.SSS
日期时间的加减

LocalDateTime提供了对日期和时间进行加减的非常简单的链式调用:

  • 对于LocalDate,只有精度大于或等于日的加减,如年、月、日;
  • 对于LocalTime,只有精度小于或等于时的加减,如时、分、秒、纳秒;
  • 对于LocalDateTime,则可以进行任意精度的时间相加减;
public static void main(String[] args) {
    LocalDateTime localDateTime = LocalDateTime.now(); //2021-06-18T16:40:40.161
    // 以下方法的参数都是long型,返回值都是LocalDateTime
    LocalDateTime plusYearsResult = localDateTime.plusYears(2L); //2023-06-18T16:40:40.161
    LocalDateTime plusMonthsResult = localDateTime.plusMonths(3L); //2021-09-18T16:40:40.161
    LocalDateTime plusDaysResult = localDateTime.plusDays(7L); //2021-06-25T16:40:40.161
    LocalDateTime plusHoursResult = localDateTime.plusHours(2L); // 2021-06-18T18:40:40.161
    LocalDateTime plusMinutesResult = localDateTime.plusMinutes(10L); //2021-06-18T16:50:40.161
    LocalDateTime plusSecondsResult = localDateTime.plusSeconds(10L); //2021-06-18T16:40:50.161
    
    //返回LocalDateTime减去指定月数得到的值
    LocalDateTime minusMonths = localDateTime.minusMonths(2); //2021-04-18T16:40:40.161
    //返回LocalDateTime减去指定小时数得到的值
    LocalDateTime minusHours = localDateTime.minusHours(2); //2021-06-18T14:40:40.161
    //返回LocalDateTime减去指定分钟数得到的值
    LocalDateTime minusMinutes = localDateTime.minusMinutes(2); //2021-06-18T16:38:40.161
    //返回LocalDateTime减去指定纳秒数得到的值
    LocalDateTime minusNanos = localDateTime.minusNanos(2); //2021-06-18T16:40:40.160999998
    //返回LocalDateTime减去指定秒数得到的值
    LocalDateTime minusSeconds = localDateTime.minusSeconds(2); //2021-06-18T16:40:38.161
    //返回LocalDateTime减去指定星期数得到的值
    LocalDateTime minusWeeks = localDateTime.minusWeeks(2); //2021-06-04T16:40:40.161
    //返回LocalDateTime减去指定年数得到的值
    LocalDateTime minusYears = localDateTime.minusYears(2); //2019-06-18T16:40:40.161

    // 也可以以另一种方式来相加减日期,即plus(long amountToAdd, TemporalUnit unit)
    // 参数1 : 相加的数量, 参数2 : 相加的单位
    LocalDateTime nextMonth = localDateTime.plus(1, ChronoUnit.MONTHS); //2021-07-18T16:40:40.161
    LocalDateTime nextYear = localDateTime.plus(1, ChronoUnit.YEARS); //2022-06-18T16:40:40.161
    LocalDateTime nextWeek = localDateTime.plus(1, ChronoUnit.WEEKS); //2021-06-25T16:40:40.161
    LocalDateTime minus = localDateTime.minus(5, ChronoUnit.MONTHS); //2021-01-18T16:40:40.161
}
指定的年、月、日

将年、月、日等修改为指定的值,并返回新的日期(时间)对象,可链式调用

public static void main(String[] args) {
    LocalDate localDate = LocalDate.now(); //2021-06-18
    //当前时间基础上,指定本年当中的第几天,取值范围为1-365,366
    LocalDate withDayOfYearResult = localDate.withDayOfYear(200); //2021-07-19
    //当前时间基础上,指定本月当中的第几天,取值范围为1-29,30,31
    LocalDate withDayOfMonthResult = localDate.withDayOfMonth(5); //2021-06-05
    //当前时间基础上,直接指定年份
    LocalDate withYearResult = localDate.withYear(2020); //2020-06-18
    //当前时间基础上,直接指定月份
    LocalDate withMonthResult = localDate.withMonth(5); //2021-05-18
    
    LocalTime localTime = LocalTime.now(); //16:52:48.330
    //当前时间基础上,直接指定小时
    LocalTime withHourResult = localTime.withHour(1); //01:52:48.330
    //当前时间基础上,直接指定分钟
    LocalTime withMinuteResult = localTime.withMinute(15); //16:15:48.330
    //当前时间基础上,直接指定秒
    LocalTime withSecondResult = localTime.withSecond(20); //16:52:20.330
    
    LocalDateTime localDateTime = LocalDateTime.now(); //2021-06-18T16:52:48.330
	//LocalDateTime方法同上
    //也可以以另一种方式
    //当前时间基础上,直接指定月份
    LocalDateTime with = localDateTime.with(ChronoField.MONTH_OF_YEAR, 3); //2021-03-18T16:52:48.330
}

注意:调整月份时,会相应地调整日期,即把2019-10-31的月份调整为9时,日期也自动变为30

LocalDateTime还有一个通用的with()方法允许我们做更复杂的运算:

public static void main(String[] args) {
    // 本月第一天0:00时刻:
    LocalDateTime firstDay = LocalDate.now().withDayOfMonth(1).atStartOfDay(); //2021-06-01T00:00
    // 本月最后1天:
    LocalDate lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()); //2021-06-30
    // 下月第1天:
    LocalDate nextMonthFirstDay = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth()); //2021-07-01
    // 本月第1个周一:
    LocalDate firstWeekday = LocalDate.now().with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)); //2021-06-07
}
获取日期的年月日周时分秒
public static void main(String[] args) {
	LocalDateTime localDateTime = LocalDateTime.now();
    int dayOfYear = localDateTime.getDayOfYear();
    int dayOfMonth = localDateTime.getDayOfMonth();
    DayOfWeek dayOfWeek = localDateTime.getDayOfWeek();

    System.out.println("今天是" + localDateTime + "\n"
            + "本年当中第" + dayOfYear + "天" + "\n"
            + "本月当中第" + dayOfMonth + "天" + "\n"
            + "本周中星期" + dayOfWeek.getValue() + "-即" + dayOfWeek + "\n");
    
    //获取当天时间的年月日时分秒
    int year = localDateTime.getYear();
    Month month = localDateTime.getMonth();
    int day = localDateTime.getDayOfMonth();
    int hour = localDateTime.getHour();
    int minute = localDateTime.getMinute();
    int second = localDateTime.getSecond();
    System.out.println("今天是" + localDateTime + "\n"
            + "年 : " + year + "\n"
            + "月 : " + month.getValue() + "-即 "+ month + "\n"
            + "日 : " + day + "\n"
            + "时 : " + hour + "\n"
            + "分 : " + minute + "\n"
            + "秒 : " + second + "\n"
    );
}
时间日期前后的比较与判断

要判断两个LocalDateTime的先后,可以使用isBefore()isAfter()方法,对于LocalDateLocalTime类似

public static void main(String[] args) {
    //判断两个时间点的前后
    LocalDateTime now = LocalDateTime.now();
    LocalDateTime target = LocalDateTime.of(2020, 12, 20, 10, 15, 0);
    System.out.println(now.isBefore(target)); // false
    System.out.println(target.toLocalDate().isBefore(LocalDate.of(2021, 10, 10))); //true
    System.out.println(target.toLocalTime().isAfter(LocalTime.parse("09:30:00"))); //true
}

注意:LocalDateTime无法与时间戳进行转换,因为LocalDateTime没有时区,无法确定某一时刻。ZonedDateTime相当于LocalDateTime加时区的组合,它具有时区,可以与long表示的时间戳进行转换。

时间、日期间隔的计算

使用Duration进行 day,hour,minute,second等的计算
使用Period进行Year,Month的计算

Period:用于计算两个“日期”间隔

public static void main(String[] args) {
    //计算两个日期的日期间隔-年月日
    LocalDate date1 = LocalDate.of(2021, 6, 20);
    LocalDate date2 = LocalDate.of(2020, 10, 10);
    //内部是用date2-date1,所以得到的结果是负数
    Period period = Period.between(date1, date2);
    System.out.println("相差年数 : " + period.getYears());
    System.out.println("相差月数 : " + period.getMonths());
    System.out.println("相差日数 : " + period.getDays());
    //另一种计算方式和表现形式
    System.out.println("-------------------------------");
    long years = period.get(ChronoUnit.YEARS);
    long months = period.get(ChronoUnit.MONTHS);
    long days = period.get(ChronoUnit.DAYS);
    System.out.println("相差的年月日分别为 : " + years + "," + months + "," + days);
}
// 相差年数 : 0
// 相差月数 : -8
// 相差日数 : -10
// -------------------------------
// 相差的年月日分别为 : 0,-8,-10

注意:当获取两个日期的间隔时,并不是单纯的年月日对应的数字相加减,而是会先算出具体差多少天,在折算成相差几年几月几日的

Duration:用于计算两个“时间”间隔

public static void main(String[] args) {
    //计算两个时间的间隔
    LocalDateTime date3 = LocalDateTime.now();
    LocalDateTime date4 = LocalDateTime.of(2022, 1, 1, 0, 0, 0);
    Duration duration = Duration.between(date3, date4);
    System.out.println(date3 + " 与 " + date4 + " 间隔  " + "\n"
            + " 天 :" + duration.toDays() + "\n"
            + " 时 :" + duration.toHours() + "\n"
            + " 分 :" + duration.toMinutes() + "\n"
            + " 毫秒 :" + duration.toMillis() + "\n"
            + " 纳秒 :" + duration.toNanos() + "\n"
    );
}
// 2021-06-18T17:21:39.622 与 2022-01-01T00:00 间隔
// 天 :196
// 时 :4710
// 分 :282638
// 毫秒 :16958300378
// 纳秒 :16958300378000000

注意:并没有获得秒差,没有该方法,不过可以通过毫秒换算

与Date的转化

格式化format为String

public static void main(String[] args) {
    LocalDateTime localDateTime = LocalDateTime.now(); //2021-06-18T16:40:40.161
    // LocalDateTime 转换 Date
    Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    // Date 转换 LocalDateTime
    LocalDateTime dateTime = LocalDateTime.ofInstant(date.toInstant(),ZoneId.systemDefault());

    String format = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    System.out.println(format);
}
//print:2021-06-18 16:40:40
获取long型毫秒时间

通过localdatetime获取long型毫秒时间

public static void main(String[] args) {
    Long second = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).getEpochSecond();
    Long milli = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
    System.out.println(second);
    System.out.println(milli);
}
// 1624010840
// 1624010840432
判断时间是否相等
public static void main(String[] args) {
    LocalDateTime local = LocalDateTime.now();
    LocalDateTime date = LocalDateTime.parse("2021-05-20T10:15:30");
	//是否相等
    boolean flag2 = local.equals(date);
    System.out.println("localDateTime1和localDateTime2是否相等 " + flag2);
    boolean flag3 = local.isEqual(date);
    System.out.println("localDateTime1和localDateTime2是否相等 " + flag3);
}
//localDateTime1和localDateTime2是否相等 false
//localDateTime1和localDateTime2是否相等 false
LocalDateTime工具类
/**
 * Java8中LocalDateTime时间工具类
 */
public class LocalDateTimeUtils {

    //获取当前时间的LocalDateTime对象
    //LocalDateTime.now();

    //根据年月日构建LocalDateTime
    //LocalDateTime.of();

    //比较日期先后
    //LocalDateTime.now().isBefore(),
    //LocalDateTime.now().isAfter(),

    /**
     * Date转换为LocalDateTime
     * @param date
     * @return
     */
    public static LocalDateTime convertDateToLDT(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    /**
     * LocalDateTime转换为Date
     * @param time
     * @return
     */
    public static Date convertLDTToDate(LocalDateTime time) {
        return Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
    }


    /**
     * 获取指定日期的毫秒
     * @param time
     * @return
     */
    public static Long getMilliByTime(LocalDateTime time) {
        return time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * 获取指定日期的秒
     * @param time
     * @return
     */
    public static Long getSecondsByTime(LocalDateTime time) {
        return time.atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();
    }

    /**
     * 获取指定时间的指定格式
     * @param time
     * @param pattern
     * @return
     */
    public static String formatTime(LocalDateTime time, String pattern) {
        return time.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 获取当前时间的指定格式
     * @param pattern
     * @return
     */
    public static String formatNow(String pattern) {
        return formatTime(LocalDateTime.now(), pattern);
    }

    /**
     * 日期加上一个数,根据field不同加不同值,field为ChronoUnit.*
     * @param time
     * @param number
     * @param field
     * @return
     */
    public static LocalDateTime plus(LocalDateTime time, long number, TemporalUnit field) {
        return time.plus(number, field);
    }

    /**
     * 日期减去一个数,根据field不同减不同值,field参数为ChronoUnit.*
     * @param time
     * @param number
     * @param field
     * @return
     */
    public static LocalDateTime minu(LocalDateTime time, long number, TemporalUnit field) {
        return time.minus(number, field);
    }

    /**
     * 获取两个日期的差  field参数为ChronoUnit.*
     *
     * @param startTime
     * @param endTime
     * @param field     单位(年月日时分秒)
     * @return
     */
    public static long betweenTwoTime(LocalDateTime startTime, LocalDateTime endTime, ChronoUnit field) {
        Period period = Period.between(LocalDate.from(startTime), LocalDate.from(endTime));
        if (field == ChronoUnit.YEARS) {
            return period.getYears();
        }
        if (field == ChronoUnit.MONTHS) {
            return period.getYears() * 12 + period.getMonths();
        }
        return field.between(startTime, endTime);
    }

    /**
     * 获取一天的开始时间,2021,6,20 00:00
     * @param time
     * @return
     */
    public static LocalDateTime getDayStart(LocalDateTime time) {
        return LocalDateTime.of(time.toLocalDate(), LocalTime.MIN);
    }

    /**
     * 获取一天的结束时间,2021,6,20 23:59:59.999999999
     * @param time
     * @return
     */
    public static LocalDateTime getDayEnd(LocalDateTime time) {
        return LocalDateTime.of(time.toLocalDate(), LocalTime.MAX);
    }
}

4、ZonedDateTime

ZonedDateTime类是用于表示带时区的日期与时间信息的类。 ZonedDateTime 类的值是不可变的,所以其计算方法会返回一个新的ZonedDateTime 实例。

可以简单地把ZonedDateTime理解成LocalDateTimeZoneIdZoneIdjava.time引入的新的时区类,注意和旧的java.util.TimeZone区别。

ZonedDateTime dateTime = ZonedDateTime.now(); // 获取当前日期和时间+默认时区(ZoneId.systemDefault())

当前时间打印结果如下:

public static void main(String[] args) {
    // 默认时区
    ZonedDateTime shanghai = ZonedDateTime.now();
    System.out.println(shanghai);
}
//print:2021-06-18T14:15:37.686+08:00[Asia/Shanghai]

指定时间,时区打印结果如下:

public static void main(String[] args) {
    ZonedDateTime dateTime1 = ZonedDateTime.now(ZoneId.of("America/New_York")); // 用指定时区获取当前时间
    System.out.println(dateTime1);
    // 指定的年月日、时分秒、纳秒以及时区ID来新建对象:
    ZoneId zoneId = ZoneId.of("UTC+1");
    ZonedDateTime dateTime2 = ZonedDateTime.of(2021, 6, 18, 6, 18, 59, 123000000, zoneId);
    System.out.println(dateTime2);
}
//print:2021-06-18T02:35:22.391-04:00[America/New_York]
//print:2021-06-18T06:18:59.123+01:00[UTC+01:00]

通过以下方法访问ZonedDateTime对象的时间

public static void main(String[] args) {
    ZonedDateTime now = ZonedDateTime.now();
    int year = now.getYear();
    Month month = now.getMonth();
    int dayOfMonth = now.getDayOfMonth();
    DayOfWeek dayOfWeek = now.getDayOfWeek();
    int dayOfYear = now.getDayOfYear();
    int hour = now.getHour();
    int minute = now.getMinute();
    int second = now.getSecond();
    int nano = now.getNano();
    // 方法中有一些返回int有一些返回枚举类型,但可以通过枚举类型中的getValue()方法来获得int值。
    System.out.println(year + "年"); //2021年
    System.out.println(month + ":" + month.getValue() + "月"); //JUNE:6月
    System.out.println(dayOfMonth + "日"); //18日
    System.out.println(dayOfWeek +  ": 星期" + dayOfWeek.getValue()); //FRIDAY: 星期5
    System.out.println("今年已过去" + dayOfYear + "天"); //今年已过去169天
    System.out.println(hour + "时"); //15时
    System.out.println(minute + "分"); //5分
    System.out.println(second + "秒"); //10秒
    System.out.println(nano + "纳秒"); //419000000纳秒
    System.out.println(now);
}
// 2021-06-18T15:05:10.419+08:00[Asia/Shanghai]
时区转换

转换时区,首先我们需要有一个ZonedDateTime对象,然后,通过withZoneSameInstant()将关联时区转换到另一个时区,转换后日期和时间都会相应调整。

示例将北京时间转换为纽约时间:

public static void main(String[] args) {
    // 以中国时区获取当前时间:
    ZonedDateTime zbj = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
    // 转换为纽约时间:
    ZonedDateTime zny = zbj.withZoneSameInstant(ZoneId.of("America/New_York"));
    System.out.println(zbj);
    System.out.println(zny);
}
//print:2021-06-18T15:12:31.588+08:00[Asia/Shanghai]
//print:2021-06-18T03:12:31.588-04:00[America/New_York]

注意,时区转换的时候,由于夏令时的存在,不同的日期转换的结果很可能是不同的

北京时间11月15日的转换结果如下:

public static void main(String[] args) {
    // 以中国时区获取当前时间:
    ZonedDateTime zbj = ZonedDateTime.of(2021, 11 , 15, 15, 12, 31, 588000000, ZoneId.of("Asia/Shanghai"));
    // 转换为纽约时间:
    ZonedDateTime zny = zbj.withZoneSameInstant(ZoneId.of("America/New_York"));
    System.out.println(zbj);
    System.out.println(zny);
}
//print:2021-11-15T15:12:31.588+08:00[Asia/Shanghai]
//print:2021-11-15T02:12:31.588-05:00[America/New_York]

这两次转换后的纽约时间有1小时的夏令时时差。涉及到时区时,千万不要自己计算时差,否则难以正确处理夏令时。

ZonedDateTimeLocalDateTime相互转换
public static void main(String[] args) {
    LocalDateTime ldt = LocalDateTime.of(2021, 6, 18, 15, 30, 10);
    ZonedDateTime zbj = ldt.atZone(ZoneId.systemDefault());
    ZonedDateTime zny = ldt.atZone(ZoneId.of("America/New_York"));
    System.out.println(zbj);
    System.out.println(zny);
}
//print:2021-06-18T15:30:10+08:00[Asia/Shanghai]
//print:2021-06-18T15:30:10-04:00[America/New_York]

以这种方式创建的ZonedDateTime,它的日期和时间与LocalDateTime相同,但附加的时区不同,因此是两个不同的时刻

有了ZonedDateTime,将其转换为本地时间就非常简单:

public static void main(String[] args) {
    LocalDateTime ldt = LocalDateTime.of(2021, 6, 18, 15, 30, 10);
    ZonedDateTime zbj = ldt.atZone(ZoneId.systemDefault());
    ZonedDateTime zny = ldt.atZone(ZoneId.of("America/New_York"));
    LocalDateTime bjDateTime = zbj.toLocalDateTime();
    System.out.println(bjDateTime);
    LocalDateTime nyDateTime = zny.toLocalDateTime();
    System.out.println(nyDateTime);
}
//print:2021-06-18T15:30:10
//print:2021-06-18T15:30:10

转换为LocalDateTime时,直接丢弃了时区信息。

小结

提示:ZonedDateTime仍然提供了plusDays()等加减操作。

LocalDateTime,Date:

没有(无法)包含时区(本地时间,隐式的指默认时区ZoneId.systemDefault)信息,其实就是本地时间,即没有包含时区信息,就像我们平时互相说的时间日期一样,意味着如果需要转换为其他有时区的时间或者转换为其他时区的时候需要传入时区,这里的传入的时区一般就是指ZoneId.systemDefault()系统默认时区,所以很多函数转换时不传会去取默认时区(ZoneId.systemDefault())了

timestamp,Instant:

时间戳(格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数),本身不包含时区(可以认为隐式包含时区 0 GMT),是一个绝对的时间间隔(离格林威治时间1970年01月01日00时00分00秒过了多久),意味这如果时间戳等于1,你要转换为北京时间(UTC+8),就必须传入时区,结果就是(GMT to UTC+8)8点,而格林威治时间(GMT to GMT)就是0点.

Instant就是java中高精度的时间戳(精确到纳秒数)

ZonedDateTime,Calendar:

包含时区信息,不传的话会使用默认时区(ZoneId.systemDefault()),时区信息会保存到对象中

其中ZonedDateTime,LocalDateTime,Instant是java8的新时间API推荐使用

取当天的0点0分0秒 转 Date(本地时间(时区))

public static void main(String[] args) {
    //带着时区(默认时区,本地日期)
    ZonedDateTime zonedDateTime = ZonedDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0);
    // 1.zonedDateTime.toInstant()转换成时间戳(因为zonedDateTime包含了时区和时间信息,可以转换为GMT的时间戳(时间+时区))
    // 2.Date.from会使用默认时区转换时间戳为本地时间(默认时区)
    Date date = Date.from(zonedDateTime.toInstant());
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    System.out.println("北京时间:" + zonedDateTime);
    System.out.println("本地时间:" + format.format(date));

    zonedDateTime = zonedDateTime.withZoneSameInstant(ZoneId.of("America/New_York"));
    date = Date.from(zonedDateTime.toInstant());
    System.out.println("纽约时间:" + zonedDateTime);
    System.out.println("本地时间:" + format.format(date));
}
// 北京时间:2021-06-18T00:00+08:00[Asia/Shanghai]
// 本地时间:2021-06-18 00:00:00
// 纽约时间:2021-06-17T12:00-04:00[America/New_York]
// 本地时间:2021-06-18 00:00:00

DateTimeFormatter

DateTimeFormatter类是Java 8中日期时间功能里,用于解析和格式化日期时间的类,位于java.time.format包下。

自定义输出的格式,或者要把一个非ISO 8601格式的字符串解析成LocalDateTime,可以使用新的DateTimeFormatter

public static void main(String[] args) {
    // 自定义格式化:
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
    System.out.println(dtf.format(LocalDateTime.now()));
    // 用自定义格式解析:
    LocalDateTime dt2 = LocalDateTime.parse("2020/10/20 15:16:17", dtf);
    System.out.println(dt2);
}
// 2021/06/18 16:19:29
// 2021-06-18T15:16:17

预定义的DateTimeFormatter实例

DateTimeFormatter类包含一系列预定义(常量)的实例,可以解析和格式化一些标准时间格式。这将让你免除麻烦的时间格式定义,类中包含如下预定义的实例:

public static void main(String[] args) {
        ZonedDateTime dateTime = ZonedDateTime.now();
        DateTimeFormatter formatter1 = DateTimeFormatter.BASIC_ISO_DATE;
        DateTimeFormatter formatter2 = DateTimeFormatter.ISO_LOCAL_DATE;
        DateTimeFormatter formatter3 = DateTimeFormatter.ISO_LOCAL_TIME;
        DateTimeFormatter formatter4 = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
        String formattedZonedDate1 = formatter1.format(dateTime);
        String formattedZonedDate2 = formatter2.format(dateTime);
        String formattedZonedDate3 = formatter3.format(dateTime);
        String formattedZonedDate4 = formatter4.format(dateTime);
        System.out.println("formattedZonedDate = " + formattedZonedDate1);
        System.out.println("formattedZonedDate = " + formattedZonedDate2);
        System.out.println("formattedZonedDate = " + formattedZonedDate3);
        System.out.println("formattedZonedDate = " + formattedZonedDate4);
    }

//        formattedZonedDate = 20210618+0800 表示今年2021年,6月18日,位于UTC+8时区。
//        formattedZonedDate = 2021-06-18
//        formattedZonedDate = 11:45:05.828
//        formattedZonedDate = 2021-06-18T11:45:05.828