In a game, I want to trigger an event every night at midnight. The following code is not working:
void Update()
{
var tomorrow = DateTime.Now.AddDays(1).ToShortDateString();
var today = DateTime.Now.ToShortDateString();
if (tomorrow == today)
{
THE THING I WANT TO HAPPEN AT MIDNIGHT;
}
}
In debugging I have found that THE THING I WANT TO HAPPEN works fine. However, the event isn't triggering from the if statement.
I searched the archives for answers, and found some, but the solutions aren't working - this is almost certainly a simple error due to my extremely low-level programming knowledge.
Any assistance would be great...Thanks!
Your code is effectively asking "1 == 1 + 1?" and the answer will always be no.
You need to keep the last execution date stored outside the method, and you also need to be more careful how you do your comparison. For instance, DateTime.Now == DateTime.Now might return false (and pretty often), because DateTime stores the time down to the tick, and if it's off by even one tick, it won't be considered equal.
Try this:
DateTime lastExecutionDate = DateTime.Utc;
void Update()
{
var tomorrow = DateTime.Now.AddDays(1).ToShortDateString();
var now = DateTime.Utc;
if (lastExecutionDate.Day < now.Day)
{
lastExecutionDate = now;
// this code will be called as close to midnight as unity allows.
}
}
I'm not sure if it will be considered midnight immediately when the game starts. If it is, try using this for lastExecutionDate instead...
DateTime lastExecutionDate = DateTime.UtcNow + TimeSpan.FromDays(1);
if (tomorrow == today) this will never be true. If it is exactly midnight (Jan 01 2000 12:00:00.0000) then this line: tomorrow = DateTime.Now.AddDays(1) will equal midnight...tomorrow, all the time (Jan 02 2000 12:00:00.0000). The same applies to every other date-time.
You're better off checking to see if the current time is midnight, then store the current date somewhere and DoTheThing() only if it's currently midnight and the stored date is not today's date.
Of course, this also ignores the issue of "do you want the event to be triggered retroactively if the application is not actively run at midnight." In which case, TimeSpans and Last_run_date may be of interest.
Related
I receive a date like 1.01.2022 h:00, m:00, s:00, ms: 00
What is the best approach to get the date at the end of the day, something like: 01.01.2022 h:23, m:59, s:59, ms: 999?
I tried those 2 ways:
var endOfDay = new TimeSpan(0, 23, 59, 59, 999);
time = time.Add(endOfDay);
and
time = time.AddDays(1).AddMilliseconds(-1);
This removes all doubt down to the resolution of a single tick. In the code below, assume that dateAndTime could include a non-zero time component.
dateAndTime.Date.AddDays(1).AddTicks(-1);
This
ensures we are only working with a date that has no time component as our reference point/date
moves us to the next date at midnight
subtracts a single tick, bringing us back to our reference date with a full-resolution time component (you could do milliseconds if you prefer, just know it's less resolution).
While this works, it's generally better to consider an alternate design that doesn't rely on a time component at all (e.g. use a given date at midnight on the next day to act as a virtual end-of-day for the given reference date).
If you want just to print out the range, the action format is opinion based. If you, however, want to check if some time is within or without the day, please do it as (note >= and <)
if (timeOfQuestion >= day.Date && timeOfQuestion < day.Date.AddDays(1)) {
...
}
Using onstructions like endOfDays = time.AddDays(1).AddMilliseconds(-1) is dangerous:
please, note that day.Date.AddMilliseconds(999.5) - double value - should be within the day.
hy am trying to compare current date and trying to perform action when specific date comes. but it doesn't work in c# unity.
here is code:
string over = "2017/06/28 22:38:30";
string dateAndTimeVar = System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
if (dateAndTimeVar == over)
{
print(dateAndTimeVar);
}
I believe that the main problem you are having is using the "==" operator.
Since you are wanting it to be equal down to the second, you are relying on Unity to run your piece of code every second. If this is not the case, then your code may be missed when the time comes.
Since you are using strings you are stuck with this option. You could instead use DateTime objects to perform the comparison.
var over = DateTime.Parse("2019/04/01 17:09:10");
var dateAndTimeVar = System.DateTime.Now;
if (dateAndTimeVar >= over)
{
print(dateAndTimeVar);
}
string dateInput = "2017/06/28 22:38:30";
DateTime parsedDate = DateTime.Parse(dateInput);
if (DateTime.Now > parsedDate)
{
print("It is Over");
}
You Can Parser to a DateTime, And Compare that.
When I had common problem, I used DateTime.Now to get current time.
For comparring DateTime objects, it's easy to use DateTime.Ticks property.
As I understood from your code, you want to execute print(dateAndTimeVar);, when comes the time.
It's better to compare, like:
if(DateTime.Now.Ticks >= dateTimeOver)
{
print(dateAndTimeVar);
}
System.DateTime.Now, in its most basic form, uses the clock of the computer running the program. Since you're including seconds in your datetimes, for the conditional to evaluate to true you would have to evaluate it at the exact second specified in the string 'over'. Going from only the posted code it would be highly unlikely that you'd be able to run the program and evaluate the conditional at the exact second specified without something else to control the environment. A simple way to debug this would be to print out the variables or use a debugger to actually see what the computer is seeing at the time of execution.
In my project I need to calculate dates for repeating events. At the beginning I just have a start date/time and the information how this event must repeat:
Every Day
Every Week
Every 2 Weeks
Every 3 Weeks
Every Month
Every 2 Months
...
What is the right way to do this? It should work correctly with different time zones and day saving time settings. I think I should just add days/weeks/month to the local DateTime and then convert it to UTC. But I'm not sure about this. What happens if I add a few days and this will be the time when we need to adjust our clocks forward one hour. In this case this time will not exists.
Below is the code I wrote, but I'm not sure that it works correctly in every case:
private static List<DateTime> FindOccurrences(DateTime localStart, Repeat repeat, int occurrences)
{
var result = new List<DateTime> { localStart };
switch (repeat)
{
case Repeat.Daily:
for (int i = 1; i <= occurrences; i++)
result.Add(localStart.AddDays(i));
break;
case Repeat.Every2Weeks:
for (int i = 1; i <= occurrences; i++)
result.Add(localStart.AddDays((7 * 2) * i));
break;
...
}
return result;
}
public List<Event> CreateRepeating(string timeZone, Repeat repeat, int repeatEnds, DateTime localStart, int eventDuration)
{
var events = new List<Event>();
var occurrences = FindOccurrences(localStart, repeat, repeatEnds);
foreach (var occurrence in occurrences)
{
var item = new Event();
item.Start = occurrence.ToUtcTime(timeZone);
item.End = occurrence.ToUtcTime(timeZone).AddMinutes(eventDuration);
events.Add(item);
}
return events;
}
PS:
All dates are stored in UTC format in the database.
Scheduling or calculating the dates of future events, especially recurring events, is a very complex subject. I've written about this a few times, though from the perspective of other languages (See: 1, 2, 3, 4).
I'm afraid this is too broad of a subject to give you the exact code to run. The details will be very specific to your application. But here are some tips.
In general:
Use UTC only for the projected moment in time that a single instance of the event is to occur.
Store the actual event in local time. Store the time zone id also.
Do not store the time zone offset. That should be looked up for each occurrence individually.
Project upcoming occurrence(s) of the event as UTC so you know how when to perform an action based on the event (or whatever makes sense for your scenario).
Decide what to do for daylight saving time, when an occurrence falls into a spring-forward gap, or a fall-back overlap. Your needs may vary, but a common strategy is to jump ahead of the spring gap, and choose the first occurrence in the fall. If you're not sure what I mean, refer to the dst tag wiki.
Think carefully about how to handle dates near the end of a month. Not all months have the same number of days, and calendar math is difficult. dt.AddMonths(1).AddMonths(1) is not necessarily the same as dt.AddMonths(2).
Stay on top of time zone data updates. Nobody can predict the future, and the governments of the world like to change things!
You need to retain the original local-time values of the schedule, so that you can re-project the UTC values of the occurrences. You should do this either periodically, or whenever you apply a time zone update (if you're tracking them manually). The timezone tag wiki has details about the different time zone databases and how they are updated.
Consider using Noda Time and IANA/TZDB time zones. They are much more suited for this type of work than the built in types and time zones Microsoft provides.
Be careful to avoid using the local time zone. You should have no calls to DateTime.Now, ToLocalTime, or ToUniversalTime. Remember, the local time zone is based on the machine where the code is running, and that should not impact the behavior of your code. Read more in The Case Against DateTime.Now.
If you are doing all of this to just kick off a scheduled job, you should probably take a look at a pre-canned solution, such as Quartz.NET. It is free, open source, highly functional, and covers a lot of edge cases you may not have thought about.
Partial answer. I'd rather change the implementation to
public enum TimePeriod {
None = 0,
Day = 1,
// Just week, no "two weeks, three weeks etc."
Week = 2,
Month = 3
}
public static class Occurrencies {
// May be you want to convert it to method extension: "this DateTime from"
public static IEnumerable<DateTime> FindInterval(DateTime from,
TimePeriod period,
int count) {
while (true) {
switch (period) {
case TimePeriod.Day:
from = from.AddDays(count);
break;
case TimePeriod.Week:
from = from.AddDays(7 * count);
break;
case TimePeriod.Month:
from = from.AddMonths(count);
break;
}
yield return from;
}
}
}
Use:
// Get 5 2-week intervals
List<DateTime> intervals2Weeks = Occurrencies
.FindInterval(DateTime.Now, TimePeriod.Week, 2)
.Take(5)
.ToList();
// Get 11 3-month intervals
List<DateTime> intervals3Months = Occurrencies
.FindInterval(DateTime.Now, TimePeriod.Month, 3)
.Take(11)
.ToList();
I have a windows service and I would like to insert a timer. How can I check if the present time is 9:00 AM ?
I would like my service to check this every day. Thank you a lot
My try:
Datetime dt=Datetime.parse("09:00:00 everyday");
if(datetime.now -dt ==0)
{
//fire event
}
Thats kinda sily of me though.
You need to make a timer and sets its interval to the timespan between now and tomorrow 9:00 AM. Next time the timer tick, set the interval again in the same way.
You should use this Timer class:
http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx
Please use DateTime.UtcNow.Hour to check current hour
By using UtcNow you will gets a DateTime object that is set to the current date and time on the computer, expressed as the Coordinated Universal Time (UTC).
var now = DateTime.Now;
var today = now.Date;
var nineAm = today.AddHours(9);
TimeSpan ts = nineAm - now;
var timeInMillisecondsTill9Am = ts.Milliseconds;
If(timeInMillisecondsTill9Am==0)
{
//your code goes here
}
Since you don't know when someone may shutdown or reboot your computer or service then you need to make sure that you use a method robust enough to handle these kinds of interruptions.
I suggest that when your service checks every 5 minutes or so if the time is after 9am and if the last run date is yesterday. If so, you update the last run date to day (perhaps in a simple text file) and then run the "9:00am" task. In this way your task only runs once per day, fairly close to 9am, and is robust against reboots.
You'll need to use a standard .NET timer to trigger the checks - and if you're clever enough you can make it fire a few seconds after 9am.
Let me know if that's a good solution.
I have never created a reminder application. Here is how I see it. Please let me know if I'm on the right way.
So I have users from different timezones.
ID DateTimeUTC TimeZoneID
1 2011-07-12 02:15:15.000 TimeZneID1
2 2011-07-13 16:00:00.000 TimeZneID2
3 2013-11-03 17:00:00.000 TimeZneID3
4 2011-08-22 03:00:00.000 TimeZneID4
5 2011-07-16 22:00:00.000 TimeZneID5
Create a scheduled process to run every 15 mins and do the steps below:
Get records;
The second is to convert DateTimeUTC to Time for the right timezone
Compare if it's match
a. Send Reminder
var tzi = TimeZoneInfo.FindSystemTimeZoneById(TimeZneID1);
var local = TimeZoneInfo.ConvertTimeFromUtc(DateTimeUTC, tzi);
var timeNow = TimeZoneInfo.ConvertTimeFromUtc(DateTime.Now, tzi);
if(local == timeNow)
SendReminder();
Is it efficient way? is it the right way?
If date/time values are already in UTC in the database, you don't need to perform any conversions, surely... you just need to see whether the current UTC instant is a match, and if so, send the reminder.
That's assuming you really mean it's UTC in the database, i.e. you've converted it from the user's local time when they entered the reminder (assuming they did so to start with).
Typically, when dealing with dates like this, you would do all of your calculations in UTC and only switch to local time when it's time (no pun intended) to display the results. I assume from your question that this is a centralized database that's managing all the tasks, and you just need them to run at the correct local time?
if ( dateTimeUtc == DateTime.UtcNow )
{
// If your reminder needs to display the local time, pass it in:
var tzi = TimeZoneInfo.FindSystemTimeZoneById(TimeZneID1);
SendReminder(TimeZoneInfo.ConvertFromUtc(DateTime.UtcNow, tzi));
}
Note that DateTime.Now is in local time; you want DateTime.UtcNow for consistancy across time zones.
Another thing to be aware of is you are only running your task scheduler every 15 minutes, so the odds of times like 02:15:15 matching exactly are slim. What you would typically want to do is check for any reminder times that came up since the last run:
var currentRun = DateTime.UtcNow;
foreach ( dateTimeUtc in GetReminderDateTimes() )
{
if ( dateTimeUtc > lastRun && dateTimeUtc <= currentRun )
{
}
}
lastRun = currentRun;
In my opinion you might be over-complicating it. Since you are storing things in UTC, have the reminders in UTC, and match on UTC. Then just associate the reminders with the users that you want to remind.
I want to do similar stuff and have been debating what would be an appropriate approach. Essentially, I am writing an application which would send a message for a particular country at midnight (localtime). I have about 100 such countries and I have to also be mindful of daylight savings. I can only think of 2 ways to do this,
1. Create a corresponding thread for each country and which wakes up every hour or so and checks if it is midnight(local time) and send out the message. So essentially, I will be creating 100 threads doing nothing most of the time.
2. In this approach, there will be only one timer which checks every minute or 30 secs the local time for 100 countries and send message. There will need to be some extra logic as there will never be an exact midnight match.
Not sure, if there is any better way to tackle above situation. It would be great if I can get some ideas/suggestions here.
Thanks,
SP.