热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

oracle判断当前日期的前一天_Java8日期API

随着Java8的发布,Oracle同时推出了一套全新的时间API,算是填坑吧。旧版本时间API的坑相信大家也早有耳闻:SimpleDate

随着Java8的发布,Oracle同时推出了一套全新的时间API,算是填坑吧。旧版本时间API的坑相信大家也早有耳闻:

  • SimpleDateFormat是线程不安全的,多线程环境下共用同一个Formatter可能会抛异常
  • month被设计得非常奇葩,用0~11表示1~12月,所以如果要表示12月,你需要填写11,一不小心就掉坑里
  • 同时存在两个Date,一个是java.util下,另一个是java.sql下,API还不同,比较混乱
  • 时间计算非常麻烦
  • API设计太散了,散落在Date、Calendar、SimpleDateFormat三个类中,想用时找都找不到,干着急
  • ...

当然,我个人觉得全新的时间API并不是特别重要,只要了解即可,所以本文不会介绍得很深。

如何创建时间:now/of

首先要说明一点,新的时间API禁止了new的方式,全部通过工厂方式创建时间对象,其中最常用的就是now/of。

public class NewDateApiTest {public static void main(String[] args) {// 旧的方式,可以newDate date = new Date();System.out.println("old api" + date);// 新的方式,只能通过给定的方法获取LocalDate newDate = LocalDate.now(); // 日期 2020-12-12LocalTime newTime = LocalTime.now(); // 时间 16:30:00:000LocalDateTime newDateTime = LocalDateTime.now(); // 日期+时间 2020-12-12T16:30:00.000System.out.println("newDate" + newDate);System.out.println("newTime" + newTime);System.out.println("newDateTime" + newDateTime);// 还可以组合哟LocalDateTime combineDateTime = LocalDateTime.of(newDate, newTime);System.out.println("combineDateTime" + combineDateTime);// 创建指定时间LocalDate customDate = LocalDate.of(2020, 11, 5);LocalTime customTime = LocalTime.of(16, 30, 0);LocalDateTime customDateTime = LocalDateTime.of(2020, 11, 5, 16, 30, 0);System.out.println("customDate" + customDate);System.out.println("customTime" + customTime);System.out.println("customDateTime" + customDateTime);}}

结果

8e4a7aba7326ac7374d7f0f192f4b97f.png

上面这段代码有两个重点和一个疑问:

  • 重点1:分为LocalDate、LocalTime和LocalDateTime,now()创建当前时间,of()创建指定时间
  • 重点2:month类的BUG被修复了,为了避免和老版本产生歧义,为month单独提供了枚举(当然,你还是可以只填写数字)
1c212250286991bb32f24142d29c3458.png
  • 可能的疑问:老的Date和新的LocalXxx打印的格式不同,但表示的是同一个时间(后面介绍格式化)


如何增减时间:plus/minus

修改时间有两个含义:

  • 增加、减少时间
  • 替换时间

先介绍增减时间的方法。

要特别注意,新的时间类都是final修饰的,不可修改且线程安全(看注释),所以随便折腾。

f14a36e102256849d91e4720200cf611.png

如果你有过实际开发经验,就会明白老的时间API对于时间计算有多么不友好。举个实际开发经常会遇到的例子吧:

  • 获得当前时间和前一天此刻的时间,比如当前是2020-12-05 17:44:00,我还需要得到2020-12-04 17:44:00
  • 获取当天的零点和23:59:59
  • 一周前的今天、一个月前的今天、一年前的今天

你知道吗,为了用旧版本时间API计算昨天的当前时间,我甚至百度了:

public class NewDateApiTest {public static void main(String[] args) {Date today = new Date();Calendar calendar = Calendar.getInstance();calendar.setTime(today);calendar.add(Calendar.DAY_OF_MONTH, -1);Date yesterday = calendar.getTime();System.out.println("today:" + today);System.out.println("yesterday:" + yesterday);}
}

至于后面的两个需求,我根本写不出来,还是只能靠百度。比如我百度的结果是:

public class NewDateApiTest {public static void main(String[] args) {long current = System.currentTimeMillis();long zeroT = current / (1000 * 3600 * 24) * (1000 * 3600 * 24) - TimeZone.getDefault().getRawOffset();long endT = zeroT + 24 * 60 * 60 * 1000 - 1;Date thisDayBegin = new Date(zeroT);Date thisDayEnd = new Date(endT);}
}

何必呢...

但这是我能力不行吗?NO,绝对是API设计得不好!API设计出来就是给普通开发人员用的,你搞得这么乱,这不是坑人嘛!很多人根本不知道去哪找对应的方法。

但新版时间API就不一样了,肯定是封装在LocalDate、LocalTime、LocalDateTime里的:

public class NewDateApiTest {public static void main(String[] args) {// 获取时间参数的年、月、日(有时需求要用到)System.out.println("获取时间参数的年、月、日:");LocalDateTime param = LocalDateTime.now();System.out.println("year:" + param.getYear());System.out.println("month:" + param.getMonth());System.out.println("day:" + param.getDayOfMonth());System.out.println("hour:" + param.getHour());System.out.println("minute:" + param.getMinute());System.out.println("second:" + param.getSecond() + "n");// 计算昨天的同一时刻(由于对象不可修改,所以返回的是新对象)System.out.println("计算前一天的当前时刻:");LocalDateTime today = LocalDateTime.now();LocalDateTime yesterday = today.plus(-1, ChronoUnit.DAYS);System.out.println("today:" + today);System.out.println("yesterday:" + yesterday);System.out.println("same object:" + today.equals(yesterday) + "n");// 计算当天的00点和24点(你看,这里就看到组合的威力了)System.out.println("计算当天的00点和24点:");LocalDateTime todayBegin = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);LocalDateTime todayEnd = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);System.out.println("todayBegin:" + todayBegin);System.out.println("todayEnd:" + todayEnd + "n");System.out.println("计算一周、一个月、一年前的当前时刻:");LocalDateTime oneWeekAgo = today.minus(1, ChronoUnit.WEEKS);LocalDateTime oneMonthAgo = today.minus(1, ChronoUnit.MONTHS);LocalDateTime oneYearAgo = today.minus(1, ChronoUnit.YEARS);System.out.println("oneWeekAgo" + oneWeekAgo);System.out.println("oneMonthAgo" + oneMonthAgo);System.out.println("oneYearAgo" + oneYearAgo + "n");}}

结果

获取时间参数的年、月、日:
year:2020
month:DECEMBER
day:5
hour:18
minute:7
second:31
计算前一天的当前时刻:
today:2020-12-05T22:07:31.831
yesterday:2020-12-04T22:07:31.831
same object:false
计算当天的00点和24点:
todayBegin:2020-12-05T00:00
todayEnd:2020-12-05T23:59:59.999999999
计算一周、一个月、一年前的当前时刻:
oneWeekAgo:2020-11-28T22:07:31.831
oneMonthAgo:2020-11-05T22:07:31.831
oneYearAgo:2019-12-05T22:07:31.831

ChronoUnit是JDK提供的枚举,放心用就是了。plus/minus呢大概就这么个用法,上面展示的是最最通用的plus/minus(long amoutToAdd, TemporalUnit unit),数字+单位,随机应变。虽然plus配合负数时效果等同于minus,但如果要减去时间,还是建议使用minus。


你也可以选择以下方法进行时间的增减:

45036dc1594201c869e16ebe79d71001.png
d3c971cb74098aa8f608acd24c340d77.png

但这些都可以用plus/minus(long amoutToAdd, TemporalUnit unit)代替:

b6b75b74169d06e743ae430364e60a49.png

如何修改时间:with

从广义上来说,修改时间和增减时间是一样的,但稍微有点区别。我们通过几个案例体会一下即可:

public class NewDateApiTest {public static void main(String[] args) {LocalDateTime now = LocalDateTime.now();System.out.println("now:" + now);// 将day修改为6号LocalDateTime modifiedDateTime = now.with(ChronoField.DAY_OF_MONTH, 6);System.out.println("modifiedDateTime:" + modifiedDateTime);}}

注意一下,之前我们plus时用的是ChronoUnit,表示增幅单位,而这里ChronoField则是指定修改的字段。

同样的,上面演示的也是最最通用的API,大家也可以用以下方法:

242728d1533237c553097954dd2d32c1.png

也就是说,直接在方法层面已经指定了要修改的字段。

如何比较时间:isAfter/isBefore/isEqual

public class NewDateApiTest {public static void main(String[] args) {LocalDateTime today = LocalDateTime.now();LocalDateTime after = today.plusSeconds(1);boolean result = after.isAfter(today);System.out.println("result=" + result);}}

902dc54ef106731b955d0118f6375937.png

时区:zone

大家之前可能应该已经多多少少见过zone,那么什么是zone,为什么需要它呢?

由于地球是圆的,并且自西向东自转,美国的黑夜是我们的白昼,我们的早上8点是地球另一面的晚上8点。当一个国际友人问你现在几点钟时,你不能回答“现在是早上8点”,而应该说“现在是北京时间早点8点”或者“现在是美国时间晚上8点”。

上面介绍的LocalDateTime等都是不包含时区信息的,但我们可以将它们转为包含时区信息的对象:

public class NewDateApiTest {public static void main(String[] args) {// 当地时间LocalDateTime now = LocalDateTime.now();System.out.println("localDateTime:" + now);// 时区(id的形式),默认的是本国时区ZoneId zoneId = ZoneId.systemDefault();// 为localDateTime补充时区信息ZonedDateTime beijingTime = now.atZone(zoneId);System.out.println("beijingTime:" + beijingTime);}
}

结果

localDateTime:2020-12-05T18:50:56.623
beijingTime:2020-12-05T18:50:56.623+08:00[Asia/Shanghai]

别问我为什么是上海时间,问就是不知道(安装Linux虚拟机也是上海时间)。

这里有个容易犯错的点,需要和大家说明一下:LocalDateTime转为ZonedDateTime时间不会变,仅仅是丰富了时区信息而已。比如:

public class NewDateApiTest {public static void main(String[] args) {// 当地时间LocalDateTime now = LocalDateTime.now();System.out.println("localDateTime:" + now);// 时区(id的形式)ZoneId zoneId = ZoneId.of("Asia/Tokyo");// 补充时区信息ZonedDateTime tokyoTime = now.atZone(zoneId);System.out.println("tokyoTime:" + tokyoTime);}
}

结果

localDateTime:2020-12-05T19:01:32.546
tokyoTime:2020-12-05T19:01:32.546+09:00[Asia/Tokyo]

时间是一样的,只是多了时区。

那么,怎样转换时区时间呢?


用withZoneSameInstant():

public class NewDateApiTest {public static void main(String[] args) {// 当地时间LocalDateTime now = LocalDateTime.now();System.out.println("localDateTime:" + now);// 时区(id的形式)ZoneId zoneId = ZoneId.of("Asia/Shanghai");// 补充时区信息ZonedDateTime shanghai = now.atZone(zoneId);System.out.println("上海时间:" + shanghai);// 当前上海时间对应东京时间是几点呢?ZonedDateTime tokyoHot = shanghai.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));System.out.println("东京时间:" + tokyoHot);}
}

结果

localDateTime:2020-12-05T19:07:23.271
上海时间:2020-12-05T19:07:23.271+08:00[Asia/Shanghai]
东京时间:2020-12-05T20:07:23.271+09:00[Asia/Tokyo]

所以,现在明白为什么新的时间API叫LocalXxx了吗?Local表示它不带时区,只是一个普普通通的时间概念,比如你的生日是1998-12-11 12:00:00,没有时区。当你需要带上时区,可以转为ZonedDateTime。


那么我怎么知道北京时间是"Asia/Shanghai",东京时间是"Asia/Tokyo"呀?

别问,问就是百度,反正我找了半天也没找到枚举。但是JDK提供了全部的ZoneId(没啥卵用):

public class NewDateApiTest {public static void main(String[] args) {Set availableZoneIds = ZoneId.getAvailableZoneIds();availableZoneIds.forEach(System.out::println);}
}

最后,ZonedDateTime转回LocalDateTime:

public class NewDateApiTest {public static void main(String[] args) {// 当地时间LocalDateTime now = LocalDateTime.now();System.out.println("localDateTime:" + now);// 时区(id的形式)ZoneId zoneId = ZoneId.of("Asia/Tokyo");// 补充时区信息ZonedDateTime tokyoTime = now.atZone(zoneId);System.out.println("tokyoTime:" + tokyoTime);// ZonedDateTime转LocalDateTimeLocalDateTime localDateTime = tokyoTime.toLocalDateTime();}
}

toLocalDateTime()即可。

LocalDateTime与Date互转

媒介是Instant(格林尼治时间)

LocalDateTime转Date

public class NewDateApiTest {public static void main(String[] args) {// 先把LocalDateTime变为ZonedDateTime,然后调用toInstant()LocalDateTime now = LocalDateTime.now();ZonedDateTime zonedDateTime = now.atZone(ZoneId.systemDefault());// Instant是代表本初子午线的时间,所以比我们的东八区要晚8小时Instant instant = zonedDateTime.toInstant();System.out.println("zonedDateTime:" + zonedDateTime);System.out.println("instant:" + instant);// 转为DateDate date = Date.from(instant);System.out.println("date:" + date);}
}


Date转LocalDateTime

public class NewDateApiTest {public static void main(String[] args) {Date date = new Date();// Date也有toInstant()Instant instant = date.toInstant();System.out.println("date:" + date);System.out.println("instant:" + instant);// 不带时区:LocalDateTime.of(),带时区:LocalDateTime.ofInstant()LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());System.out.println("localDateTime:" + localDateTime);}
}

差点忘了,转成秒也非常常用哟:

public class NewDateApiTest {public static void main(String[] args) {Date date = new Date();System.out.println(date.getTime() / 1000);LocalDateTime now = LocalDateTime.now();long result = now.toEpochSecond(ZoneOffset.ofHours(8));System.out.println(result);LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(result, 0, ZoneOffset.ofHours(8));System.out.println(localDateTime);}
}

好家伙,又学了两个方法:toEpochSecond()、ofEpochSecond()。

但还是那句话,你要的方法都在LocalXxx类中,只要你稍微找一下就能找到,还是很方便的!


格式化

public class NewDateApiTest {public static void main(String[] args) {LocalDateTime now = LocalDateTime.now();System.out.println("格式化前:" + now);String format = now.format(DateTimeFormatter.ISO_DATE_TIME);System.out.println("默认格式:" + format);String other = now.format(DateTimeFormatter.BASIC_ISO_DATE);System.out.println("其他格式:" + other);}
}

上面的代码只想透露3点信息:

  • 直接打印LocalDateTime默认是DateTimeFormatter.ISO_DATE_TIME
  • DateTimeFormatter已经提供了一些格式
  • 新版时间的格式化方法还是在自身,只不过需要传入DateTimeFormatter指定格式。旧版的格式化方法定义在SimpleDateFormat类中,传入时间+格式

之前说了,LocalXxx系列都是线程安全的,所以不会发生多线程下的格式转换错误问题。要想明白SimpleDateFormat为什么线程不安全,请戳:

https://www.bilibili.com/video/BV1tt4y1U7hL?p=3​www.bilibili.com

但一般我们都需要自定义格式,怎么做?

先观察默认的格式化是怎么做的:

e137a3535cc80af7231273741a697a3d.png

哦?DateTimeFormatter.ISO_DATE_TIME其实返回的是DateTimeFormatter对象。

来看一下正确定的自定义格式化:

public class NewDateApiTest {public static void main(String[] args) {LocalDateTime now = LocalDateTime.now();System.out.println("格式化前:" + now);String format = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));System.out.println("格式化后:" + format);}
}

结果

格式化前:2020-12-05T19:30:11.788
格式化后:2020-30-05 19:30:11

为什么可以传入DateTimeFormatter.ofPattern()呢?不是需要对象吗?

和之前学习Stream API时遇到的一样,某些方法的返回值其实也是自身类型:

3b7d329bf612f7d46254c97614f1783d.png

最后了解一下反格式化:

public class NewDateApiTest {public static void main(String[] args) {LocalDateTime now = LocalDateTime.now();System.out.println("格式化前:" + now);String format = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));System.out.println("格式化后:" + format);LocalDateTime parse = LocalDateTime.parse(format, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));System.out.println("反格式化:" + parse);}
}

结果

格式化前:2020-12-05T19:34:44.817
格式化后:2020-12-05 19:34:44.817
反格式化:2020-12-05T19:34:44.817

简而言之,一个format(),一个parse(),都要传入DateTimeFormatter对象,可以自定义也可以使用默认的。

如果你怕写错"yyyy-MM-dd HH:mm:ss.SSS",可以自定义一个常量类,或者使用第三方定义的。

public final class DatePattern {public static final String YYYY_MM_DD_HH_MM_SS_SSS = "yyyy-MM-dd HH:mm:ss.sss";public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";public static final String YYYY_MM_DD_WITH_SLASH = "yyyy/MM/dd";public static final String YYYY_MM_DD_WITH_STRIKE = "yyyy-MM-dd";// ...}

实际开发案例

@RestController
@RequestMapping("/user")
public class UserController {@PostMappingpublic UserPojo addUser(@RequestBody UserPojo userPojo) {userPojo.setBirthday(userPojo.getBirthday().plusDays(1));return userPojo;}}@Data
public class UserPojo {private String name;private LocalDateTime birthday;}

57d1120eaab562eedc4124a89eefd2a0.png

返回值是:

a8846b4091dac6fad58716752eb8ef4b.png

加了一天。

时间格式有点怪异,但是没关系啊,点到为止,下一个阶段再介绍。

这是Java8新特性的最后一节啦,撒花。

谢谢朋友们!

02b360de04f9ea18cba69e9a90401677.png

总之,不要怕记不住,反正所有方法都在LocalXxx内部,靠IDEA的提示找一下就行啦~

本文来自Java小册,如果你希望在工作一年时就收获两年的经验值,欢迎加入我们一起学习~

https://zhuanlan.zhihu.com/p/212191791​zhuanlan.zhihu.com
606a4c87f6ede2fbeb55670c284ab103.png
47981c08bdd568c8fffeeddd858ec424.png
528d65fcb9e8233855a006b2248adcf5.png



推荐阅读
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 从零学Java(10)之方法详解,喷打野你真的没我6!
    本文介绍了从零学Java系列中的第10篇文章,详解了Java中的方法。同时讨论了打野过程中喷打野的影响,以及金色打野刀对经济的增加和线上队友经济的影响。指出喷打野会导致线上经济的消减和影响队伍的团结。 ... [详细]
author-avatar
用户0a8xoj91q0
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有