เวลาปัจจุบันเป็นไมโครวินาทีใน Java


105

ในระบบ Unix มีวิธีรับการประทับเวลาที่มีความแม่นยำระดับไมโครวินาทีใน Java หรือไม่? บางอย่างเช่นgettimeofdayฟังก์ชันของ C


6
โปรดทราบว่านาฬิกาคอมพิวเตอร์ไม่ได้ถูกตั้งค่าไว้ใกล้กับระดับความแม่นยำนั้นโดยทั่วไปแล้วนาฬิกาสองเรือนในคอมพิวเตอร์ต่างกันจะแตกต่างกันอย่างน้อยสองสามมิลลิวินาทีเว้นแต่คุณจะใช้ความพยายามในการตั้งค่าขั้นตอนการซิงโครไนซ์ที่มีความแม่นยำสูง (เมื่อสิ่งนั้นเป็นไปได้ทางร่างกาย) ฉันนึกไม่ออกว่าจะมีจุดใดในการรู้เวลาของคอมพิวเตอร์ถึงไมโครวินาที (หากคุณกำลังพยายามวัดช่วงเวลาที่แม่นยำบนคอมพิวเตอร์เครื่องหนึ่งแสดงว่าเหมาะสมอย่างยิ่ง แต่คุณไม่จำเป็นต้องประทับเวลาแบบเต็ม)
David Z

2
นอกจากนี้ Unix / Linux บางเวอร์ชันจะคืนค่าความละเอียด 1 มิลลิวินาทีหรือ 10 มิลลิวินาทีในช่องไมโครวินาทีเท่านั้น
Stephen C

วิธีทางอ้อมคือผ่านJDBCหากเชื่อมต่อกับฐานข้อมูลเช่น Postgres ที่จับเวลาปัจจุบันเป็นไมโครวินาที
Basil Bourque

2
Java 9 ขึ้นไป:Instant.now()
Basil Bourque

ใช้ทันทีเพื่อคำนวณไมโครวินาทีตั้งแต่ยุค: val instant = Instant.now(); val currentTimeMicros = instant.getEpochSecond() * 1000_000 + instant.getNano() / 1000;
Michael M.

คำตอบ:


149

ไม่ Java ไม่มีความสามารถนั้น

มันมี System.nanoTime () แต่นั่นก็ให้การชดเชยจากเวลาที่รู้จักกันก่อนหน้านี้ ดังนั้นในขณะที่คุณไม่สามารถหาจำนวนสัมบูรณ์จากค่านี้ได้ แต่คุณสามารถใช้เพื่อวัดความแม่นยำระดับนาโนวินาที (หรือสูงกว่า) ได้

โปรดทราบว่า JavaDoc กล่าวว่าในขณะที่สิ่งนี้ให้ความแม่นยำระดับนาโนวินาที แต่นั่นไม่ได้หมายถึงความแม่นยำระดับนาโนวินาที ดังนั้นจงหาค่าโมดูลัสของค่าที่ส่งคืนมากพอสมควร


3
ใครจะคาดเดาได้ว่าสาเหตุที่ Java ไม่มี getTimeInMicroseconds () รวมถึง 1) ความแม่นยำที่ไม่มีในหลายแพลตฟอร์ม 2) การคืนค่าความแม่นยำในระดับมิลลิวินาทีในการโทรระดับไมโครวินาทีทำให้เกิดปัญหาในการเคลื่อนย้ายแอปพลิเคชัน
Stephen C

9
บน Linux System.nanoTime () เรียก clock_gettime (CLOCK_MONOTONIC, _) Brian Oxleyขุดซอร์สโค้ดของ Java เพื่อค้นหานักเก็ตตัวนี้
David Weber

8
คำตอบนี้ล้าสมัยแล้ว การใช้งาน OpenJDK และ Oracle ของ Java 9 มาพร้อมกับการใช้งานใหม่Clockที่ให้สำหรับการจับภาพช่วงเวลาปัจจุบันด้วยความละเอียดที่ละเอียดกว่ามิลลิวินาที ใน MacBook Pro Retina ฉันได้รับเวลาปัจจุบันเป็นหน่วยไมโครวินาที (เศษทศนิยมหกหลัก) ผลลัพธ์และความแม่นยำที่แท้จริงขึ้นอยู่กับฮาร์ดแวร์นาฬิกาของโฮสต์คอมพิวเตอร์
Basil Bourque

1
@BasilBourque ระบบจำนวนมากยังคงทำงานบน Java 8 หรือก่อนหน้านี้เนื่องจากไม่จำเป็นต้องโอนย้าย แม้ว่าคำตอบจะล้าสมัย แต่ก็ยังมีประโยชน์
Dragas

1
@Dragas ที่จริงจะต้องมีการโยกย้ายพวกเขา ... เพื่อให้ได้เวลาปัจจุบันเป็นไมโครวินาทีตามที่คำถามนี้ถาม
Basil Bourque

81

tl; dr

Java 9 ขึ้นไป: ความละเอียดสูงสุดระดับนาโนวินาทีเมื่อจับภาพช่วงเวลาปัจจุบัน นั่นคือเศษทศนิยม 9 หลัก

Instant.now()   

2017-12-23T12: 34: 56.123456789Z

เพื่อ จำกัด การmicroseconds , ตัด

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

ในทางปฏิบัติคุณจะเห็น microseconds เพียงจับด้วย.nowเป็นร่วมสมัยนาฬิกาฮาร์ดแวร์คอมพิวเตอร์ทั่วไปไม่ถูกต้องในนาโนวินาที

รายละเอียด

คำตอบอื่น ๆ ค่อนข้างล้าสมัยใน Java 8

java.time

Java 8 และใหม่กว่ามาพร้อมกับกรอบjava.time คลาสใหม่เหล่านี้แทนที่คลาสวันเวลาที่มีปัญหาซึ่งมาพร้อมกับ Java เวอร์ชันแรกสุดเช่น java.util.Date/.Calendar และ java.text.SimpleDateFormat เฟรมเวิร์กถูกกำหนดโดย JSR 310 ซึ่งได้รับแรงบันดาลใจจากJoda-Timeซึ่งขยายโดยโครงการ ThreeTen-Extra

คลาสใน java.time แก้ไขเป็นนาโนวินาทีซึ่งละเอียดกว่ามิลลิวินาทีที่ทั้งคลาสวันที่และเวลาเก่าใช้และโดย Joda-Time และละเอียดกว่าไมโครวินาทีที่ถามในคำถาม

ป้อนคำอธิบายภาพที่นี่

Clock การนำไปใช้

แม้ว่าคลาส java.time จะรองรับข้อมูลที่แสดงค่าเป็นนาโนวินาที แต่คลาสยังไม่สร้างค่าเป็นนาโนวินาที วิธีการใช้การดำเนินงานนาฬิกาเดิมเป็นชั้นเรียนวันเวลาเก่าnow() System.currentTimeMillis()เรามีClockอินเทอร์เฟซใหม่ใน java.time แต่การใช้งานสำหรับอินเทอร์เฟซนั้นเป็นนาฬิกามิลลิวินาทีแบบเดิม

ดังนั้นคุณสามารถจัดรูปแบบการแสดงผลลัพธ์ของผลลัพธ์ZonedDateTime.now( ZoneId.of( "America/Montreal" ) )เพื่อดูตัวเลขเก้าหลักของเศษส่วนวินาที แต่มีเพียงสามหลักแรกเท่านั้นที่มีตัวเลขดังนี้:

2017-12-23T12:34:56.789000000Z

นาฬิกาใหม่ใน Java 9

การใช้งาน OpenJDK และ Oracle ของ Java 9 มีการClockใช้งานเริ่มต้นใหม่ที่มีความละเอียดละเอียดมากขึ้นจนถึงระดับนาโนวินาทีเต็มของคลาส java.time

ดูปัญหา OpenJDK, เพิ่มความแม่นยำของการดำเนินการ java.time.Clock.systemUTC ที่ () ปัญหานั้นได้รับการดำเนินการเรียบร้อยแล้ว

2017-12-23T12:34:56.123456789Z

ใน MacBook Pro (Retina 15 นิ้วปลายปี 2013) ที่มี macOS Sierra ฉันได้รับช่วงเวลาปัจจุบันเป็นหน่วยไมโครวินาที (เศษทศนิยมไม่เกินหกหลัก)

2017-12-23T12:34:56.123456Z

นาฬิกาฮาร์ดแวร์

โปรดจำไว้ว่าแม้จะมีการClockใช้งานที่ละเอียดกว่าแต่ผลลัพธ์ของคุณอาจแตกต่างกันไปตามคอมพิวเตอร์ Java ขึ้นอยู่กับนาฬิกาของฮาร์ดแวร์คอมพิวเตอร์พื้นฐานเพื่อให้ทราบช่วงเวลาปัจจุบัน

  • ความละเอียดของนาฬิกาฮาร์ดแวร์ที่แตกต่างกัน ตัวอย่างเช่นหากนาฬิกาฮาร์ดแวร์ของคอมพิวเตอร์เครื่องใดเครื่องหนึ่งรองรับเฉพาะความละเอียดระดับไมโครวินาทีค่าวันที่ - เวลาใด ๆ ที่สร้างขึ้นจะมีเศษส่วนของวินาทีเพียงหกหลักโดยตัวเลขสามหลักสุดท้ายเป็นเลขศูนย์
  • ความถูกต้องของนาฬิกาฮาร์ดแวร์ที่แตกต่างกัน เพียงเพราะนาฬิกาปลุกสร้างค่ากับหลายส่วนของตัวเลขทศนิยมของสองตัวเลขเหล่านั้นอาจจะไม่ถูกต้องเพียงประมาณ, ลอยจากเวลาที่เกิดขึ้นจริงเป็นอาจจะอ่านจากนาฬิกาอะตอม กล่าวอีกนัยหนึ่งเพียงเพราะคุณเห็นตัวเลขจำนวนมากทางด้านขวาของเครื่องหมายทศนิยมไม่ได้หมายความว่าคุณสามารถเชื่อถือเวลาที่ผ่านไประหว่างการอ่านดังกล่าวให้ตรงกับระดับนาทีนั้นได้

พวกเขาอาจแก้ไขเป็นนาโนวินาที แต่ยังคงใช้ System.currentTimeMillis ดังนั้นพวกเขาจึงเพิ่ม 0 จำนวนหนึ่งในตอนท้าย ไม่ได้ช่วยอะไรเลยหากคุณพยายามหาเวลาเป็นไมโคร / นาโนวินาทีผู้ใช้สามารถเพิ่ม 0 วินาทีเป็นมิลลิวินาทีด้วยตนเอง
annedroiid

@annedroiid ฉันได้กล่าวไว้ในคำตอบของฉัน ใน Java 8 คุณสามารถจัดเก็บช่วงเวลาที่มีความละเอียดระดับนาโนวินาที แต่การจับภาพช่วงเวลาปัจจุบันจะมีหน่วยเป็นมิลลิวินาทีเท่านั้น แก้ไขใน Java 9 ด้วยการใช้งานใหม่ของClock. ถึงกระนั้นใน Java 8 คลาส java.time ก็มีประโยชน์สำหรับการเก็บค่าที่จับโดยแหล่งอื่นเช่นไมโครวินาทีบนฐานข้อมูล Postgres แต่ระวังความไม่ถูกต้องของค่าในช่วงไมโครวินาทีและนาโนวินาที ฮาร์ดแวร์คอมพิวเตอร์ทั่วไปอาจไม่มีเวลาในการติดตามนาฬิกาฮาร์ดแวร์ที่แม่นยำ ค่าที่มีเศษส่วนของวินาทีหกหรือเก้าหลักอาจไม่เป็นความจริง
Basil Bourque

มันไม่ได้เป็นChronoUnit.MICROS ?
Roland

@Roland ใช่แก้ไขแล้วขอบคุณ FYI บน Stack Overflow คุณได้รับเชิญให้แก้ไขคำตอบโดยตรงเพื่อแก้ไขข้อผิดพลาดดังกล่าว
Basil Bourque

55

คุณสามารถใช้System.nanoTime():

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

เพื่อให้ได้เวลาเป็นนาโนวินาที แต่เป็นการวัดที่สัมพันธ์กันอย่างเคร่งครัด มันไม่มีความหมายที่แน่นอน มันมีประโยชน์สำหรับการเปรียบเทียบกับเวลานาโนอื่น ๆ เพื่อวัดว่าต้องใช้เวลานานแค่ไหน


14

ตามที่ผู้โพสต์อื่น ๆ ระบุไว้แล้ว; นาฬิการะบบของคุณอาจไม่ซิงโครไนซ์ถึงไมโครวินาทีกับเวลาโลกจริง อย่างไรก็ตามการประทับเวลาที่มีความแม่นยำระดับไมโครวินาทีมีประโยชน์ในฐานะไฮบริดสำหรับทั้งการระบุเวลาผนังปัจจุบันและการวัด / ทำโปรไฟล์ระยะเวลาของสิ่งต่างๆ

ฉันติดป้ายกำกับเหตุการณ์ / ข้อความทั้งหมดที่เขียนลงในไฟล์บันทึกโดยใช้การประทับเวลาเช่น "2012-10-21 19: 13: 45.267128" สิ่งเหล่านี้ถ่ายทอดทั้งเวลาที่เกิดขึ้น (เวลา "ผนัง") และยังสามารถใช้เพื่อวัดระยะเวลาระหว่างเหตุการณ์นี้กับเหตุการณ์ถัดไปในไฟล์บันทึก (ความแตกต่างสัมพัทธ์ในหน่วยไมโครวินาที)

เพื่อให้บรรลุสิ่งนี้คุณต้องเชื่อมโยง System.currentTimeMillis () กับ System.nanoTime () และทำงานเฉพาะกับ System.nanoTime () จากนั้นเป็นต้นไป รหัสตัวอย่าง:

/**
 * 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
แนวคิดนี้ดี แต่คุณต้องซิงโครไนซ์ใหม่ทุกขณะ เวลาของระบบ (currentTime) และนาฬิกา CPU (nanoTime) ไม่ตรงกันเนื่องจากมักขึ้นอยู่กับนาฬิกาฮาร์ดแวร์ที่แตกต่างกัน นอกจากนี้เซิร์ฟเวอร์เวลาของระบบปฏิบัติการของคุณจะซิงโครไนซ์นาฬิการะบบกับแหล่งเวลาภายนอกอีกครั้งหากมี ดังนั้นคลาสที่ดูเหมือนจะแม่นยำมากของคุณจะสร้างการประทับเวลาที่อาจผิดพลาด!
h2stein

3
ดูเหมือนว่ารหัสนั้นจะไม่ปลอดภัย การโทรพร้อมกันสองครั้งMicroTimestamp.INSTANCE.get()อาจเป็นปัญหาได้
Ahmed

@ Ahmed ทำไมคุณคิดว่ารหัสไม่ปลอดภัย? ไม่มีอะไรในget()เมธอดที่อัพเดตตัวแปรสมาชิก ใช้เฉพาะตัวแปรท้องถิ่นชั่วคราว
Jason Smith

6

คุณอาจสร้างส่วนประกอบที่กำหนดออฟเซ็ตระหว่าง System.nanoTime () และ System.currentTimeMillis () และได้รับนาโนวินาทีอย่างมีประสิทธิภาพตั้งแต่ยุค

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();
    }
}

การทดสอบต่อไปนี้ให้ผลลัพธ์ที่ค่อนข้างดีบนเครื่องของฉัน

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

ความแตกต่างดูเหมือนจะแกว่งในช่วง + -3ms ฉันเดาว่าเราสามารถปรับแต่งการคำนวณออฟเซ็ตได้อีกเล็กน้อย

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

2

หากคุณสนใจ Linux: หากคุณหาซอร์สโค้ดเป็น "currentTimeMillis ()" คุณจะเห็นว่าบน Linux ถ้าคุณเรียกวิธีนี้ระบบจะคืนค่าเป็นไมโครวินาที อย่างไรก็ตาม Java จะตัดทอนไมโครวินาทีและส่งคุณกลับไปเป็นมิลลิวินาที ส่วนหนึ่งเป็นเพราะ Java ต้องเป็นข้ามแพลตฟอร์มดังนั้นการให้วิธีการเฉพาะสำหรับ Linux จึงเป็นเรื่องที่ไม่ควรพลาดในวันนี้ (จำไว้ว่ารองรับ soft link จาก 1.6 ย้อนหลัง?!) นอกจากนี้ยังเป็นเพราะในขณะที่นาฬิกาของคุณสามารถให้ค่าไมโครวินาทีใน Linux ได้ แต่นั่นไม่ได้หมายความว่าจะดีสำหรับการตรวจสอบเวลา ในระดับไมโครวินาทีคุณต้องรู้ว่า NTP ไม่ได้กำหนดเวลาของคุณใหม่และนาฬิกาของคุณไม่ได้ลอยมากเกินไปในระหว่างการเรียกวิธีการ

ซึ่งหมายความว่าตามทฤษฎีแล้วบน Linux คุณสามารถเขียน JNI wrapper ที่เหมือนกับในแพ็กเกจ System แต่ไม่ตัดทอนไมโครวินาที


2

วิธีแก้ปัญหาที่ "รวดเร็วและสกปรก" ในที่สุดฉันก็ใช้:

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

อัพเดท:

เดิมทีฉันใช้ System.nanoTime แต่แล้วฉันก็พบว่าควรใช้เฉพาะเวลาที่ผ่านไปในที่สุดฉันก็เปลี่ยนรหัสให้ทำงานกับมิลลิวินาทีหรือในบางสถานที่ใช้:

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

แต่สิ่งนี้จะเพิ่มศูนย์ที่ส่วนท้ายของค่า (micros = millis * 1000)

ทิ้งคำตอบไว้ตรงนี้เป็น "สัญญาณเตือน" เผื่อมีคนคิดว่า nanoTime :)


1
เอกสารอย่างชัดเจนว่าจะไม่ใช้System.nanoTimeเวลาผนังนาฬิกา:“วิธีการนี้สามารถใช้ในการวัดเวลาที่ผ่านไปและไม่ได้เกี่ยวข้องกับความคิดอื่น ๆ ของระบบหรือเวลาผนังนาฬิกา.”
Basil Bourque

1
@BasilBourque คุณถูกต้องหลังจากเขียนสิ่งนี้ในที่สุดฉันก็พบและเปลี่ยนรหัสของฉัน แต่ลืมอัปเดตที่นี่ขอบคุณสำหรับความคิดเห็น!
keisar

2

Java รองรับไมโครวินาทีผ่านTimeUnitenum

นี่คือเอกสาร java: Enum TimeUnit

คุณสามารถรับ microseconds ใน java ได้โดยวิธีนี้:

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

คุณยังสามารถแปลงไมโครวินาทีกลับไปเป็นหน่วยเวลาอื่นได้เช่น:

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

สิ่งนี้ไม่ถูกต้องคุณจะได้รับเวลาในความละเอียด / ความยาว MICRO แต่เวลาจะเป็นเพียงความแม่นยำของ MILI เท่านั้น (หมายถึง 3 หลักสุดท้ายจะเป็น 0 เสมอ)
Michalsx

0

หากคุณตั้งใจจะใช้มันสำหรับระบบเรียลไทม์บางทีจาวาอาจไม่ใช่ตัวเลือกที่ดีที่สุดในการรับการประทับเวลา แต่ถ้าคุณจะใช้ if สำหรับคีย์เฉพาะคำตอบของ Jason Smith ก็เพียงพอแล้ว แต่ในกรณีที่ต้องการคาดการณ์ว่า 2 รายการจะได้รับการประทับเวลาเดียวกัน (อาจเป็นไปได้หากมีการประมวลผล 2 รายการเกือบพร้อมกัน) คุณสามารถวนซ้ำได้จนกว่าการประทับเวลาสุดท้ายจะไม่เท่ากับการประทับเวลาปัจจุบัน

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



-3

นี่คือตัวอย่างของวิธีการสร้างการประทับเวลาปัจจุบันที่ไม่ได้ลงนามLong:

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

2
คำตอบที่ไม่ถูกต้อง. คลาส java.util.Date มีความละเอียดเป็นมิลลิวินาทีไม่ใช่ไมโครวินาทีที่ระบุในคำถาม การสร้าง java.sql Timestamp ไม่ดึงข้อมูลเพิ่มเติมออกมาจากอากาศ
Basil Bourque
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.