using System;
using System.Collections.Generic;
namespace GSF
{
///
/// Responsible for rounding measurements to their nearest time bucket.
/// For example. a 30 sample per second value of 5.1666766 would round to 5.1666667
///
public class DateTimeRounding
{
private readonly int m_ticksPerInterval;
private readonly int m_ticksPerHalfInterval;
private readonly int[] m_distribution;
private DateTimeRounding(int samplesPerSecond)
{
m_distribution = new int[samplesPerSecond + 1];
double ticksPerInterval = (TimeSpan.TicksPerSecond / (double)samplesPerSecond);
for (int i = 0; i < samplesPerSecond; i++)
m_distribution[i] = (int)(Math.Round(i * ticksPerInterval));
m_distribution[samplesPerSecond] = (int)TimeSpan.TicksPerSecond;
m_ticksPerInterval = (int)(ticksPerInterval);
m_ticksPerHalfInterval = m_ticksPerInterval / 2;
}
public DateTime Round(DateTime sourceTime)
{
long ticks = sourceTime.Ticks;
//Time rounded down to nearest second
long ticksTruncatedToSeconds = ticks - ticks % TimeSpan.TicksPerSecond;
//Compute the frame by taking the ticks after a second, adding 1/2 the interval value, then integer rounding
int frame = (int)(ticks - ticksTruncatedToSeconds + m_ticksPerHalfInterval) / m_ticksPerInterval;
return new DateTime(ticksTruncatedToSeconds + m_distribution[frame]);
}
public long Round(long sourceTime)
{
long baseTicks = sourceTime - sourceTime % TimeSpan.TicksPerSecond;
int frame = (int)(sourceTime - baseTicks + m_ticksPerHalfInterval) / m_ticksPerInterval;
return baseTicks + m_distribution[frame];
}
private static Dictionary s_cache = new Dictionary();
public static DateTimeRounding Create(int samplesPerSecond)
{
lock (s_cache)
{
DateTimeRounding rv;
if (!s_cache.TryGetValue(samplesPerSecond, out rv))
{
rv = new DateTimeRounding(samplesPerSecond);
s_cache.Add(samplesPerSecond, rv);
}
return rv;
}
}
}
}