Java基础--日期API学习

一、前言

        java标准库中,最早提供了两种处理日期和时间的类,但是由于很多问题,很多方法都已经弃用了,在JAVA8中引入了java.time包,解决了长久以来存在的诸多弊端。java原本自带的java.util.Date和java.util.Calendar类,实际上这两种类会有线程不安全的风险。

二、jdk8之前的api

一、java.lang.System类

1、System类提供的public static longcurrentTimeMillis()用来返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。此方法使用于计算时间差

2、计算世界时间的主要标准有

  1. UTC(Coordinated Universal Time):协调世界时间
  2. GMT(Greenwich Mean Time):格林尼治时间,也是世界时
  3. CST(Central Standard Time):古巴时间 

二、java.util.Date

一、构造器:

1、Date():使用无参构造器创建的对象可以获取本地当前时间。

2、Date(long date)

二、常用方法

1、getTime():返回自1970年1月1日00:00:00 GMT以来此Date对象表示的毫秒数。

2、toString():把此Date对象转换为以下形式的String:dowmondd hh:mm:ss zzz

yyyy其中:dow是一周中的某一天(Sun, Mon, Tue,Wed, Thu, Fri, Sat),zzz是时间

标准。

3、其他把部分方法都过时了

三、java.text.SimplateDateFormat类

1、Date类的API不易于国际化,大部分被废弃了,java.text.SimpleDateFormat类是一个不与语言环境有关的方式来格式化和解析日期的具体类。

他允许进行:

格式化:日期->文本

解析:文本->日期

2、格式化:

1、SimpateDateFormat():使用默认的模式和语言环境创建对象。

2、该构造方法可以用参数pattern指定的格式创建一个对象,该对象调用:3的方法

3、public String format(Date date):方法格式化时间对象date

3、解析:

1、public Date parse(String source):从给定字符串的开始解析文本,以生成一个日期。

四、栗子

// 产生一个Date实例
Date date= newDate();  
// 产生一个formater格式化的实例                                                              SimpleDateFormat formater = new SimpleDateFormat();                                        // 打印输出默认的格式                                                        System.out.println(formater.format(date));
SimpleDateFormat  formater2= new SimpleDateFormat("yyyy年MM月dd日EEE HH:mm:ss");
System.out.println(formater2.format(date));try{
// 实例化一个指定的格式对象
Date date2= formater2.parse("2008年08月08日星期一08:08:08");
// 将指定的日期解析后格式化按指定的格式输出
System.out.println(date2.toString());
} catch(ParseException e)
{
e.printStackTrace();
}

三、java.util.Calendar日历类

一、Calendar是一个抽象基类,主用用于完成日期字段之间相互操作的功能。

 二、获取Calendar实例的方法

1、使用Calendar.getInstance()方法

2、调用它的子类GregorianCalendar的构造器。

三、一个Calendar的实例是系统时间的抽象表示,通过get(int field)方法来取得想要的时间信息。比如YEAR、MONTH、DAY_OF_WEEK、HOUR_OF_DAY、MINUTE、SECOND

1、public void set(int field,int value)

2、public void add(int field,int amount)

3、public final Date getTime()

4、public final void setTime(Date date)

注意:获取月份时:一月是0,二月是1,以此类推,12月是11。获取星期时:周日是1,周二是2,。。。。周六是7

四、栗子

Calendar calendar = Calendar.getInstance();
// 从一个Calendar 对象中获取Date 对象
Date date = calendar.getTime();
System.out.println("data1:+"+date);
// 使用给定的Date 设置此Calendar 的时间
date = new Date(234234235235L);
System.out.println("data2:+"+date);
calendar.setTime(date);
calendar.set(Calendar.DAY_OF_MONTH, 8);
System.out.println("设置为当前时间日期为8号,时间是:" + calendar.getTime());
calendar.add(Calendar.HOUR, 2);
System.out.println("当前时间加2小时后,时间是:" + calendar.getTime());
calendar.add(Calendar.MONTH, -2);
System.out.println("当前日期减2个月后,时间是:" + calendar.getTime()

data1:+Sat Mar 18 17:04:06 CST 2023
data2:+Sat Jun 04 09:03:55 CST 1977
设置为当前时间日期为8号,时间是:Wed Jun 08 09:03:55 CST 1977
当前时间加2小时后,时间是:Wed Jun 08 11:03:55 CST 1977
当前日期减2个月后,时间是:Fri Apr 08 11:03:55 CST 1977

四、为什么会出现新的日期API

一、jdk8之前的SimpleDateFormat会有线程安全问题

1、栗子

使用十个线程,同一个SimpleDateFormat 对象解析日期文本
public class DateTest {
    final static SimpleDateFormat simple = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                try {
                    Date parse = simple.parse("2023-03-18 18:07:04");
                    System.out.println(parse);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }).start();
        }

    }
}

2、结果

3、原因 

在simple.parse("2023-03-18 18:07:04");这个方法里,

 

 

 二、Date类以及Calendar类问题2

Date类以及Calendar类出现之前,枚举类型是还没有出现的,所以在使用Calendar设置日期、月份、时间,(都是直接使用整型放到方法参数里,魔法值)所以在字段中使用整数常量导致整数常量都是可变的,而不是线程安全的,且还不满足开发规范。为了解决这个问题,标准库中就引入了,java.sql.Date作为java.util.Date的子类,但是还是没能彻底解决这个问题。

三、其他问题

可变性:像日期和时间这样的类应该是不可变的。

偏移性:Date中的年份是从1900开始的,而月份都从0开始。

格式化:格式化只对Date有用,Calendar则不行。

五、jdk8中新日期时间API

jdk8提供的最新类均生成不可变实例,他们是线程安全的,并且这些类不提供公共构造函数,只能通过工厂方法加以实例化。

一、常用类概述与功能介绍

一、Instant类:返回的当前时间,是格林尼治时间,

instant类对时间轴上的单一瞬时点建模,也就是时间戳。可用于记录应用程序中的事件时间戳。

java.time包通过值类型Instant提供机器视图,不提供处理人类意义上的时间单位

二、Duration类

表示秒或纳秒时间间隔,适合处理较短的时间,需要更高的精确性。

//Duration:用于计算两个“时间”间隔,以秒和纳秒为基准

LocalTime localTime = LocalTime.now();
LocalTime localTime1 = LocalTime.of(15, 23, 32);
//between():静态方法,返回Duration对象,表示两个时间的间隔
Duration duration = Duration.between(localTime1, localTime);


System.out.println(duration);
System.out.println(duration.getSeconds());
System.out.println(duration.getNano());


LocalDateTime localDateTime = LocalDateTime.of(2016, 6, 12, 15, 23, 32);
LocalDateTime localDateTime1 = LocalDateTime.of(2017, 6, 12, 15, 23, 32);
Duration duration1 = Duration.between(localDateTime1, localDateTime);


System.out.println(duration1.toDays());

三、Period类

表示一段时间的年、月、日

//Period:用于计算两个“日期”间隔,以年、月、日衡量

LocalDate localDate = LocalDate.now();
LocalDate localDate1 = LocalDate.of(2028, 3, 18);
Period period = Period.between(localDate, localDate1);


System.out.println(period);
System.out.println(period.getYears());
System.out.println(period.getMonths());
System.out.println(period.getDays());


Period period1 = period.withYears(2);
System.out.println(period1);

四、LocalDate(yyyy-MM-dd)、LocalTime(秒后面表示的是纳秒)、LocalDateTime

1、表示不可变的日期时间对象,表示使用ISO-8601日历系统的日期、时间、日期和时间。

它们提供了简单的本地日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息

        LocalDateTime localDateTime = LocalDateTime.of(2023, Month.MARCH,23,12,12,12);
        // LocalDateTime对象只是封装了一个时间,并没有时区相关的数据,所以要添加时区信息到对象中。
        ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("Asia/Shanghai"));
        // 更改时区查看其他时区的当前时间
        ZonedDateTime tokyoZonedDateTime1 = zonedDateTime.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));

2、java8中日期相关的api中的所有实例都是不可变,一旦创建就无法修改他们,类似于String,这对于线程安全非常有利。

3、plus方法在LocalDate、LocalTime中的应用,以minus开头的就是减少,实际上也是调用的plus方法只是入参传的是负数。

       

4、with方法在LocalDateTime中的使用

   

五、ZonedDateTime类:表示的是当前时区的当前时间

具有时区的日期时间的不可变表示,此类存储的所有日期和时间字段,精确到纳秒,时区为区域偏移量,用于处理模糊的本地日期时间。

在java中获取时区,通过ZoneId的getAvailableZoneIds方法可以获取到一个Set集合,集合中封装了600个时区。

注:ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法,也就是公历

六、新时间日期API包

java.time–包含值对象的基础包

java.time.chrono–提供对不同的日历系统的访问

java.time.format–格式化和解析时间和日期

java.time.temporal–包括底层框架和扩展特性

java.time.zone–包含时区支持的类

一、格式化与解析日期或时间:

与SimpleDateFormat不同的是,新版本的日期/时间API的格式化与解析不需要再创建转换器对象了,通过时间日期对象的parse/format方法可以直接进行转换.

java.time.format.DateTimeFormatter类:各类提供了三种格式化方法

1、预定义的标准格式。如:

ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME

2、本地化相关的格式。如:

ofLocalizedDateTime(FormatStyle.LONG)

通过DateTimeFormatter的ofLocalizedDate的方法也可以调整格式化的方式。

public static DateTimeFormatter ofLocalizedDate(FormatStyle dateStyle) {

    Objects.requireNonNull(dataStyle, message:"dateStyle");

    return new DateTimeFormatterBuilder().appendLocalized(dateStyle,timeStyle:"null")

            .toFormatter(ResolverStyle.Smart, IsoChronology.Instance);

}

此方法需要传入一个FormatStyle类对象,FormaStyle对象是一个枚举类,其中有几种方式如下:

Full:全显示(年月日+星期) Long:全显示(年月日) Medium:缩略显示(没有年月日汉字) SHORT:精简显示(精简年+月日)

3、自定义的格式。如:

ofPattern(“yyyy-MM-dd hh:mm:ss”)

 

七、调节器TemporalAdjuster与查询TemporalQuery

一、TemporalAdjuster使用

 LocalDate localDate = LocalDate.now();
 LocalDate with = localDate.with(TemporalAdjusters.firstDayOfMonth());

返回结果:

2023-05-01

TemporalAdjusters中其他方法类似使用

TemporalAdjusters类中常用静态方法的使用

static TemporalAdjuster firstDayofNextMonth()   下个月的第一天

static TemporalAdjuster firstDayOfNextYear()   下一年的第一天

static TemporalAdjuster firstDayOfYear()    当年的第一天

注意:TemporalAdjusters 是一个接口,with方法实际上传入的是这个接口的实现类对象,TemporalAdjusters并不是TemporalAdjuster的实现类,只不过TemporalAdjusters的静态方法实现了TemporalAdjuster,并且将实现类对象返回了。

二、TemporalAdjusters中DayOfWeek枚举类使用

三、自定义 TemporalAdjuster调节器

通过Java8本身提供的TemporalAdjusters中的方法可以完成一些常用的操作,如果要自定义日期时间的更改逻辑,可以通过实现TemporalAdjuster类接口的方式来完成。

1、创建类实现TemporalAdjuster接口

2、实现TemporalAdjuster中的 adjusterInto()方法,传入一个日期时间对象,完成逻辑之后返回日期事件对象。

3、通过with方法传入自定义调节器对象完成更改。

第一种写法:实现对应接口
public class PayDayAdjuster implements TemporalAdjuster {

    @Override
    public Temporal adjustInto(Temporal temporal) {
        if (Objects.isNull(temporal)) {
            return null;
        }
        LocalDate localDate = LocalDate.from(temporal);
        int dayOfMonth = localDate.getDayOfMonth();

        LocalDate moneyDayOfMonth = dayOfMonth == 15 ? localDate.withDayOfMonth(dayOfMonth) : localDate.withDayOfMonth(15);
        DayOfWeek dayOfWeek = moneyDayOfMonth.getDayOfWeek();
        if (dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY) {
            moneyDayOfMonth = moneyDayOfMonth.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
        }
        return moneyDayOfMonth;
    }
}
public class DateTest {
    public static void main(String[] args) {
        // 第一种写法
        LocalDate localDate1 = LocalDate.now();
        PayDayAdjuster payDayAdjuster = new PayDayAdjuster();
        LocalDate date1 = LocalDate.from(payDayAdjuster.adjustInto(localDate1));
        // 第二种写法
        LocalDate localDate2 = LocalDate.now().with(temporal -> {
            if (Objects.isNull(temporal)) {
                return null;
            }
            LocalDate localDate = LocalDate.from(temporal);
            int dayOfMonth = localDate.getDayOfMonth();
            LocalDate moneyDayOfMonth = dayOfMonth == 15 ? localDate.withDayOfMonth(dayOfMonth) : localDate.withDayOfMonth(15);
            DayOfWeek dayOfWeek = moneyDayOfMonth.getDayOfWeek();
            if (dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY) {
                moneyDayOfMonth = moneyDayOfMonth.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
            }
            return moneyDayOfMonth;
        });
    }
} 

四、TemporalQuery的应用

LocalDate,LocalTime)都有一个方法叫做query,可以针对日期进行查询,R    query(TemporalQuery query)这个方法是一个泛型方法,返回的数据就是传入的泛型类的类型,TemporalQuery是一个泛型接口,里面有一个抽象方法是R    queryFrom(TemporalAccessor temporal)TemporalAccessor是Temporal的父接口,实际上也就是LocalDate,LocalDateTime相关类的顶级父接口,这个queryFrom的方法的实现逻辑就是,传入一个日期/时间对象通过自定义逻辑返回数据。

public class DateTest3 {
    public static void main(String[] args) {
        LocalDate now = LocalDate.now();
        System.out.println(getDay(now));
    }

    public static Long getDay(LocalDate now) {
        return now.query(temporalAccessor -> {
            // temporalAccessor 转换为当前时间
            LocalDate from = LocalDate.from(temporalAccessor);
            // 取出当前时间是哪一年,设置这一年的五一
            LocalDate localDate = LocalDate.of(from.getYear(), Month.MAY, 1);
            // 判断当前时间是否在当年的五一时间之后,如果是年份加一
            if (from.isAfter(localDate)) {
                localDate = localDate.plus(1, ChronoUnit.YEARS);
            }
             // 计算天数差
            return ChronoUnit.DAYS.between(from, localDate);
        });
    }
}

二、时间类之间的转换

一、java.util.Date转换为java.time.LocalDate

Java8中的java.time中并没有提供太多的内置方式来转换java.util包中用预处理标准日期和时间的类,我们可以使用Instant类作为中介,也可以使用java.sql.Date和java.sql.TimeStamp类提供的方法进行转

java.time包中并没有提供很多的方式来进行直接转换,但是给之前的Date类,Calendar类在java1.8都提供了一个新的方法,叫做toInstant(),可以将当前对象转换为Instant对象,通过给Instan添加时区信息之后就可以转换为LocalDate对象。

如果想自己指定时区可以使用
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.of(ZoneId.SHORT_IDS.get("CTT")));

ZoneId.SHORT_IDS里面是一个map,封装了时区字符串

二、 java.sql.Date与java.sql.Timestamp的转换方式

java.sql.Date类中提供直接转换为LocalDate的方法,toLocalDate()

java.sql.Timestamp类是时间戳对象,通过传入一个毫秒值对象进行初始化

 三、Calendar转换为ZonedDateTime

Calendar对象字Java1.1开始提供了一个方法获取时区对象的方法,getTimeZone(),要将Calendar对象转换为ZonedDateTime需要先获取到时区对象。从Java1.8开始TimeZone类提供了一个方法可以获取到ZonedId。获取到ZonedId之后就可以初始化ZOnedDateTime对象了,ZonedDateTime类有一个ofInstant()方法,可以将一个Instant对象和ZonedId对象作为参数传入构造一个ZonedDateTime对象。

 四、Calendar转换为LocalDateTime

java.util.Calendar类转换为java.time.LocalDateTime类

Calendar对象可以获取到年月日时分秒的信息,这些信息可以作为LocalDateTime构造方法的参数

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值