在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();
-
Date
与人类使用的任何日历系统无关。 它只代表时间点。 添加30天到Date
是没有意义的,就像添加20 红色一样 。 -
添加
1000 * 60 * 60 * 24
常见方法是错误的。 您正在添加86400秒,但有一天不一定是86400秒。 由于dst,它可以长一小时或更短。 由于闰秒,它可以长一秒或更短。 -
你应该做的是将
Date
转换为Calendar
(实际上代表一些日历系统,如GregorianCalendar
。然后只需添加日期:Calendar calendar = new GregorianCalendar(/* remember about timezone! */); calendar.setTime(date); calendar.add(Calendar.DATE, 30); date = calendar.getTime();
-
使用Apache Commons Lang的
DateUtils.addDays()
:DateUtils.add(date, 30);
这不违反上面所写的内容,它会转换为下面的
Calendar
。 -
或者完全避免这种地狱,去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.Date
, Calendar
和SimpleDateFormat
。
现在处于维护模式的Joda-Time项目建议迁移到java.time类。
要了解更多信息,请参阅Oracle教程 。 并搜索Stack Overflow以获取许多示例和解释。 规范是JSR 310 。
您可以直接与数据库交换java.time对象。 使用符合JDBC 4.2或更高版本的JDBC驱动程序 。 不需要字符串,不需要java.sql.*
类。
从哪里获取java.time类?
- Java SE 8 , Java SE 9及更高版本
- 内置。
- 带有捆绑实现的标准Java API的一部分。
- Java 9增加了一些小function和修复。
- Java SE 6和Java 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的试验场。 您可以在这里找到一些有用的课程,如Interval
, YearWeek
, YearQuarter
等。