学习笔记-java日期和时间

本文深入讲解Java中的日期与时间处理,包括旧API如Date、Calendar的使用,以及Java 8引入的新API LocalDateTime、ZonedDateTime等,并介绍如何进行时区转换及时间戳的处理。

日期与时间

Date和Calendar(java.util)

  • 计算机内存储的时间是一个时间戳,各种不同时区的时间只是对时间戳的不同展示罢了,Epoch Time又称为时间戳,是long类型

  • 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
    
API
java.util(旧)
  • Date
  • Calendar
  • TimeZone
java8(新)
  • LocalDateTime
  • ZoneDateTime
  • ZoneId
Date
  • 基本用法

    Date date =new Date()
        //好多API例如 一下都不在使用
        System.out.println(date.getYear() + 1900); // 必须加上1900
            System.out.println(date.getMonth() + 1); // 0~11,必须加上1
            System.out.println(date.getDate()); // 1~31,不能加1
    
  • 控制时间显示的格式用SimpleDateFormat

    public class practice {
        public static void main(String[] args) {
            Date d=new Date();
            SimpleDateFormat t=new SimpleDateFormat("yyyy-MM-dd");
            System.out.println(t.format(d));
            System.out.println(d.getTime());//时间戳
        }
    }
    M:输出9
    MM:输出09
    MMM:输出Sep
    MMMM:输出September
    
Calendar
  • Calendar可以用于获取并设置年、月、日、时、分、秒,它和Date比,主要多了一个可以做简单的日期和时间运算的功能

  • 创建:Calendar c=Calendar.getInstance()

    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);
        }
    }
    2020-10-25 1 5:53:53.713
      // 如果我们想给它设置成特定的一个日期和时间,就必须先清除所有字段 
    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.getTime()可以将一个Calendar对象转换成Date对象,然后就可以用SimpleDateFormat进行格式化

TimeZone
  • GMT+09:00Asia/Shanghai都是有效的时区ID。要列出系统支持的所有ID,请使用TimeZone.getAvailableIDs()

    System.out.println(TimeZone.getAvailableIDs().length);
            for (String f:TimeZone.getAvailableIDs()) {
                System.out.println(f);
            }
    
  • 有了时区,我们就可以对指定时间进行转换

    例如将北京时间转化为纽约时间:

    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对象(注意Date对象无时区信息,时区信息存储在SimpleDateFormat中)。

       SimpleDateFormat t1=new SimpleDateFormat("yyyy-M-dd HH-mm");
              t1.setTimeZone(TimeZone.getTimeZone("America/New_York"));
              System.out.println(t1.format(new Date().getTime()));
      

java.time

LocalDateTime
java8的主要时间API
  1. 本地日期和时间:LocalDateTimeLocalDateLocalTime
  2. 带时区的日期和时间:ZonedDateTime
  3. 时刻:Instant
  4. 时区:ZoneIdZoneOffset
  5. 时间间隔:Duration
  6. 取代SimpleDateFormat的格式化类型DateTimeFormatter
  7. 仅有自己时区的信息
  • 基本用法 .now()

    public class Main {
        public static void main(String[] args) {
            LocalDate d = LocalDate.now(); // 当前日期
            LocalTime t = LocalTime.now(); // 当前时间
            LocalDateTime dt = LocalDateTime.now(); // 当前日期和时间
            System.out.println(d); // 严格按照ISO 8601格式打印
            System.out.println(t); // 严格按照ISO 8601格式打印
            System.out.println(dt); // 严格按照ISO 8601格式打印
        }
    }
    2020-10-25
    06:32:42.656969042
    2020-10-25T06:32:42.657019909
        本地日期和时间通过now()获取到的总是以当前默认时区返回的
    

    问题:由于获取时间有小小差异,导致两个时间不相同,解决:

    LocalDateTime dt = LocalDateTime.now(); // 当前日期和时间
    LocalDate d = dt.toLocalDate(); // 转换到当前日期
    LocalTime t = dt.toLocalTime(); // 转换到当前时间
    
  • 自己设定LocalDateTime(静态方法of 或者parse)

    // 指定日期和时间:
    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);
    
    //因为严格按照ISO 8601的格式,因此,将字符串转换为LocalDateTime就可以传入标准格式:
    LocalDateTime dt = LocalDateTime.parse("2019-11-19T15:16:17");
    LocalDate d = LocalDate.parse("2019-11-19");
    LocalTime t = LocalTime.parse("15:16:17");
    //注意ISO 8601规定的日期和时间分隔符是T
    
  • DateTimeFormatter

    **基本用法:**DateTimeFormatter.ofPattern(" ")

    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);
        }
    }
    2020/10/25 06:42:07
    2019-11-30T15:16:17
    
  • 用withXxx()和修改日期和时间

     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
        }
    
    2019-10-26T20:30:59
    2019-10-31T20:30:59
    2019-09-30T20:30:59
        
        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);
        }
    }
    
    
  • Duration两个时间之间的间隔between,Period两个日期之间的额天数until

    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
        }
    }
    PT1235H10M30S //1235小时10分钟30秒。
    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
    

    分离出duration的数字

       LocalDateTime l1=LocalDateTime.of(2020,10,24,14,55,0);
           LocalDateTime l2=LocalDateTime.now();
            Duration d= Duration.between(l1,l2);
            String time=d.toString();
            System.out.println(time);
            String [] timeCH=time.split("[A-Z]+");
            time=String.join(" ",timeCH);
            time=time.trim();
            System.out.println(time);
            timeCH=time.split("\\s");
            System.out.println(timeCH.length);
            for(String i:timeCH){
             System.out.println(i);
            }
    
      LocalDateTime l1=LocalDateTime.of(2020,10,24,14,55,0);
           LocalDateTime l2=LocalDateTime.now();
            Duration d= Duration.between(l1,l2);
            String time=d.toString();
            time=time.replace("PT","");
            time=time.replace("H","小时");
            time=time.replace("M","分钟");
            time=time.replace("S","秒");
    
            System.out.println(time);
    
ZoneDateTime:

LocalDateTime总是表示本地日期和时间,要表示一个其他时区的日期和时间,我们就需要ZonedDateTime

可以简单地把ZonedDateTime理解成LocalDateTimeZoneId

==ZonedDateTime.ofInstant(ins, ZoneId.of(zoneId))==ins是时间戳,LocalDateTime也包含时间戳信息

  • 基本用法

    //一种是通过now()方法返回当前时间
    public class Main {
        public static void main(String[] args) {
            ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区
            ZonedDateTime zny = ZonedDateTime.now(ZoneId.of("America/New_York")); // 用指定时区获取当前时间
            System.out.println(zbj);
            System.out.println(zny);
        }
    }
    
    //另一种方式是通过给一个LocalDateTime附加一个ZoneId,就可以变成ZonedDateTime:
    //不推荐
    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);
        }
    }
    2019-09-15T15:16:17Z[Etc/UTC]
    2019-09-15T15:16:17-04:00[America/New_York]
    //以这种方式创建的ZonedDateTime,它的日期和时间与LocalDateTime相同,但附加的时区不同,因此是两个不同的时刻    
        
    
  • 时区转化

    //withZoneSameInstant()
    public class Main {
        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);
        }
    }
    
Instant

时间戳在java.time中以Instant类型表示,我们用Instant.now()获取当前时间戳,

  • 获取时间戳

    public class Main {
        public static void main(String[] args) {
            Instant now = Instant.now();
            System.out.println(now.getEpochSecond()); // 秒
            System.out.println(now.toEpochMilli()); // 毫秒
        }
    }
    1603612795
    1603612795053
    
  • 既然Instant就是时间戳,那么,给它附加上一个时区(ZoneId),就可以创建出ZonedDateTime,得到了ZonedDateTime,继而可以获得了对应时区的LocalDateTime

    Instant i=Instant.ofEpochSecond(1568568760);
         ZonedDateTime zdt=i.atZone(ZoneId.systemDefault());
         LocalDateTime ldt=LocalDateTime.ofInstant(i,ZoneId.systemDefault());
         DateTimeFormatter atf=DateTimeFormatter.ofPattern("yyyy-MM");
            System.out.println(atf.format(ldt));
            System.out.println(zdt);
            System.out.println(ldt);
    
数据库中的时间

在使用Java程序操作数据库时,我们需要把数据库类型与Java类型映射起来。下表是数据库类型与Java新旧API的映射关系:

数据库对应Java类(旧)对应Java类(新)
DATETIMEjava.util.DateLocalDateTime
DATEjava.sql.DateLocalDate
TIMEjava.sql.TimeLocalTime
TIMESTAMPjava.sql.TimestampLocalDateTime
  • 实际上,在数据库中,我们需要存储的最常用的是时刻(Instant),也就是时间戳有了时间戳,就可以转化成任何一个时区的时间。

所以,最好的方法是直接用长整数long表示,在数据库中存储为BIGINT类型。

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

public class Main {
    public static void main(String[] args) {
        long ts = 1574208900000L;
        System.out.println(timestampToString(ts, Locale.CHINA, "Asia/Shanghai"));
        System.out.println(timestampToString(ts, Locale.US, "America/New_York"));
    }

    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)));
    }
}
20191120日 上午8:15
Nov 19, 2019, 7:15 PM
所以,LocalDateTime,ZoneId,Instant,ZonedDateTime和long都可以互相转换:

┌─────────────┐
│LocalDateTime│────┐
└─────────────┘    │    ┌─────────────┐
                   ├───>│ZonedDateTime│
┌─────────────┐    │    └─────────────┘
│   ZoneId    │────┘           ▲
└─────────────┘      ┌─────────┴─────────┐
                     │                   │
                     加ZoneId
                     ▼ 
                     ▼
              ┌─────────────┐     ┌─────────────┐
              │   Instant   │<───>│    long     │
              └─────────────┘     └─────────────┘
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值