I have an application which records statistics. When a user submits their statistics the information is stored and the date/time is recorded as UTC e.g.
In the UK if I recorded figures at 03/08/2010 10:30 my recorded date/time would be 03/08/2010 09:30 as we are currently observing DST.
If I was in say Australia, and I recorded my statistics at the same time locally my recorded date/time would be 02/08/2010 23:30 as I think they are around 10 hours ahead.
My application needs to generate a statistical report at 7am every morning (local time) for everyone. So when it runs it does something like this:
var currentUtc = DateTime.UtcNow.AddDays(-1); // UTC date/time 24 hours from current
This date (time is ignored) is then used to pull the statistics from the database. Now this is working fine for the UK. It is other international countries I am finding the problems. More specifically Australia.
The issue I have is Australia are 10 hours ahead so my application is being triggered to run at around 03/08/2010 22:00 UK time to generate a report for 04/08/2010 07:00 Australia time. So what is happening is something like:
// the date/time at this point would be 03/08/2010 22:00 (UTC 03/08/2010 21:00)
var currentUtc = DateTime.UtcNow.AddDays(-1);
// so currentUtc is set to 02/08/2010 22:00 (which makes sense for UK, but not for Aussie)
If the above was a report for someone in the UK it means they would receive a report for 02/08/2010 on 03/08/2010 which is correct. However, for Australia (as they are 10 hours ahead) that means they would be receiving a report for 02/08/2010 on 04/08/2010 which would be 2 days behind.
What should be happening is when my application runs at 22:00 hours on 03/08/2010 it should be generating a report for Australia which is for 03/08/2010. However, as per the setup above you can see why this isn't happening.
I can't quite seem to put my finger on this one? I have a feeling it is something to do with my ignoring the time and just relying on the UTC date but I am not sure.
Because the user's day may not start and end at midnight UTC time, your date field in the database needs to include the time as well. Otherwise your Australian report will contain more than 24 hours of statistics. The date range from 02/08/2010 to 04/08/2010 only looks odd if you ignore the time. If you include the time and double-check the range, you'll see that it does contain 24 hours just like intended. When you convert it back to local time for displaying the report it will be fine.
the mismatch between "query at 7am local time" and "previous day UTC" seems to be odd IMHO - I'd imagine the user would instead want the previous day of data, with 'day' being in their local time, so the range you would want to search within is DateTime.Today.AddDays(-1) to DateTime.Today (so it covers 'yesterday, local time'), and since you're storing in UTC, we just add in ToUniversalTime to each of those to get them into UTC for querying the database.
var startOfToday = DateTime.Today.ToUniversalTime();
var startOfYesterday = startOfToday.AddDays(-1);
var query = ...
Related
I am storing date and time separately from Angular to C#. While storing in database, I combine start date and start time field in C# and store it in dAtabase UTC like this: "2020-02-28T22:30:30Z".
While returning from c#, i create a new DateTime with start date and start time and return as one variable. However, if the date is 28/02/2020 and time is 4.00 am, with timezoneoffset of 5.30 India, the date gets rendered to 29/02/2020 4.00 am.
Is it possible to get date and time in Angular and render it separately as string etc.
Thanks
In JavaScript Date is a timestamp, counts the number of miliseconds since January 1, 1970 00:00 UTC. So you might be having a problem with timezones. Check this answer I gave a couple of weeks ago, it may guide you. I also add a function to solve it and some references there, Subtract day in conversion between Moment and LocalDate.
angular set timezone based on user browser timezone so you can change datetimes to another timezone. please read it How to convert Date in angular to another time zone
I have one issue which I resolved by myself but yet need some confirming words whether I am 100% correct on my thought, just because there is not any documentation I found to prove myself correct.
My server is in DST time currently, CRM UI is also showing 1 hour up then data stored in db. that's fine.
When I calculate and store date with plugin, after my plugin update operation finishes, CRM platform deducts 1 hour from data I saved. I have read that when we do some operation via SDK related date time, CRM stores date time as it is. is it the case that when time is in DST, platform also get involves to deduct 1 hour by then ?
As a resolution, I have commented out my line of deducting 1 hour and letting CRM to do it now.
Am I correct on my understanding or it would be appreciable if some one can provide any documentation URL.
Any time you're working in the SDK, DateTimes are retrieved and stored as UTC. The CRM web platform will convert the UTC time into the user's time. You shouldn't need to be doing any Conversions of time, just using UTC.
Let's assume your local time is UTC -1 (with DST since UTC doesn't observe it). So if UTC is 14:00, your local time is 13:00.
Let's also assume your plugin in going to populate a date attribute on the entity that is for the current time, tomorrow. If your code looks like this:
entity.new_SomeDate = DateTime.Now.AddDays(1);
service.Update(entity);
Assuming DateTime.Now is 13:00, it'll store 13:00 in the database (as if it was UTC). Then when you go to look at the time value from the CRM website, since you're UTC - 1 it'll display 12:00, even though you wanted 13:00.
Now if your code looks like this:
entity.new_SomeDate = DateTime.UtcNow.AddDays(1);
service.Update(entity);
Assuming DateTime.Now is 13:00, it'll store 14:00 in the database since DateTime.UtcNow is 14:00. Then when you go to look at the time value from the CRM website, since you're UTC - 1 it'll display 13:00, since it'll take the UTC value - 1.
Now if your code looks like this:
entity.new_SomeDate = entity.new_UserEnteredDateFromCrm.AddDays(1);
service.Update(entity);
The new_UserEnteredDateFromCrm will already have been converted from the users' Time Zone to UTC and it'll work as expected.
This makes DateTimes that you would prefer to store as Dates very difficult though (birthdays anyone?) and you may have to think through it a little more in depth.
I've figured it out myself. that date has component in it which explores what kind of date time is it in. it can be either Utc, Local or Unspecified.
when you pass any date to CRM attribute via code. make sure that date time kind is Utc, otherwise CRM service internal operation will convert it to be into Utc.
In my case, I was stucked to this because when I read date from CRM, I had to set office start in that date. e.g. I needed to make 03/02/2014 12:00 to 03/02/2014 8:30 to make incoming date aligned with office start time. I was doing operation like,
DateTime InDate = Case.CreatedOn.Value;
DateTime Dt = new DateTime(InDate.Year,InDate.Month,InDate.Day,8,30,0)
Having InDate in Utc Time, I was creating new date object so it lost DateTime kind from Utc to Local ( having in DST it signifies to be 1 hour up)
to avoid, always set DateTime kind to be exactly as provided In date to new object. e.g. above operation can be done alternatively like.
DateTime InDate = Case.CreatedOn.Value;
DateTime Dt = new DateTime(InDate.Year,InDate.Month,InDate.Day,8,30,0)
Dt = DateTime.SpecifyKind(Dt,DateTimeKind.Utc)
Hope that Helps.
I have an object that has properties currently as DateTime.
The object is marked as valid within a time frame. The default being 00:00:00 to 23:59:59
The user enters the value in the UI and the property is set via:
new DateTime(DateTime.Now.Year,
DateTime.Now.Month,
DateTime.Now.Day,
model.Hours,
model.Minutes,
model.Seconds)
This is then converted to UTC when it hits the database.
Today's date is 29th August 2013. If a colleague in India runs this program it will store the data in the database as 28th August 2013 18:30:00 as they are 5.5 hours ahead of UTC so 29th August 2013 00:00:00 becomes yesterday.
When the logic tries to determine if the object is valid the logic is:
if (DateTime.UtcNow.TimeOfDay > model.MyPropertyFromDB.TimeOfDay)
We are trying to determine if the current time is within a range of 00:00:00 and 23:59:59
This fails as 14:00 (current time) is not greater than 18:30
What would be the best approach to compare just times?
Would storing the values as DateTimeOffSet help, is using ToLocal() ok?
Other considerations are that a user in India is using the app which is hosted in the UK so it needs to be timezone aware.
Thanks
Like others, I'm still unclear on exactly what you are wanting. But clearly, you shouldn't do this:
new DateTime(DateTime.Now.Year,
DateTime.Now.Month,
DateTime.Now.Day,
model.Hours,
model.Minutes,
model.Seconds)
That would be much better as:
DateTime.Today.Add(new TimeSpan(model.Hours, model.Minutes, model.Seconds))
But why are you doing this to begin with? Either of these would give you back the local date. I assume this is going to run on a server, so do you really want the time zone of the server to influence this result? Probably not. Please read: The Case Against DateTime.Now.
If you wanted the UTC date, you could do this:
DateTime.UtcNow.Date.Add(new TimeSpan(model.Hours, model.Minutes, model.Seconds))
That would at least be universally the same regardless of your server's time zone. But still, I don't think this is what you are after.
What's not clear is why is the user only entering the time while you are assigning the current date. If the date is relevant, then shouldn't the user enter it and it would be part of your model?
If the date is not relevant, then why are you storing it? You can use a TimeSpan type for the time value internally. You didn't say what your database is, but let's just guess that it is SQL Server, in which case you could use the time type on the field in the table.
I suppose it's possible that the date is relevant, but you want to control it, while the user takes control of providing the time. If that's the case, then you must know the time zone of the user (or the time zone of whatever the context is if it's not the user). Assuming you had a Windows time zone identifier (see the timezone tag wiki), then you could do something like this:
var tz = TimeZoneInfo.FindSystemTimeZoneById(theTimeZoneId);
var local = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tz);
var dt = local.Date.Add(new TimeSpan(model.Hours, model.Minutes, model.Seconds));
If you don't have the time zone information, then this wouldn't be possible to solve.
As general advice, you might want to try using Noda Time instead of the built-in stuff. It's much better at helping you figure out this sort of thing. From the main page:
Noda Time is an alternative date and time API for .NET. It helps you to think about your data more clearly, and express operations on that data more precisely.
That appears to be directly the problem you are having here. If you want to clarify some of the questions I asked, I'd be happy to edit my answer and show you exactly how to do this with Noda Time.
Why your question is confusing
We are trying to determine if the current time is within a range of 00:00:00 and 23:59:59
All times are within that range. Well, maybe a value like 23:59:59.1 would be outside of it, but you aren't collecting fractional seconds in your model, so that's irrelevant. But why would you need to validate that? Maybe you are just trying to avoid numbers that aren't valid times at all? Like 99:99:99?
This fails as 14:00 (current time) is not greater than 18:30
Wait - you didn't say anything about comparing one time greater than another. 14:00 and 18:30 are both still in the range you specified.
What would be the best approach to compare just times?
Hard to answer. Are they both UTC times? Is one UTC and one is local? Are they both local? Do you know the time zone of the local times? Are you prepared to deal with ambiguous or invalid local times do to daylight saving time transitions?
Would storing the values as DateTimeOffSet help?
Perhaps, but you haven't given me enough information. It would help only if the date portion is relevant and the you get the correct offsets.
is using ToLocal() ok?
I would argue that no, it's not ok. Local in this context will give you the time zone of the server, which you probably don't want to introduce into your business logic.
So if I understand this correctly you have a time saved in UTC in the database and you are trying to determine whether it falls within a particular time frame? I'm not sure if you want the time frame in local time or UTC so here are both:
DateTime dbTime = model.MyPropertyFromDB;
TimeSpan minTime = new TimeSpan(0, 0, 0);
TimeSpan maxTime = new TimeSpan(23, 59, 59);
if (dbTime.TimeOfDay > minTime && dbTime.TimeOfDay < maxTime)
{
//Within time range (UTC)
}
if (dbTime.ToLocalTime().TimeOfDay > minTime && dbTime.ToLocalTime().TimeOfDay < maxTime)
{
//Within time range (local)
}
Edit: If you want to compare Now to a start and end time from an object in database:
TimeSpan now = DateTime.UtcNow.TimeOfDay;
TimeSpan startDate = model.startDate.TimeOfDay;
TimeSpan endDate = model.endDate.TimeOfDay;
if (now > startDate && now < endDate)
{
//Within time range (UTC)
}
I would say that the methodology being used here is fundamentally flawed and that you need to take a different approach.
new DateTime(DateTime.Now.Year, // Server date
DateTime.Now.Month,
DateTime.Now.Day,
model.Hours, // Local time
model.Minutes,
model.Seconds)
I can't see a way of 'normalising' the input in this way, unless you have a way of reliably knowing exactly which timezone a user is in. Simply, there's no easy way to turn a date built in this way into UTC.
My first question to you is, how is the model being passed from client to server? If you're using javascript/ajax then the solution should be fairly straightforward to solve by constructing the datetime object on the client (which will include their timezone data) and then rely on the browser to convert it to UTC for transit.
If you are using Razor\MVC then you can achieve a similar thing with forms encoding, except that you will need to call ToUTC on the server as the browser won't automatically fix the date for you for this media format.
Both methods require that you build a full datetime object on the client and then submit it, rather than trying to build it from seconds, minutes, hours on the server. You don't need to expose all this to the client of course, as long as the datetime is fully formed at the point of submission.
Once you've got a nice UTC datetime, you can extract just the time if you don't need the rest of it.
Hope this helps.
Pete
I have a C# time application where the user can create a timer and start it, it will do something like that on the server:
(the dates are stored in CEST (the problem is summer and winter time))
// insert into db
var entry = new TimeEntry();
entry.Start = DateTime.Now; (04/03/2013 12:00)
_timeEntries.Add(entry); // => now it's in the db
if he stops it then it do something like that:
// update the entry
var entry = _timeEntries.Get(1) // 1 is the id from the created entry before
entry.End = DateTime.Now; (04/03/2013 12:37)
_timeEntrires.Update(entry); // => now it updates it in the db
Something like that will be shown for the user in the browser:
TimerId: 1
Start: 04/03/2013 12:00
Stop: 04/03/2013 12:25
Total (in min): 25
Now I think there will be some problems when the user changes his timezone.
What's the best way to solve this? Store the dates as UTC and then convert with JavaScript?
Thanks for your helps :-).
From : Determine a User's Timezone
You will have to pass the offset from the client to the server like this:
new Date().getTimezoneOffset()/60;
getTimezoneOffset() will subtract your time from GMT and return the
number of minutes. So if you live in GMT-8, it will return 480. To put
this into hours, divide by 60. Also, notice that the sign is the
opposite of what you need -- it's calculating GMT's offset from your
time zone, not your time zone's offset from GMT. To fix this, simply
multiply by -1.
Since these are event times, you should be working strictly with UTC, and taking that value from the server.
When the client application calls you with whatever you're using for start and stop input commands, use DateTime.UtcNow() to get the time and save that in the database.
You really don't want to use local time at all for the recording of these times. Otherwise, if the start or end fall within an ambiguous period (such as a DST/SummerTime "fall-back" transition), you would have no way of knowing if the event fell before or after the transition.
Context always matters with DateTime questions, and in the context of "event times" - your only reliable options are UTC DateTime, or DateTimeOffset. Local calendar times (client or server) are not good for specific event times. They are useful in other contexts though.
When you actually show the data back to your user, if you're just showing the duration between start and end, then you can simply calculate it from the UTC times you recorded. If you want to show the specific times back to the user, that's when you would convert the UTC time to whatever time zone the user wants to display it in. If you opt for DateTimeOffset, the advantage is that you don't have to convert at this stage.
I have a question about DateTimeOffset and daylight savings time. To explain my question lets assume that right now the date and time is:
11/6/2010 10:15:00 AM
If I run this code:
DateTimeOffset myTime = DateTimeOffset.Now;
Console.WriteLine("Local time: " + myTime.ToLocalTime().DateTime);
Then I get this result:
Local time: 11/6/2010 10:15:00 AM
Meaning that the event happened at 10:15 in the morning (my time zone is Mountain Daylight Time (-6 offset)).
So, then I save this DateTimeOffset to my SQL Server 2008 db (as a DateTimeOffset). The next day I want to display it to the user. But now daylight savings has expired.
If I run the above WriteLine with the saved off value (from the previous day) what will display?
The offset stored in the database is -6. But now that daylight savings is over, the current offset is -7. So as I understand the documentation, it will first convert my time to UTC time (so it takes 10:15 AM and adds 6 hours (4:15 pm). It will then subtract the current offset of the local time (4:15 pm - 7 = 9:15 AM).
So if I my math is right, now when I display my event, it will show that it occurred at 9:15 AM rather than 10:15 AM.
This is not good. I want to store time zone information, but I need my times to stay static in the same time zone. (Meaning that if the event happened at 10:15 AM in Utah, then the next time I look at it (in Utah), I need to see that it was at 10:15 AM, regardless if the daylight savings time change has happened.
I can't think I am the first one to have this issue. What do others do to fix this? (Or do I have the facts wrong?)
No, it won't add the current offset - it'll add the offset at that date which is still -6. So it should still display 10:15AM, because it knows the date involved, and thus the time zone rules in force on that date.
You may well want to store a simple UTC time and the time zone identifier separately, by the way. If you're storing a time zone, then using DateTimeOffset won't be particularly helpful over just a UTC date/time. (On the other hand, it's clearer that it does represent an instant in time - DateTime is a horribly confused type which doesn't let you easily express what you're trying to represent.)
Of course I'd personally encourage you to look at Noda Time which in my very biased opinion is a rather clearer date/time API than the built-in one... but which isn't quite ready for production use. (We're getting there though...)