Determine if time falls in designated hour range - c#

I am new at C#. I'd like to check whether a time is between 2 given hours, and if so then do something. Can anyone give me an example?
pseudocode example:
int starthour = 17;
int endhour = 2;
if ( hour between starthour and endhour){
dosomething();
}
How do I write a check on whether hour is between starthour and endhour? In C#, the time is returned in AM/PM format so I don't know if it will understand the 17 number as "5 PM".

Assuming you're talking about the current time, I'd do something like this:
// Only take the current time once; otherwise you could get into a mess if it
// changes day between samples.
DateTime now = DateTime.Now;
DateTime today = now.Date;
DateTime start = today.AddHours(startHour);
DateTime end = today.AddHours(endHour);
// Cope with a start hour later than an end hour - we just
// want to invert the normal result.
bool invertResult = end < start;
// Now check for the current time within the time period
bool inRange = (start <= now && now <= end) ^ invertResult;
if (inRange)
{
DoSomething();
}
Adjust the <= in the final condition to suit whether you want the boundaries to be inclusive/exclusive.
If you're talking about whether a time specified from elsewhere is within a certain boundary, just change "now" for the other time.

Actually, if we're dealing with pure hours here like a Abelian Ring from 0 to 23 and 0 again, I believe the following is actually a working solution:
(start <= end && start <= t && t <= end) or (start > end && (start <= t || t <= end))
Complex though this is, it is essentially an if-else where you have a different algorithm depending on whether start <= end or not, where t is the time you wish to test. In the first case, start and end are normal order, so t must be both greater than start and less than end. In the case where start is greater than end, the times outside the opposite range are what we want:
NOT(end < t and t < start)
Using DeMorgan's theorem:
NOT(end < t) or NOT(t < start)
NOT(t < start) or NOT(end < t)
t >= start or end >= t
start <= t or t <= end
This should solve your and my problems.
#JonSkeet
The thing is, looking at your algorithm, let's assume for a moment the time is 1am, day 1.
Now holds 1am Day 1
Today holds midnight Day 1
Start holds 5pm Day 1 (given the original example)
End holds 2am Day 1 (again from the example)
End holds 2am Day 2 (since start > end)
Now, unless I'm mistaken, start ≰ now since start is 5pm Day 1 and now is 1am Day 1 which is before now, therefore the test fails but the original question wanted 1am included in the range since 1am is between 5pm and 2am. Did I miss something?
#Brian
Also, looking at your code, I think you can detect 1am but now you would have a problem with 10pm (22:00) since your times become:
Start is 17
End is 26
Now is 22 + 24 = 46! so you will fail in the less-than test.
Clearly, the general case is very tricky! More so when you're restricted to Google Spreadsheets as I am.

When subtracting DateTimes, you get a TimeSpan struct that you can query for things like the total number of hours (the TotalHours property):
TimeSpan ts = starttime - endtime;
if(ts.TotalHours > 2)
{
dosomething();
}
If you want to see if the times are identical, then you can use TotalMilliseconds - for identical DateTimes, this will be equal to 0.

If you want to compare minutes also like I do you can use this snippet of code in java.
//Initialize now, sleepStart, and sleepEnd Calendars
Calendar now = Calendar.getInstance();
Calendar sleepStart = Calendar.getInstance();
Calendar sleepEnd = Calendar.getInstance();
//Assign start and end calendars to user specified star and end times
long startSleep = settings.getLong("startTime", 0);
long endSleep = settings.getLong("endTime", 0);
sleepStart.setTimeInMillis(startSleep);
sleepEnd.setTimeInMillis(endSleep);
//Extract hours and minutes from times
int endHour = sleepEnd.get(Calendar.HOUR_OF_DAY);
int startHour = sleepStart.get(Calendar.HOUR_OF_DAY);
int nowHour = now.get(Calendar.HOUR_OF_DAY);
int endMinute = sleepEnd.get(Calendar.MINUTE);
int startMinute = sleepStart.get(Calendar.MINUTE);
int nowMinute = now.get(Calendar.MINUTE);
//get our times in all minutes
int endTime = (endHour * 60) + endMinute;
int startTime = (startHour * 60) + startMinute;
int nowTime = (nowHour * 60) + nowMinute;
/*****************What makes this 100% effective***************************/
//Test if end endtime is the next day
if(endTime < startTime){
if(nowTime > 0 && nowTime < endTime)
nowTime += 1440;
endTime += 1440;
}
/**************************************************************************/
//nowTime in range?
boolean inRange = (startTime <= nowTime && nowTime <= endTime);
//in range so calculate time from now until end
if(inRange){
int timeDifference = (endTime - nowTime);
now.setTimeInMillis(0);
now.add(Calendar.MINUTE, timeDifference);
sleepInterval = now.getTimeInMillis() / 1000;
editor.putBoolean("isSleeping", true);
editor.commit();
Log.i(TAG, "Sleep Mode Detected");
returned = true;
}

bool CheckHour(DateTime check, DateTime start, DateTime end)
{
if (check.TimeOfDay < start.TimeOfDay)
return false;
else if (check.TimeOfDay > end.TimeOfDay)
return false;
else
return true;
}

int starthour = 17;
int endhour = 2;
int nowhour = DateTime.Now.Hour;
if (endhour < starthour)
{
endhour+=24;
nowhour+=24;
}
if (starthour <= nowhour && nowhour <= endhour)
{
dosomething();
}
I'm not sure which I prefer between this code and Jon Skeet's code.

Using Jon Skeet's solution above I added a fix where if start time is after beginning time eg You start the job after 6pm at night and end it the next morning at 5am. then you need to check this and apply another day to the end time. Hope it helps, I personally have spent too much time on this piece of work. have a great day :)
if (stopHour < startHour)
{
end = today.AddHours(stopHour+24);
}
Full Code is below.
private static bool IsInRunHours()
{
try
{
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("appSettings");
// after = 18 before = 5
// Only take the current time once; otherwise you could get into a mess if it
// changes day between samples.
DateTime now = DateTime.Now;
DateTime today = now.Date;
Int32 startHour = ConfigurationManager.AppSettings["UpdateRunAfter"].ToInt();
Int32 stopHour = ConfigurationManager.AppSettings["UpdateRunBefore"].ToInt();
DateTime start = today.AddHours(startHour);
DateTime end = today.AddHours(stopHour);
if (stopHour < startHour)
{
end = today.AddHours(stopHour+24);
}
//ConfigurationManager.AppSettings["UpdateRunBefore"].ToInt()
//ConfigurationManager.AppSettings["UpdateRunAfter"].ToInt()
// Cope with a start hour later than an end hour - we just
// want to invert the normal result.
bool invertResult = end < start;
// Now check for the current time within the time period
bool inRange = (start <= now && now <= end) ^ invertResult;
if (inRange)
{
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}

Related

How to know if current time is between two timespans?

I have two time spans like so:
TimeSpan Starttime : 16:37:00
TimeSpan EndTime: 17:37:00
current time:
DateTime currentDate = DateTime.Now;
TimeSpan now = currentDate.TimeOfDay;
Problem:
I can't figure out how to know if the current time is between starttime and endtime. i want to send mesages only between those two timespans.
How do i do this?
My attempt:
if(startTime.Hours < now.Hours && endTime.Hours > now.Hours)
// do stuff
This does not cover all scenarios since I need it to be exactly between starttime and endtime to the last second but I dont know how to do this.
You can just use:
if (startTime < now && now < endTime)
Note that:
This doesn't check the date; doesn't look like that's an issue here
Depending on why you're doing this, you may want to consider intervals such as "10pm-2am" at which point you effectively want to reverse the logic
In most cases, it's worth making the lower-bound inclusive and the upper-bound exclusive, e.g.
if (startTime <= now && now < endTime)
That's useful because then you can have several intervals where the end of one interval is the start of the next interval, and any one time is in exactly one interval.
To handle the "10pm-2am" example, you'd want something like:
if (interval.StartTime < interval.EndTime)
{
// Normal case, e.g. 8am-2pm
return interval.StartTime <= candidateTime && candidateTime < interval.EndTime;
}
else
{
// Reverse case, e.g. 10pm-2am
return interval.StartTime <= candidateTime || candidateTime < interval.EndTime;
}

Is this a good algorithm to find each week in a given month?

I need to take a month (defined as a start and end date) and return a set of date ranges for each week in that month. A week is defined as Sunday through Saturday. A good way to visualize it is if you double click on your Windows date in the start bar:
The month of October 2011 has 6 weeks: 10/1-10/1, 10/2-10/8, 10/9-10/15, 10/16-10/22, 10/23-10/29 and 10/30-10/31.
I can describe each week as a struct:
struct Range
{
public DateTime Start;
public DateTime End;
public Range(DateTime start, DateTime end)
{
Start = start;
End = end;
}
}
I need to write a function that takes a month and returns an array of ranges within it. Here's my first attempt, which appears to work and addresses the obvious edge cases:
public static IEnumerable<Range> GetRange(DateTime start, DateTime end)
{
DateTime curStart = start;
DateTime curPtr = start;
do
{
if (curPtr.DayOfWeek == DayOfWeek.Saturday)
{
yield return new Range(curStart, curPtr);
curStart = curPtr.AddDays(1);
}
curPtr = curPtr.AddDays(1);
} while (curPtr <= end);
if(curStart <= end)
yield return new Range(curStart, end);
}
I would like to know if there's a cleaner or more obvious approach to do the same. I'm not overly concerned about performance, but I'd like to improve code readability and make the algorithm a bit more concise. Perhaps there's a very creative solution involving a single LINQ expression or something. Thanks!
This is based on simply incrementing by 7, as suggested by Previti, ready for international use. If your C# is < 4.0, remove the default parameter = DayOfWeek.Sunday
public static IEnumerable<Range> GetRange(DateTime start, DateTime end, DayOfWeek startOfTheWeek = DayOfWeek.Sunday)
{
if (start > end)
{
throw new ArgumentException();
}
// We "round" the dates to the beginning of the day each
start = start.Date;
end = end.Date;
// The first week. It could be "shorter" than normal. We return it "manually" here
// The 6 + startOfWeek - start.DayOfWeek will give us the number of days that you
// have to add to complete the week. It's mod 7. It's based on the idea that
// the starting day of the week is a parameter.
DateTime curDay = new DateTime(Math.Min(start.AddDays((6 + (int)startOfTheWeek - (int)start.DayOfWeek) % 7).Ticks, end.Ticks), start.Kind);
yield return new Range(start, curDay);
curDay = curDay.AddDays(1);
while (curDay <= end)
{
// Each time we add 7 (SIX) days. This is because the difference between
// as considered by the problem, it's only 6 * 24 hours (because the week
// doesn't end at 23:59:59 of the last day, but at the beginning of that day)
DateTime nextDay = new DateTime(Math.Min(curDay.AddDays(6).Ticks, end.Ticks), start.Kind);
yield return new Range(curDay, nextDay);
// The start of the next week
curDay = nextDay.AddDays(1);
}
}
Some small notes: Math.Min isn't defined for DateTime, so I cheat a little by taking the Ticks of the DateTimes and comparing them. Then I rebuild the DateTime. I always use the DateTimeKind of the start date.
When you debug yield code, remember to "materialize" the result through the use of ToList or ToArray, otherwise the code won't be executed :-)

Calculate StartDate when End Date & back days given

I am developing c#.net solution where i have to calculate Start Date based on provided End date and Duration (back days) without weekends.
i.e. End Date: 05/5/2011
Back days: 5
Start Date = (05/5/2011) - 5 days (Excludes weekends)
Start Date = 29/04/2011
thanks you,
Something like this is probably what I'd do:
DateTime CalcStartDate(DateTime endTime, int daysBack)
{
DateTime startTime = endTime.Date;
while (daysBack > 0)
{
startTime = startTime.AddDays(-1);
if (startTime.DayOfWeek != DayOfWeek.Saturday && startTime.DayOfWeek != DayOfWeek.Sunday)
{
--daysBack;
}
}
return startTime;
}
Or even better, Bala's suggestion of using a library. Date and time is messy, a hardened/tested library is usually a good choice.
Bala R has the answer correctly. Here is a link to an article on how to use the AddBusinessDays() method:
http://www.codeproject.com/KB/cs/AddBusinessDay.aspx
I don't know how far back you are going to go. If it is a lot of days back then looping through the days might be a little CPU intensive. Well, probably not with modern processors...
I decided to implement a solution without a loop.
My code is a little more difficult to read, but it should be more efficient performance-wise.
public static class DateTimeExtensions
{
public static DateTime SubtractBusinessDays(this DateTime fromDateTime, int days)
{
var subtractDays = days % 5;
var dayNumber = fromDateTime.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)fromDateTime.DayOfWeek;
var addDays = Math.Max(dayNumber - 5, 0);
var result = fromDateTime.AddDays(addDays - subtractDays - (days / 5 * 7));
if ((addDays + dayNumber) % 7 <= subtractDays)
result = result.AddDays(-2);
return result;
}
}

A tricky DateTime algorithm problem

.NET gives me the current time in both local and UTC time zones (or any other) in the DateTime struct.
Given only hour/minute variables, find when the next occurrence of this time period (eg 6:30 PM/AM) will occur, with the ability to retrieve more future times at will.
It sounds easy but indeed friends this has been breaking my noodle for a good while.
Edit:
Example:
~-------|now|------??-----------|future known time|------------~
~-------2pm------??2-----------9am------------~
??2 = 19
If I understand correctly you want to know how much time has to pass in order to hit the next given hour:minute. You can use the TimeSpan struct for this.
//this is your target time from 1 to 12 h
var future = new TimeSpan(11, 30, 0);
//TimeOfDay gives you the time elapsed since midnight as a TimeSpan
var difference = future.Subtract(DateTime.Now.TimeOfDay);
//check for negative TimeSpan,
//it means the target time occurs on the next day, just add 24 hours
if (difference < TimeSpan.Zero)
difference = difference.Add(TimeSpan.FromDays(1));
Now you have a TimeSpan that represents what you need. You can use its properties to express it as you seem fit. For example:
difference.TotalHours; //(double) total time as a fractional hour
difference.Hours; //(int) just the hour component of the total time
As for retreiving more future times (am and pm), you can just add 12 more hours to difference to get the next occurence.
This was cooded in stackoverflow form, so it's probably there are some typos. Either way, you'll get the big picture.
public DateTime search(int hour, int min) {
if (hour >= 12)
return partialSearch(hour - 12, hour, min);
else
return partialSearch(hour, hour + 12, min);
}
public DateTime partialSearch(int morningHour, int afternoonHour, int min) {
DateTime now = DateTime.now;
if (now.hour == morningHour || now.hour == afternoonHour) {
if (now.minutes <= min) {
return now.AddMinutes(min - now.minutes);
}
now = now.AddHour(1);
}
now = now.AddMinutes(-now.Minutes); // set the minutes to 0
while(now.hour != morningHour || now.hour != afternoonHour) {
now = now.AddHour(1);
}
return now.addMinutes(min);
}
Not sure if I fully understood your requirements, but it seems you are looking for something like this?
public class TimeIterator
{
public DateTime CurrDateTime { get; set; }
public IEnumerable<DateTime> GetTimes(short count)
{
for (short i = 1; i <= count; i++)
yield return this.CurrDateTime.AddHours(i * 12);
}
public TimeIterator()
{
this.CurrDateTime = DateTime.Now;
}
}
This code can be easily adjusted to work with any time interval - not just 12 hour interval

Algorithm to check whether a certain hour falls into a given time period

Assume I've got a start and stop hour in which some processing should take place:
Start 20:00
End 07:00
Now what is the best algorithm to check if a certain DateTime hour value falls within this range? Thanks in advance!
Please note that the start and end times mentioned above indicate that we are dealing with an "overnight-job". Meaning that the period to be checked starts at 20:00 in the evening and ends at 07:00 on the following morning.
Assume you only have the time and not the date.
if end_time >= start_time:
return start_time <= current_time <= end_time
else:
return start_time <= current_time or current_time <= end_time
If you are sure it is in the same day
you also do not seem to care for seconds
Transform everything in minutes
startminute = 20 * 60 + 0
endminute = 7 * 60 + 0
eventminute = x * 60 + y // with event having the form xx:yy
return startminute < eventminute && eventminute < endminute
Another option would be to get the 3 times in DateTime format
DateTime start, end, event
return (start < event && event < end);
Assuming you have a start, end and now DateTime, you could use
bool within = start.TimeOfDay.TotalHours <= now.TimeOfDay.TotalHours &&
now.TimeOfDay.TotalHours <= end.TimeOfDay.TotalHours;
Without knowing how to do it in C#, I'd go with converting start and end time to a timestamp and then do a simple if (end time >= given time AND start time <= given time) comparison. Maybe that'll get you started or give you a hint what to search for.
I think c# supports greater than/less than operators for DateTime variables, so just say
if ((beginDateTime<myDateTime)&&(myDateTime<endDateTime))
{
...
}
Also greater than or equal to and less than or equal to are supported.
With an overnight window, I don't think there's anything particularly clever to be done except to directly check your DateTime's TimeOfDay against the boundaries:
using System;
namespace Question2355777
{
class Program
{
private static bool IsInOvernightWindow(
DateTime dateTimeUnderTest,
TimeSpan morningEnd,
TimeSpan eveningStart)
{
TimeSpan timeOfDay = dateTimeUnderTest.TimeOfDay;
return timeOfDay <= morningEnd || timeOfDay >= eveningStart;
}
static void Main(string[] args)
{
TimeSpan eveningStart = TimeSpan.FromHours(20);
TimeSpan morningEnd = TimeSpan.FromHours(7);
Console.WriteLine("{0} {1}",
DateTime.Today.AddHours(3),
IsInOvernightWindow(
DateTime.Today.AddHours(3),
morningEnd,
eveningStart));
Console.WriteLine("{0} {1}",
DateTime.Today.AddHours(12),
IsInOvernightWindow(
DateTime.Today.AddHours(12),
morningEnd,
eveningStart));
Console.WriteLine("{0} {1}",
DateTime.Today.AddHours(21),
IsInOvernightWindow(
DateTime.Today.AddHours(21),
morningEnd,
eveningStart));
Console.ReadLine();
}
}
}
produces
01/03/2010 03:00:00 True
01/03/2010 12:00:00 False
01/03/2010 21:00:00 True
// initializing with some sample values
TimeSpan start = TimeSpan.FromHours(20);
TimeSpan end = TimeSpan.FromHours(7);
DateTime now = DateTime.Now.TimeOfDay;
return start<end
? start <= now.TotalHours && now.TotalHours <= end
: start <= now.TotalHours || now.TotalHours <= end;
Use the TimeOfDay method:
DateTime dtStart = new DateTime(0,0,0,20,0,0);
DateTime dtEnd = new DateTime(0,0,0,7,0,0);
if (DateTime.Now.TimeOfDay < dtEnd.TimeOfDay || DateTime.Now.TimeOfDay > dtStart.TimeOfDay)
{
// your code here
}
Count all the times in units of minutes beginning from the start day.
struct DateTime {
uint_8 hour_;
uint_8 minute_;
};
bool
isTimeWithin( DateTime start, DataTime stop, DateTime query ) {
// The following times are counted from the beginning of start day
uint_16 startInMins = (60 * start.hour_ + start.minute_);
// Added 24*60 since "stop" is always "overnight" from "start"
uint_16 stopInMins = 24 * 60 + (60 * stop.hour_ + stop.minute_);
// The ternary operator checks whether "query" is in the same day as
// "start" or the next day
uint_16 queryInMins = (query.hour_ < start.hour_ ? 24 * 60 : 0 ) +
(60 * query.hour_ + query.minute_);
return ((startInMins <= queryInMins) && (queryInMins <= stopInMins));
}
EDIT: Improved Formatting.
if ALL you have are those values for start and stop, don't you have an empty set?
Ignore the obvious assumption - start day X, end day X + Y.
[edit]
since the question has been edited, so shall be the answer....
for start time, end time and 'test' time get the number of milliseconds from the epoch (define that any way you would like)
and then check to see if test is >= start and <= end
[/edit]

Categories