가장 가까운 X 분으로 시간을 어떻게 반올림 할 수 있습니까?


160

반올림에 대한 간단한 기능이 있습니까 UPDateTime가장 가까운 15 분은?

예 :

2011-08-11 16:59 된다 2011-08-11 17:00

2011-08-11 17:00 로 유지 2011-08-11 17:00

2011-08-11 17:01 된다 2011-08-11 17:15

답변:


287
DateTime RoundUp(DateTime dt, TimeSpan d)
{
    return new DateTime((dt.Ticks + d.Ticks - 1) / d.Ticks * d.Ticks, dt.Kind);
}

예:

var dt1 = RoundUp(DateTime.Parse("2011-08-11 16:59"), TimeSpan.FromMinutes(15));
// dt1 == {11/08/2011 17:00:00}

var dt2 = RoundUp(DateTime.Parse("2011-08-11 17:00"), TimeSpan.FromMinutes(15));
// dt2 == {11/08/2011 17:00:00}

var dt3 = RoundUp(DateTime.Parse("2011-08-11 17:01"), TimeSpan.FromMinutes(15));
// dt3 == {11/08/2011 17:15:00}

13
이 솔루션은 확장 방법으로 유틸리티 라이브러리에 넣었습니다.
JYelton

1
상단 극단에 가까운 반올림 시간에주의하십시오. 계산하는 눈금이 DateTime.MaxValue.Ticks보다 큰 경우 예외가 발생 될 수 있습니다. 안전하고 계산 된 값과 DateTime.MaxValue.Ticks의 최소값을 취하십시오.
Paul Raff

4
이 방법으로 DateTime 객체의 정보를 잃지 않습니까? 종류와 시간대와 같이 설정되어 있습니까?
Evren Kuzucuoglu

11
@ user14 .. (+ d. 틱-1)은 필요한 경우 반올림합니다. /와 *는 반올림됩니다. 라운드 12를 다음 5로 예 : (12 +
5-1

12
@ dtb 하나의 작은 추가, 그렇지 않으면 아마 약간 버그가 있습니다 : 당신은 날짜 시간 종류를 유지해야합니다 ;-) DateTime RoundUp(DateTime dt, TimeSpan d) { return new DateTime(((dt.Ticks + d.Ticks - 1) / d.Ticks) * d.Ticks, dt.Kind); }
njy

107

포함하지 않는 용액으로 들어오는 승산분할 long 번호.

public static DateTime RoundUp(this DateTime dt, TimeSpan d)
{
    var modTicks = dt.Ticks % d.Ticks;
    var delta = modTicks != 0 ? d.Ticks - modTicks : 0;
    return new DateTime(dt.Ticks + delta, dt.Kind);
}

public static DateTime RoundDown(this DateTime dt, TimeSpan d)
{
    var delta = dt.Ticks % d.Ticks;
    return new DateTime(dt.Ticks - delta, dt.Kind);
}

public static DateTime RoundToNearest(this DateTime dt, TimeSpan d)
{
    var delta = dt.Ticks % d.Ticks;
    bool roundUp = delta > d.Ticks / 2;
    var offset = roundUp ? d.Ticks : 0;

    return new DateTime(dt.Ticks + offset - delta, dt.Kind);
}

용법:

var date = new DateTime(2010, 02, 05, 10, 35, 25, 450); // 2010/02/05 10:35:25
var roundedUp = date.RoundUp(TimeSpan.FromMinutes(15)); // 2010/02/05 10:45:00
var roundedDown = date.RoundDown(TimeSpan.FromMinutes(15)); // 2010/02/05 10:30:00
var roundedToNearest = date.RoundToNearest(TimeSpan.FromMinutes(15)); // 2010/02/05 10:30:00

8
나는 이것이 곱셈과 나눗셈을 사용하는 것보다 빠를 것이라고 확신했지만 테스트에서 그렇지 않은 것으로 나타났습니다. 10000000 회 이상 반복하면 모듈 방식은 내 컴퓨터에서 ~ 610ms가 걸리고 mult / div 메소드는 ~ 500ms가 걸렸습니다. FPU가 오래된 문제를 비 문제로 생각합니다. 내 테스트 코드는 다음과 같습니다. pastie.org/8610460
viggity

1
확장 기능을 많이 사용합니다. 감사!
TravisWhidden

1
@Alovchin 감사합니다. 답변을 업데이트했습니다. 차이점을 보여주기 위해 코드로이 ideone을 만들었습니다. ideone.com/EVKFp5
redent84

1
이 꽤 오래지만, 마지막 %d.TicksRoundUp필요? d.Ticks - (dt.Ticks % d.Ticks))반드시보다 작을 d.Ticks것이므로 답이 똑같아 야합니까?
Nate Diamond

1
지적했듯이 모듈러스는 CPU에서 나누기 작업이 필요합니다. 그러나 정수 나누기의 라우팅 속성을 사용하는 것이 더 우아하다는 데 동의합니다.
Alex

19

가장 가까운 시간 간격으로 반올림 해야하는 경우 (최대가 아님) 다음을 사용하는 것이 좋습니다.

    static DateTime RoundToNearestInterval(DateTime dt, TimeSpan d)
    {
        int f=0;
        double m = (double)(dt.Ticks % d.Ticks) / d.Ticks;
        if (m >= 0.5)
            f=1;            
        return new DateTime(((dt.Ticks/ d.Ticks)+f) * d.Ticks);
    }

이 답변은 올바르게 반올림되지 않습니다. (문제는 반올림 있습니다 ABT 때문에 아이러니하게도 다운 투표) : user1978424은 단지 아래의 가까운 간격 라운드에 정확하게 어떻게 쇼를 게시있다
stitty을

8
void Main()
{
    var date1 = new DateTime(2011, 8, 11, 16, 59, 00);
    date1.Round15().Dump();

    var date2 = new DateTime(2011, 8, 11, 17, 00, 02);
    date2.Round15().Dump();

    var date3 = new DateTime(2011, 8, 11, 17, 01, 23);
    date3.Round15().Dump();

    var date4 = new DateTime(2011, 8, 11, 17, 00, 00);
    date4.Round15().Dump();
}

public static class Extentions
{
    public static DateTime Round15(this DateTime value)
    {   
        var ticksIn15Mins = TimeSpan.FromMinutes(15).Ticks;

        return (value.Ticks % ticksIn15Mins == 0) ? value : new DateTime((value.Ticks / ticksIn15Mins + 1) * ticksIn15Mins);
    }
}

결과 :

8/11/2011 5:00:00 PM
8/11/2011 5:15:00 PM
8/11/2011 5:15:00 PM
8/11/2011 5:00:00 PM

3
2011-08-11 17:00:01에 잘립니다2011-08-11 17:00:00
JYelton

1
@JYelton : +1을 지적 해 주셔서 감사합니다. 그것을 수용하기 위해 코드를 변경했습니다.
블라드 베 즈덴

쉬운 확인을 위해 코드 Linqpad 형식을 제공하면 시간을 크게 절약 할 수 있습니다. 사용하기 매우 쉽습니다.
Adam Garner

6

휠을 재발 명하는 것을 싫어하기 때문에 아마도이 알고리즘을 따라 DateTime 값을 지정된 시간 단위 (Timespan)로 반올림합니다.

  • DateTime반올림 할 값을 전체 및 소수 TimeSpan단위를 나타내는 10 진수 부동 소수점 값으로 변환합니다 .
  • 를 사용하여 정수로 반올림하십시오 Math.Round().
  • 반올림 된 정수에 TimeSpan단위 의 틱 수를 곱하여 틱으로 다시 스케일합니다 .
  • DateTime반올림 된 틱 수에서 새 값을 인스턴스화 하여 호출자에게 리턴하십시오.

코드는 다음과 같습니다.

public static class DateTimeExtensions
{

    public static DateTime Round( this DateTime value , TimeSpan unit )
    {
        return Round( value , unit , default(MidpointRounding) ) ;
    }

    public static DateTime Round( this DateTime value , TimeSpan unit , MidpointRounding style )
    {
        if ( unit <= TimeSpan.Zero ) throw new ArgumentOutOfRangeException("unit" , "value must be positive") ;

        Decimal  units        = (decimal) value.Ticks / (decimal) unit.Ticks ;
        Decimal  roundedUnits = Math.Round( units , style ) ;
        long     roundedTicks = (long) roundedUnits * unit.Ticks ;
        DateTime instance     = new DateTime( roundedTicks ) ;

        return instance ;
    }

}

이것은 반올림에 대한 좋은 코드 가까운 DateTime , 그러나 나는 또한 라운드 할 수있는 능력 원하는 최대 의 배수를 unit . 에 전달 MidpointRounding.AwayFromZero하려면 Round원하는 효과를 가지고 있지 않습니다. 당신은 MidpointRounding논쟁 을 받아들임으로써 다른 생각을 가지고 있습니까?
HappyNomad

2

내 버전

DateTime newDateTimeObject = oldDateTimeObject.AddMinutes(15 - oldDateTimeObject.Minute % 15);

방법으로 다음과 같이 잠글 것입니다.

public static DateTime GetNextQuarterHour(DateTime oldDateTimeObject)
{
    return oldDateTimeObject.AddMinutes(15 - oldDateTimeObject.Minute % 15);
}

그리고 그렇게 불립니다

DateTime thisIsNow = DateTime.Now;
DateTime nextQuarterHour = GetNextQuarterHour(thisIsNow);

이것은 몇 초를 설명하지 않습니다
Alex Norcliffe

1

우아한?

dt.AddSeconds(900 - (x.Minute * 60 + x.Second) % 900)

1
더 정확한 버전은 다음과 같습니다. x.AddSeconds (900-(x.AddSeconds (-1) .Minute * 60 + x.AddSeconds (-1) .Second) % 900) .AddSeconds (-1). "체재"조건.
Olaf

1

주의 : 위의 공식은 올바르지 않습니다. 즉 다음과 같습니다.

DateTime RoundUp(DateTime dt, TimeSpan d)
{
    return new DateTime(((dt.Ticks + d.Ticks - 1) / d.Ticks) * d.Ticks);
}

다음과 같이 다시 작성해야합니다.

DateTime RoundUp(DateTime dt, TimeSpan d)
{
    return new DateTime(((dt.Ticks + d.Ticks/2) / d.Ticks) * d.Ticks);
}

1
동의하지 않습니다. 정수 나누기 / d.Ticks는 가장 가까운 15 분 간격으로 반올림 되므로 (이 "블록"이라고 함) 반 블록 만 추가해도 반올림이 보장되지는 않습니다. 4.25 개의 블록이있을 때 고려하십시오. 0.5 블록을 추가 한 다음 보유하고있는 정수 블록 수를 테스트하면 여전히 4 개만 남습니다. 전체 블록보다 적은 틱을 추가하는 것이 올바른 조치입니다. 반올림하기 전에 항상 다음 블록 범위로 이동하지만 정확한 블록 사이를 이동하지 못하게합니다. (IE, 4.0 블록에 전체 블록을 추가하면 4. 4.99는 4가 될 때 5.0이 5로 반올림됩니다.)
Brendan Moore

1

모듈로를 사용하고 불필요한 계산을 피하는 더 자세한 솔루션입니다.

public static class DateTimeExtensions
{
    public static DateTime RoundUp(this DateTime dt, TimeSpan ts)
    {
        return Round(dt, ts, true);
    }

    public static DateTime RoundDown(this DateTime dt, TimeSpan ts)
    {
        return Round(dt, ts, false);
    }

    private static DateTime Round(DateTime dt, TimeSpan ts, bool up)
    {
        var remainder = dt.Ticks % ts.Ticks;
        if (remainder == 0)
        {
            return dt;
        }

        long delta;
        if (up)
        {
            delta = ts.Ticks - remainder;
        }
        else
        {
            delta = -remainder;
        }

        return dt.AddTicks(delta);
    }
}

0

가장 가까운 1 분으로 반올림하는 간단한 솔루션입니다. DateTime의 TimeZone 및 Kind 정보를 유지합니다. 가장 가까운 5 분으로 반올림해야하는 경우 자신의 필요에 맞게 추가로 수정할 수 있습니다.

DateTime dbNowExact = DateTime.Now;
DateTime dbNowRound1 = (dbNowExact.Millisecond == 0 ? dbNowExact : dbNowExact.AddMilliseconds(1000 - dbNowExact.Millisecond));
DateTime dbNowRound2 = (dbNowRound1.Second == 0 ? dbNowRound1 : dbNowRound1.AddSeconds(60 - dbNowRound1.Second));
DateTime dbNow = dbNowRound2;

0

이 방법을 사용하면 지정된 날짜를 사용하여 이전에 datetime 객체에 지정된 세계화 및 날짜 시간 종류를 유지합니다.

const long LNG_OneMinuteInTicks = 600000000;
/// <summary>
/// Round the datetime to the nearest minute
/// </summary>
/// <param name = "dateTime"></param>
/// <param name = "numberMinutes">The number minute use to round the time to</param>
/// <returns></returns>        
public static DateTime Round(DateTime dateTime, int numberMinutes = 1)
{
    long roundedMinutesInTicks = LNG_OneMinuteInTicks * numberMinutes;
    long remainderTicks = dateTime.Ticks % roundedMinutesInTicks;
    if (remainderTicks < roundedMinutesInTicks / 2)
    {
        // round down
        return dateTime.AddTicks(-remainderTicks);
    }

    // round up
    return dateTime.AddTicks(roundedMinutesInTicks - remainderTicks);
}

.Net 피들 테스트

TimeSpan을 사용하여 반올림하려는 경우이를 사용할 수 있습니다.

/// <summary>
/// Round the datetime
/// </summary>
/// <example>Round(dt, TimeSpan.FromMinutes(5)); => round the time to the nearest 5 minutes.</example>
/// <param name = "dateTime"></param>
/// <param name = "roundBy">The time use to round the time to</param>
/// <returns></returns>        
public static DateTime Round(DateTime dateTime, TimeSpan roundBy)
{            
    long remainderTicks = dateTime.Ticks % roundBy.Ticks;
    if (remainderTicks < roundBy.Ticks / 2)
    {
        // round down
        return dateTime.AddTicks(-remainderTicks);
    }

    // round up
    return dateTime.AddTicks(roundBy.Ticks - remainderTicks);
}

타임스 팬 바이올린


가장 가까운 7 분으로 반올림하려는 경우 var d = new DateTime(2019, 04, 15, 9, 40, 0, 0);// // 9:42 여야하지만 이러한 방법 중 어느 것도 그렇게 작동하지 않습니까?
DotnetShadow

편집 @soulflyman 대답 같은 외모 올바른 결과를 얻을 것이다
DotnetShadow
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.