Timer Max interval - c#

Using System.Windows.Form.Timer the interval is an int, which gives a maximum interval limit of around 25 days. I know I could create some arbitrary algorithm to start another timer once the limit is reached, but that's just daft.
MISLEADING-IGNORE-->So if I want to set it to around 29 days (2619609112.7228003) milliseconds?<--MISLEADING-IGNORE
EDIT:
The real question here is how can I set System.Windows.Form.Timer to a value higher than maxInt?
The purpose is that I need to set an interval from whenever to the first day of the next month, so it could be 28,29,30 or 31 days, and when that interval expires, calculate the interval to the next first day of the month.
(Basically a Crystal Report is to be run on the 1st day of the month and printed (around 500 pages), because of the length of the reports it is to be run out of hours so it doesn't tie up the printer.)
e.g. run it today (today is 1/12/15), 1/1/16 is next 'first day of the month' so set the interval to the milliseconds between now and then.
1/1/16 comes around so the timer ticks, then calculate and set the interval for 1/2/2016 (the next first day of the month).
#SeeSharp - I did see that question, but I am working on a legacy app and am unsure of the implications of changing the timer, but if I can't get this timer to work I may look at the threading one, thanks.
EDIT2: Thanks for all of your suggestions, I've opted for a 3rd party plugin called FluentScheduler

Set the timer interval to one day (say) and use it to count the number of days up to 29.
Edit
Set the timer to half a day (say) and use it to check that the date is the first of the month.

How about a Month timer - This will fire close to midnight when the month changes. May be that suits your requirement better ?
If we have to consider day-light saving too, then perhaps the timer should fire at 2:00 AM on the 1st day of month so I'll make it configurable.
Here is a code to explain my idea -
public class MonthTimer : IDisposable
{
public event EventHandler<MonthChangedEventArgs> MonthChanged;
DateTime mLastTimerDate;
Timer mTimer;
public MonthTimer(TimeSpan timeOfFirstDay)
: this(DateTime.Now, timeOfFirstDay)
{
}
public MonthTimer(DateTime currentDate, TimeSpan timeOfFirstDay)
{
mLastTimerDate = currentDate.Date;
var milliSecondsInDay = new TimeSpan(1, 0, 0, 0).TotalMilliseconds;
Contract.Assert(timeOfFirstDay.TotalMilliseconds <= milliSecondsInDay); // time within 1st day of month
DateTime currentDateLastSecond = currentDate.Date.AddDays(1).AddTicks(-1); // one tick before midnight
TimeSpan timeSpanInCurrentDate = currentDateLastSecond.Subtract(currentDate); // remaining time till today ends
// I want the timer to check every day at specifed time (as in timeOfFirstDay) if the month has changed
// therefore at first I would like timer's timeout to be until the same time, following day
var milliSecondsTillTomorrow = (timeSpanInCurrentDate + timeOfFirstDay).TotalMilliseconds;
// since out milliseconds will never exceed - . Its okay to convert them to int32
mTimer = new Timer(TimerTick, null, Convert.ToInt32(milliSecondsTillTomorrow), Convert.ToInt32(milliSecondsInDay));
}
private void TimerTick(object state)
{
if(DateTime.Now.Month != mLastTimerDate.Month)
{
if (MonthChanged != null)
MonthChanged(this, new MonthChangedEventArgs(mLastTimerDate, DateTime.Now.Date));
}
mLastTimerDate = DateTime.Now.Date;
}
public void Dispose()
{
mTimer.Dispose();
}
}
public class MonthChangedEventArgs : EventArgs
{
public MonthChangedEventArgs(DateTime previousMonth, DateTime currentMonth)
{
CurrentMonth = currentMonth;
PreviousMonth = previousMonth;
}
public DateTime CurrentMonth
{
get;
private set;
}
public DateTime PreviousMonth
{
get;
private set;
}
}
client code
// Check the new month around 2 AM on 1st day
mMonthTimer = new MonthTimer(new TimeSpan(2, 0, 0));
mMonthTimer.MonthChanged += mMonthTimer_MonthChanged;
One thing I'm not using System.Threading.Timer, therefor the even handler will be called on a separate thread & not UI thread as incase of System.Windows.Forms.Timer if this is an issue in yr case do let me know.
Also do write me a comment if it serves yr purpose or any if any issues

Try Microsoft's Reactive Framework (NuGet "Rx-Main").
You can write this:
Observable
.Timer(DateTimeOffset.Now.AddDays(29.0))
.Subscribe(x =>
{
/* 29 Days Later */
});

Related

How to subtract two times from two datetime and calculate is elapse time from 61 seconds or not?

I'm working on an asp.net core MVC project. This project about identifying online and offline users, I have two datetime, one of the stores in a database, and another is current datetime, and I must know that time stored in a database elapsed from 61 seconds or not?
I subtract two Datetime and finally use TotalSeconds property.but my output is -22095 or 2319208 and so on.
public void CheckUserStatus()
{
DateTime now = DateTime.Now;
var userTime = _context.Sessions.Where(x => x.LastOnline).Select(x => new {x.LastConnectTime, x.Id});
foreach (var time in userTime)
{
TimeSpan diffrence = now.Subtract(time.LastConnectTime);
int mytime = Convert.ToInt32(diffrence.TotalSeconds);
if ( mytime < 61)
{
Console.WriteLine(time.Id);
}
}
}
I expect out of time base on seconds, for example, right now my output is -22095 or 2319208, and so on but I don't know 2319208 is a regular time or not?
You can easily check that like this :
DateTime now = DateTime.Now;
TimeSpan past = now - now.Subtract(TimeSpan.FromSeconds(60));
TimeSpan post = now - now.Subtract(TimeSpan.FromSeconds(61));
Console.WriteLine(now);
// Should be False: Passed time is less than 60 seconds
Console.WriteLine(past.TotalSeconds > 60);
// Should be True: Passed time is more than 60 seconds
Console.WriteLine(post.TotalSeconds > 60);

Better way to get Interval bounds when only time is given

I have to find out the limits of interval i.e upper-bound and lower-bound of an interval based on interval type when datetime is given.
Example: say given time = 12:05 (then, this lies in the interval range 12:00 - 1:00 if interval type is hourly; 12:00 - 12:30 if interval type is half-an hour based;
12:00 - 12:15 if interval type is quarterly. likewise interval type can be anything.
Currently i am loading all different set of interval ranges in a dictionary object on an application load and then i fetch interval range from this dictionary for the given time.
Sorry, I know this problem statement looks simple but couldn't think of other approaches as of now. It would be helpful if someone can help me here. Thanks in advance.
You can calculate the range start by dividing the total minutes by your interval and then subtracting the remainder from the total minutes. After that, you can easily get the end of the range.
First, you need to get the time part from your DateTime object as TimeSpan by using DateTime.TimeOfDay. Then use TimeSpan.TotalMinutes.
Here's a good start:
public class TimeRange
{
public TimeRange(TimeSpan from, TimeSpan to)
{
From = from;
To = to;
}
public TimeSpan From { get; set; }
public TimeSpan To { get; set; }
}
public TimeRange GetRange(DateTime d, int minutesInterval)
{
TimeSpan time = d.TimeOfDay;
var from = time.TotalMinutes - (time.TotalMinutes % minutesInterval);
var to = from + minutesInterval;
return new TimeRange(TimeSpan.FromMinutes(from), TimeSpan.FromMinutes(to));
}
For clarity, I created a simple class called TimeRange to represent the start and end of the interval range. You can, however, feel free to handle this in a different way.
Usage:
DateTime d = DateTime.Now;
TimeRange range = GetRange(d, 60);
//TimeRange range = GetRange(d, 15);
Console.WriteLine("From: {0}\r\nTo: {1}", range.From, range.To);
Try it online.

Calculating time with hours and minutes only

I am attempting to create a timesheet calculator which takes calculates the time an employee works and I am close, with one problem.
As I perform the calculation, I only want hours and minutes to display. I am able to get that done, but that causes an issue. If the employee punches out before a full minute is elapsed, that minute is not included in the calculation.
For example, if an emp punches in at 12:00:30 and punches out at 5:00:29, that last minute is not counted in the calculation, so the time shows as 4:59 instead of 5:00.
How do I get the calculation to be based on the hours and minutes and exclude seconds completely?
This is the code I have:
private void btnPunchOut_Click(object sender, EventArgs e)
{
DateTime stopTime = DateTime.Now;
lblPunchOutTime.Text = stopTime.ToShortTimeString();
TimeSpan timeWorked = new TimeSpan();
timeWorked = stopTime - startTime;
lblTimeWorked.Text = timeWorked.ToString(#"hh\:mm");
}
Use TimeSpan.TotalSeconds perhaps...And then add 30 seconds or more, before you convert it to hours by dividing by 3600.
As in
lblTimeWorked.Text = ((timeWorked.TotalSeconds+30)/3600).ToString("0.00") + " hours";
Use Timespan.TotalHours if you want the hours.
But if you want to be accurate, you should create a separate class dedicated to calculating the hours worked by a staff member. Then you can encapsulate lots of business rules in the dedicated class. Staff have entitlements and overtime, expenses or penalty rates - so this can get complex if done properly.
If you want a calculation that really ignores the seconds, the clearest way to accomplish that is to get rid of the seconds on both the start time and the end time. It might not seem accurate because it allows a difference of one second to become a difference of one minute. But that could still be a valid business rule, that you want to subtract according the the minutes that appeared on the clock rather than the actual elapsed seconds.
In other words,
1:00:01 is adjusted to 1:00:00.
1:00:59 is adjusted to 1:00:00.
1:01:00 is "adjusted" to 1:01:00.
1:01:01 is adjusted to 1:01:00.
You can accomplish that with an extension like this:
public static class TimespanExtensions
{
public static TimeSpan TrimToMinutes(this TimeSpan input)
{
return TimeSpan.FromMinutes(Math.Truncate(input.TotalMinutes));
}
}
(I'm sure there's a more efficient way of truncating the seconds, but at least this is clear.)
Now instead of having to figure out how to calculate the difference while rounding seconds or adding seconds, you just trim the seconds before calculating the difference. Here's a unit test:
[TestMethod]
public void NumberOfMinutesIgnoresSeconds()
{
var startTime = TimeSpan.FromSeconds(59).TrimToMinutes();
var endTime = TimeSpan.FromSeconds(60).TrimToMinutes();
Assert.AreEqual(1, (endTime - startTime).TotalMinutes);
}
One Timespan represents 59 seconds, and the next one is 60, or the first second of the next minute. But if you trim the seconds and then calculate the difference you get exactly one minute.
In the context of your code,
DateTime stopTime = DateTime.Now;
lblPunchOutTime.Text = stopTime.ToShortTimeString();
var timeWorked = stopTime.TrimToMinutes() - startTime.TrimToMinutes();
lblTimeWorked.Text = timeWorked.ToString(#"hh\:mm");

Not allowing a user to put "Clock Out" time < "Clock In" time?

Public void Fee()
{
TimeSpan span1 = TimeSpan.FromHours(dtmIn.Value.Hour);
TimeSpan span2 = TimeSpan.FromHours(dtmOut.Value.Hour);
TimeSpan span3 = TimeSpan.FromMinutes(dtmIn.Value.Minute);
TimeSpan span4 = TimeSpan.FromMinutes(dtmOut.Value.Minute);
TimeSpan span5 = span2.Subtract(span1) + span4.Subtract(span3);
lblTotal.Text = (span5.TotalHours * 3).ToString("$#.00");
}
I do not want the user to be able to be able to clock in during PM and clock out during AM(basically overnight working). Also, not allowing the clock out time being before the clock in time.
You should call new TimeSpan(hours, minutes, seconds: 0) and check whether the in TimeSpan is > the out TimeSpan.
It appears from your code sample that dtmIn and dtmOut are nullable DateTime variables. If so, all you have to do is this:
if (dtmIn.Value >= dtmOut.Value)
{
//'in' time is equal to or greater than 'out' time
... show my error message ...
}
Of course you will need to ensure the DateTime? variables have a value (i.e. do appropriate error checking before using them in the expression).
You probably need to be a little more specific with your logic. Do you mean...
The user should be able to work overnight? If so, that means you need to check to make sure that the date they clocked in is the same as the date they clocked out. `
For example...
if (dtmIn.Value.Date != dtmOut.Value.Date)
{
...
}
The user should not be able to work more than 24 hours? If so, you should subtract the two dates and use the resulting TimeSpan to see how many days they worked.
For example...
if ((dtmOut.Value - dtmIn.Value).TotalDays > 1)
{
...
}
In neither case should you check the time explicitly. For one, if I worked 25 hours then my check out time would still be after the check in time.

DateTime interval restriction in C#

The problem:
I am in process of implementing a scheduler for my advisor in school. The scheduler supposes to setup a 15 minutes interval time slot from 8:00 AM to 5:00 PM, Monday to Friday. In addition, the advisor will have to specify the start and end dates of the scheduler. The scheduler will also feature an option to specify if the 15 minutes time slot is not open. Meaning my advisor will be able to mark specific time slot as NOT AVAILABLE.
What I have so far:
I have created a simple class:
public class TimeSlot
{
public DateTime dateTime
{
get;
set;
}
public bool isAvailable
{
get;
set;
}
TimeSlot(DateTime dt, bool Avalible)
{
dateTime = dt;
isAvailable = Avalible;
}
}
The class basically represents an object for one time slot in the scheduler. I also have a list of time slots that keeps a list of the valid time slots:
List<TimeSlot> TSList = new List<TimeSlot>();
Note that a valid time slot means the following:
Date is within: Monday to Friday.
Time is within: 8:00 AM to 5:00 PM
Time slots are within: 15 minutes interval.
In addition, I have a method that fill in the TSList as the following:
private void button_Next_Click(object sender, RoutedEventArgs e)
{
/* Getting the values of fromDate and toDate from the GUI controls*/
DateTime fromDate = datePicker1.SelectedDate.Value;
DateTime toDate = datePicker2.SelectedDate.Value;
while (fromDate <= toDate)
{
/*This ensures that we only deal with days Monday to Friday*/
if (fromDate.DayOfWeek.ToString() != "Saturday" && fromDate.DayOfWeek.ToString() != "Sunday")
{
/*PROBLEM HERE!!*/
}
/*Updating fromDate: Incrementing fromDate by 1 day*/
fromDate = fromDate.AddDays(1);
}
}
Notes that I was only able to satisfy the first condition in my valid time slot conditions. Thus, I was only able to restrict the dates to be within Monday to Friday range.
The questions:
I am trying to achieve the missing two valid conditions for a time slot:
How to restrict the times to be only 8:00am to 5:00 pm?
How to make time slots separated by 15 minutes interval?
First, please use DayOfWeek.Saturday and DayOfWeek.Sunday for the comparision, converting to a string is not necessary...
Then just use a simple loop like
DateTime startSlot = fromDate.Date.AddHours(8); // Starts at 8:00AM
while (startSlot.Hour < 17) {
// Construct time slot class
startSlot = startSlot.AddMinutes(15);
}
This gives you startSlot values starting at 8:00am at every date ranging to 5pm (i.e. the last one is 4:45pm).
Why are you considering building this out of nothing?
Why are you not starting with one of the many calendar management programs that are available off the shelf? For example, Microsoft Outlook contains calendar and schedule management, and you can do all of what you describe, easily. It also integrates with other scheduling tools via .ICS files, it syncs with mobile devices, syncs with Google Calendar, and so on.
But there are lots of other options. Google Calendar is another obvious one.
I don't know why you would ever consider starting from scratch. Unless it's an academic exercise (and no, I don't mean that you work in academia), then you should use larger building blocks to start.
It's like building a structure, starting with sand and water, instead of pre-fabricated concrete block.
Just quick implementation. Let me know if you need some comments.
// Round interval
const int roundInterval = 15;
var remainder = fromDate.TimeOfDay.Minutes % roundInterval;
var curTime = remainder == 0 ? fromDate : fromDate.AddMinutes(roundInterval - remainder);
curTime = curTime.AddSeconds(-curTime.TimeOfDay.Seconds);
var delta = TimeSpan.FromMinutes(roundInterval);
while (curTime < toDate)
{
while (curTime.DayOfWeek == DayOfWeek.Saturday || curTime.DayOfWeek == DayOfWeek.Sunday)
{
curTime = curTime.Date.AddDays(1);
}
if (curTime.TimeOfDay.Hours < 8)
{
curTime = curTime.AddHours(8 - curTime.TimeOfDay.Hours);
curTime = curTime.AddMinutes(-curTime.TimeOfDay.Minutes);
continue;
}
if (curTime.TimeOfDay.Hours >= 17)
{
curTime = curTime.AddHours(24 - curTime.TimeOfDay.Hours);
curTime = curTime.AddMinutes(-curTime.TimeOfDay.Minutes);
continue;
}
TSList.Add(new TimeSlot(curTime, true));
curTime = curTime.Add(delta);
}
}
DateTime myScheduledTimeSlot = new DateTime(2010, 10, 26, 8, 45, 0);
// Use existing check to check day of week constraint...
// Check if the datetime falls on a correct minute boundary
switch (myScheduledTimeSlot.Minute)
{
case 0:
case 15:
case 30:
case 45:
// The time slot is valid
break;
default:
// The time slot is not valid
break;
}
It is pretty simple to check whether it falls in a 15 minute slot as you don't have weird boundaries keeping every hour identical. I'd recommend checking out Quart.NET if you want to save some time doing eventing/scheduling.

Categories

Resources