C # DateTime.Now chính xác


97

Tôi vừa gặp phải một số hành vi không mong muốn với DateTime.UtcNow trong khi thực hiện một số bài kiểm tra đơn vị. Có vẻ như khi bạn gọi DateTime.Now/UtcNow liên tục, nó dường như trả lại cho bạn cùng một giá trị trong một khoảng thời gian dài hơn mong đợi, thay vì ghi lại các gia số mili giây chính xác hơn.

Tôi biết có một lớp Đồng hồ bấm giờ sẽ phù hợp hơn để thực hiện các phép đo thời gian chính xác, nhưng tôi tò mò liệu ai đó có thể giải thích hành vi này trong DateTime không? Có độ chính xác chính thức được ghi lại cho DateTime.Now không (ví dụ: chính xác trong vòng 50 mili giây?)? Tại sao DateTime.Now lại được tạo ra kém chính xác hơn những gì mà hầu hết các đồng hồ CPU có thể xử lý? Có lẽ nó chỉ được thiết kế cho CPU có mẫu số chung thấp nhất?

public static void Main(string[] args)
{
    var stopwatch = new Stopwatch();
    stopwatch.Start();
    for (int i=0; i<1000; i++)
    {
        var now = DateTime.Now;
        Console.WriteLine(string.Format(
            "Ticks: {0}\tMilliseconds: {1}", now.Ticks, now.Millisecond));
    }

    stopwatch.Stop();
    Console.WriteLine("Stopwatch.ElapsedMilliseconds: {0}",
        stopwatch.ElapsedMilliseconds);

    Console.ReadLine();
}

Khoảng thời gian mà bạn nhận lại cùng một giá trị là bao nhiêu?
ChrisF


Khi tôi chạy đoạn mã trên, tôi chỉ nhận được 3 giá trị duy nhất cho tích tắc và mili giây và thời gian dừng xem cuối cùng là 147 mili giây, vì vậy có vẻ như trên máy của tôi, nó chỉ chính xác trong khoảng 50 mili giây ...
Andy White

Tôi nên nói, vòng lặp chạy một loạt các lần, nhưng tôi chỉ thấy 3 giá trị khác biệt ...
Andy trắng

Đối với bất kỳ ai đến đây, đây là TL; DR // Sử dụng chức năng QueryPerformanceCounter "Lấy giá trị hiện tại của bộ đếm hiệu suất, là dấu thời gian có độ phân giải cao (<1us) có thể được sử dụng cho các phép đo khoảng thời gian." (Đối với mã số quản lý, lớp System.Diagnostics.Stopwatch sử dụng QPC làm cơ sở thời gian chính xác của nó.) Msdn.microsoft.com/en-us/library/windows/desktop/...
AnotherUser

Câu trả lời:


179

Tại sao DateTime.Now lại được tạo ra kém chính xác hơn những gì mà hầu hết các đồng hồ CPU có thể xử lý?

Một đồng hồ tốt nên được cả hai chính xácchính xác ; những cái đó khác nhau. Như một câu chuyện cười cũ, một chiếc đồng hồ dừng chính xác hai lần một ngày, một chiếc đồng hồ chậm một phút không bao giờ chính xác bất cứ lúc nào. Nhưng đồng hồ chậm một phút luôn chính xác đến từng phút gần nhất, trong khi đồng hồ dừng không có độ chính xác hữu ích nào cả.

Tại sao các DateTime nên chính xác tới, nói một micro khi nó có thể không có thể được chính xác đến micro? Hầu hết mọi người không có bất kỳ nguồn nào cho các tín hiệu thời gian chính thức chính xác đến từng micro giây. Do đó, đưa ra sáu chữ số sau chữ số thập phân của độ chính xác , năm chữ số cuối cùng trong số đó là rác sẽ nằm .

Hãy nhớ rằng, mục đích của DateTime là đại diện cho một ngày và giờ . Thời gian chính xác cao hoàn toàn không phải là mục đích của DateTime; như bạn lưu ý, đó là mục đích của StopWatch. Mục đích của DateTime là đại diện cho ngày và giờ cho các mục đích như hiển thị thời gian hiện tại cho người dùng, tính toán số ngày cho đến thứ Ba tới, v.v.

Nói tóm lại, "mấy giờ rồi?" và "điều đó mất bao lâu?" là những câu hỏi hoàn toàn khác nhau; không sử dụng một công cụ được thiết kế để trả lời câu hỏi này để trả lời câu hỏi kia.

Cảm ơn vì câu hỏi; điều này sẽ làm cho một bài viết blog tốt! :-)


2
@Eric Lippert: Raymond Chen có một bài học cũ nhưng hay về chủ đề khác biệt giữa "độ chính xác" và "độ chính xác": blog.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx
jason

3
Ok, điểm tốt về độ chính xác so với độ chính xác. Tôi đoán rằng tôi vẫn không thực sự mua tuyên bố rằng DateTime là không chính xác bởi vì "nó không nhất thiết phải như vậy." Nếu tôi có một hệ thống giao dịch và tôi muốn đánh dấu ngày giờ cho mỗi bản ghi, đối với tôi, việc sử dụng lớp DateTime có vẻ trực quan, nhưng có vẻ như có nhiều thành phần thời gian chính xác / chính xác hơn trong .NET, vậy tại sao DateTime lại thực hiện ít khả năng hơn. Tôi đoán tôi sẽ phải làm một số đọc thêm ...
Andy trắng

11
OK @Andy, giả sử bạn có một hệ thống như vậy. Trên một máy, bạn đánh dấu một giao dịch là xảy ra vào ngày 1 tháng 1, 12: 34: 30.23498273. Trên một máy khác trong cụm của bạn, bạn đánh dấu một giao dịch là xảy ra vào ngày 1 tháng 1, 12: 34: 30.23498456. Giao dịch nào xảy ra trước? Trừ khi bạn biết rằng đồng hồ của hai cỗ máy được đồng bộ hóa với nhau trong vòng một micro giây, bạn sẽ không biết cái nào xuất hiện trước. Độ chính xác thêm là rác gây hiểu nhầm . Nếu tôi làm theo cách của mình, tất cả DateTimes sẽ được làm tròn đến giây gần nhất, giống như chúng trong VBScript.
Eric Lippert

14
không phải điều này sẽ giải quyết được vấn đề bạn đề cập, vì các PC không đồng bộ trung bình thường hết tính năng theo phút . Bây giờ nếu làm tròn đến 1s không giải quyết được gì thì tại sao lại làm tròn? Nói cách khác, tôi không theo lập luận của bạn về lý do tại sao các giá trị tuyệt đối phải có độ chính xác nhỏ hơn độ chính xác của phép đo thời gian delta.
Roman Starkov

3
Giả sử tôi đang tạo một nhật ký hoạt động yêu cầu (1) biết khi nào điều gì đó xảy ra về không gian lịch (trong vòng vài giây) (2) biết rất chính xác khoảng cách giữa các sự kiện (trong vòng 50 mili giây). Nghe có vẻ như đặt cược an toàn nhất cho điều này là sử dụng DateTime. Bây giờ cho dấu thời gian của hành động đầu tiên, sau đó sử dụng Đồng hồ bấm giờ cho các hành động tiếp theo để xác định độ lệch so với DateTime ban đầu. Đây có phải là cách tiếp cận mà bạn sẽ khuyên, Eric?
devuxer

18

Độ chính xác của DateTime hơi cụ thể đối với hệ thống mà nó đang được chạy. Độ chính xác liên quan đến tốc độ của công tắc ngữ cảnh, có xu hướng vào khoảng 15 hoặc 16 ms. (Trên hệ thống của tôi, nó thực sự là khoảng 14 ms so với thử nghiệm của tôi, nhưng tôi đã thấy một số máy tính xách tay có độ chính xác gần hơn với 35-40 ms.)

Peter Bromberg đã viết một bài báo về thời gian mã chính xác cao trong C #, thảo luận về điều này.


2
4 chiếc máy Win7 mà tôi đã sử dụng trong nhiều năm đều có độ chính xác khoảng 1ms. Now () sleep (1) Now () luôn dẫn đến sự thay đổi ~ 1ms về datetime khi tôi thử nghiệm.
Bengie

Tôi cũng thấy độ chính xác ~ 1ms.
Timo

12

Tôi muốn có một Datetime.Now chính xác :), vì vậy tôi đã nấu điều này:

public class PreciseDatetime
{
    // using DateTime.Now resulted in many many log events with the same timestamp.
    // use static variables in case there are many instances of this class in use in the same program
    // (that way they will all be in sync)
    private static readonly Stopwatch myStopwatch = new Stopwatch();
    private static System.DateTime myStopwatchStartTime;

    static PreciseDatetime()
    {
        Reset();

        try
        {
            // In case the system clock gets updated
            SystemEvents.TimeChanged += SystemEvents_TimeChanged;
        }
        catch (Exception)
        {                
        }
    }

    static void SystemEvents_TimeChanged(object sender, EventArgs e)
    {
        Reset();
    }

    // SystemEvents.TimeChanged can be slow to fire (3 secs), so allow forcing of reset
    static public void Reset()
    {
        myStopwatchStartTime = System.DateTime.Now;
        myStopwatch.Restart();
    }

    public System.DateTime Now { get { return myStopwatchStartTime.Add(myStopwatch.Elapsed); } }
}

2
Tôi thích giải pháp này, nhưng tôi không chắc chắn, vì vậy tôi đã đặt câu hỏi của riêng mình ( stackoverflow.com/q/18257987/270348 ). Theo nhận xét / câu trả lời từ Servy, bạn không nên đặt lại đồng hồ bấm giờ.
RobSiklos

1
Có thể không phải trong bối cảnh của bạn, nhưng việc đặt lại có ý nghĩa trong bối cảnh của tôi - tôi chỉ đảm bảo rằng nó được thực hiện trước khi thời gian thực sự bắt đầu.
Jimmy

Bạn không cần đăng ký và bạn không cần đặt lại đồng hồ bấm giờ. Chạy mã này sau mỗi ~ 10ms là không cần thiết và tiêu tốn CPU. Và mã này hoàn toàn không an toàn. Chỉ cần khởi tạo myStopwatchStartTime = DateTime.UtcNow; một lần, trong hàm tạo tĩnh.
VeganHunter

1
@VeganHunter Tôi không chắc mình có hiểu nhận xét của bạn đúng không, nhưng bạn có vẻ nghĩ rằng TimeChanged được gọi sau mỗi ~ 10ms? Nó không.
Jimmy

@Jimmy, bạn nói đúng. Thật tệ, tôi đã hiểu sai mã. Sự kiện SystemEvents.TimeChanged chỉ được gọi nếu người dùng thay đổi thời gian hệ thống. Đó là một sự kiện hiếm hoi.
VeganHunter

6

Từ MSDN, bạn sẽ thấy DateTime.Nowcó độ phân giải xấp xỉ 10 mili giây trên tất cả các hệ điều hành NT.

Độ chính xác thực tế phụ thuộc vào phần cứng. Có thể đạt được độ chính xác tốt hơn bằng cách sử dụng QueryPerformanceCounter.


5

Đối với những gì nó đáng giá, thiếu thực sự kiểm tra nguồn .NET, Eric Lippert đã đưa ra một nhận xét về câu hỏi SO này nói rằng DateTime chỉ chính xác đến khoảng 30 mili giây. Theo lời của ông, lý do cho việc không chính xác nano giây là nó "không cần thiết."


4
Và nó có thể tồi tệ hơn. Trong VBScript, hàm Now () làm tròn kết quả trả về đến giây gần nhất, cho rằng giá trị được trả về có đủ độ chính xác khả dụng để chính xác đến từng micro giây. Trong C #, cấu trúc được gọi là DateTime; nó nhằm biểu thị ngày và giờ cho các lĩnh vực phi khoa học điển hình trong thế giới thực, chẳng hạn như khi bảo hiểm nhân thọ của bạn hết hạn hoặc bao lâu kể từ lần khởi động lại cuối cùng của bạn. Nó không dành cho thời gian chính xác cao dưới giây.
Eric Lippert

3

Từ tài liệu MSDN :

Độ phân giải của thuộc tính này phụ thuộc vào bộ đếm thời gian của hệ thống.

Họ cũng tuyên bố rằng độ phân giải gần đúng trên Windows NT 3.5 trở lên là 10 mili giây :)


1

Độ phân giải của thuộc tính này phụ thuộc vào bộ đếm thời gian hệ thống, phụ thuộc vào hệ điều hành cơ bản. Nó có xu hướng từ 0,5 đến 15 mili giây.

Do đó, các lệnh gọi lặp lại đến thuộc tính Hiện hành trong một khoảng thời gian ngắn, chẳng hạn như trong một vòng lặp, có thể trả về cùng một giá trị.

Liên kết MSDN

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.