DateTime Round Up and Down - c#

Ive been looking for a proper rounding mechanism but nothing I find seems to be exactly what I need.
I need to round up and round down seperately and I also need to account for the the case when its already rounded.
I need the following rounding to happen
5:00 -> RoundDown() -> 5:00
5:04 -> RoundDown() -> 5:00
5:09 -> RoundDown() -> 5:00
5:10 -> RoundDown() -> 5:10
4:00 -> RoundUp() -> 4:00
4:50 -> RoundUp() -> 4:50
4:51 -> RoundUp() -> 5:00
4:56 -> RoundUp() -> 5:00
Basically I need it to RoundUp() or RoundDown() to the nearest 10 minutes explicitly but it should also leave time untouched if it already is in a multiple of 10 minutes. Also I'd like to truncate any seconds to that they have no effect on the rounding procedure
4:50:45 -> 4:50:00 -> RoundUp() -> 4:50
Does anyone have any handy code to accomplish this.
I found this code somewhere but it rounds 5:00 -> RoundUp() -> 5:10 rather than leaving it intact because its already a multiple of 10 and needs no rounding. Also Im not sure how seconds would effect it
public static DateTime RoundDateTime(this DateTime dt, int minutes, RoundingDirection direction)
{
TimeSpan t;
switch (direction)
{
case RoundingDirection.Up:
t = (dt.Subtract(DateTime.MinValue)).Add(new TimeSpan(0, minutes, 0)); break;
case RoundingDirection.Down:
t = (dt.Subtract(DateTime.MinValue)); break;
default:
t = (dt.Subtract(DateTime.MinValue)).Add(new TimeSpan(0, minutes / 2, 0)); break;
}
return DateTime.MinValue.Add(new TimeSpan(0,
(((int)t.TotalMinutes) / minutes) * minutes, 0));
}
Hope someone can edit that method to make it work for me. Thanks

This will let you round according to any interval given.
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));
}
}
To take care of truncating the seconds, I would simply subtract the seconds and milliseconds from the date-time before sending them into the rounding functions.

How about:
case RoundingDirection.Up:
t = dt.AddMinutes((60 - dt.Minute) % 10);
case RoundingDirection.Down:
t = dt.AddMinutes(-dt.Minute % 10);
Demo: http://ideone.com/AlB7Q

Here is a fast way to truncate (round down)
var now = DateTime.Now;
var nowTicks = now.Ticks;
//removing the nanoseconds, miliseconds, and seconds from the nowTicks
var lastMinute = new DateTime(nowTicks - (nowTicks % (1000*1000*10*60)));

This function will round up or down to the nearest interval (minutes).
private static DateTime NormalizeReadingInterval(DateTime originalTime, int interval)
{
if (originalTime.Minute % interval == 0) return originalTime;
var epochTime = new DateTime(1900, 1, 1);
var minutes = (originalTime - epochTime).TotalMinutes;
var numIntervals = minutes / interval;
var roundedNumIntervals = Math.Round(numIntervals, 0);
return epochTime.AddMinutes(roundedNumIntervals * interval);
}

Another approach avoiding arithmetic using type long.
Using integer division, where a & b are positive integers:
a/b // rounding down
(a+b-1)/b // rounding up
((2*a)+b)/(2*b) // rounding to the nearest (0.5 up)
To round up:
public static DateTime UpToNearestXmin( DateTime dt, int block )
{
int a = dt.Minute;
int b = block;
int mins = block * (( a + b - 1 ) / b );
return new DateTime( dt.Year, dt.Month, dt.Day, dt.Hour, 0, 0 ).AddMinutes( mins );
}
To round down or to nearest, change the mins calculation as appropriate.
The minutes are rounded. The seconds & milliseconds are zeroed which is expected behaviour.

Related

C# time bool always returning false

I have a bool that tells me if the current time is in between two other times it will go dark at 8 (20:00) in the evening and light at 7 in the morning.. I am not sure if I should be doing 7 or 07 I have tried booth but still getting false?
Can anyone tell me why this is always returning false? Not much else to say really just that it's always returning false when it is currently in between the two times currently.. GTM Timezone London, Thanks!
public static bool NightTime
{
get
{
TimeSpan span = LastMoodlightUpdate - DateTime.Now;
TimeSpan start = new TimeSpan(20, 0, 0); //Dark at 20:00
TimeSpan end = new TimeSpan(07, 0, 0); //Light at 07:00
TimeSpan now = DateTime.Now.TimeOfDay;
return ((now > start) && (now < end));
}
}
The problem here is that comparing two TimeSpan values is a simple numeric comparison.
As such, no time can both be larger than 20:00 and smaller than 07:00 in terms of values. We humans can deal with such incongruities, but the computer can't.
You need to consider what you want here:
|---------|..................................|-----------|
00 07 20 24
You want the times in dashes, basically you should use || instead of &&:
return (now > start) || (now < end);
Since the time of day cannot be negative, nor can it reach 24:00 or higher, this will give you what you want.
I don't get the purpose of all your code. Isn't it as simple as this?
public static bool NightTime
{
get
{
var hour = System.DateTime.Now.Hour;
return (hour <=7 || hour >= 20);
}
}

Semi-Monthly Date Calculation in C#

I am trying to do calculations on date using "Semi-Monthly". How many days in semi month? AddMonths (0.5) does not work :)
Console.WriteLine("SemiMonthly : " + date.AddMonths(0.5)); //This does not work
I guess that adding half of a month means according to this month, then you could do:
public static DateTime AddHalfMonth(DateTime dt, MidpointRounding rounding)
{
int daysInMonth = System.DateTime.DaysInMonth(dt.Year, dt.Month);
if(daysInMonth % 2 == 0)
return dt.AddDays(daysInMonth / 2);
else if(rounding == MidpointRounding.ToEven)
return dt.AddDays(daysInMonth / 2);
else
return dt.AddDays((daysInMonth + 1) / 2);
}
You can use it in this way:
DateTime inHalfMonth = AddHalfMonth(date, MidpointRounding.ToEven);
If you want, you can add half days of the current month by doing this:
DateTime a = new DateTime();
a.AddDays(DateTime.DaysInMonth(a.Year, a.Month)/2);
You're going to have to define what exactly "semi-monthly" means, and in doing so, you'll answer your own question.
For simplicity, I would suggest you just use the first and 15th of each month.
You will need to define your own convention for this - there is no canonical notion of a "half month".
Borrowed some concepts from Tim's answer but the additional logic allows for a schedule to never deviate from the half-month mark. I had noticed that if I ran the function recursively starting from Jan 16 the 24th item was ending up Dec 20-something. I've simplified the function and made it an extension.
public static class DateTimeExtensions
{
public static DateTime AddHalfMonth(this DateTime dt)
{
int daysInMonth = System.DateTime.DaysInMonth(dt.Year, dt.Month);
if (daysInMonth % 2 == 0 || dt.Day < daysInMonth / 2)
{
return dt.AddDays(daysInMonth / 2);
}
return dt.AddDays((daysInMonth + 1) / 2);
}
}

Best way too add a "Custom TimeSpan format Specifier" to support 10th's of minutes

I have this absurd situation where i need to support a Custom TimeSpan Format with a unit of Tenths of minutes with Timespan.TryParse.
i.e. hh:mm:t
where t denotes 10ths of minute (6 second intervals)
What would be the easiest way of adding this to the Custom Timespan format parsing specifies?
Is there some override facility that would make this easy?
Edit
var mask = "hmmt";
var value = "0011";
// 0 hours
// 01 minutes
// 1 tenths of minutes
TimeSpan.TryParseExact(value, mask, null, out time)
the mask is configurable by the user and i need the ability to add some sort of custom specifier like "t" to denote tenths of minutes
the user in essence adds this mask, as the value comes from various pabx phone systems that output duration's in many weird and wonderful ways. one of those ways is the use of 10ths of minutes
if I understand correctly, then for example .3 at the end means 3/10 of a minute, making it 18 seconds. If so, this is the way, in case you have time like "hh:mm:t" as you wrote:
public static class TimeSpanExtension
{
public static TimeSpan TryParseTenth(string timeSpanString)
{
//change following line to accomodate date format if needed
timeSpanString += "0";
TimeSpan ts = new TimeSpan();
if (TimeSpan.TryParse(timeSpanString, out ts))
{
// recalculate from tenth of minute into seconds
float realSeconds = ts.Seconds * 60 / 100;
//final operation to correct
return ts.Subtract(new TimeSpan(0, 0, ts.Seconds - (int)realSeconds));
}
else
return TimeSpan.Zero;
}
}
usage:
string time = "06:55:3";
var timeSpan = TimeSpanExtension.TryParseTenth(time);
resulting in 6h55m18s as I wrote at the top

Bad calculations after Python to C# port

I'm working on a time-decay algorithm for a post system based on Reddit's model here:
http://amix.dk/blog/post/19588
My working port is here:
public class Calculation
{
protected DateTime Epoch = new DateTime(1970, 1, 1);
protected long EpochSeconds(DateTime dt)
{
var ts = dt.Subtract(Convert.ToDateTime("1/1/1970 8:00:00 AM"));
return ((((((ts.Days * 24) + ts.Hours) * 60) + ts.Minutes) * 60) + ts.Seconds);
}
protected int Score(int upVotes, int downVotes)
{
return upVotes - downVotes;
}
public double HotScore(int upVotes, int downVotes, DateTime date)
{
var s = Score(upVotes, downVotes);
var order = Math.Log(Math.Max(Math.Abs(s), 1), 10);
var sign = Math.Sign(s);
var seconds = EpochSeconds(date) - 1134028003;
return Math.Round(order + sign * ((double)seconds / 45000), 7);
}
}
Based on the model output from the link provided, I should see gradual decay at 0-13 hours, and sharp decay after that.
What I'm seeing is very homogeneous decay, and scores much higher than the output from the original code (original code: 3480-3471).
Here is how I'm testing:
Calculation c = new Calculation();
double now = c.HotScore(100, 2, DateTime.Now);
double fivehoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-5));
double tenhoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-10));
double elevenhoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-11));
double twelvehoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-12));
double thirteenhoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-13));
double fiftyhoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-50));
double onehundredhoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-100));
Console.WriteLine(now.ToString());
Console.WriteLine(fivehoursago.ToString());
Console.WriteLine(tenhoursago.ToString());
Console.WriteLine(elevenhoursago.ToString());
Console.WriteLine(twelvehoursago.ToString());
Console.WriteLine(thirteenhoursago.ToString());
Console.WriteLine(fiftyhoursago.ToString());
Console.WriteLine(onehundredhoursago.ToString());
Console.ReadLine();
Output values:
now: 4675.2993816
five hours: 4674.8993816
ten hours: 4674.4993816
eleven hours: 4674.4193816
twelve hours: 4674.3393816
thirteen hours: 4674.2593816
fifty hours: 4671.2993816
one-hundred hours: 4667.2993816
Clearly it's SORT of working right, but something is off. It could be related to the lack of true *nix Epoch support, or the lack of analogous microseconds calculation, but something isn't quite right.
Possible reference resources:
http://blogs.msdn.com/b/brada/archive/2004/03/20/93332.aspx
http://codeclimber.net.nz/archive/2007/07/10/convert-a-unix-timestamp-to-a-.net-datetime.aspx
Your primary problem is that the hot algorithm is time dependent. Your calculating the hot score at DateTime.Now, whereas the article was written on 23. Nov 2010 (look at the bottom of the article).
With some trial and error, it seems the data was calculated at approximately 2010-11-23 07:35. Try using that value rather than DateTime.Now, and you should get about the same results as the data in the graph shown.
Mind you, you could make the following improvements to your code:
public class Calculation
{
private static readonly DateTime Epoch = new DateTime(1970, 1, 1);
private double EpochSeconds(DateTime dt)
{
return (dt - Epoch).TotalSeconds;
}
private int Score(int upVotes, int downVotes)
{
return upVotes - downVotes;
}
public double HotScore(int upVotes, int downVotes, DateTime date)
{
int s = Score(upVotes, downVotes);
double order = Math.Log(Math.Max(Math.Abs(s), 1), 10);
int sign = Math.Sign(s);
double seconds = EpochSeconds(date) - 1134028003;
return Math.Round(order + sign * seconds / 45000, 7);
}
}
My results:
3479.0956039
3478.6956039
3478.2956039
3478.2156039
3478.1356039
3478.0556039
3475.0956039
3471.0956039
Changes:
Used the declared Epoch rather than a convert of 1970-01-01 08:00:00 (I think 08:00 is a mistake).
You can subtract two dates using a - b; which is the same as a.Subtract(b) but more succinct and it mirrors the original Python code.
A timespan does give you microsecond precision (Ticks are the smallest unit and equal 100 nanoseconds).
Also, TotalSeconds gives you the total number of seconds within a time span; no need to recalculate that. The fractional part even gives you your microsecond precision.
By returning double from EpochSeconds, you keep this precision.
Made the data types explicit rather than var to clearly indicate what variable is what (they match the method signatures, so no implicit upcasting).
Changed unneeded protected to private and made the Epoch a constant.

Is there a better way in C# to round a DateTime to the nearest 5 seconds?

I want to round a DateTime to the nearest 5 seconds. This is the way I'm currently doing it but I was wondering if there was a better or more concise way?
DateTime now = DateTime.Now;
int second = 0;
// round to nearest 5 second mark
if (now.Second % 5 > 2.5)
{
// round up
second = now.Second + (5 - (now.Second % 5));
}
else
{
// round down
second = now.Second - (now.Second % 5);
}
DateTime rounded = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, second);
Please note that I've found these two previous questions, however they truncate rather than round the time.
(Sorry for the resurrection; I recognize it's an old and answered question - just adding some extra code for Google's sake.)
I started with JayMcClellan's answer, but then I wanted it to be more generic, rounding to arbitrary intervals (not just 5 seconds). So I ended up leaving Jay's method for one that uses Math.Round on ticks and put it into an extension method that can take arbitrary intervals and also offers the option of changing the rounding logic (banker's rounding versus away-from-zero). I'm posting here in case this is helpful to someone else as well:
public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval, MidpointRounding roundingType) {
return new TimeSpan(
Convert.ToInt64(Math.Round(
time.Ticks / (decimal)roundingInterval.Ticks,
roundingType
)) * roundingInterval.Ticks
);
}
public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval) {
return Round(time, roundingInterval, MidpointRounding.ToEven);
}
public static DateTime Round(this DateTime datetime, TimeSpan roundingInterval) {
return new DateTime((datetime - DateTime.MinValue).Round(roundingInterval).Ticks);
}
It won't win any awards for bare efficiency, but I find it easy to read and intuitive to use. Example usage:
new DateTime(2010, 11, 4, 10, 28, 27).Round(TimeSpan.FromMinutes(1)); // rounds to 2010.11.04 10:28:00
new DateTime(2010, 11, 4, 13, 28, 27).Round(TimeSpan.FromDays(1)); // rounds to 2010.11.05 00:00
new TimeSpan(0, 2, 26).Round(TimeSpan.FromSeconds(5)); // rounds to 00:02:25
new TimeSpan(3, 34, 0).Round(TimeSpan.FromMinutes(37); // rounds to 03:42:00...for all your round-to-37-minute needs
The Ticks count of a DateTime represents 100-nanosecond intervals, so you can round to the nearest 5 seconds by rounding to the nearest 50000000-tick interval like this:
DateTime now = DateTime.Now;
DateTime rounded = new DateTime(((now.Ticks + 25000000) / 50000000) * 50000000);
That's more concise, but not necessarily better. It depends on whether you prefer brevity and speed over code clarity. Yours is arguably easier to understand.
Like you mentioned, it's fairly easy to truncate. So, just add 2.5 seconds, then truncate down.
I can't think of a better way, although I would probably factor out the round method:
static int Round(int n, int r)
{
if ((n % r) <= r / 2)
{
return n - (n % r);
}
return n + (r - (n % r));
}
Also, % returns an int, so comparing it to 2.5 strikes me as a little odd, even though it is correct. I'd use >= 3.
How about this (blending a few answers together)? I think it conveys the meaning well and should handle the edge cases (rounding to the next minute) elegantly due to AddSeconds.
// truncate to multiple of 5
int second = 5 * (int) (now.Second / 5);
DateTime dt = new DateTime(..., second);
// round-up if necessary
if (now.Second % 5 > 2.5)
{
dt = dt.AddSeconds(5);
}
The Ticks approach as shown by Jay is more concise, but may be a bit less readable. If you use that approach, at least reference TimeSpan.TicksPerSecond.
I couldn't recognize the difference between C# and a bar of soap (well, I couldn't when I originally wrote this answer, things have changed quite a bit in the years since) but, if you're looking for a more concise solution, I would just put the whole thing in a function - there's little that will be more concise in your code than a simple call to said function:
DateTime rounded = roundTo5Secs (DateTime.Now);
Then you can put whatever you want in the function and just document how it works, such as (assuming these are all integer operations):
secBase = now.Second / 5;
secExtra = now.Second % 5;
if (secExtra > 2) {
return new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute,
secBase + 5);
}
return new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute,
secBase);
You may also need some extra checks if secBase goes to 60 (unless C# DateTime objects are smart enough to bump up the minute (and hour if minute goes to 60, and so on).
Technically, you can never correctly round to an odd interval given only seconds.
2, 4, 6, 8, 10 <-- are no problem
If you are 'distributing' times in intervals and if the jitter is low, truncation is a lot
more tractable.
If you can pass milliseconds and round at a 500mS mark, you will be able to to odd
seconds and also slash the effect of jitter way down or eliminate it entirely.
Most simple and accurate one-liner:
private static DateTime QuantizeToEachNthSecond(DateTime dateTime, int nthSecond = 5)
{
return dateTime.AddTicks(-(dateTime.Ticks % (nthSecond * TimeSpan.TicksPerSecond)));
}
so, if you like each 5th second, if will be truncated to e.g. "10:12:02" -> "10:12:00", "10:12:08" -> "10:12:05" and so on.

Categories

Resources