Cara memotong milidetik dari .NET DateTime


334

Saya mencoba membandingkan cap waktu dari permintaan masuk ke nilai tersimpan basis data. SQL Server tentu saja menjaga ketepatan milidetik pada waktu itu, dan ketika membaca .NET DateTime, itu termasuk milidetik itu. Permintaan yang masuk ke sistem, bagaimanapun, tidak menawarkan presisi itu, jadi saya hanya perlu menjatuhkan milidetik.

Saya merasa seperti kehilangan sesuatu yang jelas, tetapi saya belum menemukan cara yang elegan untuk melakukannya (C #).


(Percobaan ke-3 ...) Karena 20% jawaban ( 1 , 2 , 3 ) menjelaskan cara menghilangkan atau menghapus komponen milidetik dari stringrepresentasi terformat dari a DateTime, mungkin diperlukan pengeditan untuk menjelaskan bahwa untuk "memotong" / "drop" milidetik berarti "menghasilkan DateTimenilai di mana semua komponen tanggal / waktu yang sama kecuali TimeOfDay.TotalMillisecondsadalah 0." Orang-orang tidak membaca, tentu saja, tetapi hanya untuk menghilangkan ambiguitas.
BACON

Jawaban:


557

Berikut ini akan berfungsi untuk DateTime yang memiliki milidetik fraksional, dan juga mempertahankan properti Kind (Lokal, Utc atau Tidak Terdefinisi).

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

atau yang setara dan lebih pendek:

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

Ini dapat digeneralisasi menjadi metode ekstensi:

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

yang digunakan sebagai berikut:

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
...

Sementara saya akan memberikan ini karena Anda secara teknis benar, bagi orang yang membaca data dari SQL Server untuk membandingkan dengan beberapa data yang didistribusikan (permintaan berbasis web, dalam kasus saya), jumlah resolusi ini tidak diperlukan.
Jeff Putz

1
Bagus. Jelas seseorang perlu memberikan kelas DateTime beberapa metode ekstensi untuk membulatkan ke terdekat apa pun sehingga jenis pengkodean yang baik ini akan digunakan kembali.
chris.w.mclean

Ini sangat tidak mungkin, tetapi tidakkah pendekatan ini pecah ketika ticks = 0?
adotout

@ adotout, metode Truncate di atas akan melempar DivideByZeroException jika parameter timeSpan adalah nol, apakah ini yang Anda maksud dengan "pendekatan break when ticks = 0"? Akan lebih baik untuk melempar ArgumentException ketika timeSpan adalah nol.
Joe

145
var date = DateTime.Now;

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

34
Jelas dan sederhana, hanya ingat untuk menambahkan ", date.Kind" ke akhir konstruktor untuk memastikan bahwa Anda tidak kehilangan informasi penting.
JMcDaniel

9
Berhati-hatilah dengan solusi ini dalam kode sensitif kinerja. Aplikasi saya menghabiskan 12% waktu CPU di System.DateTime.GetDatePart .
Kolonel Panic

3
Ini sederhana, tetapi lebih lambat dari pertanyaan yang ditandai sebagai jawaban terbaik. Bukan berarti ini bisa menjadi hambatan, tetapi kira-kira lebih lambat 7-8x.
Jonas

Pernyataan "jauh lebih lambat" tidak tepat benar, perbedaannya antara 50% dan sekitar 100% tergantung pada runtime; net 4.7.2: 0.35µs vs 0.62 µs dan core 3.1: 0.18 µs vs 0.12 µs itu mikro-detik (10 ^ -6 detik)
remaja

62

Berikut adalah metode ekstensi berdasarkan pada jawaban sebelumnya yang akan memungkinkan Anda memotong ke resolusi apa pun ...

Pemakaian:

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

Kelas:

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
Ini adalah solusi yang sangat fleksibel dan dapat digunakan kembali, yang ringkas dan ekspresif tanpa terlalu bertele-tele. Pilihan saya sebagai solusi terbaik.
Jaans

2
Anda sebenarnya tidak membutuhkan tanda kurung di sekitar% operan.
ErikE

8
.. tapi orangtua menambahkan kejelasan, menurut pendapat saya.
orion elenzil

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

70
-1: Akan bekerja hanya jika nilai DateTime tidak termasuk fraksi satu milidetik.
Joe

7
Menggunakan metode ini menyebabkan beberapa pengujian unit saya gagal: Diharapkan: 2010-05-05 15: 55: 49.000 Tetapi sebelumnya: 2010-05-05 15: 55: 49.000. Saya menduga karena apa yang disebutkan Joe tentang pecahan satu milidetik.
Seth Reno

6
Tidak berfungsi untuk serialisasi, mis. 2010-12-08T11: 20: 03.000099 + 15: 00 adalah output, tidak sepenuhnya memotong milidetik.
joedotnot

5
The Millisecondproperti memberikan bilangan bulat antara 0 dan 999 (inklusif). Jadi jika waktu sehari sebelum operasi adalah, katakanlah 23:48:49.1234567, maka bilangan bulat itu akan menjadi 123, dan waktu hari setelah operasi adalah 23:48:49.0004567. Jadi itu belum terpotong ke sejumlah detik.
Jeppe Stig Nielsen

11

Terkadang Anda ingin memotong ke sesuatu berbasis kalender, seperti tahun atau bulan. Inilah metode ekstensi yang memungkinkan Anda memilih resolusi apa pun.

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

Alih-alih menjatuhkan milidetik kemudian membandingkan, mengapa tidak membandingkan perbedaannya?

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

atau

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

3
opsi pertama tidak berfungsi, karena TotalSeconds adalah ganda; itu juga mengembalikan milidetik.
Jowen

1
Membandingkan perbedaan tidak memberikan hasil yang sama dengan memotong lalu membandingkan. Misalnya 5.900 dan 6.100 berjarak kurang dari sedetik, jadi akan dibandingkan dengan metode Anda. Tetapi nilai terpotong 5 dan 6 berbeda. Yang sesuai tergantung pada kebutuhan Anda.
Joe

7

Kurang jelas tetapi lebih dari 2 kali lebih cepat:

// 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
Perhatikan bahwa opsi kedua,, d.AddMilliseconds(-d.Millisecond)tidak harus memindahkan DateTime tepat ke sebelumnya, detik penuh. d.Ticks % TimeSpan.TicksPerMillisecondticks (suatu tempat antara 0 dan 9.999) di luar detik Anda akan tetap ada.
Technetium

5

Untuk membulatkan ke yang kedua:

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

Ganti dengan TicksPerMinutemembulatkan ke menit.


Jika kode Anda sensitif terhadap kinerja, berhati-hatilah

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

Aplikasi saya menghabiskan 12% waktu CPU di System.DateTime.GetDatePart .


3

Cara untuk membaca mudah adalah ...

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

Dan lagi ...

//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
Mengubah ke string dan parsing adalah ide yang buruk dalam hal kinerja.
Jeff Putz

2
@JeffPutz benar, tetapi adalah tapi sederhana. Cocok untuk tes otomatis di mana nilai yang dimasukkan dan ditarik dari DB kehilangan kutu (situasi tepat saya). Namun jawaban ini bisa lebih sederhana dari itu, karena var now = DateTime.Parse(DateTime.Now.ToString())berfungsi dengan baik.
Grimm The Opiner

1
@ GrimmTheOpiner - "... berfungsi dengan baik", sebagian besar, tetapi tidak dijamin. Kegunaannya adalah: "Membulatkan DateTime ke ketepatan yang mana pun 'Long time' dikonfigurasi seperti pada preferensi Panel Kontrol pengguna saat ini". Yang biasanya, tetapi tidak harus, detik.
Joe

1
seperti kesederhanaannya, kinerja bukan masalah untuk situasi pengujian otomatis.
liang

1

Seputar tanggapan Diadistis. Ini berhasil bagi saya, kecuali saya harus menggunakan Lantai untuk menghapus bagian pecahan divisi sebelum perkalian. Begitu,

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

menjadi

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

Saya akan mengharapkan pembagian dua nilai Long menghasilkan Long, sehingga menghapus bagian desimal, tetapi mengatasinya sebagai Double yang meninggalkan nilai yang sama persis setelah perkalian.

Eppsy


1

2 Metode ekstensi untuk solusi yang disebutkan di atas

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

pemakaian:

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

1

Bukan solusi tercepat tetapi sederhana dan mudah dipahami:

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 

Dan Dengan Menggunakan

ToLongDateString() // its show 19 February 2016.

: P


-1. Saya bisa melihat bagaimana pertanyaannya mungkin disalahtafsirkan sebagai meminta untuk menghasilkan, stringbukan DateTime, tetapi ini menghilangkan komponen waktu dari output sepenuhnya . (Itu membuat mengakses Todayproperti tidak perlu juga.)
BACON

0

Metode Baru

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

// tentukan parameter pass String dd-mmm-yyyy return 24-feb-2016

Atau ditampilkan di kotak teks

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

// pakai PageonLoad


-1. Saya bisa melihat bagaimana pertanyaannya mungkin disalahtafsirkan sebagai meminta untuk menghasilkan, stringbukan DateTime, tetapi ini menghilangkan komponen waktu dari output sepenuhnya . (Itu membuat mengakses Todayproperti tidak perlu juga.)
BACON

0

Dalam kasus saya, saya bertujuan untuk menghemat TimeSpan dari alat datetimePicker tanpa menyimpan detik dan milidetik, dan inilah solusinya.

Pertama-tama, konversikan datetimePicker.value ke format yang Anda inginkan, yang milik saya adalah "HH: mm" lalu konversikan kembali ke TimeSpan.

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

Cara yang lebih baik (maksud yang lebih jelas, hindari pemformatan dan parsing kembali dari a string) untuk melakukan ini adalah DateTime datetime = datetimepicker1.Value; TimeSpan timeSpan = new TimeSpan(datetime.Hour, datetime.Minute, 0); Atau Anda bisa menggunakan variasi metode ekstensi Joe yang beroperasi pada TimeSpannilai dan digunakan TimeSpan timeSpan = datetime.TimeOfDay.Truncate(TimeSpan.FromSeconds(1));untuk memotong detik.
BACON

0

Ini adalah versi saya dari metode ekstensi yang diposting di sini dan dalam pertanyaan serupa. Ini memvalidasi nilai kutu dengan cara yang mudah dibaca dan mempertahankan DateTimeKind dari instance DateTime asli. (Ini memiliki efek samping yang halus tetapi relevan ketika menyimpan ke database seperti MongoDB.)

Jika tujuan sebenarnya adalah memotong DateTime ke nilai yang ditentukan (mis. Jam / Menit / Detik / MS) Saya sarankan menerapkan metode ekstensi ini dalam kode Anda. Ini memastikan bahwa Anda hanya dapat memotong ke presisi yang valid dan menjaga metadata DateTimeKind penting dari instance asli Anda:

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.");
}

Maka Anda dapat menggunakan metode seperti ini:

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

dateTime.Kind => DateTimeKind.Utc

-1

Saya tahu jawabannya cukup terlambat, tetapi cara terbaik untuk menghilangkan milidetik adalah

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

Coba cetak nilai variabel, itu akan menunjukkan waktu tanggal, tanpa milidetik.


1
Ini tidak ideal. Anda punya string saat itu dan bukan DateTime.
Jeff Putz
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.