Comparing dates, looking for the next instance of a specific time - c#

Based on my unit-test I'm trying to calculate when the next instance of 0400 hrs are and return this;
public void when_given_a_date_i_should_return_next_time_of_day_that_equals_04_hrs()
{
var dateTimeNow = new DateTime(2012, 6, 11, 14, 22, 0);
var dateTimeExpected = new DateTime(2012, 6, 12, 4, 0, 0);
Assert.AreEqual(dateTimeExpected, t.CalculateIncremental(dateTimeNow));
}
The point of CalculateIncremental is to publish a message with nservicebus every 24 hrs, at exactly 04:00 am using RequestUtcTimeout. This again to trigger some functionality.

Something like this:
private static DateTime GetNext4AM(DateTime input)
{
var result = new DateTime(input.Year, input.Month, input.Day, 4, 0, 0);
if (result > input)
{
return result;
}
else
{
return result.AddDays(1);
}
}
Should return the next occurrence of 4 AM, which can be on the same day (if the input is earlier than 04:00) or the next. However:
The point [..] is to publish a message with nservicebus every 24 hrs, at exactly 04:00 am using RequestUtcTimeout. This again to trigger some functionality.
You should use a scheduler for that, like Quartz.NET or simply the Windows Task Scheduler.

Check the hour of dateTimeNow, if it's less than 4, return its date and 4AM as the hour. If it's 4 or more, return the date of the next day, and 4AM as the hour. Decide what you want to do with 4:00:00 - either this day or the next one.

var now = new DateTime();
var dateTimeExpected = new DateTime(now.GetFullYear(),
now.GetMonth(), now.GetDay(), 4, 0, 0);
if (dateTimeExpected.CompareTo(now) > 0)
dateTimeExpected = dateTimeExpected.AddDays(1);

Related

TimeSpan calculation is different between TimeOnly and DateTime

I'm having a hard time understanding why this is happening or even if this should be happening. If I calculate the TimeSpan between two DateTime objects (same date, different times) and compare it to the same calculation using two TimeOnly objects I get different results.
var start = new DateTime(1, 1, 1, 14, 0, 0);
var end = new DateTime(1, 1, 1, 10, 0, 0);
Console.WriteLine(end - start); // Prints -4 hours
However...
var start = new TimeOnly(14, 0, 0);
var end = new TimeOnly(10, 0, 0);
Console.WriteLine(end - start); // Prints 20 hours???
Isn't the span between starting at 2pm and ending at 10am always a span of -4 hours? Interestingly enough if I take the second one and do Console.WriteLine(end.ToTimeSpan() - start.ToTimeSpan()); I end up with -4 hours.
This feels like an error on TimeOnly's part but I don't know. Here is a fiddle I did comparing results between NodaTime, System.DateTime, converting System.TimeOnly to TimeSpan, and System.TimeOnly.

How to test a Date against Cron Expression

I need to test if a certain date matches a cron expression using Quartz.Net. I'm trying to get the below code to work, but fail.
// The data I'm testing is Dec 4, 2018 which is a Tuesday.
var date = new DateTimeOffset(2018, 12, 4, 0, 0, 0, TimeSpan.FromHours(0));
// Expression must define every monday, time is irrelevant
var expression = new CronExpression("0 0 0 ? * MON *");
// This returns 2018-12-09. But the next Monday after my date is 2018-12-10 !!
var next = expression.GetNextValidTimeAfter(date);
If you look at my comments, Quartz returns 2018-12-09 as the next Monday. But that is a Sunday...I don't understand why, perhaps misunderstanding the time-element - which I don't need at all.
Btw here's my expression from CronMaker:
So I guess the expression is ok.
Any hints?
Found out why. The CronExpression was missing the TimeZone.
So for example:
var date = new DateTimeOffset(2018, 12, 4, 0, 0, 0, TimeSpan.FromHours(0));
var expression = new CronExpression("0 0 0 ? * MON *") { TimeZone = TimeZoneInfo.Utc };
var next = expression.GetNextValidTimeAfter(date);
Will return correct for me now.

Assert.Equal is not working with dates

I have written a simple test method to verify a method that converts a long value to DateTime, following is the code...
[TestMethod]
public void ConvertToDateTime_TestMethod()
{
long date = 1500024370374;
DateTime result = date.GetDateTime();
DateTime comparisonDate = new DateTime(2017, 07, 14, 9, 26, 10);
Assert.AreEqual(result, comparisonDate);
}
Point is that both date and time values are exactly same (also verified in watch window) why does it gives this exception? I found this article that uses a delta to compare if minor time difference is failing the comparison but still unsure why I still need an extra function to compare date/time assertions.
Edit-1: Here is my date conversion method which I am testing
public static DateTime GetDateTime(this long milliseconds)
{
try
{
return new DateTime(1970, 1, 1).AddMilliseconds(Convert.ToDouble(milliseconds));
}
catch { throw; }
}
Even though there is an alternate to this with new datetime(long), but logically whats wrong with this?
To the nearest second (the granularity the console is showing you) they are equal. To the nearest millisecond, they are not
long date = 1500024370374;
DateTime result = new DateTime(1970,1,1).AddMilliseconds(date);
DateTime comparisonDate = new DateTime(2017, 07, 14, 9, 26, 10);
Console.WriteLine(result.ToString("dd-MM-yyyy HH:mm:ss.fffff"));
// 14-07-2017 09:26:10.37400
Console.WriteLine(comparisonDate.ToString("dd-MM-yyyy HH:mm:ss.fffff"));
// 14-07-2017 09:26:10.00000
Live example: http://rextester.com/PYDLAD6594

Comparing times when over 24 hours

I have a TimeSpan field that adds up time spent on something. For example the time could be 33 hours, so the format is 33:56:00
I want to compare this to 10 hours to calculate how many over hours were done.
TimeSpan totalActualHours = new TimeSpan(0, 0, 0, 0, 0);
if (totalActualHours > TimeSpan.Parse(txtEstimateHrs.Text))
{
tmpOverHours = totalActualHours.Subtract(TimeSpan.Parse(txtEstimateHrs.Text));
}
But since totalActualHours is over 24 hours the format is coming out like 1.09:56:00 instead of 33:56:00. So txtEstimateHrs.Text is equal to 10 and I want to see if 33:56:00 is greater and if so then how many hours is it greater?
So the code is comparing if (1.09:56:00 > 10.00:00:00) so it never goes into the if statement.
The issue here is Timespan in converting the hours into days so 33 hours changes to 1 day and 9 hours, and the txtEstimateHrs.Text is an integar 10 and that changes to 10 days. I need both times to be in hours format and be able to compare them
You just need to properly construct the timespan object using the appropiate format. In your case, you can choose between
hour, min sec
day, hour, min, sec, millisec
Sample code:
Case 1
TimeSpan tmpOverHours;
TimeSpan totalActualHours = new TimeSpan(33, 56, 0);
TimeSpan hoursToCompare = new TimeSpan(int.Parse(txtEstimateHrs.Text), 0, 0);
if (totalActualHours > hoursToCompare)
{
tmpOverHours = totalActualHours.Subtract(hoursToCompare);
}
Case 2
TimeSpan tmpOverHours;
TimeSpan totalActualHours = new TimeSpan(0, 33, 56, 0, 0);
TimeSpan hoursToCompare = new TimeSpan(0, int.Parse(txtEstimateHrs.Text), 0, 0, 0);
if (totalActualHours > hoursToCompare)
{
tmpOverHours = totalActualHours.Subtract(hoursToCompare);
}
It seems you are having a parsing error when you are doing:
TimeSpan.Parse(txtEstimateHrs.Text)
if the text is "10" the parse method will interpret the value as days.
So you could change that code to something like:
TimeSpan.FromHours(int.Parse(txtEstimateHrs.Text))
Which will parse the number in the textbox into an int and use that to create a TimeSpan which correctly has the number of hours and not days.
Edit: On a side note, don't parse the text twice, better use a variable to hold the parsed TimeSpan and then use it.
I am not sure i understood your requirement but you can use the TimeSpan.Compare() method.
var t1 = new TimeSpan(33, 21, 12);
var t2 = new TimeSpan(10, 0, 0);
if (TimeSpan.Compare(t1, t2) > 0)
{
Console.WriteLine(t1.ToString() + " is longer");
}
Edit:
The above code will work fine if the Timespan objects can be created correctly. In case you are working with strings in the format of hh:mm:ss then you will need to split them and call the correct Timespan constructor. Something like below:
public static TimeSpan ConvertStringToTimeStamp(string s)
{
// add checks for input like >0, not null or empty
var split = s.Split(':');
TimeSpan ts;
switch (split.Length)
{
case 3:
ts = new TimeSpan(int.Parse(split[0]), // hours
int.Parse(split[1]), // minutes
int.Parse(split[2])); // seconds // seconds);
break;
case 2:
ts = new TimeSpan(int.Parse(split[0]), // hours
int.Parse(split[1]), // minutes
0); // 0 seconds
break;
case 1:
ts = new TimeSpan(int.Parse(split[0]), // hours
0, // 0 minutes
0); // 0 seconds
break;
default:
throw new Exception("Invalid Input");
}
return ts;
}

Update a Date to a specific month

In my MVC application, my users said that they do written tests 2 times a year.. in March and September. However, if a user fails a test then they retest in 90 days (doesn't have to be March or September).
For example, if a user takes their very first test on 3/2/2016, and that user passes, then that user doesn't need to take the other test until anyday in September. But if that user fails, then that user needs to retake the test on the 31st of May. So let me try and illustrate this with a little bit of code.
DateTime usersTestDate = new DateTime(2016, 3, 2);
// user fails
usersTestDate = usersTestDate.AddDays(90);
// usersTestDate is now 5/31/2016
// now the user retakes the test and passes
// usersTestDate should now be in September of 2016.
How do I get that to happen, since usersTestDate could essentially be any date in the book if the user fails.. Basically, if the user passes a retake of the exam in any months besides March or September.. how do I get their new date to be in either March or September?
I have created a dotnetfiddle
Any help is appreciated.
UPDATE
If they fail again after already failing once, then they keep retaking every 90 days. If they keep failing their March test past September then they skip September and just go to March again if they pass
if (userTestDate.Month < 3)
userTestDate = new DateTime(userTestDate.Year, 3, 1);
else if (userTestDate.Month < 9)
userTestDate = new DateTime(userTestDate.Year, 9, 1);
else
userTestDate = new DateTime(userTestDate.Year + 1, 3, 1);
You don't specify which day of the month the March & September dates should be, so I have arbitrarily chosen the first.
I think this captures what you're after. I set the next test date to the last day in Sept/March, although you could easily change that based on your requirements.
public DateTime GetNextTestDate(DateTime testDate, bool passed)
{
if (passed)
{
if (testDate.Month >= 3 && testDate.Month < 9)
return new DateTime(testDate.Year, 9, 30);
else
return new DateTime(testDate.Year + (testDate.Month >= 9 && testDate.Month <= 12 ? 1 : 0), 3, 31); // (add a year if we're 9-12)
}
else
return testDate.AddDays(90);
}
To simplify the logic consider the following.
When someone passes, we do not need to care about the last test date; we can just schedule them for the next available test, based off the current date. The code below says you can only book for that test the month before
If they fail, then add 90 days to the last test date.
public DateTime NextTestDate(DateTime testDate, bool passed)
{
if (passed)
{
var now = DateTime.Now;
if (DateTime.Now.Month < 3)
return new DateTime(now.Year, 3, 2);
if (DateTime.Now.Month < 9)
return new DateTime(now.Year, 9, 2);
return new DateTime(now.Year + 1, 3, 2);
}
return testDate.AddDays(90);
}

Categories

Resources