การปัดเศษวัตถุ DateTime


106

ฉันต้องการปัดเศษวันที่ / เวลาเป็นช่วงเวลาที่ใกล้ที่สุดสำหรับแอปพลิเคชันสร้างแผนภูมิ ฉันต้องการลายเซ็นวิธีการขยายดังต่อไปนี้เพื่อให้การปัดเศษได้รับความแม่นยำทุกระดับ:

static DateTime Round(this DateTime date, TimeSpan span);

แนวคิดก็คือถ้าฉันผ่านไปในช่วงเวลาสิบนาทีมันจะปัดเศษเป็นช่วงสิบนาทีที่ใกล้ที่สุด ฉันไม่สามารถเข้าใจการใช้งานได้และหวังว่าพวกคุณคนหนึ่งจะเคยเขียนหรือใช้สิ่งที่คล้ายกันมาก่อน

ฉันคิดว่าการใช้งานพื้นเพดานหรือที่ใกล้ที่สุดก็ดี

ความคิดใด ๆ ?

แก้ไข: ขอบคุณ @tvanfosson & @ShuggyCoUk การใช้งานมีลักษณะดังนี้:

public static class DateExtensions {
    public static DateTime Round(this DateTime date, TimeSpan span) {
        long ticks = (date.Ticks + (span.Ticks / 2) + 1)/ span.Ticks;
        return new DateTime(ticks * span.Ticks);
    }
    public static DateTime Floor(this DateTime date, TimeSpan span) {
        long ticks = (date.Ticks / span.Ticks);
        return new DateTime(ticks * span.Ticks);
    }
    public static DateTime Ceil(this DateTime date, TimeSpan span) {
        long ticks = (date.Ticks + span.Ticks - 1) / span.Ticks;
        return new DateTime(ticks * span.Ticks);
    }
}

และเรียกเช่นนั้น:

DateTime nearestHour = DateTime.Now.Round(new TimeSpan(1,0,0));
DateTime minuteCeiling = DateTime.Now.Ceil(new TimeSpan(0,1,0));
DateTime weekFloor = DateTime.Now.Floor(new TimeSpan(7,0,0,0));
...

ไชโย!


1
การใช้งานบางส่วนอาจช่วยได้เช่นกัน: stackoverflow.com/questions/766626/…
Matt Hamilton


3
อย่าลืมเพิ่ม DateTimeKind เดิมลงในวันที่ที่สร้างขึ้นใหม่เช่น DateTime ใหม่ (ติ๊ก * span.Ticks, date.Kind);
น.

คำตอบ:


130

ชั้น

long ticks = date.Ticks / span.Ticks;

return new DateTime( ticks * span.Ticks );

รอบ (ขึ้นที่จุดกึ่งกลาง)

long ticks = (date.Ticks + (span.Ticks / 2) + 1)/ span.Ticks;

return new DateTime( ticks * span.Ticks );

เพดาน

long ticks = (date.Ticks + span.Ticks - 1)/ span.Ticks;

return new DateTime( ticks * span.Ticks );

5
ฉันเพิ่งพบปัญหาที่ DateTimeKind ไม่ได้รับการเก็บรักษาไว้ การปรับแต่งต่อไปนี้ไปยังบรรทัดสุดท้ายในแต่ละวิธีช่วยในกรณีของฉัน:return new DateTime(ticks * span.Ticks, date.Kind);
Peet

39

วิธีนี้จะช่วยให้คุณปัดเศษไปยังช่วงเวลาที่กำหนด มันเร็วกว่าการหารแล้วคูณเห็บเล็กน้อย

public static class DateTimeExtensions
{
  public static DateTime Floor(this DateTime dateTime, TimeSpan interval)
  {
    return dateTime.AddTicks(-(dateTime.Ticks % interval.Ticks));
  }

  public static DateTime Ceiling(this DateTime dateTime, TimeSpan interval)
  {
    var overflow = dateTime.Ticks % interval.Ticks;

    return overflow == 0 ? dateTime : dateTime.AddTicks(interval.Ticks - overflow);
  }

  public static DateTime Round(this DateTime dateTime, TimeSpan interval)
  {
    var halfIntervalTicks = (interval.Ticks + 1) >> 1;

    return dateTime.AddTicks(halfIntervalTicks - ((dateTime.Ticks + halfIntervalTicks) % interval.Ticks));
  }
}

11

คุณควรมีความชัดเจนหากคุณต้องการให้การปัดเศษเป็น:

  1. เป็นจุดเริ่มต้นจุดสิ้นสุดหรือช่วงกลางของช่วงเวลา
    • การเริ่มต้นนั้นง่ายที่สุดและมักจะเป็นไปตามที่คาดหวัง แต่คุณควรมีความชัดเจนในข้อมูลจำเพาะเริ่มต้นของคุณ
  2. คุณต้องการให้เคสแบบมีขอบเขตปัดเศษอย่างไร
    • โดยปกติจะเป็นปัญหาหากคุณปัดเศษไปตรงกลางแทนที่จะเป็นจุดสิ้นสุด
    • เนื่องจากการปัดเศษไปตรงกลางเป็นความพยายามในการตอบคำถามที่ปราศจากอคติคุณจึงต้องใช้บางสิ่งเช่นBankers Rounding ในทางเทคนิครอบครึ่งหนึ่งเพื่อให้ปราศจากอคติอย่างแท้จริง

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

โซลูชันของ tvanfosson ครอบคลุมทุกกรณีที่ระบุไว้ใน 1 ตัวอย่างจุดกึ่งกลางจะเอนเอียงขึ้นไป เป็นที่น่าสงสัยว่านี่จะเป็นปัญหาในการปัดเศษเวลา



-2

หากคุณต้องการปัดเศษค่าชั่วโมงเป็นเพดาน

Console.WriteLine(DateTime.Now.ToString("M/d/yyyy hh:00:00"));

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