Thời gian hiện tại tính bằng micro giây trong java


104

Trên hệ thống Unix, có cách nào để lấy dấu thời gian với độ chính xác ở mức micro giây trong Java không? Một cái gì đó giống như gettimeofdaychức năng của C.


6
Hãy nhớ rằng đồng hồ máy tính không được đặt ở bất kỳ đâu gần mức độ chính xác đó - hai đồng hồ trên các máy tính khác nhau thường sẽ khác nhau ít nhất vài mili giây, trừ khi bạn đã nỗ lực thiết lập quy trình đồng bộ hóa độ chính xác cao (khi một điều như vậy thậm chí có thể thực hiện được). Tôi không thể tưởng tượng được sẽ có điểm gì khi biết thời gian của máy tính đến từng micro giây. (Nếu bạn đang cố gắng đo một khoảng thời gian chính xác trên một máy tính, thì chắc chắn, điều đó hoàn toàn hợp lý, nhưng bạn không cần dấu thời gian đầy đủ.)
David Z

2
Ngoài ra, một số phiên bản Unix / Linux chỉ trả về độ chi tiết 1 mili giây hoặc 10 mili giây trong trường micro giây.
Stephen C

Một cách gián tiếp là thông qua JDBC , nếu được kết nối với cơ sở dữ liệu như Postgres để nắm bắt thời gian hiện tại tính bằng micro giây.
Basil Bourque

2
Java 9 trở lên:Instant.now()
Basil Bourque

Sử dụng Instant để tính micro giây kể từ Epoch: val instant = Instant.now(); val currentTimeMicros = instant.getEpochSecond() * 1000_000 + instant.getNano() / 1000;
Michael M.

Câu trả lời:


148

Không, Java không có khả năng đó.

Nó có System.nanoTime (), nhưng điều đó chỉ mang lại sự bù đắp so với một số thời gian đã biết trước đó. Vì vậy, trong khi bạn không thể lấy số tuyệt đối từ số này, bạn có thể sử dụng nó để đo độ chính xác nano giây (hoặc cao hơn).

Lưu ý rằng JavaDoc nói rằng mặc dù điều này cung cấp độ chính xác nano giây, điều đó không có nghĩa là độ chính xác nano giây. Vì vậy, hãy lấy một số mô đun lớn thích hợp của giá trị trả về.


3
Người ta sẽ phỏng đoán rằng các lý do Java không có getTimeInMicroseconds () bao gồm 1) độ chính xác không khả dụng trên nhiều nền tảng, 2) trả về độ chính xác mili giây trong một lệnh gọi micro giây dẫn đến các vấn đề về tính di động của ứng dụng.
Stephen C

9
Trên Linux, System.nanoTime () gọi clock_gettime (CLOCK_MONOTONIC, _). Brian Oxley đã đào sâu vào mã nguồn của Java để tìm ra nugget này.
David Weber

7
Câu trả lời này hiện đã lỗi thời . Các triển khai OpenJDK và Oracle của Java 9 đi kèm với một triển khai mới Clockcung cấp khả năng ghi lại khoảnh khắc hiện tại với độ phân giải nhỏ hơn mili giây. Trên MacBook Pro Retina, tôi nhận được thời gian hiện tại tính bằng micro giây (sáu chữ số của phần thập phân). Kết quả thực tế và độ chính xác phụ thuộc vào phần cứng đồng hồ cơ bản của máy tính chủ.
Basil Bourque vào

1
@BasilBourque rất nhiều hệ thống vẫn chạy trên Java 8 hoặc thậm chí sớm hơn vì không cần phải di chuyển chúng. Mặc dù câu trả lời đã lỗi thời nhưng nó vẫn hữu ích.
Dragas

1
@Dragas Trên thực tế, sẽ cần phải di chuyển chúng… để có thời gian hiện tại tính bằng micro giây như câu hỏi này.
Basil Bourque

78

tl; dr

Java 9 trở lên: Độ phân giải lên tới nano giây khi chụp khoảnh khắc hiện tại. Đó là 9 chữ số của phân số thập phân.

Instant.now()   

2017-12-23T12: 34: 56.123456789Z

Để giới hạn đến micro giây , hãy cắt bớt .

Instant                // Represent a moment in UTC. 
.now()                 // Capture the current moment. Returns a `Instant` object. 
.truncatedTo(          // Lop off the finer part of this moment. 
    ChronoUnit.MICROS  // Granularity to which we are truncating. 
)                      // Returns another `Instant` object rather than changing the original, per the immutable objects pattern. 

2017-12-23T12: 34: 56.123456Z

Trong thực tế, bạn sẽ chỉ thấy micro giây được ghi lại .nowvì đồng hồ phần cứng máy tính thông thường hiện đại không chính xác tính bằng nano giây .

Chi tiết

Các câu trả lời khác có phần lỗi thời so với Java 8.

java.time

Java 8 trở lên đi kèm với khuôn khổ java.time . Các lớp mới này sẽ thay thế các lớp ngày-thời gian có sai sót gây rắc rối được vận chuyển bằng các phiên bản Java sớm nhất như java.util.Date/.Calendar và java.text.SimpleDateFormat. Khung được định nghĩa bởi JSR 310, lấy cảm hứng từ Joda-Time , được mở rộng bởi dự án ThreeTen-Extra.

Các lớp trong java.time phân giải thành nano giây , mịn hơn nhiều so với mili giây được sử dụng bởi cả các lớp date-time cũ và bởi Joda-Time. Và mịn hơn so với micro giây được hỏi trong Câu hỏi.

nhập mô tả hình ảnh ở đây

Clock Thực hiện

Trong khi các lớp java.time hỗ trợ dữ liệu đại diện cho các giá trị tính bằng nano giây, thì các lớp này chưa tạo ra các giá trị tính bằng nano giây. Các now()phương pháp sử dụng cùng một triển khai đồng hồ cũ như các lớp ngày-giờ cũ , System.currentTimeMillis(). Chúng tôi có Clockgiao diện mới trong java.time nhưng việc triển khai cho giao diện đó là đồng hồ mili giây cũ.

Vì vậy, bạn có thể định dạng biểu diễn văn bản của kết quả ZonedDateTime.now( ZoneId.of( "America/Montreal" ) )để xem chín chữ số của một phân số thứ hai nhưng chỉ ba chữ số đầu tiên sẽ có các số như sau:

2017-12-23T12:34:56.789000000Z

Đồng hồ mới trong Java 9

Các triển khai OpenJDK và Oracle của Java 9 có Clocktriển khai mặc định mới với độ chi tiết tốt hơn, lên đến khả năng nano giây đầy đủ của các lớp java.time.

Xem sự cố OpenJDK, Tăng độ chính xác của việc triển khai java.time.Clock.systemUTC () . Vấn đề đó đã được thực hiện thành công.

2017-12-23T12:34:56.123456789Z

Trên MacBook Pro (Retina, 15 inch, cuối năm 2013) với macOS Sierra, tôi nhận được thời điểm hiện tại tính bằng micro giây (tối đa sáu chữ số của phần thập phân).

2017-12-23T12:34:56.123456Z

Đồng hồ phần cứng

Hãy nhớ rằng ngay cả với một Clocktriển khai mới tốt hơn , kết quả của bạn có thể khác nhau tùy theo máy tính. Java phụ thuộc vào đồng hồ của phần cứng máy tính bên dưới để biết thời điểm hiện tại.

  • Các nghị quyết của các đồng hồ phần cứng khác nhau. Ví dụ: nếu đồng hồ phần cứng của một máy tính cụ thể chỉ hỗ trợ độ chi tiết micro giây , thì mọi giá trị ngày-giờ được tạo sẽ chỉ có sáu chữ số của giây phân số với ba chữ số cuối cùng là số không.
  • Độ chính xác của đồng hồ phần cứng rất khác nhau. Chỉ vì đồng hồ tạo ra một giá trị với một số chữ số của phần thập phân của giây, những chữ số đó có thể không chính xác, chỉ là những con số gần đúng, lệch so với thời gian thực như có thể được đọc từ đồng hồ nguyên tử . Nói cách khác, chỉ vì bạn nhìn thấy một loạt các chữ số ở bên phải dấu thập phân không có nghĩa là bạn có thể tin tưởng thời gian đã trôi qua giữa các lần đọc như vậy là đúng với độ phút đó.

Chúng có thể phân giải thành nano giây nhưng chúng vẫn dựa trên System.currentTimeMillis, vì vậy chúng chỉ thêm một loạt các số 0 vào cuối. Không giúp ích được gì nếu bạn đang cố tính thời gian bằng micro / nano giây, người dùng chỉ có thể thêm số 0 vào thời gian mili giây theo cách thủ công.
annedroiid

@annedroiid Tôi đã đề cập điều đó trong Câu trả lời của mình. Trong Java 8, bạn có thể lưu trữ các khoảnh khắc với độ phân giải nano giây, nhưng việc ghi lại khoảnh khắc hiện tại chỉ tính bằng mili giây. Đã được khắc phục trong Java 9 với một triển khai mới của Clock. Mặc dù vậy, trong Java 8, các lớp java.time rất hữu ích để giữ các giá trị được các nguồn khác thu thập, chẳng hạn như micro giây trên cơ sở dữ liệu Postgres. Nhưng hãy cẩn thận về sự không chính xác của các giá trị trong phạm vi micro giây và nano giây; phần cứng máy tính thông thường có thể không mang thời gian theo dõi đồng hồ phần cứng một cách chính xác. Giá trị có sáu hoặc chín chữ số của phân số giây có thể không đúng.
Basil Bourque vào

Phải không ChronoUnit.MICROS ?
Roland

@Roland Có, hiện đã được sửa, cảm ơn. FYI, trên Stack Overflow, bạn được mời trực tiếp chỉnh sửa Câu trả lời để khắc phục lỗi như vậy.
Basil Bourque

55

Bạn có thể sử dụng System.nanoTime():

long start = System.nanoTime();
// do stuff
long end = System.nanoTime();
long microseconds = (end - start) / 1000;

để tính thời gian tính bằng nano giây nhưng nó là một thước đo tương đối nghiêm ngặt. Nó không có ý nghĩa tuyệt đối. Nó chỉ hữu ích khi so sánh với các thời gian nano khác để đo thời gian một việc cần làm.


14

Như các áp phích khác đã chỉ ra; đồng hồ hệ thống của bạn có thể không được đồng bộ hóa đến micro giây so với giờ thế giới thực. Tuy nhiên, các dấu thời gian chính xác đến micro giây hữu ích như một kết hợp để vừa cho biết thời gian trên tường hiện tại, vừa đo / xác định thời gian của mọi thứ.

Tôi gắn nhãn tất cả các sự kiện / thông báo được ghi vào tệp nhật ký bằng dấu thời gian như "2012-10-21 19: 13: 45.267128". Chúng truyền tải cả thời điểm nó xảy ra (thời gian "tường") và cũng có thể được sử dụng để đo khoảng thời gian giữa sự kiện này và sự kiện tiếp theo trong tệp nhật ký (chênh lệch tương đối tính bằng micro giây).

Để đạt được điều này, bạn cần liên kết System.currentTimeMillis () với System.nanoTime () và hoạt động độc quyền với System.nanoTime () từ thời điểm đó trở đi. Mã ví dụ:

/**
 * Class to generate timestamps with microsecond precision
 * For example: MicroTimestamp.INSTANCE.get() = "2012-10-21 19:13:45.267128"
 */ 
public enum MicroTimestamp 
{  INSTANCE ;

   private long              startDate ;
   private long              startNanoseconds ;
   private SimpleDateFormat  dateFormat ;

   private MicroTimestamp()
   {  this.startDate = System.currentTimeMillis() ;
      this.startNanoseconds = System.nanoTime() ;
      this.dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS") ;
   }

   public String get()
   {  long microSeconds = (System.nanoTime() - this.startNanoseconds) / 1000 ;
      long date = this.startDate + (microSeconds/1000) ;
      return this.dateFormat.format(date) + String.format("%03d", microSeconds % 1000) ;
   }
}

5
Ý tưởng là tốt, nhưng bạn phải đồng bộ hóa lại mọi lúc mọi nơi. Thời gian hệ thống (currentTime) và đồng hồ CPU (nanoTime) KHÔNG đồng bộ, vì chúng thường dựa trên các đồng hồ phần cứng khác nhau. Ngoài ra, máy chủ thời gian của hệ điều hành của bạn sẽ đồng bộ hóa lại đồng hồ hệ thống với nguồn thời gian bên ngoài, nếu có. Do đó, lớp học có vẻ rất chính xác của bạn sẽ tạo ra những con dấu thời gian có thể không tốt!
h2stein

3
Mã đó dường như không an toàn cho chuỗi. Hai cuộc gọi đồng thời đến MicroTimestamp.INSTANCE.get()có thể có vấn đề.
Ahmed

@Ahmed Tại sao bạn cho rằng mã không an toàn? Không có gì trong get()phương thức cập nhật các biến thành viên; nó chỉ sử dụng các biến cục bộ tạm thời.
Jason Smith

6

Bạn có thể tạo một thành phần xác định độ lệch giữa System.nanoTime () và System.currentTimeMillis () và hiệu quả nhận được nano giây kể từ kỷ nguyên.

public class TimerImpl implements Timer {

    private final long offset;

    private static long calculateOffset() {
        final long nano = System.nanoTime();
        final long nanoFromMilli = System.currentTimeMillis() * 1_000_000;
        return nanoFromMilli - nano;
    }

    public TimerImpl() {
        final int count = 500;
        BigDecimal offsetSum = BigDecimal.ZERO;
        for (int i = 0; i < count; i++) {
            offsetSum = offsetSum.add(BigDecimal.valueOf(calculateOffset()));
        }
        offset = (offsetSum.divide(BigDecimal.valueOf(count))).longValue();
    }

    public long nowNano() {
        return offset + System.nanoTime();
    }

    public long nowMicro() {
        return (offset + System.nanoTime()) / 1000;
    }

    public long nowMilli() {
        return System.currentTimeMillis();
    }
}

Thử nghiệm sau đây cho kết quả khá tốt trên máy của tôi.

    final Timer timer = new TimerImpl();
    while (true) {
        System.out.println(timer.nowNano());
        System.out.println(timer.nowMilli());
    }

Sự khác biệt dường như dao động trong khoảng + -3ms. Tôi đoán người ta có thể điều chỉnh tính toán bù đắp thêm một chút.

1495065607202174413
1495065607203
1495065607202177574
1495065607203
...
1495065607372205730
1495065607370
1495065607372208890
1495065607370
...

2

Nếu bạn quan tâm đến Linux: Nếu bạn tìm ra mã nguồn thành "currentTimeMillis ()", bạn sẽ thấy rằng, trên Linux, nếu bạn gọi phương thức này, nó sẽ quay lại một micro giây. Tuy nhiên, Java sau đó cắt bớt phần nghìn giây và trả lại cho bạn phần nghìn giây. Điều này một phần là do Java phải là nền tảng chéo nên việc cung cấp các phương pháp dành riêng cho Linux là một điều không thể tránh khỏi trong ngày (hãy nhớ rằng hỗ trợ liên kết mềm thô sơ từ 1.6 trở về trước?!). Đó cũng là vì, trong khi đồng hồ của bạn có thể trả lại cho bạn micro giây trong Linux, điều đó không nhất thiết có nghĩa là nó sẽ tốt cho việc kiểm tra thời gian. Ở mức micro giây, bạn cần biết rằng NTP không sắp xếp lại thời gian của bạn và đồng hồ của bạn không bị trôi quá nhiều trong các cuộc gọi phương thức.

Điều này có nghĩa là, trên lý thuyết, trên Linux, bạn có thể viết một trình bao bọc JNI giống với trình bao bọc trong gói Hệ thống, nhưng không được cắt bớt micro giây.


2

một giải pháp "nhanh chóng và bẩn thỉu" mà cuối cùng tôi đã sử dụng:

TimeUnit.NANOSECONDS.toMicros(System.nanoTime());

CẬP NHẬT:

Ban đầu tôi đã sử dụng System.nanoTime nhưng sau đó tôi phát hiện ra nó chỉ nên được sử dụng trong thời gian trôi qua, cuối cùng tôi đã thay đổi mã của mình để hoạt động với mili giây hoặc tại một số nơi sử dụng:

TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis());

nhưng điều này sẽ chỉ thêm số không vào cuối giá trị (micros = millis * 1000)

Để lại câu trả lời này ở đây như một "dấu hiệu cảnh báo" trong trường hợp ai đó nghĩ về nanoTime :)


1
Tài liệu nói rõ ràng là không sử dụng System.nanoTimecho thời gian trên đồng hồ treo tường: “Phương pháp này chỉ có thể được sử dụng để đo thời gian đã trôi qua và không liên quan đến bất kỳ khái niệm nào khác về hệ thống hoặc thời gian trên đồng hồ treo tường”.
Basil Bourque

1
@BasilBourque bạn nói đúng, sau khi viết bài này, cuối cùng tôi đã phát hiện ra và thay đổi mã của mình nhưng quên cập nhật ở đây, cảm ơn đã nhận xét!
keisar

2

Java hỗ trợ micro giây thông qua TimeUnit enum.

Đây là tài liệu java: Enum TimeUnit

Bạn có thể nhận được micro giây trong java bằng cách này:

long microsenconds = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis());

Bạn cũng có thể chuyển đổi micro giây trở lại đơn vị thời gian khác, ví dụ:

long seconds = TimeUnit.MICROSECONDS.toSeconds(microsenconds);

Điều này không chính xác, bạn sẽ nhận được thời gian ở độ phân giải / độ dài MICRO, nhưng bản thân thời gian sẽ luôn chỉ với độ chính xác MILI (có nghĩa là 3 chữ số cuối sẽ luôn là 0).
Michalsx

0

Nếu bạn định sử dụng nó cho hệ thống thời gian thực, có lẽ java không phải là lựa chọn tốt nhất để lấy dấu thời gian. Nhưng nếu bạn định sử dụng if cho khóa duy nhất, thì câu trả lời của Jason Smith sẽ đủ. Nhưng trong trường hợp, để dự đoán 2 mục cuối cùng nhận được cùng một dấu thời gian (có thể xảy ra nếu 2 mục đó được xử lý gần như đồng thời), bạn có thể lặp lại cho đến khi dấu thời gian cuối cùng không bằng với dấu thời gian hiện tại.

String timestamp = new String();
do {
    timestamp = String.valueOf(MicroTimestamp.INSTANCE.get());
    item.setTimestamp(timestamp);
} while(lasttimestamp.equals(timestamp));
lasttimestamp = item.getTimestamp();

0

Sử dụng Instant để tính toán micro giây kể từ Epoch:

val instant = Instant.now();
val currentTimeMicros = instant.getEpochSecond() * 1000_000 + instant.getNano() / 1000;


-3

Dưới đây là một ví dụ về cách tạo Dấu thời gian hiện tại UnsignedLong:

UnsignedLong current = new UnsignedLong(new Timestamp(new Date().getTime()).getTime());

2
Câu trả lời chưa chính xác. Lớp java.util.Date có độ phân giải mili giây, không phải micro giây được chỉ định trong Câu hỏi. Tạo java.sql.Timestamp không kéo dữ liệu bổ sung ra ngoài.
Basil Bourque
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.