我正在使用毫秒进行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毫秒,所以我的乘法以某种方式关闭。
有谁可以指出我做错了什么?我应该使用多长时间吗?
答案 0 :(得分:32)
我应该使用多长时间?
是。问题在于,由于MILLIS_IN_SECOND
等等都是int
s,因此当您将它们相乘时,您会得到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;
答案 1 :(得分:23)
如果在Android上,我建议:
DateUtils.SECOND_IN_MILLIS
DateUtils.MINUTE_IN_MILLIS
DateUtils.HOUR_IN_MILLIS
DateUtils.DAY_IN_MILLIS
DateUtils.WEEK_IN_MILLIS
DateUtils.YEAR_IN_MILLIS
答案 2 :(得分:10)
虽然其他人已经指出arithmetic overflow,但您也可以尝试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));
答案 3 :(得分:8)
private static final long MILLISECONDS_IN_YEAR = MILLIS_IN_SECOND * ...
右侧的所有操作数都是int
s,所以乘法是用32位有符号整数完成的,它们会溢出。将第一个投放到long
,您将获得预期值。
private static final long MILLISECONDS_IN_YEAR = (long)MILLIS_IN_SECOND * ...
答案 4 :(得分:5)
你需要很长时间。 Ints包裹了大约20亿。
答案 5 :(得分:5)
你溢出int
类型。在Java中,两个int
s上的原始算术运算的结果是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;
答案 6 :(得分:4)
要解决此问题,您可以在第一个字母后添加 L :例如 1000L 强>
long MILLS_IN_YEAR = 1000L * 60 * 60 * 24 * 365; // Returns 31536000000
答案 7 :(得分:3)
关于您使用int
vs long
(Answer by Ruakh)导致完全错误的数字1471228928
,integer overflow是正确的。但此外,您的问题提出了太阳年与日历年的问题。
我知道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亿
Your source使用太阳年长度的近似值,大约365.2425 24小时天。这是地球绕太阳运行所需的时间。
数学:
365.2425 * 24 * 60 * 60 * 1000 =31,556,951,999.999996≈31,556,952,000ms
请参阅this calculator。
在西历(格里高利/ ISO)中,我们使用多年甚至365天24小时的日子,忽略了地球围绕太阳运行需要额外四分之一天的事实。我们通过每四年额外插入一天来弥补差异(大致是四年的倍数,除了可被100整除但不能被400整除的年份),the Leap Day。
考虑到平日年365天,24小时工作日并且没有异常现象,例如夏令时(DST),日历年的长度为31,536,000,000
毫秒。正如您在问题中所建议的那样,不是31,556,952,000
。
31,536,000,000 =(365 * 24 * 60 * 60 * 1000)
请参阅this calculator。
366天的闰年将为31,622,400,000
毫秒。
31,622,400,000 =(366 * 24 * 60 * 60 * 1000)
现代java.time类取代了与最早版本的Java捆绑在一起的旧日期时间类。事实证明,这些旧课程令人困惑和麻烦。
ChronoUnit
闰年和其他异常可能意味着一年内意外的毫秒数。因此,如果精度对您的情况很重要,那么您应该让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 [America / Montreal]
stop.toString():2016-01-01T00:00-05:00 [America / Montreal]
millisInYear:31536000000
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和& SimpleDateFormat
现在位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。
您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*
类。
从哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。
答案 8 :(得分:0)
试试这个
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