Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comparing time regardless of date

Tags:

c#

.net

datetime

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 image 615
Jon Avatar asked Aug 29 '13 13:08

Jon


People also ask

How do you compare two dates without the time portion?

If you want to compare only the month, day and year of two dates, following code works for me: SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); sdf. format(date1). equals(sdf.

How do you compare only dates?

CompareTo(DateOnly) Compares the value of this instance to a specified DateOnly value and returns an integer that indicates whether this instance is earlier than, the same as, or later than the specified DateTime value.

How do I compare two different date formats?

function compare() { var d1=new Date('2020-01-23'); //yyyy-mm-dd. var d2=new Date('2020-01-21'); //yyyy-mm-dd.


1 Answers

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.

like image 52
Matt Johnson-Pint Avatar answered Oct 06 '22 11:10

Matt Johnson-Pint