2012-02-04 77 views
19

Tôi đang thực hiện một số tính toán ngày trong Java bằng cách sử dụng mili giây. Tôi không có nhiều kinh nghiệm làm việc với mili giây và thậm chí không thể xác định có bao nhiêu mili giây trong một năm. Đây là nỗ lực của tôi:Java Mili giây trong năm

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 

Tôi biết rằng 1 năm = 31556952000 mili giây, vì vậy phép nhân của tôi bị tắt bằng cách nào đó.

Có ai có thể chỉ ra những gì tôi đang làm sai? Tôi có nên sử dụng lâu không?

+2

Tại sao bạn không thử? Hoặc tìm kiếm giá trị tối đa của một số nguyên có dấu 32 bit là gì? –

+0

@Dave Newton Tôi đã thử nó và mã trả về một giá trị mà tôi biết là không chính xác. Một int làm việc cho mili giây trong năm, tuy nhiên tôi tính giá trị sai. –

+0

Có lẽ năm nhuận là không liên quan ... –

Trả lời

28

Tôi có nên sử dụng lâu không?

Có. Vấn đề là, kể từ MILLIS_IN_SECOND và cứ thế là tất cả int s, khi bạn nhân chúng, bạn sẽ nhận được int. Bạn đang chuyển đổi số int thành số long, nhưng chỉ sau khi số int nhân này đã dẫn đến câu trả lời sai.

Để sửa lỗi này, bạn có thể cast là người đầu tiên một 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

Giải thích tuyệt vời. Vì vậy, các diễn viên biến ints một thời gian dài trước khi nhân chúng? –

+1

@ kmb385 Khi bạn nhân một cái gì đó với một cái gì đó lớn hơn kết quả của bạn sẽ luôn luôn là loại lớn hơn. – kechapito

+5

@ kmb385: Vâng, chính xác. Hoặc, đúng hơn - tôi cho rằng bản thân * tự * chỉ chuyển * đầu tiên * 'int' thành một' dài', nhưng sau đó, Java sẽ chuyển đổi mỗi 'int' thành' long' trước khi nhân nó với một 'dài '. – ruakh

4

Bạn cần một thời gian dài. Ints quấn quanh 2 tỷ.

7
private static final long MILLISECONDS_IN_YEAR = MILLIS_IN_SECOND * ... 

Tất cả các toán hạng ở phía bên tay phải là int s, do đó nhân được thực hiện với 32bit ký số nguyên, mà tràn. Truyền phiên bản đầu tiên đến long và bạn sẽ nhận được giá trị mong đợi.

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

Hoặc chỉ đơn giản là sử dụng 'Long' cho tất cả những người khác thay vì' int', thêm 16 byte bị damned! –

+0

Cảm ơn sự giúp đỡ. Tôi không biết rằng int tràn, tôi nghĩ rằng tôi sẽ nhận được một lỗi thời gian chạy. –

+0

@BrianRoach: Tôi không biết, tôi thấy hơi khó chịu khi phải bỏ 'HOURS_IN_DAY' thành' int' để tôi có thể chuyển nó thành hàm 'int'-expecting, chỉ vì ai đó có khái niệm sai lầm về việc giữ kiểu của nó phù hợp với 'MILLISECONDS_IN_YEAR'. – ruakh

4

Bạn đang tràn loại int. Trong Java, kết quả của phép toán số nguyên sơ qua hai số intint. Loại toán hạng quyết định điều này, không phải kiểu của biến kết quả. Hãy thử:

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; 
+0

Điều đó có hiệu quả. Cảm ơn bạn. –

0

thử này

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 
8

khi những người khác đã chỉ ra arithmetic overflow, bạn cũng có thể thử TimeUnit để giải quyết vấn đề:

Calendar calendar = Calendar.getInstance(); 
calendar.set(Calendar.YEAR, year); 
int daysInYear = calendar.getActualMaximum(Calendar.DAY_OF_YEAR); 
System.out.println(TimeUnit.DAYS.toMillis(daysInYear)); 
2

Để fi x điều này, bạn có thể đặt chữ cái L sau cái đầu tiên: ví dụ:1000L

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

Nếu trên android, tôi đề nghị:

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 
+6

Trong tài liệu lớp học cho 'DateUtils.YEAR_IN_MILLIS' nó nói:" Hằng số này thực sự là độ dài 364 ngày, không phải là một năm! " –

1

31.536.000.000

Xét một năm đồng bằng 365 ngày với ngày 24 giờ và không có bất thường cho tài khoản như Daylight Savin g Thời gian (DST), một năm là 31,536,000,000 mili giây dài. Không phải 31,556,952,000 như bạn đề xuất trong Câu hỏi của bạn.

31.536.000.000 = (365 * 24 * 60 * 60 * 1000)

Một Leap Year với 366 ngày sẽ 31,622,400,000 mili giây.

31.622.400.000 = (366 * 24 * 60 * 60 * 1000)

java.time

Các lớp java.time hiện đại thay thế các lớp học ngày thời gian cũ đi kèm với các phiên bản sớm nhất của Java. Những lớp học cũ đã chứng tỏ là khó hiểu và rắc rối.

ChronoUnit

Leap năm và bất thường khác có thể có nghĩa là một số bất ngờ của mili giây trong một năm. Vì vậy, bạn nên để java.time thực hiện một phép tính thực tế, nếu độ chính xác là quan trọng trong tình huống của bạn.

Lớp ChronoUnit có thể tính toán thời gian đã trôi qua trong một đơn vị nhất định.

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

Chúng tôi cần xác định chính xác thời điểm bắt đầu ngày đầu tiên của năm và của năm tiếp theo. Chúng tôi làm điều đó bằng cách trải qua lớp LocalDate, đại diện cho giá trị chỉ có ngày không có thời gian trong ngày và không có múi giờ.

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

Bằng cách chỉ định múi giờ (ZoneId) chúng tôi nhận ZonedDateTime đối tượng cho những khoảnh khắc đặc biệt trên timeline.

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

Cuối cùng, tính thời gian trôi qua tính bằng mili giây.

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

bắt đầu: 2015-01-01T00: 00-05: 00 [America/Montreal] | ngừng: 2016-01-01T00: 00-05: 00 [Mỹ/Montreal] | millisInYear: 31536000000

Các vấn đề liên quan