Time Math Guidance needed - c#

I have a DateTime object that is 10:00 AM
This time represents what time of day a report should be run.
I want to calculate the amount of time remaining from NOW until 10:00 AM
part of my confusion is NOW might be after 10:am or BEFORE 10am,
I keep playing around with TimeSpan, but my results are not quite right... I am sure this is simple, but it is one of those things I have been working of for a few hours and I need a push in the right direction...
I want the timespan object timeTillRun to be correct...here is what I have tried:
{
DateTime scheduledRun = DateTime.Today.AddHours(_timeToStart);//_timeToStart = 10
TimeSpan timeTillRun = DateTime.Now - scheduledRun;
}

This will work... but you need to reverse the order of subtraction:
TimeSpan timeTillRun = scheduledRun - DateTime.Now;
Note that if it's currently after 10AM, timeTillRun will be negative. You will presumably also need to check if the current time is on or after 10AM, then add 10 hours and one day to DateTime.Today to obtain the next run time. Alternatively, you could test if timeTillRun is negative; if so, just add one day to it (timeTillRun += new TimeSpan(1, 0, 0, 0)).

Try this
DateTime timeToStart = DateTime.Today.AddHours(10);
TimeSpan timeTillRun;
// Checking to see if current time is passed schedule run, if it is then we add a day (this is assuming this is run daily, if days are skipped like weekends for example then this would need some tweaking)
if (DateTime.Now > timeToStart)
timeTillRun = DateTime.Now.AddDays(1.0) - timeToStart;
else
timeTillRun = DateTime.Today - timeToStart;
double totalHoursRemaining = timeTillRun.TotalHours; // get total hours remaining
string prettyRemaining = String.Format("{0} day and {1} hours", timeTillRun.Days, timeTillRun.Hours); // can do some outputting here

Related

Problem with time intervals with given start time and end time c#

I am trying to develop a simple app for my upskill for c#, however I am stuck and new to the functionality of time with c#,
what I need:
I have a 3 text boxes that will contain start time, end time and time interval.
say user entered 7:00 AM , 12:00 PM , and 60 minutes it will store it inside a datatable and add it inside a listbox.
7:00 AM
8:00 AM
9:00 AM
10:00 AM
11:00 AM
12:00 AM
current approach:
I think I need to use the DateTime.AddMinutes(interval) but how am I going to arrive to the logic of it will stop if it reaches the end time? using the DateTime method? I am really confused on what to use, I saw TimeRange, TimeSpan etc.
You can use TimeSpan and DateTime together (to calculate and print respectively)
TimeSpan start = DateTime.Parse("7:00 AM").TimeOfDay;
TimeSpan end = DateTime.Parse("12:00 PM").TimeOfDay;
TimeSpan interval = new TimeSpan(0, 60, 0);
// If Start is bigger than end, Add a day to the end.
if (start > end)
end = end.Add(new TimeSpan(1, 0, 0, 0));
while (true)
{
Console.WriteLine((new DateTime() + start).ToString("hh:mm tt"));
start = start.Add(interval);
if (start > end)
break;
}
Output looks like this,
07:00 AM
08:00 AM
09:00 AM
10:00 AM
11:00 AM
12:00 PM
MS Documentation on TimeSpan
You can use TimeSpan with boolean logical operator to test if the currentTime is less than your endTime.
Below is example code.
TimeSpan startTime;
int interval;
TimeSpan tInterval = new TimeSpan(interval, 0, 0);
TimeSpan endTime;
TimeSpan currentTime = startTime;
while( (currentTime = startTime + tInterval) <= endTime)
{
// add currentTime to list box
}
This should take care of the issue with the End Times being "earlier" than the Start Time:
private static void TestTimeSpan()
{
int minutes = 60;
var interval = new TimeSpan(0,minutes,0);
TimeSpan start = DateTime.Parse("7:00 PM").TimeOfDay;
TimeSpan end = DateTime.Parse("1:00 AM").TimeOfDay;
//End of input data--start of solution
var diffSpan = end - start;
var diffMinutes = diffSpan.TotalMinutes > 0 ? diffSpan.TotalMinutes : diffSpan.TotalMinutes + (60 * 24);
var myTimeList = new List<TimeSpan>();
for(int i = 0; i < diffMinutes + minutes; i += minutes)
{
myTimeList.Add(start);
start = start.Add(interval);
}
myTimeList.ForEach(x => Console.WriteLine((new DateTime() + x).ToString("hh:mm tt")));
}
EDIT
Creating a sequence of Time values based in two input times and an interval is straight forward until the "start time" is earlier than the "end time", because just checking to see if the "end time" is greater than the start time fails your algorithm immediately.
This code utilizes the fact that there are only 24 hours in the day. Since the interval value is given in minutes, we can use that to section those minutes into "steps" of time. This code proceeds to step through each interval in time and capture the time at that step and save that in a List of TimeSpan (the captured value could easily be of type string--formatted as desired).
The trick here is when the "end time" is earlier than the "start time" we get a negative TimeSpan which is then used to calculate the steps to the "end time" on the next day. This is where the (60 * 24) [60 minutes x 24 hrs] part comes in to create the correct "diffMinutes" using a ternary operator.
After that the code simple iterates over the List "myTimeList" to write the formatted TimeSpan to the console. However, this 'List' is just a portable collection that can be sent anywhere in you code to do anything needed.
There are lots of other solutions, this one just seems straightforward, to me.

Get undertime and overtime automatically

Could you please help me? I'm trying to create an attendance system wherein the undertime and overtime will be automatically computed, but there's an error. For example the employee's scheduled out is 11 PM, and their overtime 12 AM which is 1 hour, but the output is 23. Can anyone help me how to compute elapsed time?
string datetime = "12:00 AM"; // this is the time you are going to time out
string OUT = "11:00 PM"; // this is your scheduled out
DateTime d1 = Convert.ToDateTime(datetime);
DateTime d2 = Convert.ToDateTime(OUT);
TimeSpan ts = d2.Subtract(d1);
MessageBox.Show(ts.TotalHours.ToString()); // output is 23 which is wrong, it must be 1 hour overtime only
IMO, in order to fix your problem, you have to work with the actual datetime objects.
Always record actual system datetime without manipulating parts of it.
For your case you should have the fields for record scheduled_work_start, scheduled_work_finished and actual_work_finished
for an instance, say one of the employees starts work at 10-01-2019 14:00:00 (2 PM) and finishes her time at 10-01-2019 23:00:00 (11 PM) and assume she did one hour overtime.
The system should record the actual_work_finished time as 11-01-2019 00:00:00 (12 AM)
When you require to calculate or find out the extra time
calculate:
var over_time_in_hours =
actual_work_finished.Substract(scheduled_work_finished).TotalHours;
Hope this makes sense.
If you print your d1 and d2 times:
d1 time => "09.01.2019 00:00:00".
d2 time => "09.01.2019 23:00:00".
Then 23-0 = 23 is the expected result.
By the way you can achieve your result by adding 1 day to d1 time object and subtract this result from d2 object:
TimeSpan ts = d1.AddDays(1).Subtract(d2);
Console.WriteLine(ts.TotalHours.ToString());
Let's start by naming your variables something that helps us to reason about the code.
// this is the time you are going to time out
DateTime actual = Convert.ToDateTime("12:00 AM");
// this is your scheduled out
DateTime scheduled = Convert.ToDateTime("11:00 PM");
TimeSpan overtime = scheduled.Subtract(actual);
What we find is that you're performing the wrong calculation to start with. This would be the correct calculation:
TimeSpan overtime = actual.Subtract(scheduled);
When we do that though we are now getting -23 hours. This is because your actual time isn't after your scheduled time. For that you need to add a day.
Try this:
// this is the time you are going to time out
DateTime actual = Convert.ToDateTime("12:00 AM").AddDays(1);
// this is your scheduled out
DateTime scheduled = Convert.ToDateTime("11:00 PM");
TimeSpan overtime = actual.Subtract(scheduled);
Then you get the result that you want - i.e. 1 hour.

Subtracting timespan from 24 hours

I'm trying to subtract my potentially negative timespan values from 24 hours to change them into positive values.
As an example case:
I want to find how much time is there till 8:00 AM.
If it's 16:00 PM now, timespan gives me -8 ish value so I want to substract it from 24 to get 16.
I'm trying this but it's giving me this error
The DateTime represented by the string is not supported in calendar
System.Globalization.GregorianCalendar.
What I tried ;
string startTime = String.Format("{0:t}", "8:00");
TimeSpan timeLeft = Convert.ToDateTime(startTime).Subtract(DateTime.Now);
if (timeLeft.TotalMinutes < 0 )
{
timeLeft = Convert.ToDateTime(String.Format("{0:H}","24:00")).Subtract(Convert.ToDateTime(timeLeft.Negate())) ;
}
How can I achieve subtracting my potentially negative timespans from 24 hours?
You are confusing TimeSpan and DateTime. I guess there is an easier way:
var eightOClock = TimeSpan.FromHours(8);
var now = DateTime.Now;
var till8again = now.TimeOfDay > eightOClock
? TimeSpan.FromHours(32) - now.TimeOfDay
: eightOClock - now.TimeOfDay;
So if TimeOfDay is less than eight hours (it's before 8am), we take the difference to 8am. If it's greater than 8am, we take the difference to 32hours, which is 8am tomorrow.
A DateTime is an absolute date, happening at a certain day, month, year... It must not be used to represent a specific hour.
So your attempt to convert "8:00", or "24:00" in a DateTime will forcibly fail.
For this you must use TimeSpan (or eventually an integer if you always work with hours).
You can use for example
if(DateTime.Now.TimeOfDay > TimeSpan.FromHours(8))
To see if it's more or less than 8:00.
TimeOfDay will return you the amount of time elapsed for today since midnight.
DateTime has also a lot of useful methods to Add or Substract time, see https://msdn.microsoft.com/fr-fr/library/system.datetime(v=vs.110).aspx for details
Use TimeSpan, and if the startDate is less the Now, add a day to it and then make the comparison.
TimeSpan startTime = new TimeSpan(8,0,0);
TimeSpan now = DateTime.Now.TimeOfDay;
startTime = startTime < now ? startTime.Add(TimeSpan.FromDays(1)) : startTime;
TimeSpan diff = startTime - now;
Another point: the error is coming from the fact that 24:00 doesn't represent 12:00 midnight. 0:00 represents midnight, and that will be a valid DateTime.

Calculate approximate date from approximate timespan

Since Sony changed how their trophy server authentication works, I am looking for an alternative solution to approximate trophy dates. The UK Playstation Network website has a string underneath unlocked trophies approximating how long ago trophies were earned. I have the following code:
private TimeSpan ParseTimeSpan(string datestring)
{
TimeSpan ts = new TimeSpan();
datestring = datestring.Replace("Attained:", ""); // strip Attained:
string[] parts = datestring.Split(' ');
int numeric = int.Parse(parts[0]);
string duration = parts[1];
switch(duration)
{
case "minutes":
ts = new TimeSpan(0, numeric, 0);
break;
case "hours":
ts = new TimeSpan(numeric, 0, 0);
break;
case "days":
ts = new TimeSpan(numeric, 0, 0, 0);
break;
case "months":
ts = new TimeSpan(numeric * 30, 0, 0);
break;
case "years":
ts = new TimeSpan(numeric * 365, 0, 0);
break;
}
return ts;
}
If I have the string Attained:17 months ago underneath one of my trophies, it should take the current month and subtract 17 months. If I have the string Attained:3 hours ago, it should take the current date and subtract 3 hours to approximate an earned date. If I have the string Attained: 5 minutes ago, it should take the current date and subtract 5 minutes to approximate an earned date.
My plan is have this code run as a web service and accompany with a desktop client. I'm unsure of whether I should return a TimeSpan or do just calculate the date directly? Is there a better approach? Not all months are 30 days and some years are more than 365 days (leap years) so doing hard coded calculations won't necessarily work.
You don't have to worry about some months having 30 days and some not. If you have the timespan you can directly decrement the current date by that span.
TimeSpan ts = new TimeSpan(5,0,0);
var earned = DateTime.Now - ts;
Console.Write(earned);
This can be done in or out of your service, but I would perform it in the service if the actual date is what's needed.
Also you can add a regular expression if you need to add some flexibility later on.
string input = "Attained:17 months ago";
string pattern = #"Attained:(?<value>[0-9]+) (?<unit>(years|months|days)) ago";
var match = Regex.Match(input, pattern);
if(match.Success)
{
int value = Int32.Parse(match.Groups["value"].Value);
string unit = match.Groups["unit"].Value;
Console.WriteLine(value);
Console.WriteLine(unit);
}
Could make unit an enum as well
Noda Time is a much more flexible framework for working with time and date values than the types provided in the .NET base class library. It includes, among other things, a Period type, which represents some amount of time in elapsed years, months, days, etc. The actual elapsed time represented is unfixed. As you say, some months or years are longer than others, so Period is the type to use if you are not dealing with a fixed amount of time (as opposed to a fixed Duration). A Period can be added to the various date/time types in the Noda Time library.
While you can add and subtract a TimeSpan from a DateTime[Offset], it's not a terribly useful type if you need to deal with units of time longer than hours. Consider using Noda Time for nontrivial time/date computations.

Exact c# result of sql datediff

I'm trying to get the number of days (calculated byu datediff) in sql and the number of days in c# (calculated by DateTime.now.Substract) to be the same, but they return different results....
//returns 0
int reso = DateTime.Now.Subtract(expirationDate).Days;
vs
//returns 1
dateDiff(dd,getDate(),ExpirationDate)
In both cases, ExpirationDate is '10/1/2011 00:00:00', and the code and the DB are sitting on the same server. I want the return int to be the same. I suspect I'm missing something stupid... ideas??
dateDiff(dd,getDate(),ExpirationDate) Is doing a days comparison. DateTime.Now.Subtract(expirationDate).Days is doing a date and time
For example
SELECT dateDiff(dd,'10/1/2011 23:59:00' , '10/2/2011') returns one day even when only one minute apart.
If you want the same in C# you need to remove the time component
e.g.
DateTime dt1 = new DateTime(2011,10,1, 23,59,0);
DateTime dt2 = new DateTime(2011,10,2, 0,0,0);
Console.WriteLine((int) dt2.Subtract(dt1.Subtract(dt1.TimeOfDay)));
So in your case it would be something like
DateTime CurrentDate = DateTime.Now;
int reso = CurrentDate.Subtract(CurrentDate.TimeOfDay).Subtract(DateTime.expirationDate).Days;
I haven't tested it but I would not do
DateTime.Now.Subtract(DateTime.Now.Subtract.TimeOfDay)
Because the second call to Now wouldn't be guaranteeing to be the same as first call to Now
In any case Stealth Rabbi's answer seems more elegant anyway since you're looking for a TimeSpan not a DateTime
10/1/2011 is less than 1 day away from DateTime.Now. Since you're getting back a TimeSpan and then applying Days to it, you're getting back a TimeSpan that is < 1 day. So it'll return 0 Days.
Instead, just use the Date component of those DateTimes and it'll correctly report the number of days apart - like this:
DateTime now = DateTime.Now;
DateTime tomorrow = new DateTime(2011, 10, 1);
var val = (tomorrow.Date - now.Date).Days;
This will yield you 1 day.
I'm assuming you want the number of Total days, not the number of days from the largest previous unit. You'd want to use the TotalDays property. Also, you may find it easier to use the minus operator to do a subtraction
DateTime d1 = DateTime.Now;
DateTime d2 = new DateTime(2009, 1, 2);
TimeSpan difference = d1 - d2;
Console.WriteLine(difference.TotalDays); // Outputs (today):1001.46817997424

Categories