年份中的Java毫秒数

我在Java中使用毫秒进行一些日期计算,并注意到以下问题:

private static final int MILLIS_IN_SECOND = 1000; private static final int SECONDS_IN_MINUTE = 60; private static final int MINUTES_IN_HOUR = 60; private static final int HOURS_IN_DAY = 24; private static final int DAYS_IN_YEAR = 365; //I know this value is more like 365.24... private static final long MILLISECONDS_IN_YEAR = MILLIS_IN_SECOND * SECONDS_IN_MINUTE * MINUTES_IN_HOUR * HOURS_IN_DAY * DAYS_IN_YEAR; System.out.println(MILLISECONDS_IN_YEAR); //Returns 1471228928 

我知道1年大约是= 31,556,952,000毫秒,所以我的乘法不知何故。

谁能指出我做错了什么? 我应该用多久?

我应该用多久?

是。 问题是,因为MILLIS_IN_SECOND等等都是int ,所以当你将它们相乘时,你得到一个int 。 你将int转换为long ,但只有 int乘法已经导致错误的答案之后。

要解决此问题,您可以将第一个转换为long

  private static final long MILLISECONDS_IN_YEAR = (long)MILLIS_IN_SECOND * SECONDS_IN_MINUTE * MINUTES_IN_HOUR * HOURS_IN_DAY * DAYS_IN_YEAR; 

如果在Android上,我建议:

android.text.format.DateUtils

 DateUtils.SECOND_IN_MILLIS DateUtils.MINUTE_IN_MILLIS DateUtils.HOUR_IN_MILLIS DateUtils.DAY_IN_MILLIS DateUtils.WEEK_IN_MILLIS DateUtils.YEAR_IN_MILLIS 

虽然其他人已经指出了算术溢出 ,但您也可以尝试使用TimeUnit来解决问题:

 Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.YEAR, year); int daysInYear = calendar.getActualMaximum(Calendar.DAY_OF_YEAR); System.out.println(TimeUnit.DAYS.toMillis(daysInYear)); 
 private static final long MILLISECONDS_IN_YEAR = MILLIS_IN_SECOND * ... 

右边的所有操作数都是int ,所以乘法是用32位有符号整数完成的,它们会溢出。 将第一个转换为long ,您将获得预期值。

 private static final long MILLISECONDS_IN_YEAR = (long)MILLIS_IN_SECOND * ... 

你需要很长时间。 Ints包裹了大约20亿。

你溢出了int类型。 在Java中,对两个int进行原始算术运算的结果是int 。 操作数的类型决定了这一点,而不是结果变量的类型。 尝试:

 private static final int MILLIS_IN_SECOND = 1000; private static final int SECONDS_IN_MINUTE = 60; private static final int MINUTES_IN_HOUR = 60; private static final int HOURS_IN_DAY = 24; private static final int DAYS_IN_YEAR = 365; //I know this value is more like 365.24... private static final long MILLISECONDS_IN_YEAR = (long) MILLIS_IN_SECOND * SECONDS_IN_MINUTE * MINUTES_IN_HOUR * HOURS_IN_DAY * DAYS_IN_YEAR; 

要解决此问题,您可以将字母L放在第一个之后: 例如 1000L

 long MILLS_IN_YEAR = 1000L * 60 * 60 * 24 * 365; // Returns 31536000000 

TL;博士

Ruakh的回答是正确的,你使用int vs long ( 整数溢出 )是一个完全错误的数字的原因, 1471228928 。 但此外,您的问题提出了太阳年与日历年的问题。

我知道1年= 31556952000毫秒

不,这将是太阳年的长度,而不是日历年。 日历年为31,536,000,000毫秒。

现代java.time类和ChronoUnit可以计算日历年号。

 Year y = Year.now( // Determine the year of the current date (today). ZoneId.of( "America/Montreal" ) // Determining the year means determining the current date. And determining a date requires a time zone. For any given moment, the date varies around the globe by zone. ) ; // Returns a `Year` object. long millisInYear = ChronoUnit.MILLIS.between( y.atDay( 1 ) // Get the first of the year. Returns a `LocalDate`. .atStartOfDay( // Determine the first moment of the day. Not always 00:00:00 because of anomalies such as Daylight Saving Time (DST). ZoneId.of( "America/Montreal" ) ) // Returns a `ZonedDateTime` object. , y.plusYears(1) // Move to the following year. .atDay( 1 ) // Get the first of the following year. Returns a `LocalDate`. .atStartOfDay( ZoneId.of( "America/Montreal" ) ) // Returns a `ZonedDateTime` object. ) ; 

315.36亿

31,556,952,000 =太阳年

您的来源使用太阳年长度的近似值,大约365.2425 24小时天。 这是地球绕太阳运行所需的时间。

数学:

365.2425 * 24 * 60 * 60 * 1000 =31,556,951,999.999996≈31,556,952,000ms

看到这个计算器 。

31,536,000,000 =日历年

在西历(格里高利/ ISO)中,我们使用多年甚至365天24小时的日子,忽略了地球围绕太阳的轨道需要额外四分之一天的事实。 我们通过每四年插入一个额外的日子来弥补差异(大致是几年的倍数,除了可被100整除但不能被400整除的年份), 闰日 。

考虑到平日年365天,24小时工作日并且没有exception现象,例如夏令时(DST),日历年长度为31,536,000,000毫秒。 您在问题中的建议不是31,556,952,000

31,536,000,000 =(365 * 24 * 60 * 60 * 1000)

看到这个计算器 。

366天的闰年将是31,622,400,000毫秒。

31,622,400,000 =(366 * 24 * 60 * 60 * 1000)

java.time

现代java.time类取代了与最早版本的Java捆绑在一起的旧日期时间类。 事实certificate,那些旧课程令人困惑和麻烦。

ChronoUnit

闰年和其他exception可能意味着一年中意外的毫秒数。 因此,如果精度在您的情况下很重要,那么您应该让java.time进行实际计算。

ChronoUnit类可以计算某个单位的经过时间。

 long millisInYear = ChronoUnit.MILLIS.between( start , stop ); 

我们需要确定一年中第一天和下一年开始的确切时刻。 我们通过遍历LocalDate类来实现这一点,该类表示没有时间且没有时区的仅日期值。

 LocalDate startLd = LocalDate.of ( 2015 , 1 , 1 ); LocalDate stopLd = startLd.plusYears ( 1 ); 

通过分配时区( ZoneId ),我们可以获得时间线上特定时刻的ZonedDateTime对象。

 ZoneId z = ZoneId.of ( "America/Montreal" ); ZonedDateTime start = startLd.atStartOfDay ( z ); ZonedDateTime stop = stopLd.atStartOfDay ( z ); 

最后,计算经过的时间(以毫秒为单位)。

 long millisInYear = ChronoUnit.MILLIS.between ( start , stop ); 

start.toString():2015-01-01T00:00-05:00 [美国/蒙特利尔]

stop.toString():2016-01-01T00:00-05:00 [美国/蒙特利尔]

millisInYear:31536000000


关于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 9Java SE 10及更高版本
    • 内置。
    • 带有捆绑实现的标准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等。

尝试这个

  int MILLIS_IN_SECOND = 1000; int SECONDS_IN_MINUTE = 60; int MINUTES_IN_HOUR = 60; int HOURS_IN_DAY = 24; int DAYS_IN_YEAR = 365; long MILLISECONDS_IN_YEAR = (long) MILLIS_IN_SECOND * SECONDS_IN_MINUTE * MINUTES_IN_HOUR * HOURS_IN_DAY * DAYS_IN_YEAR; System.out.println(MILLISECONDS_IN_YEAR); // Returns 31536000000