在java中添加30天到目前为止

为什么当我今天增加30天到今天的时候 – 30,当我加20时它会增加?

这是一个样本

import java.text.DateFormat; import java.util.Date; public class DatePlus { public static void main(String[] args) { Date now = new Date(); Date now1 = new Date(); Date now2 = new Date(); DateFormat currentDate = DateFormat.getDateInstance(); Date addedDate1 = addDays(now2, 20); Date addedDate2 = addDays(now1, 30); System.out.println(currentDate.format(now)); System.out.println(currentDate.format(addedDate1)); System.out.println(currentDate.format(addedDate2)); } public static Date addDays(Date d, int days) { d.setTime(d.getTime() + days * 1000 * 60 * 60 * 24); return d; } } 

“这是控制台”

 Jul 30, 2012 Aug 19, 2012 Jul 10, 2012 

这是因为30 * 1000 * 60 * 60 * 24溢出Integer.MAX_VALUE ,而20 * 1000 * 60 * 60 * 24则没有。

使用日历。 http://docs.oracle.com/javase/6/docs/api/java/util/GregorianCalendar.html

伪代码:

 Calendar c=new GregorianCalendar(); c.add(Calendar.DATE, 30); Date d=c.getTime(); 
  1. Date与人类使用的任何日历系统无关。 它只代表时间点。 添加30天到Date是没有意义的,就像添加20 红色一样

  2. 添加1000 * 60 * 60 * 24常见方法是错误的。 您正在添加86400秒,但有一天不一定是86400秒。 由于dst,它可以长一小时或更短。 由于闰秒,它可以长一秒或更短。

  3. 应该做的是将Date转换为Calendar (实际上代表一些日历系统,如GregorianCalendar 。然后只需添加日期:

     Calendar calendar = new GregorianCalendar(/* remember about timezone! */); calendar.setTime(date); calendar.add(Calendar.DATE, 30); date = calendar.getTime(); 
  4. 使用Apache Commons Lang的DateUtils.addDays()

     DateUtils.add(date, 30); 

    这不违反上面所写的内容,它会转换为下面的Calendar

  5. 或者完全避免这种地狱,去Joda Time 。

days是整数。

30 * 1000 * 60 * 60 * 24是2,592,000,000,大于2,147,483,647(Java中最大的int)。 你有一个缓冲区溢出,你的结果是一个相当小的负数(检查它是二进制,转换回int作为2补充)

简单的解决方法是将其中一个值转换为long,以便将表达式的结果存储为long,这样可以保存该值而不会溢出。

  (long) days * 1000L * 60 * 60 * 24 

总是建议使用日历或其他api(我听说过JodaTime,但没有使用它)来操纵日期。

您在计算中遇到整数溢出。 days * 1000 * 60 * 60 * 24的值大于days = 30时signed string允许的最大值,但是当days = 20时不是。当你增加一个有符号整数时,它的最大可能值是而不是增加价值,标志翻转,它变为负面! 这就是为什么你的日期倒退 – 你正在为它添加一个负数。

要解决此问题,可以使用long数据类型,其最大值大于整数,如下所示:

 long secondsToAdd = days; secondsToAdd *= (1000*60*60*24); d.setTime(d.getTime() + secondsToAdd); 

TL;博士

 LocalDate.now().plusDays( 30 ) 

细节

正如其他人指出的那样,具体问题是32位整数溢出。

该代码也忽略了时区的关键问题。

更大的问题是尝试滚动自己的日期时间计算而不是使用体面的库。

java.time

Java 8及更高版本中内置的java.time类取代了麻烦的旧日期时间类。

Instant是UTC时间轴上的一个时刻,分辨率为纳秒。

 Instant now = Instant.now(); 

应用时区以获取ZonedDateTime

 ZoneId zoneId = ZoneId.of( "America/Montreal" ); ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId ); 

现在添加你的30天。 让java.time处理exception,例如夏令时(DST)。

 ZonedDateTime zdtLater = zdt.plusDays( 30 ); 

或许你的意思是一个月。 让java.time处理有关不同长度的月份的详细信息。

 ZonedDateTime zdtMonthLater = zdt.plusMonths( 1 ); 

有关在新旧类型之间进行转换的信息,请参阅此问题 。 查看添加到旧类的新方法,例如:

 java.util.Date utilDate = java.util.Date.from( zdt.toInstant() ); java.util.Calendar utilCalendar = java.util.GregorianCalendar.from( ZonedDateTime ); 

如果您想要一个仅限日期的值,请使用LocalDate 。 与上述相同,添加天或月。


关于java.time

java.time框架内置于Java 8及更高版本中。 这些类取代了麻烦的旧遗留日期时间类,如java.util.DateCalendarSimpleDateFormat

现在处于维护模式的Joda-Time项目建议迁移到java.time类。

要了解更多信息,请参阅Oracle教程 。 并搜索Stack Overflow以获取许多示例和解释。 规范是JSR 310 。

您可以直接与数据库交换java.time对象。 使用符合JDBC 4.2或更高版本的JDBC驱动程序 。 不需要字符串,不需要java.sql.*类。

从哪里获取java.time类?

  • Java SE 8Java SE 9及更高版本
    • 内置。
    • 带有捆绑实现的标准Java API的一部分。
    • Java 9增加了一些小function和修复。
  • Java SE 6Java SE 7
    • 许多java.timefunction都被反向移植到ThreeTen-Backport中的 Java 6和7。
  • Android的
    • 更高版本的Android捆绑java.time类的实现。
    • 对于早期的Android(<26), ThreeTenABP项目采用ThreeTen-Backport (如上所述)。 请参见如何使用ThreeTenABP ….

ThreeTen-Extra项目使用其他类扩展了java.time。 该项目是未来可能添加到java.time的试验场。 您可以在这里找到一些有用的课程,如IntervalYearWeekYearQuarter等。