How to specify the time precision for a comparison - c#

I have two date values that are being returned, I then need to compare these to ensure lastWriteDate of the file has not been updated since caching.
The problem I've got is, the lastWriteDate has the milliseconds included, and the clientDate doesn't. So when compared, the lastWriteDate will always be greater if it is in the same second.
if (!(Request.Headers["If-Modified-Since"] == null))
{
DateTime clientDate = Convert.ToDateTime(Request.Headers["If-Modified-Since"]);
DateTime lastWriteDate = System.IO.File.GetLastWriteTime(newImagePath);
if (lastWriteDate <= clientDate)
{
//Code here not being reached
}
}
Those miliseconds aren't important for the comparison I'm making, so what would be the best way to compare these values without them?

Subtract one date from the other. THen you will end up with a TimeSpan object, of which you can simply check the number of seconds and ignore the milliseconds.

You can truncate the milliseconds in this way:
if (lastWriteDate.AddMilliseconds(-lastWriteDate.Millisecond) <= clientDate)
{
// ....
}
You could also create a TimeSpan and check if it's lower than your maximum:
TimeSpan diff = clientDate - lastWriteDate;
if (diff < TimeSpan.FromSeconds(1))
{
// ....
}

Related

Rounding time in Nodatime to nearest interval

We need to floor a time to the nearest arbitrary interval (represented by e.g. a Timespan or a Duration).
Assume for an example that we need to floor it to the nearest ten minutes.
e.g. 13:02 becomes 13:00 and 14:12 becomes 14:10
Without using Nodatime you could do something like this:
// Floor
long ticks = date.Ticks / span.Ticks;
return new DateTime( ticks * span.Ticks );
Which will use the ticks of a timespan to floor a datetime to a specific time.
It seems NodaTime exposes some complexity we hadn't considered before. You can write a function like this:
public static Instant FloorBy(this Instant time, Duration duration)
=> time.Minus(Duration.FromTicks(time.ToUnixTimeTicks() % duration.BclCompatibleTicks));
But that implementation doesn't seem correct.
"Floor to nearest ten minutes" seems to be dependent on timezone/offset of the time.
While might be 13:02 in UTC, in Nepal which has an offset of +05:45, the time would be 18:47.
This means that in UTC, flooring to the nearest ten minutes, would mean subtracting two minutes, while in Nepal, it would mean subtracting seven minutes.
I feel like I should be able to round a ZonedDateTime or an OffsetDateTime by an arbitrary timespan somehow. I can get close by writing a function like this
public static OffsetDateTime FloorToNearestTenMinutes(this OffsetDateTime time)
{
return time
.Minus(Duration.FromMinutes(time.Minute % 10))
.Minus(Duration.FromSeconds(time.Second));
}
but that doesn't allow me to specify an arbitrary duration, as the OffsetDateTime has no concept of ticks.
How do I round an Instant/ZonedDateTime/OffsetDateTime correctly, with an arbitrary interval, taking into account time zones?
For OffsetDateTime, I'd advise you to write a Func<LocalTime, LocalTime> which is effectively an "adjuster" in Noda Time terminology. You can then just use the With method:
// This could be a static field somewhere - or a method, so you can use
// a method group conversion.
Func<LocalTime, LocalTime> adjuster =>
new LocalTime(time.Hour, time.Minute - time.Minute % 10, 0);
// The With method applies the adjuster to just the time portion,
// keeping the date and offset the same.
OffsetDateTime rounded = originalOffsetDateTime.With(adjuster);
Note that this only works because your rounding will never change the date. If you need a version that can change date as well (e.g. rounding 23:58 to 00:00 of the next day) then you'd need to get the new LocalDateTime and construct a new OffsetDateTime with that LocalDateTime and the original offset. We don't have a convenience method for that, but it's just a matter of calling the constructor.
ZonedDateTime is fundamentally trickier due to the reasons you've given. Right now, Nepal doesn't observe DST - but it might do so in the future. Rounding near the DST boundary could take you into an ambiguous or even skipped time, potentially. That's why we don't provide a similar With method for ZonedDateTime. (In your case it isn't likely, although it's historically possibly... with date adjusters you could easily end up in this situation.)
What you could do is:
Call ZonedDateTime.ToOffsetDateTime
Round the OffsetDateTime as above
Call OffsetDateTime.InZone(zone) to get back to a ZonedDateTime
You could then check that the offset of the resulting ZonedDateTime is the same as the original, if you wanted to detect weird cases - but you'd then need to decide what to actually do about them. The behaviour is fairly reasonable though - if you start with a ZonedDateTime with a time portion of (say) 01:47, you'll end up with a ZonedDateTime in the same time zone from 7 minutes earlier. It's possible that wouldn't be 01:40, if a transition occurred within the last 7 minutes... but I suspect you don't actually need to worry about it.
I ended up taking some stuff from Jon Skeets answer and rolling my own Rounder that takes in an arbitrary Duration to round with. (Which was one of the key things I needed, which is also why I'm not accepting that answer).
Per Jons suggestion I convert the Instant to an OffsetDateTime and apply the rounder, which takes in an arbitrary duration. Example and implementation is below:
// Example of usage
public void Example()
{
Instant instant = SystemClock.Instance.GetCurrentInstant();
OffsetDateTime offsetDateTime = instant.WithOffset(Offset.Zero);
var transformedOffsetDateTime = offsetDateTime.With(t => RoundToDuration(t, Duration.FromMinutes(15)));
var transformedInstant = transformedOffsetDateTime.ToInstant();
}
// Rounding function, note that it at most truncates to midnight at the day.
public static LocalTime RoundToDuration(LocalTime timeToTransform, Duration durationToRoundBy)
{
var ticksInDuration = durationToRoundBy.BclCompatibleTicks;
var ticksInDay = timeToTransform.TickOfDay;
var ticksAfterRounding = ticksInDay % ticksInDuration;
var period = Period.FromTicks(ticksAfterRounding);
var transformedTime = timeToTransform.Minus(period);
return transformedTime;
}
For anyone interested here is my implementation, which correctly accounts for the occasions we cross a day, and always rounds up (rather than floors):
public static class RoundingExtensions
{
private static readonly Duration OneDay = Duration.FromDays(1);
public static LocalTime RoundUpToDuration(this LocalTime localDateTime, Duration duration)
{
if (duration <= Duration.Zero) return localDateTime;
var ticksInDuration = duration.BclCompatibleTicks;
var ticksInDay = localDateTime.TickOfDay;
var ticksAfterRounding = ticksInDay % ticksInDuration;
if (ticksAfterRounding == 0) return localDateTime;
// Create period to add ticks to get to next rounding.
var period = Period.FromTicks(ticksInDuration - ticksAfterRounding);
return localDateTime.Plus(period);
}
public static OffsetDateTime RoundUpToDuration(this OffsetDateTime offsetDateTime, Duration duration)
{
if (duration <= Duration.Zero) return offsetDateTime;
var result = offsetDateTime.With(t => RoundUpToDuration(t, duration));
if (OffsetDateTime.Comparer.Instant.Compare(offsetDateTime, result) > 0) result = result.Plus(OneDay);
return result;
}
public static ZonedDateTime RoundUpToDuration(this ZonedDateTime zonedDateTime, Duration duration)
{
if (duration <= Duration.Zero) return zonedDateTime;
var odt = zonedDateTime.ToOffsetDateTime().RoundUpToDuration(duration);
return odt.InZone(zonedDateTime.Zone);
}
}

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);

How to get the last count, or the last time in a day

I have a problem finding the last time in the day (the time it's the biggest) picture below, how can I get that time?
I have to compare this time with his shift, but when I do it, I always read for the first time.
This is my code:
foreach (var shift in shifts)
{
if (von.ZPZ_Von <= shift.Arbeitsbeginn.AddMinutes(-20) &&
bis.ZPZ_Bis >= shift.Arbetsende.AddMinutes(-10))
return null;
else if (von.ZPZ_Von >= shift.Arbeitsbeginn.AddMinutes(20) &&
bis.ZPZ_Bis >= shift.Arbetsende.AddMinutes(10))
return null;
else if (von.ZPZ_Von <= shift.Arbeitsbeginn.AddMinutes(5)
&& bis.ZPZ_Bis <= shift.Arbetsende.AddMinutes(10)
)
return shift;
}
It is a method that finds the shift of workers, and if in the correct shift the worker returns the shift, if the worker comes 20 minutes or works more than 10 minutes then returns null.
This looks like data for one day:
So I need to compare the ZPZ_Bis with the last, or rather, the time.
At the moment, my method always compares ZPZ_Bis with the first departure time, i. 1899-12-30 09:52:00.000 in this case.
I would be grateful if somebody could help me with this problem, I have not really known how to handle this in the last few days.
this is my whole method:
private A_Arbeitszeitplan DetectShift(List<A_Arbeitszeitplan> shifts, PRAESENZZEIT von, PRAESENZZEIT bis, List<PRAESENZZEIT>arrivals)
If you only wish to use the TimeSpan of your DateTime you can get it like so:
From a DateTime, you can use .TimeOfDay - but that gives you a
TimeSpan representing the time into the day (10 hours).
Of course you need to compare TimeSpans with eachother:
if (von.ZPZ_Von.TimeOfDay <= shift.Arbeitsbeginn.AddMinutes(-20).TimeOfDay &&
bis.ZPZ_Bis.TimeOfDay >= shift.Arbetsende.AddMinutes(-10).TimeOfDay)
return null;

Calculating how many minutes there are between two times

I have a datagridview in my application which holds start and finish times. I want to calculate the number of minutes between these two times. So far I have got:
var varFinish = tsTable.Rows[intCellRow]["Finish Time"];
TimeSpan varTime = (DateTime)varFinish - (DateTime)varValue;
int intMinutes = TimeSpan.FromMinutes(varTime);
But the last line won't compile because it says I am using invalid arguments for the Timespan constructor. I've researched quite a bit about how to calculate the number of minutes between two times, but I'm hitting a bit of a brick wall. Can someone please advise me on the best way to achieve my objective.
EDIT/
Now my code is as follows:
var varFinish = tsTable.Rows[intCellRow]["Finish Time"];
TimeSpan varTime = (DateTime)varFinish - (DateTime)varValue;
int intMinutes = (int)varTime.TotalMinutes;
But I am getting an invalid cast on the second line. Both varFinish and varValue are times e.g. 10:00 and 8:00 say. So not sure why they won't cast to type DateTime?
Try this
DateTime startTime = varValue
DateTime endTime = varTime
TimeSpan span = endTime.Subtract ( startTime );
Console.WriteLine( "Time Difference (minutes): " + span.TotalMinutes );
Edit:
If are you trying 'span.Minutes', this will return only the minutes of timespan [0~59], to return sum of all minutes from this interval, just use 'span.TotalMinutes'.
double minutes = varTime.TotalMinutes;
int minutesRounded = (int)Math.Round(varTime.TotalMinutes);
TimeSpan.TotalMinutes: The total number of minutes represented by this instance.
In your quesion code you are using TimeSpan.FromMinutes incorrectly. Please see the MSDN Documentation for TimeSpan.FromMinutes, which gives the following method signature:
public static TimeSpan FromMinutes(double value)
hence, the following code won't compile
var intMinutes = TimeSpan.FromMinutes(varTime); // won't compile
Instead, you can use the TimeSpan.TotalMinutes property to perform this arithmetic. For instance:
TimeSpan varTime = (DateTime)varFinish - (DateTime)varValue;
double fractionalMinutes = varTime.TotalMinutes;
int wholeMinutes = (int)fractionalMinutes;
You just need to query the TotalMinutes property like this varTime.TotalMinutes
If the difference between endTime and startTime is greater than or equal to 60 Minutes , the statement:endTime.Subtract(startTime).Minutes; will always return (minutesDifference % 60). Obviously which is not desired when we are only talking about minutes (not hours here).
Here are some of the ways if you want to get total number of minutes(in different typecasts):
// Default value that is returned is of type *double*
double double_minutes = endTime.Subtract(startTime).TotalMinutes;
int integer_minutes = (int)endTime.Subtract(startTime).TotalMinutes;
long long_minutes = (long)endTime.Subtract(startTime).TotalMinutes;
string string_minutes = (string)endTime.Subtract(startTime).TotalMinutes;

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.

Categories

Resources