Cách cắt bớt mili giây của .NET DateTime


333

Tôi đang cố gắng so sánh một dấu thời gian từ một yêu cầu đến với một giá trị được lưu trữ cơ sở dữ liệu. Tất nhiên, SQL Server giữ một số độ chính xác của mili giây và khi đọc vào .NET DateTime, nó bao gồm các mili giây đó. Tuy nhiên, yêu cầu đến hệ thống không cung cấp độ chính xác đó, vì vậy tôi chỉ cần giảm một phần nghìn giây.

Tôi cảm thấy như tôi đang thiếu một cái gì đó rõ ràng, nhưng tôi đã không tìm thấy một cách thanh lịch để làm điều đó (C #).


(Thử lần thứ 3 ...) Vì 20% câu trả lời ( 1 , 2 , 3 ) mô tả cách bỏ qua hoặc xóa thành phần mili giây khỏi stringbiểu diễn được định dạng của a DateTime, có lẽ cần phải chỉnh sửa để làm rõ rằng "cắt ngắn" / "thả" mili giây có nghĩa là "tạo ra một DateTimegiá trị mà tất cả các thành phần ngày / giờ đều giống nhau ngoại trừ TimeOfDay.TotalMilliseconds0." Mọi người không đọc, tất nhiên, nhưng chỉ để loại bỏ bất kỳ sự mơ hồ.
BACON

Câu trả lời:


555

Phần sau sẽ hoạt động cho DateTime có mili giây, và cũng giữ nguyên thuộc tính Kind (Local, Utc hoặc Undinite).

DateTime dateTime = ... anything ...
dateTime = new DateTime(
    dateTime.Ticks - (dateTime.Ticks % TimeSpan.TicksPerSecond), 
    dateTime.Kind
    );

hoặc tương đương và ngắn hơn:

dateTime = dateTime.AddTicks( - (dateTime.Ticks % TimeSpan.TicksPerSecond));

Điều này có thể được khái quát thành một phương pháp mở rộng:

public static DateTime Truncate(this DateTime dateTime, TimeSpan timeSpan)
{
    if (timeSpan == TimeSpan.Zero) return dateTime; // Or could throw an ArgumentException
    if (dateTime == DateTime.MinValue || dateTime == DateTime.MaxValue) return dateTime; // do not modify "guard" values
    return dateTime.AddTicks(-(dateTime.Ticks % timeSpan.Ticks));
}

được sử dụng như sau:

dateTime = dateTime.Truncate(TimeSpan.FromMilliseconds(1)); // Truncate to whole ms
dateTime = dateTime.Truncate(TimeSpan.FromSeconds(1)); // Truncate to whole second
dateTime = dateTime.Truncate(TimeSpan.FromMinutes(1)); // Truncate to whole minute
...

Mặc dù tôi sẽ cung cấp cho bạn điều này bởi vì bạn đúng về mặt kỹ thuật, đối với những người đọc dữ liệu ngoài SQL Server để so sánh với một số dữ liệu phân tán (trong trường hợp của tôi, yêu cầu dựa trên Web), thì độ phân giải này là không cần thiết.
Jeff Putz

1
Đẹp. Rõ ràng ai đó cần cung cấp cho lớp DateTime một số phương thức mở rộng để làm tròn gần nhất bất cứ thứ gì để loại mã hóa tốt này sẽ được sử dụng lại.
chris.w.mclean

Điều này rất khó xảy ra, nhưng cách tiếp cận này không bị phá vỡ khi tick = 0?
quảng cáo

@adotout, phương thức Truncate ở trên sẽ đưa ra DivideByZeroException nếu tham số timeSpan bằng 0, đây có phải ý của bạn là "cách tiếp cận bị phá vỡ khi tick = 0"? Sẽ tốt hơn nếu ném ArgumentException khi timeSpan bằng không.
Joe

145
var date = DateTime.Now;

date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, date.Kind);

34
Rõ ràng và đơn giản, chỉ cần nhớ thêm ", date.Kind" vào cuối của hàm tạo để đảm bảo rằng bạn không bị mất một thông tin quan trọng.
JMcDaniel

9
Hãy thận trọng về giải pháp này trong mã nhạy cảm hiệu suất. Ứng dụng của tôi đã dành 12% thời gian CPU trong System.DateTime.GetDatePart .
Đại tá hoảng loạn

3
Nó đơn giản, nhưng chậm hơn câu hỏi được đánh dấu là câu trả lời tốt nhất. Không phải đây có thể là một nút cổ chai, nhưng nó chậm hơn khoảng 7-8 lần.
Jonas

Các câu lệnh "chậm hơn nhiều" không phải là chính xác, sự khác biệt là giữa 50% và khoảng 100% tùy thuộc vào thời gian chạy; net 4.7.2: 0.35 Nhẫn so với 0.62 Nhậnlõi 3.1: 0.18 Bút so với 0.12 Trao đổi đó là micro giây (10 ^ -6 giây)
juwens

61

Đây là một phương pháp mở rộng dựa trên câu trả lời trước đó sẽ cho phép bạn cắt ngắn mọi độ phân giải ...

Sử dụng:

DateTime myDateSansMilliseconds = myDate.Truncate(TimeSpan.TicksPerSecond);
DateTime myDateSansSeconds = myDate.Truncate(TimeSpan.TicksPerMinute)

Lớp học:

public static class DateTimeUtils
{
    /// <summary>
    /// <para>Truncates a DateTime to a specified resolution.</para>
    /// <para>A convenient source for resolution is TimeSpan.TicksPerXXXX constants.</para>
    /// </summary>
    /// <param name="date">The DateTime object to truncate</param>
    /// <param name="resolution">e.g. to round to nearest second, TimeSpan.TicksPerSecond</param>
    /// <returns>Truncated DateTime</returns>
    public static DateTime Truncate(this DateTime date, long resolution)
    {
        return new DateTime(date.Ticks - (date.Ticks % resolution), date.Kind);
    }
}

1
Đây là một giải pháp thực sự linh hoạt và có thể sử dụng lại, ngắn gọn và biểu cảm mà không quá dài dòng. Phiếu bầu của tôi là giải pháp tốt nhất.
Jaans

2
Bạn thực sự không cần dấu ngoặc đơn xung quanh% toán hạng.
ErikE

8
.. nhưng các parens thêm rõ ràng, theo ý kiến ​​của tôi.
orion elenzil

28
DateTime d = DateTime.Now;
d = d.AddMilliseconds(-d.Millisecond);

70
-1: Chỉ hoạt động nếu giá trị DateTime không bao gồm các phân số của một phần nghìn giây.
Joe

7
Sử dụng phương pháp này khiến một số bài kiểm tra đơn vị của tôi không thành công: Dự kiến: 2010-05-05 15: 55: 49.000 Nhưng là: 2010-05-05 15: 55: 49.000. Tôi đoán là do những gì Joe đã đề cập về phân số của một phần nghìn giây.
Seth Reno

6
Không hoạt động để tuần tự hóa, ví dụ 2010-12-08T11: 20: 03.000099 + 15: 00 là đầu ra, không hoàn toàn cắt giảm mili giây.
joedotnot

5
Các Millisecondbất động sản đưa ra một số nguyên từ 0 đến 999 (bao gồm). Vì vậy, nếu thời gian trong ngày trước khi hoạt động là, thì, 23:48:49.1234567số nguyên đó sẽ là 123và thời gian trong ngày sau khi hoạt động là 23:48:49.0004567. Vì vậy, nó đã không bị cắt ngắn đến toàn bộ số giây.
Jeppe Stig Nielsen

11

Đôi khi bạn muốn cắt ngắn một cái gì đó dựa trên lịch, như năm hoặc tháng. Đây là một phương pháp mở rộng cho phép bạn chọn bất kỳ độ phân giải.

public enum DateTimeResolution
{
    Year, Month, Day, Hour, Minute, Second, Millisecond, Tick
}

public static DateTime Truncate(this DateTime self, DateTimeResolution resolution = DateTimeResolution.Second)
{
    switch (resolution)
    {
        case DateTimeResolution.Year:
            return new DateTime(self.Year, 1, 1, 0, 0, 0, 0, self.Kind);
        case DateTimeResolution.Month:
            return new DateTime(self.Year, self.Month, 1, 0, 0, 0, self.Kind);
        case DateTimeResolution.Day:
            return new DateTime(self.Year, self.Month, self.Day, 0, 0, 0, self.Kind);
        case DateTimeResolution.Hour:
            return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerHour));
        case DateTimeResolution.Minute:
            return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerMinute));
        case DateTimeResolution.Second:
            return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerSecond));
        case DateTimeResolution.Millisecond:
            return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerMillisecond));
        case DateTimeResolution.Tick:
            return self.AddTicks(0);
        default:
            throw new ArgumentException("unrecognized resolution", "resolution");
    }
}

9

Thay vì giảm mili giây sau đó so sánh, tại sao không so sánh sự khác biệt?

DateTime x; DateTime y;
bool areEqual = (x-y).TotalSeconds == 0;

hoặc là

TimeSpan precision = TimeSpan.FromSeconds(1);
bool areEqual = (x-y).Duration() < precision;

3
tùy chọn đầu tiên không hoạt động, vì TotalSeconds là gấp đôi; nó cũng trả về mili giây.
Jowen

1
So sánh sự khác biệt không cho kết quả tương tự như cắt ngắn sau đó so sánh. Ví dụ: 5.900 và 6.100 cách nhau chưa đến một giây, do đó sẽ so sánh bằng với phương thức của bạn. Nhưng các giá trị rút gọn 5 và 6 là khác nhau. Cái nào phù hợp phụ thuộc vào yêu cầu của bạn.
Joe

7

Ít rõ ràng hơn nhưng nhanh hơn 2 lần:

// 10000000 runs

DateTime d = DateTime.Now;

// 484,375ms
d = new DateTime((d.Ticks / TimeSpan.TicksPerSecond) * TimeSpan.TicksPerSecond);

// 1296,875ms
d = d.AddMilliseconds(-d.Millisecond);

3
Lưu ý rằng tùy chọn thứ hai, d.AddMilliseconds(-d.Millisecond)không nhất thiết phải chuyển chính xác DateTime sang giây trước, toàn bộ giây. d.Ticks % TimeSpan.TicksPerMillisecondđánh dấu (ở đâu đó trong khoảng từ 0 đến 9,999) ngoài lần thứ hai của bạn sẽ vẫn còn.
Technetium

5

Để làm tròn xuống thứ hai:

dateTime.AddTicks(-dateTime.Ticks % TimeSpan.TicksPerSecond)

Thay thế bằng TicksPerMinuteđể làm tròn xuống đến phút.


Nếu mã của bạn nhạy cảm với hiệu suất, hãy thận trọng về

new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second)

Ứng dụng của tôi đã dành 12% thời gian CPU trong System.DateTime.GetDatePart .


3

Một cách để đọc dễ dàng là ...

//Remove milliseconds
DateTime date = DateTime.Now;
date = DateTime.ParseExact(date.ToString("yyyy-MM-dd HH:mm:ss"), "yyyy-MM-dd HH:mm:ss", null);

Và hơn thế nữa...

//Remove seconds
DateTime date = DateTime.Now;
date = DateTime.ParseExact(date.ToString("yyyy-MM-dd HH:mm"), "yyyy-MM-dd HH:mm", null);

//Remove minutes
DateTime date = DateTime.Now;
date = DateTime.ParseExact(date.ToString("yyyy-MM-dd HH"), "yyyy-MM-dd HH", null);

//and go on...

4
Chuyển đổi thành chuỗi và phân tích cú pháp là một ý tưởng khủng khiếp về hiệu suất.
Jeff Putz

2
@JeffPutz đúng, nhưng nó nhưng đơn giản. Thích hợp cho kiểm tra tự động trong đó giá trị được chèn và kéo từ DB mất dấu tích (tình huống chính xác của tôi). Tuy nhiên, câu trả lời này có thể còn đơn giản hơn nó, vì nó var now = DateTime.Parse(DateTime.Now.ToString())hoạt động tốt.
Grimm The Opiner

1
@GrimmTheOpiner - "... hoạt động tốt", chủ yếu, nhưng không được bảo đảm. Những gì nó làm là: "Làm tròn một DateTime thành bất kỳ độ chính xác nào 'Thời gian dài' được định cấu hình như trong tùy chọn Bảng điều khiển của người dùng hiện tại". Mà thường, nhưng không nhất thiết, giây.
Joe

1
Giống như sự đơn giản của nó, hiệu suất không phải là vấn đề đối với tình huống thử nghiệm tự động.
liang

1

Về phản ứng Diadistis. Điều này làm việc cho tôi, ngoại trừ tôi phải sử dụng Tầng để loại bỏ phần phân số của phép chia trước khi nhân. Vì thế,

d = new DateTime((d.Ticks / TimeSpan.TicksPerSecond) * TimeSpan.TicksPerSecond);

trở thành

d = new DateTime(Math.Floor(d.Ticks / TimeSpan.TicksPerSecond) * TimeSpan.TicksPerSecond);

Tôi đã dự kiến ​​việc chia hai giá trị Long sẽ dẫn đến Long, do đó loại bỏ phần thập phân, nhưng nó giải quyết nó thành Double để lại cùng một giá trị sau khi nhân.

Eppsy


1

2 Phương pháp mở rộng cho các giải pháp được đề cập ở trên

    public static bool LiesAfterIgnoringMilliseconds(this DateTime theDate, DateTime compareDate, DateTimeKind kind)
    {
        DateTime thisDate = new DateTime(theDate.Year, theDate.Month, theDate.Day, theDate.Hour, theDate.Minute, theDate.Second, kind);
        compareDate = new DateTime(compareDate.Year, compareDate.Month, compareDate.Day, compareDate.Hour, compareDate.Minute, compareDate.Second, kind);

        return thisDate > compareDate;
    }


    public static bool LiesAfterOrEqualsIgnoringMilliseconds(this DateTime theDate, DateTime compareDate, DateTimeKind kind)
    {
        DateTime thisDate = new DateTime(theDate.Year, theDate.Month, theDate.Day, theDate.Hour, theDate.Minute, theDate.Second, kind);
        compareDate = new DateTime(compareDate.Year, compareDate.Month, compareDate.Day, compareDate.Hour, compareDate.Minute, compareDate.Second, kind);

        return thisDate >= compareDate;
    }

sử dụng:

bool liesAfter = myObject.DateProperty.LiesAfterOrEqualsIgnoringMilliseconds(startDateTime, DateTimeKind.Utc);

1

Không phải là giải pháp nhanh nhất nhưng đơn giản và dễ hiểu:

DateTime d = DateTime.Now;
d = d.Date.AddHours(d.Hour).AddMinutes(d.Minute).AddSeconds(d.Second)

0
DateID.Text = DateTime.Today.ToShortDateString();

Use ToShortDateString() //Date 2-02-2016
Use ToShortDateString() // Time 

Và bằng cách sử dụng

ToLongDateString() // its show 19 February 2016.

: P


-1. Tôi có thể thấy làm thế nào câu hỏi có thể bị hiểu sai là yêu cầu sản xuất stringthay vì a DateTime, nhưng điều này bỏ qua hoàn toàn các thành phần thời gian từ đầu ra . (Điều đó cũng khiến việc truy cập vào Todaytài sản không cần thiết.)
BACON

0

Phương pháp mới

String Date = DateTime.Today.ToString("dd-MMM-yyyy"); 

// xác định tham số truyền chuỗi dd-mmm-yyyy return 24-feb-2016

Hoặc hiển thị trên hộp văn bản

txtDate.Text = DateTime.Today.ToString("dd-MMM-yyyy");

// đưa vào PageonLoad


-1. Tôi có thể thấy làm thế nào câu hỏi có thể bị hiểu sai là yêu cầu sản xuất stringthay vì a DateTime, nhưng điều này bỏ qua hoàn toàn các thành phần thời gian từ đầu ra . (Điều đó cũng khiến việc truy cập vào Todaytài sản không cần thiết.)
BACON

0

Trong trường hợp của tôi, tôi đã nhắm đến việc lưu TimeSpan từ công cụ datetimePicker mà không lưu giây và mili giây, và đây là giải pháp.

Đầu tiên chuyển đổi datetimePicker.value sang định dạng mong muốn của bạn, cái của tôi là "HH: mm" sau đó chuyển đổi lại thành TimeSpan.

var datetime = datetimepicker1.Value.ToString("HH:mm");
TimeSpan timeSpan = Convert.ToDateTime(datetime).TimeOfDay;

Một cách tốt hơn (mục đích rõ ràng hơn, tránh định dạng và phân tích lại từ a string) để làm điều này sẽ là DateTime datetime = datetimepicker1.Value; TimeSpan timeSpan = new TimeSpan(datetime.Hour, datetime.Minute, 0); Hoặc bạn có thể sử dụng một biến thể của phương thức mở rộng của Joe hoạt động trên TimeSpancác giá trị và sử dụng TimeSpan timeSpan = datetime.TimeOfDay.Truncate(TimeSpan.FromSeconds(1));để cắt ngắn giây.
BACON

0

Đây là phiên bản của tôi về các phương pháp mở rộng được đăng ở đây và trong các câu hỏi tương tự. Điều này xác nhận giá trị tick theo cách dễ đọc và bảo tồn DateTimeKind của phiên bản DateTime ban đầu. (Điều này có tác dụng phụ tinh tế nhưng có liên quan khi lưu trữ vào cơ sở dữ liệu như MongoDB.)

Nếu mục tiêu thực sự là cắt ngắn DateTime thành một giá trị được chỉ định (ví dụ: Giờ / phút / giây / MS), tôi khuyên bạn nên triển khai phương thức tiện ích mở rộng này trong mã của mình. Nó đảm bảo rằng bạn chỉ có thể cắt ngắn đến độ chính xác hợp lệ và nó bảo tồn siêu dữ liệu DateTimeKind quan trọng của thể hiện ban đầu của bạn:

public static DateTime Truncate(this DateTime dateTime, long ticks)
{
    bool isValid = ticks == TimeSpan.TicksPerDay 
        || ticks == TimeSpan.TicksPerHour 
        || ticks == TimeSpan.TicksPerMinute 
        || ticks == TimeSpan.TicksPerSecond 
        || ticks == TimeSpan.TicksPerMillisecond;

    // /programming/21704604/have-datetime-now-return-to-the-nearest-second
    return isValid 
        ? DateTime.SpecifyKind(
            new DateTime(
                dateTime.Ticks - (dateTime.Ticks % ticks)
            ),
            dateTime.Kind
        )
        : throw new ArgumentException("Invalid ticks value given. Only TimeSpan tick values are allowed.");
}

Sau đó, bạn có thể sử dụng phương pháp như thế này:

DateTime dateTime = DateTime.UtcNow.Truncate(TimeSpan.TicksPerMillisecond);

dateTime.Kind => DateTimeKind.Utc

-1

Tôi biết câu trả lời là khá muộn, nhưng cách tốt nhất để thoát khỏi mili giây là

var currentDateTime = DateTime.Now.ToString("s");

Hãy thử in giá trị của biến, nó sẽ hiển thị thời gian ngày, không có mili giây.


1
Điều này không lý tưởng. Bạn đã có một chuỗi sau đó và không phải là DateTime.
Jeff Putz
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.