วิธีตัดทอนมิลลิวินาทีออกจาก. NET DateTime


334

ฉันกำลังพยายามเปรียบเทียบการประทับเวลาจากคำขอขาเข้ากับค่าฐานข้อมูลที่เก็บไว้ แน่นอนว่า SQL Server ช่วยให้มิลลิวินาทีมีความแม่นยำและเมื่ออ่านเป็น. NET DateTime จะรวมมิลลิวินาทีเหล่านั้นด้วย อย่างไรก็ตามคำขอที่เข้ามาในระบบไม่ได้ให้ความแม่นยำนั้นดังนั้นฉันจึงต้องลดลงมิลลิวินาที

ฉันรู้สึกว่าฉันขาดอะไรบางอย่างที่เห็นได้ชัด แต่ฉันไม่ได้พบวิธีที่สง่างามที่จะทำ (C #)


(ลองครั้งที่ 3 ... ) ตั้งแต่ 20% ของคำตอบ ( 1 , 2 , 3 ) อธิบายถึงวิธีการละเว้นหรือลบองค์ประกอบมิลลิวินาทีจากการstringเป็นตัวแทนในรูปแบบของ a DateTimeอาจจำเป็นต้องมีการแก้ไขเพื่อให้ชัดเจนว่า "ตัด" / "drop" milliseconds หมายถึง "สร้างDateTimeค่าที่ส่วนประกอบวันที่ / เวลาทั้งหมดเหมือนกันยกเว้นTimeOfDay.TotalMillisecondsคือ0" ผู้คนไม่ได้อ่านแน่นอน แต่เพื่อกำจัดความคลุมเครือใด ๆ
BACON

คำตอบ:


557

ต่อไปนี้จะทำงานกับ DateTime ที่มีเศษส่วนเป็นมิลลิวินาทีและยังคงคุณสมบัติชนิด (Local, Utc หรือ Undefined)

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

หรือเทียบเท่าและสั้นกว่า:

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

นี่อาจเป็นวิธีทั่วไปในการขยาย:

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

ซึ่งใช้ดังนี้:

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

ในขณะที่ฉันจะให้สิ่งนี้กับคุณเพราะคุณถูกต้องทางเทคนิคสำหรับคนที่อ่านข้อมูลจาก SQL Server เพื่อเปรียบเทียบกับข้อมูลที่แจกจ่ายบางอย่าง (คำขอทางเว็บในกรณีของฉัน) ความละเอียดนี้ไม่จำเป็น
เจฟฟ์ Putz

1
ดี เห็นได้ชัดว่ามีคนต้องการให้ชั้นเรียน DateTime บางวิธีการขยายรอบที่ใกล้ที่สุดสิ่งที่เพื่อให้การเขียนโปรแกรมที่ดีประเภทนี้จะได้รับการใช้ซ้ำ
chris.w.mclean

สิ่งนี้ไม่น่าเป็นไปได้มาก แต่วิธีนี้จะไม่แตกเมื่อเห็บ = 0?
adotout

@adotout วิธีการ Truncate ด้านบนจะส่ง DivideByZeroException ถ้าพารามิเตอร์ timeSpan เป็นศูนย์นี่คือสิ่งที่คุณหมายถึงโดย "วิธีการแบ่งเมื่อเห็บ = 0"? มันจะเป็นการดีกว่าถ้าคุณโยน ArgumentException เมื่อ timeSpan เป็นศูนย์
Joe

145
var date = DateTime.Now;

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

34
ชัดเจนและเรียบง่ายอย่าลืมเพิ่ม ", date.Kind" ที่ส่วนท้ายของ Constructor เพื่อให้แน่ใจว่าคุณจะไม่สูญเสียข้อมูลที่สำคัญ
JMcDaniel

9
ระมัดระวังเกี่ยวกับวิธีแก้ปัญหานี้ในโค้ดที่ไวต่อประสิทธิภาพ แอปของฉันถูกใช้จ่าย 12% ของเวลา CPU ในSystem.DateTime.GetDatePart
พันเอก Panic

3
มันง่าย แต่ช้ากว่าคำถามที่ทำเครื่องหมายว่าเป็นคำตอบที่ดีที่สุด ไม่ใช่ว่านี่อาจจะเป็นคอขวด แต่ช้าลงประมาณ 7-8x
Jonas

คำสั่ง "ช้ากว่ามาก" ไม่ถูกต้อง exaclty ความแตกต่างอยู่ระหว่าง 50% และประมาณ 100% ขึ้นอยู่กับรันไทม์ สุทธิ 4.7.2: 0.35 vss 0.62 andsและcore 3.1: 0.18 vss เทียบกับ 0.12 that'ssนั่นคือไมโครวินาที (10 ^ -6 วินาที)
juwens

61

นี่คือวิธีการขยายตามคำตอบก่อนหน้าซึ่งจะช่วยให้คุณตัดทอนความละเอียดใด ๆ ...

การใช้งาน:

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

ประเภท:

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
นี่เป็นวิธีการแก้ปัญหาที่ยืดหยุ่นและสามารถนำกลับมาใช้ใหม่ได้ซึ่งมีความกระชับและแสดงออกโดยไม่ต้องใช้คำมากเกินไป คะแนนของฉันเป็นทางออกที่ดีที่สุด
Jaans

2
คุณไม่จำเป็นต้องใส่วงเล็บรอบ ๆ ตัวถูกดำเนินการ%
ริ

8
.. แต่ผู้อุปถัมภ์เพิ่มความชัดเจนในความคิดของฉัน
orion elenzil

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

70
-1: จะทำงานเฉพาะในกรณีที่ค่า DateTime ไม่รวมเศษส่วนของมิลลิวินาที
Joe

7
การใช้วิธีนี้ทำให้การทดสอบหน่วยของฉันบางอย่างล้มเหลว: คาดหวัง: 2010-05-05 15: 55: 49.000 แต่เดิม: 2010-05-05 15: 55: 49.000 ฉันเดาเพราะสิ่งที่โจพูดถึงเศษเสี้ยววินาที
Seth Reno

6
ไม่ทำงานสำหรับการทำให้เป็นอนุกรมเช่น 2010-12-08T11: 20: 03.000099 + 15: 00 เป็นเอาต์พุตไม่ได้ตัดออกเป็นมิลลิวินาทีอย่างสมบูรณ์
joedotnot

5
Millisecondคุณสมบัติให้จำนวนเต็มระหว่าง 0 และ 999 (รวม) ดังนั้นถ้าเวลาของวันก่อนที่จะดำเนินการได้พูด23:48:49.1234567แล้วว่าจะเป็นจำนวนเต็มและช่วงเวลาของวันหลังจากการดำเนินการคือ123 23:48:49.0004567ดังนั้นจึงไม่ถูกตัดทอนเป็นจำนวนวินาทีทั้งหมด
Jeppe Stig Nielsen

11

บางครั้งคุณต้องการตัดทอนสิ่งที่ยึดตามปฏิทินเช่นปีหรือเดือน ต่อไปนี้เป็นวิธีส่วนขยายที่ให้คุณเลือกความละเอียดใด ๆ

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

แทนที่จะทิ้งมิลลิวินาทีมาเปรียบเทียบกันทำไมไม่ลองเปรียบเทียบความแตกต่างล่ะ?

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

หรือ

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

3
ตัวเลือกแรกไม่ทำงานเพราะ TotalSeconds เป็นสองเท่า มันจะส่งคืนหน่วยเป็นมิลลิวินาที
Jowen

1
การเปรียบเทียบความแตกต่างไม่ได้ให้ผลลัพธ์เหมือนกับการตัดทอนแล้วเปรียบเทียบ เช่น 5.900 และ 6.100 อยู่ห่างกันน้อยกว่าหนึ่งวินาทีดังนั้นจะเปรียบเทียบเท่ากับวิธีการของคุณ แต่ค่าที่ถูกตัดทอน 5 และ 6 นั้นแตกต่างกัน ซึ่งมีความเหมาะสมขึ้นอยู่กับความต้องการของคุณ
Joe

7

ชัดเจนน้อยกว่า แต่เร็วกว่า 2 เท่า:

// 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
โปรดทราบว่าตัวเลือกที่สองd.AddMilliseconds(-d.Millisecond)ไม่จำเป็นต้องย้าย DateTime ให้ตรงกับวินาทีเต็มก่อนหน้า d.Ticks % TimeSpan.TicksPerMillisecondเห็บ (ประมาณระหว่าง 0 ถึง 9,999) เกินกว่าวินาทีของคุณจะยังคงอยู่
Technetium

5

วิธีปัดเศษเป็นวินาที:

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

แทนที่ด้วยTicksPerMinuteเพื่อปัดเศษเป็นนาที


หากรหัสของคุณอ่อนไหวต่อประสิทธิภาพโปรดใช้ความระมัดระวัง

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

แอปของฉันถูกใช้จ่าย 12% ของเวลา CPU ในSystem.DateTime.GetDatePart


3

วิธีการอ่านง่ายคือ ...

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

และอื่น ๆ...

//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
การแปลงเป็นสตริงและการแยกวิเคราะห์เป็นความคิดที่น่ากลัวในแง่ของประสิทธิภาพ
Jeff Putz

2
@JeffPutz จริง แต่มันเป็นแต่ง่าย เหมาะสำหรับการทดสอบอัตโนมัติที่ค่าถูกแทรกและดึงออกจากฐานข้อมูลจะสูญเสียเห็บ (สถานการณ์ที่แน่นอนของฉัน) อย่างไรก็ตามคำตอบนี้อาจจะง่ายกว่าที่คิดเพราะvar now = DateTime.Parse(DateTime.Now.ToString())ใช้ได้ดี
Grimm The Opiner

1
@GrimmTheOpiner - "... ใช้งานได้ดี" ส่วนใหญ่ แต่ไม่รับประกัน มันทำอะไร: "ปัดเศษ DateTime ให้เป็นความแม่นยำ 'Long time' ที่มีการกำหนดค่าเช่นเดียวกับในการตั้งค่าแผงควบคุมของผู้ใช้ปัจจุบัน" ซึ่งโดยปกติแล้ว แต่ไม่จำเป็นต้องเป็นวินาที
โจ

1
เช่นเดียวกับความเรียบง่ายประสิทธิภาพไม่ได้เป็นปัญหาสำหรับสถานการณ์การทดสอบอัตโนมัติ
เหลียง

1

เกี่ยวกับการตอบสนองของ Diadistis สิ่งนี้ได้ผลสำหรับฉันยกเว้นฉันต้องใช้ Floor เพื่อลบส่วนที่เป็นเศษส่วนของการหารก่อนการคูณ ดังนั้น,

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

กลายเป็น

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

ฉันคาดว่าการหารของค่าสองค่าแบบยาวจะส่งผลให้มีค่าเป็น Long ดังนั้นจึงลบส่วนทศนิยมออก

Eppsy


1

2 วิธีการขยายสำหรับการแก้ปัญหาที่กล่าวถึงข้างต้น

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

การใช้งาน:

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

1

ไม่ใช่วิธีที่เร็วที่สุด แต่ง่ายและเข้าใจง่าย:

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 

และโดยการใช้

ToLongDateString() // its show 19 February 2016.

: P


-1 ฉันสามารถดูว่าคำถามที่อาจได้รับการตีความตามที่ถามในการผลิตstringแทนDateTimeแต่ละเว้นนี้ส่วนประกอบเวลาจากการส่งออกอย่างสมบูรณ์ (นั่นทำให้การเข้าถึงTodayคุณสมบัติไม่จำเป็นเช่นกัน)
BACON

0

วิธีการใหม่

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

// กำหนดพารามิเตอร์สตริงพาส dd-mmm-yyyy ส่งคืน 24-feb-2016

หรือแสดงบนกล่องข้อความ

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

// วางบน PageonLoad


-1 ฉันสามารถดูว่าคำถามที่อาจได้รับการตีความตามที่ถามในการผลิตstringแทนDateTimeแต่ละเว้นนี้ส่วนประกอบเวลาจากการส่งออกอย่างสมบูรณ์ (นั่นทำให้การเข้าถึงTodayคุณสมบัติไม่จำเป็นเช่นกัน)
BACON

0

ในกรณีของฉันฉันตั้งเป้าที่จะบันทึก TimeSpan จากเครื่องมือ datetimePicker โดยไม่บันทึกวินาทีและมิลลิวินาทีและนี่คือวิธีแก้ปัญหา

ขั้นแรกให้แปลง datetimePicker.value เป็นรูปแบบที่คุณต้องการซึ่งเหมืองคือ "HH: mm" แล้วแปลงกลับเป็น TimeSpan

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

วิธีที่ดีกว่า (เจตนาชัดเจนหลีกเลี่ยงการจัดรูปแบบและแยกวิเคราะห์จาก a string) เพื่อทำเช่นนี้DateTime datetime = datetimepicker1.Value; TimeSpan timeSpan = new TimeSpan(datetime.Hour, datetime.Minute, 0); หรือคุณสามารถใช้รูปแบบของวิธีการขยายของ Joeที่ทำงานกับTimeSpanค่าและใช้TimeSpan timeSpan = datetime.TimeOfDay.Truncate(TimeSpan.FromSeconds(1));เพื่อตัดทอนวินาที
BACON

0

นี่เป็นวิธีส่วนขยายเวอร์ชันของฉันที่โพสต์ที่นี่และในคำถามที่คล้ายกัน สิ่งนี้ตรวจสอบความถูกต้องของค่า ticks ในวิธีที่ง่ายต่อการอ่านและเก็บรักษา DateTimeKind ของอินสแตนซ์ DateTime ดั้งเดิม (สิ่งนี้มีผลข้างเคียงที่บอบบาง แต่มีความเกี่ยวข้องเมื่อจัดเก็บในฐานข้อมูลเช่น MongoDB)

หากเป้าหมายที่แท้จริงคือการตัดทอน DateTime ให้เป็นค่าที่ระบุ (เช่นชั่วโมง / นาที / วินาที / MS) ฉันแนะนำให้ใช้วิธีการขยายนี้ในรหัสของคุณแทน ช่วยให้มั่นใจได้ว่าคุณสามารถตัดให้มีความแม่นยำที่ถูกต้องเท่านั้นและยังคงรักษาข้อมูลเมตา DateTimeKind ที่สำคัญของอินสแตนซ์ดั้งเดิมของคุณ:

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

จากนั้นคุณสามารถใช้วิธีนี้:

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

dateTime.Kind => DateTimeKind.Utc

-1

ฉันรู้ว่าคำตอบนั้นค่อนข้างช้า แต่วิธีที่ดีที่สุดในการกำจัดมิลลิวินาทีก็คือ

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

ลองพิมพ์ค่าของตัวแปรมันจะแสดงเวลาวันที่โดยไม่ต้องมิลลิวินาที


1
มันไม่เหมาะ คุณมีสตริงแล้วไม่ใช่ DateTime
Jeff Putz
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.