I have a simple problem that I'm having more trouble with than I should.
It's pretty straight forward: I have a time the process started _startTime, and the current time. I also have the total number of records I need to process _records, and the current record number _current.
How can I get a simple linear prediction of the end time? I've ended up in a rabbit hole of differencing ticks and stuff, but I imagine there's some simple DateTime or TimeSpan trickery I could be using,
TimeSpan delta = DateTime.Now - _startTime;
float progress = (float)_records / (float)_current;
hmm really sounds much simpler than it is probably
TimeSpan timeTakenSoFar = DateTime.Now - _startTime;
double percentageDoneSoFar = (double)_current / (double)_records;
TimeSpan timeTakingInTotal = timeTakenSoFar / percentageDoneSoFar;
DateTime finishTime = _startTime.Add(timeTakingInTotal);
This should work.
Update:
According to MSDN you cannot divide TimeSpan, but in that case you can use ticks: (I don't have a C# compiler at hand to check if it's 100% syntactically correct)
TimeSpan timeTakenSoFar = DateTime.Now - _startTime;
double percentageDoneSoFar = (double)_current / (double)_records;
double timeTakingInTotalInTicks = timeTakenSoFar.Ticks / percentageDoneSoFar;
DateTime finishTime = _startTime.AddTicks((long)timeTakingInTotal);
Take a sample every few cycles, and determine how long they took from start time.
now.subtract(_startTtime)
Divide that by the _current pointer, and get the time per cycle.
Multiple the time/cycle with ((_records - _current) <- cycles you have left to do)
The higher your "sample" resolution, the more accurate your result... but it is still just a prediction.
var restRecords = _records - _current;
var averageTimeForRecord = delta.TotalMilliseconds / _current;
var restTime = restRecords * averageTimeForRecord;
var finishDate = DateTime.Now.AddMilliseconds(restTime);
Maybe that is what you need?
Related
I feel like this is something really simple, but my Google Fu is letting me down as I keep finding difference calculations.
I have a time (e.g. 1800 hours) stored in a DateTime object. The date is null and immaterial. All I want to know is how many milliseconds until the NEXT occurrence of that time.
So, if I run the calculation at 0600 - it will return 12 hours (in ms). At 1750, it will return ten minutes (in ms) and at 1900 it will return 24 hours (in ms).
All the things I can find show me how to calculate differences, which doesn't work once you're past the time.
Here is what I tried, but fails once you're past the time and gives negative values:
DateTime nowTime = DateTime.Now;
TimeSpan difference = _shutdownTime.TimeOfDay - nowTime.TimeOfDay;
double result = difference.TotalMilliseconds;
You're already doing everything you should, except for one thing: handling negative results.
If the result is negative, it means the time you want to calculate the duration until has already passed, and then you want it to mean "tomorrow" instead, and get a positive value.
In this case, simply add 24 hours:
DateTime nowTime = DateTime.Now;
TimeSpan difference = _shutdownTime.TimeOfDay - nowTime.TimeOfDay;
double result = difference.TotalMilliseconds;
if (result < 0)
result += TimeSpan.FromHours(24).TotalMilliseconds;
The next thing to consider is this: If the time you want to calculate the duration until is 19:00 hours, and the current time is exactly 19:00 hours, do you want it to return 0 (zero) or 24 hours worth of time? Meaning, do you really want the next such occurrence?
If so, then change the above if-statement to use <=:
DateTime nowTime = DateTime.Now;
TimeSpan difference = _shutdownTime.TimeOfDay - nowTime.TimeOfDay;
double result = difference.TotalMilliseconds;
if (result <= 0)
result += TimeSpan.FromHours(24).TotalMilliseconds;
However, note that this will be prone to the usual problems with floating point values. If the current time is 18:59:59.9999999, do you still want it to return the current time (a minuscule portion of time) until 19:00 today, or do you want it to flip to tomorrow? If so, change the comparison to be slightly different:
DateTime nowTime = DateTime.Now;
TimeSpan difference = _shutdownTime.TimeOfDay - nowTime.TimeOfDay;
double result = difference.TotalMilliseconds;
if (result <= -0.0001)
result += TimeSpan.FromHours(24).TotalMilliseconds;
where -0.0001 is a value that corresponds to "the range of inaccuracy you're prepared to accept being tomorrow instead of today in terms of milliseconds".
When doing calculations like this it is important to take possible DST changes under consideration so that your results remain correct.
Suppose your operational parameters are:
var shutdownTime = TimeSpan.FromHours(18);
// just to illustrate, in Europe there is a DST change on 2013-10-27
// normally you 'd just use DateTime.Now here
var now = new DateTime(2013, 10, 26, 20, 00, 00);
// do your calculations in local time
var nextShutdown = now.Date + shutdownTime;
if (nextShutdown < now) {
nextShutdown = nextShutdown.AddDays(1);
}
// when you want to calculate time spans in absolute time
// (vs. wall clock time) always convert to UTC first
var remaining = nextShutdown.ToUniversalTime() - now.ToUniversalTime();
Console.WriteLine(remaining);
The answer to your question would now be remaining.TotalMilliseconds.
I have the folowing code,
now the question is what is the best way to preform this:
also take in notice the "minAdd" can pass 60, meaning 90min add (an hour an half etc)
thanks,
int minAdd = Convert.ToInt16(txtMinAdd.text);
DateTime now = DateTime.Now;
DateTime nextEvent = DateTime.Now.AddMinutes(minAdd);
TimeSpan diff = now - nextEvent;
if (diff > minAdd) -------------- PROBLEM HERE
{
//act here
}
EDIT: As noted by Reed, the code you've shown is pretty pointless. I assume you actually want to get nextEvent from somewhere else.
I suspect you just want:
if (diff.TotalMinutes > minAdd)
{
}
Or you could use:
TimeSpan minTimeSpan = TimeSpan.FromMinutes(Convert.ToInt16(txtMinAdd.text));
...
if (diff > minTimeSpan)
{
}
Since diff is based on nextEvent, which is based exactly on minAdd, there is no reason for this check - it will never be true.
Also, in your code, diff will always be negative if minAdd is positive, as you're subtracting a future event (nextEvent) from DateTime.Now.
If you are trying to schedule an event to occur at a point in time, you may want to consider using a Timer, and scheduling the timer to occur at some point in time based on the event time:
DateTime nextEvent = DateTime.Now.AddMinutes(minAdd);
TimeSpan diff = nextEvent - DateTime.Now;
// Schedule a timer to occur here
someTimer.Interval = diff.TotalMilliseconds; // Timer intervals are typically in ms
Is there any kind of mathematical way to cut DateTime down to a exact Hour, Day or so? Similiar to round of a decimal to int.
Period.Day
If the original value was 2011-01-01 13:00:00, it ends up in 2011-01-01 00:00:00
if Period.Hour
If the original value was 2011-03-11 13:32:00, it ends up in 2011-03-11 13:00:00
I think about something like below. This are of course works fine, but the range-array are iterated through anyway, later. Better if I was possible to calculate directly on that iteration, instead of it's own. But someType can't be put into that iteration (it depends on someType).
if (someType == Period.Day)
range.ForEach(d => d.time = new DateTime(d.time.Year, d.time.Month, d.time.Day,0,0,0));
if (someType == Period.Hour)
range.ForEach(d => d.time = new DateTime(d.time.Year, d.time.Month, d.time.Day, d.time.Hour, 0, 0));
Rounding down to a day is equivalent to time.Date, rounding to nearest (up on midpoint) is simply ( time + 12hours ).Date.
For rounding down to a full hour I can't think of code that's nicer to read than yours. For rounding up to the nearest hour you can apply your code to time + 30mins.
There is probably a faster method for rounding to the nearest hour:
const Int64 HourInTicks=...;
Int64 timeInTicks=time.Ticks;
Int64 trucatedToHour=timeInTicks-timeInTicks%HourInTicks;
But I'd avoid that, unless you really need the performance, which is unlikely.
(My round to nearest might have issues on days where the local time offset changes if you're using local time)
To round down to day you can use the DateTime.Date Property.
To round down to hour, I'm afraid you'll have to either use what you did in your example or something like:
d.Date.AddHours(d.Hour)
I'll do the following:
private static readonly DateTime Epoch = new DateTime(1970, 1, 1);
public static DateTime Round(this DateTime d, Period p)
{
var ts = d - Epoch;
if (p == Period.Hour)
{
var hours = (long)ts.TotalHours;
return Epoch.AddHours(hours);
}
else if (p == Period.Days)
{
var days = (long)ts.TotalDays;
return Epoch.AddDays(days);
}
// ...
}
I believe the following C# code will round a DateTime value to nearest minute, and I think it will be easy to generalize it to round to other units.
//round to nearest minute; add 30 seconds for rounding to nearest minute
effectiveDateTime = effectiveDateTime.AddSeconds(30);
TimeSpan timeComponent = effectiveDateTime.TimeOfDay;
effectiveDateTime = effectiveDateTime.Date;
effectiveDateTime = effectiveDateTime.AddHours(timeComponent.Hours).
AddMinutes(timeComponent.Minutes);
Not sure if this approach is effective, but looks quite nice using string format (in this case cutting down to hours):
var date = DateTime.UtcNow;
var cutDownDate = Convert.ToDateTime(date.ToString("yyyy-MM-dd hh"));
Answer Is there a better way in C# to round a DateTime to the nearest 5 seconds? contains an excellent generic DateTime rounding approach.
Edit:
This answer was before the updated question title and is an algorithm for rounding to nearest not rounding down.
Best method for day:
DateTime now = DateTime.Now;
DateTime roundDay = now.Date;
I have some DateTime variable, and I want to use System.Threading.Timer to wait until this time arrive. If time is in the past I want the timer to tick immediately.
The problem is TimeSpan.TotalMilliseconds is double and timer due time biggest type islong.
I've tried to max the due-time to long.MaxValue using this code:
DateTime someUtcTime;
// Max due time to long.MaxValue
double doubleDueTime = Math.Min(
(double)long.MaxValue,
someUtcTime.Subtract(DateTime.UtcNow).TotalMilliseconds);
// Avoid negative numbers
long dueTime = Math.Max(0L, (long)doubleDueTime);
_timer.Change(dueTime, System.Threading.Timeout.Infinite);
but it turns out that casting long.MaxValue to double and back to long result a negative number (in unchecked code of curse). plz send me teh codez.
Edit: apparently, no matter which of Timer.Change overload you use, they are all limited to 4294967294 (UInt32.MaxValue - 1) milliseconds.
Solution:
cover both extreme cases (someUtcTime = DateTime.MaxValue; UtcNow = DateTime.MinValue; and vice versa).
const uint MAX_SUPPORTED_TIMEOUT = uint.MaxValue - 1; //0xfffffffe
DateTime someUtcTime;
double doubleDueTime = (someUtcTime - DateTime.UtcNow).TotalMilliseconds;
// Avoid negative numbers
doubleDueTime = Math.Max(0d, doubleDueTime);
// Max due time to uint.MaxValue - 1
uint dueTime = (uint)Math.Min(MAX_SUPPORTED_TIMEOUT, doubleDueTime);
Since (DateTime.MaxValue - DateTime.MinValue).TotalMilliseconds is 315537897600000 and long.MaxValue is 9223372036854775807 (long can represent a value 4 orders of magnitude larger than the largest possible number of milliseconds between any two DateTime values), you can never have a time too far in the future.
This will suffice:
DateTime someUtcTime;
// Max due time to long.MaxValue
double doubleDueTime = (someUtcTime - DateTime.UtcNow).TotalMilliseconds;
// Avoid negative numbers
long dueTime = Math.Max(0L, (long)doubleDueTime);
_timer.Change(dueTime, System.Threading.Timeout.Infinite);
Maybe just work with the Ticks property (Ticks are Long) and multiply by TimeSpan.TicksPerMillisecond (constant, 10,000 ticks per millisecond).
Timespan timespan = someUtcTime.Subtract(DateTime.UtcNow);
long time = timespan.TotalMilliseconds <= long.MaxValue ? (long)timespan.TotalMilliseconds : -1;
if (time == -1) {
sorry.nocando();
}
else {
just.doit();
}
BTW: with a long millisecond you can have a timespan of 292471208 years, I don't think your code will be used by then. There is possibility that the sun will already have expanded past mars and earth is no more :D
Timers are designed to wait for intervals (typically shorter than 24 hours).
What is your intended use? A scheduler would typically have a list of events/tasks, and periodically every minute (maybe every second) check to see if any tasks need to be fired.
Perhaps something like Quartz.Net might be appropriate?
I am trying to write a function that will convert a DateTime.Now instance to the number of seconds it represents so that I can compare that to another DateTime instance. Here is what I currently have:
public static int convertDateTimeToSeconds(DateTime dateTimeToConvert)
{
int secsInAMin = 60;
int secsInAnHour = 60 * secsInAMin;
int secsInADay = 24 * secsInAnHour;
double secsInAYear = (int)365.25 * secsInADay;
int totalSeconds = (int)(dateTimeToConvert.Year * secsInAYear) +
(dateTimeToConvert.DayOfYear * secsInADay) +
(dateTimeToConvert.Hour * secsInAnHour) +
(dateTimeToConvert.Minute * secsInAMin) +
dateTimeToConvert.Second;
return totalSeconds;
}
I realize that I am truncating the calculation for seconds in a year, but I don't need my calculation to be precise. I'm really looking to know if the method that I am using to calculate seconds is correct.
Does anyone have anything that could better compute seconds given from a DateTime object?
Also, Should the return type be int64 if I am coding in C# if I am going to calculate all the seconds since 0 AD?
The DateTime type supports comparison operators:
if (dateTimeA > dateTimeB)
{
...
This also works for DateTime values returned by DateTime.AddSeconds:
if (dateTimeA.AddSeconds(42) > dateTimeB)
{
...
If you really want the number of seconds that elapsed since 01/01/0001 00:00:00, you can calculate the difference between the two DateTime values. The resulting TimeSpan value has a TotalSeconds property:
double result = DateTime.Now.Subtract(DateTime.MinValue).TotalSeconds;
It really doesn't make sense to convert a DateTime object to seconds. Seconds only make sense if you are dealing with a length of time (TimeSpan). Should you want to compare two dates to get the number of seconds between them:
TimeSpan diff = DateTime.Now - PreviousDateTime;
double seconds = diff.TotalSeconds;
If the purpose is finding the number of seconds between two dates, you'd be much better off using the TimeSpan object.
TimeSpan span = date2 - date1;
double seconds = span.TotalSeconds;
See suggestion from thread below:
How do I convert ticks to minutes?
TimeSpan.FromTicks(DateTime.Now.Ticks).TotalSeconds;
Assuming you really need to get at the seconds for the datetime object, you could directly get the "Ticks" property from it. These aren't in seconds but you can easily divide by the proper factor to convert the Ticks to seconds.
See: http://msdn.microsoft.com/en-us/library/system.datetime.ticks.aspx
So, something like:
DateTime.Now.Ticks/TimeSpan.TicksPerSecond
If you want to compare 2 DateTime object, why just not use the provided operators?
http://msdn.microsoft.com/en-us/library/aa326723%28v=VS.71%29.aspx
DateTime a, b;
if (a > b) //a is after b
I would use the TimeSpan class to get the exact difference between two DateTime instances. Here is an example:
DateTime dt1 = DateTime.Now;
DateTime dt2 = new DateTime(2003,4,15);
TimeSpan ts = dt1.Subtract(dt2);
Once the TimeSpan value (ts, in the code snippet above) is available, you can examine its values to correctly convert the TimeSpan to a given number of seconds.
Using a TimeSpan to get the elapsed time between two DateTimes is probably the best way to go but if you really want to get the number of seconds for a given DateTime you could do something like the following:
DateTime dateTimeToConvert = DateTime.Now;
TimeSpan tsElapsed = dateTimeToConvert - DateTime.MinValue;
return tsElapsed.TotalSeconds;
Note that tsElapsed.TotalSeconds is a Double, not an Int.
Do note that the goal is to get the number of seconds since DateTime.MinVal (the first day of the calendar). I say this, because I see all of these answers for "you do time comparisons like this... add in the object, multiply by that object and do cross-calculus on them, divide by the quotient of the summed result, and Boom! not what you asked."
There's a really simple answer here. Ticks are 100-nanosecond increments. DateTime object.Ticks is the number of ticks that have occurred since 1/1/0001. Ie, year zero. There are 10 million nanoseconds in a second. so...
public static long convertDateTimeToSeconds(DateTime dateTimeToConvert) {
// According to Wikipedia, there are 10,000,000 ticks in a second, and Now.Ticks is the span since 1/1/0001.
long NumSeconds= dateTimeToConvert.Ticks / 10000000;
return NumSeconds;
}