日期与时间

news2024/11/15 10:04:47

目录

一、基本概念

1.1、日期和时间

1.2、本地时间

1.3、时区

1.4、夏令时

1.5、本地化

二、Date 和 Calendar

2.1、Date

2.2、Calendar

三、TimeZone

四、LocalDateTime

4.1、LocalDateTime

4.2、Duration和Period

五、ZoneDateTime

5.1、创建实例

5.2、时区转换

六、DateTimeFormatter

6.1、创建格式化实例

七、Instant

八、新旧 API 转换

8.1 、旧API转新API

8.2 、新API转旧API

8.3、数据库中存储日期和时间


一、基本概念

1.1、日期和时间

        在计算机中,经常需要处理日期和时间。

        日期示例:

  •         2022-12-09
  •         2020-1-1

        时间示例:

  •         12:30:59
  •         2022-1-1 20:21:59
  •         2022-12-09 20:21:59

        日期是指某一天,可以看成它不是连续变化的,而是离散的。

        时间可以分两种:一直不带日期,例如,12:30:59。另外一种是带日期的时间,例如 2022-12-09 20:21:59 ,带日期的时间能唯一确认某个时刻,不带日期的时间无法确认一个唯一时刻。

1.2、本地时间

        当我们说当前时刻是2022年12月9日晚上21:59的时候,我们说的实际是本地时间。在国内是北京时间。在这个时刻,地球上不同地方的人同时看一眼表,各自的本地时间是不同的。

         所以,不同的时区,在同一时刻,本地时间是不同的。全球一共分为 24 个时区,伦敦所在的时区称为标准时区,其他时区按东 / 西偏移的小时区分,北京时区是东八区(UTC +8 (CST))

1.3、时区

        靠本地时间无法唯一确定一个准确的时刻,需要给本地时间加上一个时区。时区用 GMT(格林尼治标准时间) 或UTC(协调世界时简称)加时区偏移表示 例如 UTC/GMT +8小时 (东八区) 表示北京时间。

        还有一种是以洲/ 城市 表示例如,Asia/Shanghai 表示上海所在地的时区。需要注意的是城市名称不是任意城市,而是由国际标准组织规定的城市。

        因为时区的存在,它们的时刻是相同的。例如分别两个时区的两个人通话,如果这一刻通电话,它们各自报出自己手表的时间虽然是不同的,但是这个两个时间表示的是时刻是相同的。

1.4、夏令时

        夏令时,(Daylight Saving Time:DST),也叫夏时制,又称“日光节约时制”和“夏令时间”,是一种为节约能源而人为规定地方时间的制度,在这一制度实行期间所采用的统一时间称为“夏令时间”。一般在天亮早的夏季人为将时间调快一小时,可以使人早起早睡,减少照明量,以充分利用光照资源,从而节约照明用电。各个采纳夏时制的国家具体规定不同。全世界有近110个国家每年要实行夏令时。
        1986年4月,中国中央有关部门发出“在全国范围内实行夏时制的通知”,具体做法是:每年从四月中旬第一个星期日的凌晨2时整(北京时间),将时钟拨快一小时,即将表针由2时拨至3时,夏令时开始;到九月中旬第一个星期日的凌晨2时整(北京夏令时),再将时钟拨回一小时,即将表针由2时拨至1时,夏令时结束。从1986年到1991年的六个年度,除1986年因是实行夏时制的第一年,从5月4日开始到9月14日结束外,其它年份均按规定的时段施行。在夏令时开始和结束前几天,新闻媒体均刊登有关部门的通告。1992年起,夏令时暂停实行。

1.5、本地化

        计算机中,通常使用 Locale 表示一个国家或者地区的日期、时间、数字、货币等格式。 Locale 由 语言_国家 的字母缩写构成。例如 zh_CN 表示中文 ,en_US 表示美国。语言使用小写,国家使用大写。

        例如

        zh_CN  2022-12-19

        en_US 19/30/2022

        计算机用 Locale 在日期、时间、货币和字符串之间进行转化。例如网站可以根据用户所在的 Locale 对用户显示如下:

中国用户美国用户
购买价格¥123,400.00$123,400.00
购买日期2016-11-3011/30/2016

二、Date 和 Calendar

        在编程中,经常看到日期和时间表现方式如下:

  • 2019-11-20 0:15:01 GMT+00:00
  • 2019年11月20日8:15:01
  • 11/19/2019 19:15:01 America/New_York

        实际上以上数据的展示格式分别按英国时区、中国时区、纽约时区对同一个时刻进行展示。而这个 ”同一时刻“在计算机中存储的本质上只是一个整数,称它为 Epoch Time 。

        Epoch 是计算机从1970年1月1日零点(格林威治时区 / GMT + 00:00) 到现在所经历的秒数,例如:

        1574208900表示从从1970年1月1日零点GMT时区到该时刻一共经历了1574208900秒,换算成伦敦、北京和纽约时间分别是:

        

1574208900 = 北京时间2019-11-20 8:15:00
           = 伦敦时间2019-11-20 0:15:00
           = 纽约时间2019-11-19 19:15:00

        在计算机中,只需要存储一个整数 1574208900 表示某一时刻,当需要显示某一地区时间时,就格式化一个字符串。

        Epoch Time 又称为时间戳,在不同的语言中,会有几种存储格式:

  •         以秒为单位的整数:1574208900,缺点是精度只能到秒
  •         以毫秒为单位的整数:1574208900123,最后3位表示毫秒数
  •         以秒为单位的浮点数:1574208900.123,小数点后面表示零点几秒

        Java 中获取当前时间的毫秒数常用方法如下:

public class Main {
    public static void main(String[] args) {
        long time= System.currentTimeMillis();
        System.out.println(time);  //1670677161211
    }
}

        Java 标准库API

        定义在 java.util 这个包,主要包括 Date、Calendar 和 TimeZone 等

        (Java 8 )定义在 java.time 这个包,主要包括 LocalDateTime 、ZoneDateTime 、ZoneId 等。

2.1、Date

        java.util.Date 是用于表示一个日期和时间的对象。需要注意的是 java.util.Date 区分,后者用于数据库中。

        示例

import java.util.Date;

public class Main {
    public static void main(String[] args) {
        // 获取当前时间:
        Date date = new Date();
        // 获取年份
        System.out.println(date.getYear() + 1900); // 必须加上1900
        // 获取月份
        System.out.println(date.getMonth() + 1); // 0~11,必须加上1
        // 获取日
        System.out.println(date.getDate()); // 1~31,不能加1
        // 转换为String:
        System.out.println(date.toString());
        // 转换为GMT时区:
        System.out.println(date.toGMTString());
        // 转换为本地时区:
        System.out.println(date.toLocaleString());
    }
}

        getYear() 返回的年份必须加上 1900,getMonth() 返回的月份 0-11分别表示1-12月,所以需要加 1,而 getDate() 返回的日期范围是1~31,又不能加 1 。

        输出本地时区表示的日期和时间,不同计算机可能会有不同的结果。如果需要特定格式,可以使用 SimpleDateFormat 对一个Date 进行转换。

  •         yyyy : 年
  •         MM : 月
  •         dd:日
  •         HH:小时
  •         mm:分钟
  •         ss:秒

        示例

import java.text.SimpleDateFormat;
import java.util.Date;

public class Main {
    public static void main(String[] args) {
        // 获取当前时间:
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(sdf.format(date));
    }
}

        Java 的格式预定义了许多不同的格式。详细JDK

  •         M :月份  输出格式 9
  •         MM : 月份 输出格式 09
  •         MMM: 月份 输出格式 September
import java.text.SimpleDateFormat;
import java.util.Date;

public class Main {
    public static void main(String[] args) {
        // 获取当前时间:
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("E MMM dd, yyyy");  // 例如    星期六 十二月 10, 2022
        System.out.println(sdf.format(date));
    }
}

         Date 对象不能转换时区,除了 toGMTString() 可以按 GMT+0:00 输出外,Date 总数以当前计算机系统的默认时区为基础进行输出。对日期和时间加减操作不是很方便。

2.2、Calendar

        Calendar 可以用于获取并设置年、月、日、时、分、秒,它和 Date 相比,主要多了一个可以做简单的日期和时间运算的功能。

        示例

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class Main {
    public static void main(String[] args) {
        // 获取当前时间:
        Calendar c = Calendar.getInstance();
        // 年
        int y = c.get(Calendar.YEAR);
        // 月
        int m = 1 + c.get(Calendar.MONTH);
        // 日
        int d = c.get(Calendar.DAY_OF_MONTH);
        // 星期
        int w = c.get(Calendar.DAY_OF_WEEK);
        // 小时
        int hh = c.get(Calendar.HOUR_OF_DAY);
        // 分
        int mm = c.get(Calendar.MINUTE);
        // 秒
        int ss = c.get(Calendar.SECOND);
        // 毫秒
        int ms = c.get(Calendar.MILLISECOND);
        System.out.println(y + "-" + m + "-" + d + " " + w + " " + hh + ":" + mm + ":" + ss + "." + ms);  // 例如 2022-12-10 7 21:35:18.246
    }

}

        注意 CaLendar 获取年月日这些信息变成了 get(int field),返回年份不需要转换(加1900),返回月份仍然需要加 1 ,返回星期需要特别注意下 1~7 分别表示周日、周一....,周六 。 

        Calendar 只有一种方式获取,即 Calendar.getIntance(),而且获取就是当前时间。如果需要设置一个特定时间,就必须先清楚所有字段:

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class Main {
    public static void main(String[] args) {
        // 当前时间:
        Calendar c = Calendar.getInstance();
        // 清除所有:
        c.clear();
        // 设置2019年:
        c.set(Calendar.YEAR, 2019);
        // 设置9月:注意8表示9月:
        c.set(Calendar.MONTH, 8);
        // 设置2日:
        c.set(Calendar.DATE, 2);
        // 设置时间:
        c.set(Calendar.HOUR_OF_DAY, 21);
        c.set(Calendar.MINUTE, 22);
        c.set(Calendar.SECOND, 23);
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(c.getTime()));
        // 2019-09-02 21:22:23
    }


}

          Calendar 对日期和时间操作示例:

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

public class DateUtil {

    /**
     * 获取年
     *
     * @param date
     * @return
     */
    public static int getYear(Date date) {
        GregorianCalendar gc = new GregorianCalendar();
        gc.setTime(date);
        return gc.get(Calendar.YEAR);
    }

    /**
     * 获取月
     *
     * @param date
     * @return
     */
    public static int getMonth(Date date) {
        GregorianCalendar gc = new GregorianCalendar();
        gc.setTime(date);
        return gc.get(Calendar.MONTH) + 1;
    }

    /**
     * 获取日
     *
     * @param date
     * @return
     */
    public static int getDay(Date date) {
        GregorianCalendar gc = new GregorianCalendar();
        gc.setTime(date);
        return gc.get(Calendar.DAY_OF_MONTH) + 1;
    }

    /**
     * 增加或递减日
     *
     * @param date
     * @param num
     * @return
     */
    public static Date addDay(Date date, int num) {
        return add(date, Calendar.DAY_OF_MONTH, num);
    }

    /**
     * 增加或递减月
     *
     * @param date
     * @param num
     * @return
     */
    public static Date addMonth(Date date, int num) {
        return add(date, Calendar.MONTH, num);
    }

    /**
     * 增加或递减年
     *
     * @param date
     * @param num
     * @return
     */
    public static Date addYear(Date date, int num) {
        return add(date, Calendar.YEAR, num);
    }

    /**
     * 日期和时间增加或递减方法
     *
     * @param date 日期
     * @param type 类型(年、月、日等)
     * @param num  新增或递减数
     * @return
     */
    private static Date add(Date date, int type, int num) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(type, num);
        return cal.getTime();
    }


}

三、TimeZone

       Java 中时区用 TimeZone 对象表示。

import java.util.TimeZone;

public class Main {
    public static void main(String[] args) {
        TimeZone tzDefault = TimeZone.getDefault(); // 当前时区
        TimeZone tzGMT9 = TimeZone.getTimeZone("GMT+09:00"); // GMT+9:00时区
        TimeZone tzNY = TimeZone.getTimeZone("America/New_York"); // 纽约时区
        System.out.println(tzDefault.getID()); // Asia/Shanghai
        System.out.println(tzGMT9.getID()); // GMT+09:00


    }


}

        时区的唯一标识是以一个字符串表示的ID,获取指定 TimeZone 对象以这个ID为参数获取。GMT+09:00、Asia/Shanghai都是有效的时区ID:

import java.util.TimeZone;

public class Main {
    public static void main(String[] args) {
        String[] ids =  TimeZone.getAvailableIDs();
        for (String id : ids) {
            System.out.println(id);
        }

    }
}

        有了时区,就可以对指定时间进行转换。例如将北京时间 2019-11-20 8:15:00 转换成纽约时间:

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;

public class Main {
    public static void main(String[] args) {
        // 当前时间:
        Calendar c = Calendar.getInstance();
        // 清除所有:
        c.clear();
        // 设置为北京时区:
        c.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
        // 设置年月日时分秒:
        c.set(2019, 10 /* 11月 */, 20, 8, 15, 0);
        // 显示时间:
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
        System.out.println(sdf.format(c.getTime()));
        // 2019-11-19 19:15:00
    }
}

        利用 Calendar 进行时间转换步骤:

        1、清楚所有字段

        2、设置指定时区

        3、设置日期和时间

        4、创建 SimpleDateFormat 并设置目标时区

        5、格式化获取的Date 对象

四、LocalDateTime

        Java 8  开始,java.time 包提供了新的日期和时间操作 API 主要涉及类型有:

        本地日期和时间:LocalDateTime 、LocalDate、LocalTime

        带时区的日期和时间: ZoneDateTime

        时刻 : Instant

        时区: ZoneId、ZoneOffset

        时间间隔:Duration

        格式化类型:DateTimeFormatter

        详细 JDK

        其中新的 API 修正了旧 API 不合理的常量设计:        

  •         Month 的范围用1~12表示1-12月
  •         Week 的范围用1~7表示周一到周日

4.1、LocalDateTime

        表示一个本地日期和时间。  

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

public class Main {
    public static void main(String[] args) {
        //LocalDate d = LocalDate.now(); // 当前日期
       // LocalTime t = LocalTime.now(); // 当前时间
        LocalDateTime dt = LocalDateTime.now(); // 当前日期和时间
        LocalDate d = dt.toLocalDate();// 当前日期
        LocalTime t = dt.toLocalTime();// 当前时间
        System.out.println(d); // 严格按照ISO 8601格式打印  // 例如 2022-12-10
        System.out.println(t); // 严格按照ISO 8601格式打印   //例如 22:37:01.025
        System.out.println(dt); // 严格按照ISO 8601格式打印  //例如 2022-12-10T22:37:01.025
    }
}

        通过指定日期和时间创建 LocalDateTime 可以通过 of() 方法:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

public class Main {
    public static void main(String[] args) {
        // 指定日期和时间:
        LocalDate d2 = LocalDate.of(2019, 11, 30); // 2019-11-30, 注意11=11月
        LocalTime t2 = LocalTime.of(15, 16, 17); // 15:16:17
        LocalDateTime dt2 = LocalDateTime.of(2019, 11, 30, 15, 16, 17);
        LocalDateTime dt3 = LocalDateTime.of(d2, t2);
    }
}

        字符串转为 LocalDateTime 就可以标准格式:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

public class Main {
    public static void main(String[] args) {
        LocalDateTime dt = LocalDateTime.parse("2019-11-19T15:16:17");
        LocalDate d = LocalDate.parse("2019-11-19");
        LocalTime t = LocalTime.parse("15:16:17");
        System.out.println(dt);
        System.out.println(d);
        System.out.println(t);
    }
}

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

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        LocalDateTime dt = LocalDateTime.of(2019, 10, 26, 20, 30, 59);
        System.out.println(dt);
        // 加5天减3小时:
        LocalDateTime dt2 = dt.plusDays(5).minusHours(3);
        System.out.println(dt2); // 2019-10-31T17:30:59
        // 减1月:
        LocalDateTime dt3 = dt2.minusMonths(1);
        System.out.println(dt3); // 2019-09-30T17:30:59
    }
}

        对日期调整操作使用withXxx()方法,例如:withHour(15)会把10:11:12变为15:11:12:

  •         调整年:withYear()
  •         调整月:withMonth()
  •         调整日:withDayOfMonth()
  •         调整时:withHour()
  •         调整分:withMinute()
  •         调整秒:withSecond()
import java.time.LocalDateTime;

public class Main {
    public static void main(String[] args) {
        LocalDateTime dt = LocalDateTime.of(2019, 10, 26, 20, 30, 59);
        System.out.println(dt);
        // 日期变为31日:
        LocalDateTime dt2 = dt.withDayOfMonth(31);
        System.out.println(dt2); // 2019-10-31T20:30:59
        // 月份变为9:
        LocalDateTime dt3 = dt2.withMonth(9);
        System.out.println(dt3); // 2019-09-30T20:30:59
    }
}

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

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;

public class Main {
    public static void main(String[] args) {
        // 本月第一天0:00时刻:
        LocalDateTime firstDay = LocalDate.now().withDayOfMonth(1).atStartOfDay();
        System.out.println(firstDay);

        // 本月最后1天:
        LocalDate lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());
        System.out.println(lastDay);

        // 下月第1天:
        LocalDate nextMonthFirstDay = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth());
        System.out.println(nextMonthFirstDay);

        // 本月第1个周一:
        LocalDate firstWeekday = LocalDate.now().with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
        System.out.println(firstWeekday);
    }
}

        断两个LocalDateTime的先后,可以使用isBefore()、isAfter()方法,对于LocalDate和LocalTime类似

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

public class Main {
    public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime target = LocalDateTime.of(2019, 11, 19, 8, 15, 0);
        System.out.println(now.isBefore(target));
        System.out.println(LocalDate.now().isBefore(LocalDate.of(2019, 11, 19)));
        System.out.println(LocalTime.now().isAfter(LocalTime.parse("08:15:00")));
    }
}

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

4.2、Duration和Period

        Duration表示两个时刻之间的时间间隔。另一个类似的Period表示两个日期之间的天数。

import java.time.*;

public class Main {
    public static void main(String[] args) {
        LocalDateTime start = LocalDateTime.of(2019, 11, 19, 8, 15, 0);
        LocalDateTime end = LocalDateTime.of(2020, 1, 9, 19, 25, 30);
        Duration d = Duration.between(start, end);
        System.out.println(d); // PT1235H10M30S

        Period p = LocalDate.of(2019, 11, 19).until(LocalDate.of(2020, 1, 9));
        System.out.println(p); // P1M21D
    }
}

        注意到两个LocalDateTime之间的差值使用Duration表示,类似PT1235H10M30S,表示1235小时10分钟30秒。而两个LocalDate之间的差值用Period表示,类似P1M21D,表示1个月21天。

        Duration和Period的表示方法也符合ISO 8601的格式,它以P...T...的形式表示,P...T之间表示日期间隔,T后面表示时间间隔。如果是PT...的格式表示仅有时间间隔。利用ofXxx()或者parse()方法也可以直接创建Duration:

Duration d1 = Duration.ofHours(10); // 10 hours
Duration d2 = Duration.parse("P1DT2H3M"); // 1 day, 2 hours, 3 minutes

        LocalDateTime 是表示本地日期和时间,表示一个带时区的日期和时间,就需要ZoneDateTime 。

五、ZoneDateTime

5.1、创建实例

        创建ZoneDateTime示例 

import java.time.*;

public class Main {
    public static void main(String[] args) {
        ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区  //例如2022-12-10T23:09:37.412+08:00[Asia/Shanghai]
        ZonedDateTime zny = ZonedDateTime.now(ZoneId.of("America/New_York")); // 用指定时区获取当前时间 // 例如2022-12-10T10:09:37.412-05:00[America/New_York]
        System.out.println(zbj);
        System.out.println(zny);
    }
}

        通过给一个LocalDateTime附加一个ZoneId

import java.time.*;

public class Main {
    public static void main(String[] args) {
        LocalDateTime ldt = LocalDateTime.of(2019, 9, 15, 15, 16, 17);
        ZonedDateTime zbj = ldt.atZone(ZoneId.systemDefault());
        ZonedDateTime zny = ldt.atZone(ZoneId.of("America/New_York"));
        System.out.println(zbj);
        System.out.println(zny);
    }
}

5.2、时区转换

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

import java.time.*;

public class Main {
    public static void main(String[] args) {
        // 以中国时区获取当前时间:
        ZonedDateTime zbj = ZonedDateTime.now(ZoneId.of("Asia/Shanghai")); // 例如2022-12-10T23:15:54.522+08:00[Asia/Shanghai]
        // 转换为纽约时间:
        ZonedDateTime zny = zbj.withZoneSameInstant(ZoneId.of("America/New_York")); // 例如2022-12-10T10:15:54.522-05:00[America/New_York]
        System.out.println(zbj);
        System.out.println(zny);
    }
}

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

ZonedDateTime zdt = ...
LocalDateTime ldt = zdt.toLocalDateTime();

        要特别注意,时区转换的时候,由于夏令时的存在,不同的日期转换的结果很可能是不同的。我们转换需要用ZonedDateTime  ,因为直接丢弃了时区信息。

六、DateTimeFormatter

        和 SimpleDateFormat 对象相比,DateTimeFormatter 不但是不变的对象,还是线程安全的。

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

  •         日期 : yyyy-MM-dd
  •         时间:HH:mm:ss
  •         带毫秒时间:HH:mm:ss:SSS
  •         日期和时间:yyyy-MM-dd'T'HH:mm:ss
  •         带毫秒的日期和时间:yyyy-MM-dd'T'HH:mm:ss.SSS

        示例

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

public class Main {
    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("2019-11-30 15:16:17", dtf);
        System.out.println(dt2);

    }
}

6.1、创建格式化实例

import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {

        /**
         * 创建格式化实例方法
         */

        // 方式一
        DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");

        // 方法二
        DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("E, yyyy-MMMM-dd HH:mm", Locale.US);
    }


}

        按照Locale默认习惯格式化示例

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {

        ZonedDateTime zdt = ZonedDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm ZZZZ"); // 例如2022-12-11T11:57 GMT+08:00
        System.out.println(formatter.format(zdt));

        DateTimeFormatter zhFormatter = DateTimeFormatter.ofPattern("yyyy MMM dd EE HH:mm", Locale.CHINA); // 例如2022 十二月 11 星期日 11:57
        System.out.println(zhFormatter.format(zdt));

        DateTimeFormatter usFormatter = DateTimeFormatter.ofPattern("E, MMMM/dd/yyyy HH:mm", Locale.US); // 例如Sun, December/11/2022 11:57
        System.out.println(usFormatter.format(zdt));
    }


}

        对ZonedDateTime或LocalDateTime进行格式化,需要使用DateTimeFormatter类。

        DateTimeFormatter可以通过格式化字符串和Locale对日期和时间进行定制输出。

七、Instant

        Java 8 可以用Instant中Instant.now()获取当前时间戳。效果和System.currentTimeMillis()类似。

        示例

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class Main {
    public static void main(String[] args) {

        Instant now = Instant.now();
        long l = now.getEpochSecond();
        System.out.println("l = "+l); // 例如 1670734622
        // 以指定时间戳创建Instant:
        Instant ins = Instant.ofEpochSecond(l);  
        ZonedDateTime zdt = ins.atZone(ZoneId.systemDefault());
        System.out.println(zdt); // 2022-12-11T12:57:02+08:00[Asia/Shanghai]
    }


}

        Instant就是时间戳,那么,给它附加上一个时区,就可以创建出ZonedDateTime。

        对于某一个时间戳,给它关联上指定的ZoneId,就得到了ZonedDateTime,继而可以获得了对应时区的LocalDateTime。

        所以,LocalDateTime,ZoneId,Instant,ZonedDateTime和long都可以互相转换。

八、新旧 API 转换

8.1 、旧API转新API

        旧式的 Date 或 Calendar 转换新 API 对象,可以通过 toInstant() 方法转换为 Instant 对象,再继续转换为 ZoneDateTime:

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;

public class Main {
    public static void main(String[] args) {

        // Date -> Instant:
        Instant ins1 = new Date().toInstant();

    // Calendar -> Instant -> ZonedDateTime:
        Calendar calendar = Calendar.getInstance();
        Instant ins2 = calendar.toInstant();
        ZonedDateTime zdt = ins2.atZone(calendar.getTimeZone().toZoneId());
    }


}

        旧的 TimeZone 提供了一个 toZoneId() ,可以把自己变成新的 ZoneId 。

8.2 、新API转旧API

        把新的 ZoneDateTime 转换为旧的API对象,借助long型时间戳做一个”中转“:

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class Main {
    public static void main(String[] args) {

        // ZonedDateTime -> long:
        ZonedDateTime zdt = ZonedDateTime.now();
        long ts = zdt.toEpochSecond() * 1000;

    // long -> Date:
        Date date = new Date(ts);

    // long -> Calendar:
        Calendar calendar = Calendar.getInstance();
        calendar.clear();
        calendar.setTimeZone(TimeZone.getTimeZone(zdt.getZone().getId()));
        calendar.setTimeInMillis(zdt.toEpochSecond() * 1000);
    }


}

        新的ZoneId转换为旧的TimeZone,需要借助ZoneId.getId()返回的String完成。

8.3、数据库中存储日期和时间

        除了旧式的 java.util.Date , 还可以找到另外一个 java.sql.Date ,它继承自 java.util.Date , 但会自动忽略所有时间相关信息。

        在数据库中,也存在几种日期和时间类型:

        DATETIME: 日期和时间

        DATE: 日期

        TIME:时间

        TIMESTAMP:和DATATIME类似,但是数据库会在创建和跟新记录同时修改TIMESTAMP 

        数据库类型与Java新旧API的映射关系:

数据库对应Java类(旧)对应Java类(新)
DATETIMEjava.util.DateLocalDateTime
DATEjava.sql.DateLocalDate
TIMEjava.sql.TimeLocalTime
TIMESTAMPjava.sql.TimestampLocalDateTime

        数据库中,我们需要存储的最常用的是时刻(Instant),因为有了时刻信息,旧可以根据用户自己的选择的时区,显示正确的本地时间。所以,最好的方法直接用长整型 long 表示,在数据库中存储为 BIGINT 类型。

        存储以个 long 型时间戳,我们可以编写一个 timestampToString() 的方法,为不同用户以不同偏好来显示不同的本地时间:

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

public class Main {
    public static void main(String[] args) {
        Instant now = Instant.now();
        long ts = now.toEpochMilli();  // 例如  1670736392596
        System.out.println("ts="+ts);
        System.out.println(timestampToString(ts, Locale.CHINA, "Asia/Shanghai")); // 例如 2022-12-11 下午1:26
        System.out.println(timestampToString(ts, Locale.US, "America/New_York"));// 例如  Dec 11, 2022 12:26 AM
    }

    static String timestampToString(long epochMilli, Locale lo, String zoneId) {
        Instant ins = Instant.ofEpochMilli(epochMilli);
        DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT);
        return f.withLocale(lo).format(ZonedDateTime.ofInstant(ins, ZoneId.of(zoneId)));
    }


}

        处理日期和时间时,尽量使用新的java.time包。

        在数据库中存储时间戳时,尽量使用long型时间戳,它具有省空间,效率高,不依赖数据库的优点。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/79888.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电子学会2020年12月青少年软件编程(图形化)等级考试试卷(一级)答案解析

目录 一、单选题(共25题,每题2分,共50分) 二、判断题(共10题,每题2分,共20分) 三、编程题【该题由测评师线下评分】(共2题,共30分) 青少年软件…

【MySQL数据库】索引到底是什么,怎么创建索引的效率最高

面试官:我看你简历上写了MySQL,对MySQL InnoDB引擎的索引了解吗? 候选者:嗯啊,使用索引可以加快查询速度,其实上就是将无序的数据变成有序(有序就能加快检索速度) 候选者&#xff…

微服务守护神-Sentinel-其他

引言 书接上篇 微服务守护神-Sentinel-热点-授权-系统规则 ,上面介绍了Sentinel热点、授权、系统规则,本篇继续来Sentinel 剩下其他操作 自定义异常返回 当触发sentinel流控规则之后, sentinel就干巴巴返回异常信息,单纯的文字…

JVM监控及诊断工具

本文目录命令行jps 查看正在运行的Java进程jstat:查看JVM统计信息jinfo:实时查看和修改JVM配置参数jmap:导出内存映像文件&内存使用情况jhat:JDK自带堆分析工具jstack:打印JVM中线程快照jcmd:多功能命令…

docker、LXC、LXD的区别及传统的虚拟机与操作系统虚拟化的区别

1. 概念解释 1.1. Docker Docker是一个用于在集中式平台上创建、部署和运行应用程序的开源工具。这使得主机的操作系统通过容器运行具有相同Linux内核的应用程序,而不是创建一个完整的虚拟机。使用docker容器不需要考虑Ram和磁盘空间的分配。它能够自己处理这些需…

CopyOnWriteArrayList源码解析

CopyOnWriteArrayList源码解析 简介 我们知道 ArrayList 是线程不安全的,其存在一个古老的线程安全的 Vector,但是由于 Vector 效率太低 (方法都加了synchronzed),在 JDK1.5 时 Doug Lea 提供了一个效率较高的线程安全的 CopyOnWriteArrayL…

虚拟内存(深入理解计算机系统原书第3版9节)

深入理解计算机系统(原书第3版)读书笔记,其实就是嚼碎了原文然后把一部分挑了出来摘要,免得读着读着忘了 文章目录前言一、物理和虚拟寻址二、地址空间三、虚拟内存作为缓存的工具1、DRAM缓存的组织结构2、页表3、页命中4、缺页5、…

[附源码]Python计算机毕业设计SSM基于的小区物业管理系统(程序+LW)

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

微服务框架 SpringCloud微服务架构 微服务保护 33 授权规则 33.1 授权规则

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式,系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 微服务保护 文章目录微服务框架微服务保护33 授权规则33.1 授权规则33.1.1 授权规则33 授权规则 33.1 授权规则 33.1.1 授权规则 看看sen…

康鹏科技将于12月16日上会:曾在纽交所上市,由杨建华家族控股

近日,上海康鹏科技股份有限公司(下称“康鹏科技”)在上海证券交易所递交招股书(上会稿),准备在科创板上市。据贝多财经了解,康鹏科技将于2022年12月16日接受科创板上市委的现场审议。 根据公开信…

基于C++实现(MFC)职工工作量统计系统【100010022】

【职工工作量统计系统设计】 1、问题描述 职工包括姓名、职工号、性别、年龄、所在部门、联系方式等信息。 工作量包括职工号、完成的产品数量等信息。 该设计系统能够对职工的工作量进行统计,并排出名次。注意,一个职工的工作量是可以多次输入的。 2…

Alibaba官方上线,Java并发编程全彩图册(终极版)GitHub已置顶

都说程序员工资高、待遇好, 2022 金九银十到了,你的小目标是 30K、40K,还是 16薪的 20K?作为一名 Java 开发工程师,当能力可以满足公司业务需求时,拿到超预期的 Offer 并不算难。然而,提升 Java…

Python图像识别实战(一):实现按数量随机抽取图像复制到另一文件夹(附源码和实现效果)

前面我介绍了可视化的一些方法以及机器学习在预测方面的应用,分为分类问题(预测值是离散型)和回归问题(预测值是连续型)(具体见之前的文章)。 从本期开始,我将做一个关于图像识别的…

Qt实现全局鼠标事件监听器-Linux

Qt实现全局鼠标事件监听器-Linux版🦑 var code “bc8d4eb4-a9df-48e9-8028-bbe1ae7fbd05” 文章目录Qt实现全局鼠标事件监听器-Linux版🦑1、概述🦞2、实现效果🍰3、实现方式🦀4、关键代码🍦5、源代码&…

SpringMVC基础篇:第一个MVC程序和细节分析

文章整理自孙哥说SpringMVC,相关课程联系孙哥学习谢谢。第一章:编码开发一:思路分析二:SpringMVC程序编码三:控制器提供多个服务方法四:注意事项第二章:细节分析一:控制器创建次数二…

C++ 【set、map模拟实现】

目录 set概念 set基本使用 map概念 map的使用 map统计次数 operator[] operator[]底层如何实现? set和map迭代器封装 红黑树迭代器基本结构 operator operator-- operator[] 源代码链接 map、set底层都使用平衡搜索树(即红黑树),容器中的元素…

HanLP 基于朴素贝叶斯 训练 文本分类

一、HanLP 朴素贝叶斯分类器 HanLP 针对文本分类算法已经帮我们实现 朴素贝叶斯法 ,用户可以无需关心内部细节,HanLP 也提供了相关自定义训练接口,前提需要将数据集根据分类放到不同的目录中,例如: 官方给出了相关性能…

HanLP 基于SVM支持向量机 训练 文本分类

一、HanLP 基于SVM支持向量机分类器 上篇文章通过朴素贝叶斯文本分类器,训练测试了 搜狗文本分类语料库迷你版 ,本篇继续测试SVM支持向量机分类器。 由于HanLP 官方给出的 SVM 分类器依赖了第三方库,没有集成在主项目中,需要拉取…

问题解决(1)——VS中scanf报错怎么解决

目录 方法一: 方法二: 方法三: 各位好,博主新建了个公众号《自学编程村》,拉到底部即可看到,有情趣可以关注看看哈哈,关注后还可以加博主wx呦~~~(公众号拉到底部就能看到呦~~&am…

Redis【13】-修改数据库后,如何保证Redis与数据库的数据一致性

一、需求起因 在高并发的业务场景下,数据库大多数情况都是用户并发访问最薄弱的环节。所以,就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问MySQL等数据库。 这个业务场景,主要是解决读数据从Redi…