I am creating a function that will set the date of an event, based on the current time.
I have an enumeration of events:
public enum EventTimings
{
Every12Hours, // 12pm and midnight
Weekly // sunday at midnight
}
public static DateTime CalculateEventTime(EventTimings eventTime)
{
DateTime time;
switch(eventTime)
{
case EventTimings.Every12Hours:
break;
}
return time;
}
So (Every12Hour event type) if the current time is 10am, then the eventdate will be the same day but at 12pm.
How should I write this?
I also have to make sure this works for December 31st and any other strange outlier date/time.
Is datetime the best for this scenerio?
If you want to be able to test anything, I would make the DateTime you are trying to "round" explicit, something like
public static DateTime RoundedDate(DateTime eventTime, EventTimings strategy)
{
switch (strategy)
case EventTimings.Weekly :
return WeeklyRounding(eventTime);
... etc ...
That way you can now write a specialized method for the 12-hour interval, the week interval, and test it for any input date possible, without depending on your computer clock.
You could also try something like this, although it breaks down if you want to do something monthly (because months each have a different number of days.) Also, while this simplified method will ensure a returned date at 12 and midnight, the weekly offset would be every 7 days from the starting day... not necessarily on Sundays. You could easily accomodate that behavior with a switch statement, though. The overloaded method also allows you some flexibility to provide a custom offset.
Also, to answer your question, yes I would use System.DateTime and System.TimeSpan. They handle determining whether a year or month has "rolled over" for you.
public enum EventTimings : int
{
Default = 12, // Default every 12 hours.
NoonAndMidnight = 12, // Every 12 hours.
Weekly = 168, // 168 hours in a week.
ThirtyDays = 720 // 720 hours in 30 days.
}
public DateTime CalculateDateTime(DateTime starting, EventTimings timing)
{
return CalculateDateTime(starting, TimeSpan.FromHours((int)timing));
}
public DateTime CalculateDateTime(DateTime starting, TimeSpan span)
{
DateTime baseTime = new DateTime(starting.Year, starting.Month, starting.Day, starting.Hour >= 12 ? 12 : 0, 0, 0);
return baseTime.Add(span);
}
I agree to keep it generic by making the reference date an input parameter instead of current datetime. However as you have asked about the logic for your eventTime values as well, this is how I would go about.
How should I write this?
For every12hours, check the hour property of the input date and check if it is less than 12. If so, then create a new TimeSpan for 12pm and add it to the datepart of the input date.
If not, add 1 day to the input date, create a TimeSpan for 12am and add it to the datepart of inputdate.
For weekly (Monday 12am), check the dayoftheweek of the inputdate and just add number of days to make it equal to the incoming Monday (Which is as simple as (8 - dayoftheweek)) and add a 12am TimeSpan to the date of the incoming Monday's date.
public enum EventTimings
{
Every12Hours, // 12pm and midnight
Weekly // sunday at midnight
}
public static DateTime CalculateEventTime(EventTimings eventTime, DateTime inputDate)
{
DateTime time = DateTime.Now;
switch (eventTime)
{
case EventTimings.Every12Hours:
time = inputDate.Hour > 12 ? inputDate.AddDays(1).Date + new TimeSpan(0, 0, 0) : inputDate.Date + new TimeSpan(12, 0, 0);
return time;
case EventTimings.Weekly:
int dayoftheweek = (int) inputDate.DayOfWeek;
time = inputDate.AddDays(8 - dayoftheweek).Date + new TimeSpan(0, 0, 0);
return time;
// other cases
}
}
Is datetime the best for this scenerio?
Yes. Your datetime calculations using DateTime and TimeSpan should take care of leap years, daylight savings or endofyear scenarios. Additionally you could try adding SpecifyKind to denote it is local time.
The algorithm I'd follow goes like this...
Put noon on the day of eventTime into a variable
Check if that variable is before eventTime
If it's not, add 12 hours to it
Return the variable
switch (strategy)
{
case EventTimings.Every12Hours:
//get noon for the event date
DateTime x = eventTime.Date.AddHours(12);
//check to see if x is after the eventTime
if (x < eventTime)
{
//if so, advance x by 12 hours to get midnight on the next day
x = x.AddHours(12);
}
return x;
break;
//other cases...
}
Related
I run into this question of how to determine if DateTime.UtcNow (e.g. 2018-01-01T20:00:00Z) falls within the given range of days and times that are in another timezone. There are no specific dates given, just the days of the week, and the time of the day. The given time is in ISO 8601 standard format.
To simplify this question, it can be how to check if a UTC time is within business hours in China.
For example, the given day and time range is given by someone form China in time zone +08:00, it can be: FromDayOfWeek = "Friday", FromTimeOfDay = "17:00:00+8:00", ToDayOfWeek = "Monday", ToTimeOfWeek = "08:00:00+8:00". I need to determine if "now" in China is sometime between the given range (Friday 17:00:00+8:00 - Monday 08:00:00+8:00).
I'm stuck at how to convert the DateTime and get the day of the week in that local time, since 2018-01-01T20:00:00Z is Monday in UK, but at the same time, since China is +08:00, it is already Tuesday in China.
My approach:
// parse the time to get the zone first (+08:00)
TimeSpan ts = TimeSpan.Parse("-08:00");
// Create a custom time zone since the time zone id is not given, and cannot be searched by SearchTimeZoneById
TimeZoneInfo tzi = TimeZoneInfo.CreateCustomTimeZone(zoneId, ts, displayName, standardName);
DateTime localDateTime = TimeZoneInfo.ConvertTime(Date.UtcNow, tzi);
String localDay = localDateTime.DayOfWeek;
// Determine if localDay is between FromDayOfWeek and ToDayOfWeek
// cast the days to integers from 1 (Monday) to 7 (Sunday)
// create an array of days in integar days = [5, 6, 7, 1]
// if days.contains(localDays), check the times
...
Can anyone suggest some better solutions? I am not sure if mine works, and there are holes in how to deal with Day Light Saving time, since the zone will change, and how to check the time range. I am new to C#, any suggestions of libraries I can use is great!
Instead of converting both start and end times from UTC, just convert the other time into UTC
TimeZoneInfo chinaTimeZone = TimeZoneInfo.CreateCustomTimeZone(zoneID, TimeSpan.Parse("-08:00"), displayName, standardName);
DateTime FromTime = new DateTime(2018, 0, 19, 13, 0, 0); // year, month, day, hour, minute, second : Friday 1pm
DateTime ToTime = new DateTime(2018, 0, 21, 1, 0, 0); // year, month, day, hour, minute, second : Monday 1am
DateTime nowinUTC = DateTime.UtcNow;
DateTime nowInChina = TimeZoneInfo.ConvertTimeFromUtc(nowinUTC, chinaTimeZone);
if(FromTime< nowInChina && ToTime> nowInChina)
{
// Time is within the from and two times
}
Per the comments on #Moffen's answer, you only want to check if Now is within a specific DayOfWeek range:
public void CheckAll(List<SomeClass> spans)
{
var chinaTZ = TimeZoneInfo.CreateCustomTimeZone(zoneID, TimeSpan.Parse("-08:00"), displayName, standardName);
var nowInChina = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, chinaTZ);
foreach ( var span in spans )
{
if (InRange(nowInChina, span.startDay, span.endDay))
// Do something on success
// Check for valid times here
;
else
// Do something on Failure
;
}
}
public bool InRange(DateTime dateToCheck, DayOfWeek startDay, DayOfWeek endDay)
{
// Initialise as one day prior because first action in loop is to increment current
var current = (int)startDay - 1;
do
{
// Move to next day, wrap back to Sunday if went past Saturday
current = (current + 1) % 7;
if (dateToCheck.DayOfWeek == (DayOfWeek)current)
return true;
} while (current != (int)endDay);
return false;
}
What is the best way to compare two DateTime in a specific format and trigger code if DateTime has passed.
My DateTime is formatted as 4/26/2017 10:00:00 AM
DateTime currentDateTime = DateTime.Now;
DateTime eventDateTime = DateTime.Parse("4/26/2017 10:00:00 AM");
int result = DateTime.Compare(currentDateTime, eventDateTime);
if (result < 0)
Response.Write( "is earlier than Do Nothing");
else if (result == 0)
Response.Write("is the same time as: Do Nothing");
else
Response.Write("Time is greater, Trigger Action ");
Is the above code fine for comparison or we can improve it.
For my opinion, the method you suggested is the most efficiant and accepted way to compare 2 DateTime variables in C#, considering you need to take action if the 2 dates are also equal.
Side note:
If you only needed to compare the 2 DateTime without the equal condition, you could just write:
if (currentDateTime < eventDateTime)
Response.Write("is earlier than Do Nothing");
else
Response.Write("Time is greater, Trigger Action");
which is a bit cleaner and more efficiant.
To compare Dates, your method is efficient one because according to MSDN
The CompareTo method compares the Ticks property of the current instance and value but ignores their Kind property. Before comparing DateTime objects, make sure that the objects represent times in the same time zone.
So as it does compare Ticks of two instances of DateTime, so it is the efficient method for comparison.
As a side note, if you want to find interval between DateTime Instances then you can use DateTime.Subtraction it will give TimeSpan of both DateTime instances. So you can find total difference in their minutes, hours, days, seconds, milliseconds by using TimeSpan properties.
DateTime date1 = new DateTime(2010, 1, 1, 8, 0, 15);
DateTime dateNow = DateTime.Now;
TimeSpan interval = dateNow.Subtract(date1);
double totalHours= interval.TotalHours;
double totalMinutes = interval.TotalMinutes;
double totalSeconds= interval.TotalSeconds;
double totalMilliseconds= interval.TotalMilliseconds;
You can use nested if else statement as below:
if (currentDateTime < eventDateTime)
Response.Write("is earlier than Do Nothing");
else if(currentDateTime > eventDateTime)
Response.Write("time is greater, Trigger Action");
else
Response.Write("is the same time as: Do Nothing");
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.
How would you construct a data-structure -represented as Schedule class below- which would give you enough information in order to provide your ExtensionMethod a simple and enough information so that it can set testResult_# variables.
Note that : I am not asking how to implement GetScheduleById(string) method but asking for defining the Schedule data-structure in aw way that it can be simple as much as possible by providing all the needed info to this method.
var datetimeUnderTest = DateTime.Now;
Schedule workingDaysSchedule = ScheduleRepository.GetScheduleById("dubaiWorkingDays");
Schedule workingHoursSchedule = ScheduleRepository.GetScheduleById("usaWorkingHours");
Schedule winterSchedule = ScheduleRepository.GetScheduleById("brasilWinter");
Schedule nightsInWeekendSchedule = ScheduleRepository.GetScheduleById("NightsInWeekends");
Schedule nightsInWeekDaysSchedule = ScheduleRepository.GetScheduleById("NightsInWeekDays");
// IsInSchedule is an Extension method on DateTime
bool testResult_0 = datetimeUnderTest.IsInSchedule(workingDaysSchedule );
bool testResult_1= datetimeUnderTest.IsInSchedule(workingHoursSchedule );
bool testResult_2 = datetimeUnderTest.IsInSchedule(winterSchedule);
bool testResult_3 = datetimeUnderTest.IsInSchedule(nightsInWeekendSchedule );
bool testResult_4 = datetimeUnderTest.IsInSchedule(nightsInWeekDaysSchedule );
Thanks!
Seems to me that a Schedule is just a list of date/time ranges. Or, perhaps a set of time ranges in each of the 7 week days. For example, a workingDaysSchedule would probably consist of one entry for each day (Monday through Friday). That entry would be from 00:00 to 23:59:59.
nightsInWeekendSchedule would have entries for Friday and Saturday, whatever the night time hours are.
The simplest way to model it (not necessarily the most efficient), would be something like:
class TimeRange
{
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
}
class Schedule
{
private List<List<TimeRange>> Days = new List<List<TimeRange>>
{
new List<TimeRange>(), // Sunday
new List<TimeRange>(), // Monday
// etc.
};
public Add(DayOfWeek day, DateTime startTime, DateTime endTime);
public Contains(DateTime test);
}
Given a DateTime value, you can find the day of week (DateTime.DayOfWeek), and then do a linear search on the list of ranges for that day.
There are a few complications that you'll have to take into account. An 11:00 pm to 7:00 am shift on Friday, for example, will actually be two ranges: 11:00 pm to midnight on Friday, and then 12:01 am to 7:00 am on Saturday. Your code to add the range will have to split the range and add to both Friday and Saturday's schedule.
Or, you could avoid the complication of the Add method and just code the range using the collection initializer syntax.
Every time that I create a non-nullable datetime in my mvc3 application it defaults to now(), where now is current date with current time. I would like to default it to today's date with 12am as the time.
I'm trying to default the time in my mvc...but...the following isn't setting to todays date #12am. Instead it defaults to now with current date and time.
private DateTime _Begin = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 12, 0, 0);
public DateTime Begin { get { return _Begin; } set { _Begin = value; } }
How can I set to 12am for the current date for non-nullable datetime?
You can use the Date property of the DateTime object - eg
DateTime midnight = DateTime.Now.Date;
So your code example becomes
private DateTime _Begin = DateTime.Now.Date;
public DateTime Begin { get { return _Begin; } set { _Begin = value; } }
PS. going back to your original code setting the hours to 12 will give you time of noon for the current day, so instead you could have used 0...
var now = DateTime.Now;
new DateTime(now.Year, now.Month, now.Day, 0, 0, 0);
I believe you are looking for DateTime.Today. The documentation states:
An object that is set to today's date, with the time component set to 00:00:00.
http://msdn.microsoft.com/en-us/library/system.datetime.today.aspx
Your code would be
DateTime _Begin = DateTime.Today;
Using some of the above recommendations, the following function and code is working for search a date range:
Set date with the time component set to 00:00:00
public static DateTime GetDateZeroTime(DateTime date)
{
return new DateTime(date.Year, date.Month, date.Day, 0, 0, 0);
}
Usage
var modifieddatebegin = Tools.Utilities.GetDateZeroTime(form.modifieddatebegin);
var modifieddateend = Tools.Utilities.GetDateZeroTime(form.modifieddateend.AddDays(1));
Only need to set it to
DateTime.Now.Date
Console.WriteLine(DateTime.Now.Date.ToString("yyyy-MM-dd HH:mm:ss"));
Console.Read();
It shows
"2017-04-08 00:00:00"
on my machine.
Related, so I thought I would post for others. If you want to find the UTC of the start of today (for your timezone) the following code works for any UTC offset (-23.5 thru +23.5). This looks like we add X hours then subtract X hours, but the important thing is the ".Date" after the add.
double utcOffset= 10.0; // Set to your UTC offset in hours (eg. Melbourne Australia)
var now = DateTime.UtcNow;
var startOfToday = now.AddHours(utcOffset - 24.0).Date;
startOfToday = startOfToday.AddHours(24.0 - utcOffset);
Most of the suggested solutions can cause a 1 day error depending on the time associated with each date. If you are looking for an integer number of calendar days between to dates, regardless of the time associated with each date, I have found that this works well:
return (dateOne.Value.Date - dateTwo.Value.Date).Days;
Try this:
DateTime Date = DateTime.Now.AddHours(-DateTime.Now.Hour).AddMinutes(-DateTime.Now.Minute)
.AddSeconds(-DateTime.Now.Second);
Output will be like:
07/29/2015 00:00:00