Comparing DateTime.Now to value stored in SQL - c#

I am converting ADODB namespace code to SqlClient. I am trying to replace code that utilized recordset.Value. I am having problems converting this line of code. How can I subtract DateTime.Now from the value in the time_of_lock (Datetime data type) column in SQL?
else if (DateTime.Now - rs.Fields["time_of_lock"].Value < TimeSpan.FromMinutes(15))

I am assuming you can get two DateTime values from your code. If so, try pop both into the following function:
private bool TimesApartNoMoreThan(DateTime first, DateTime second, int threshold)
{
return (second - first).TotalMinutes > threshold;
}
when you subtract two DateTimes, you get a TimeSpan. Having obtained that, you can represent it as whatever unit of duration you need (in this case, minutes):

From your statement, I am assuming rs.Fields["time_of_lock"] is of DateTime datatype. If that is the case, please try this:
DateTime.Now .Subtract(rs.Fields["time_of_lock"].Value).TotalMinutes < 15

Related

Cast DateTime to Unix Timestamp in Select statement?

I do have a database table with millions of archived value rows that consist of a value (single), two integer id fields, and a datetime field (and some other fields that are not relevant for the cache I want to build). The table structure is fixed, I can't change it. Now I want to read all rows into an array of objects of a simple class that I want to keep in memory for caching purposes.
In order to keep the memory consumption low, I want to use a unix timestamp instead of a datetime object. This is also useful because the frontend charts etc. that will consecutively work with this cache also natively work with Unix Timestamps.
For the cache creation I want to select directly into an ArchiveElement. This works well for most fields, but I don't know how to create a Unix Timestamp on the fly WITHIN the select statement:
ArchiveCache = db.ArchiveValues.Select(x => new ArchiveElement() {
DataPointId = (UInt16)x.DataPointId,
StationId = (UInt16)x.StationId,
Value = (Single)x.Value,
DateValue = x.DateValue // <-- Here I want to cast into Unix Timestamp
});
This is the ArchiveElement class:
public class ArchiveElement
{
public UInt32 DateValue;
public UInt16 DataPointId;
public UInt16 StationId;
public Single Value;
}
I do have a function in my application that can convert a DateTime into a Unix TimeStamp, but that does not work within the scope of the select statement. So I need to find another way.
One way would be to insert a .ToList() before the .Select(..) statement, so that I have access to my own function, but that would be an ugly workaround as it fetches a lot of unneccessary fields from the DB.
Is there a way to somehow convert the datetime "in place"?
So first you declare the Unix Epoch time:
var unixEpoch = DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
Now in your Select, just subtract the epoch from your date value which gives a TimeSpan type from which you can get the TotalSeconds which is the value you need:
ArchiveCache = db.ArchiveValues.Select(x => new ArchiveElement() {
DataPointId = (UInt16)x.DataPointId,
StationId = (UInt16)x.StationId,
Value = (Single)x.Value,
DateValue = (x.DateValue - unixEpoch).TotalSeconds
});
Note: This assumes that your DateValue property is a double, you may need to cast it to a long.
Edit
To cope with Entity Framework, this may work:
ArchiveCache = db.ArchiveValues.Select(x => new ArchiveElement() {
DataPointId = (UInt16)x.DataPointId,
StationId = (UInt16)x.StationId,
Value = (Single)x.Value,
DateValue = SqlFunctions.DateDiff("ss", unixEpoch, x.DateValue)
});
This may require a reference/import for System.Data.Entity.SqlServer.
DATEDIFF(SECOND,{d '1970-01-01'}, GETUTCDATE())
This will return the unix seconds in sql.

Restrict DateTime with a minimum date and time (not fall below 00:00)

I am using DateTime and it's methods like DateTime.AddMinutes etc.
Is there a way to make DateTime throw an Exception when adding a negative number of minutes/seconds etc that make the time fall beneath 00:00 and not turn the time to 23:59?
At Domain Model Level
At the domain model you could a Decorator/Wrapper class. Just use a class that has a private DateTime object and pass through every method of that object that remains unaltered and implement those that are altered, essentially just by writing:
public bool Equals(DateTime dt){
return this.dateTime.Equals(DateTime dt);
}
There are however a couple of issues with this:
In the above example you might want to compare your DateTime object with itself, but that won't be possible.
DateTime has certain attributes eg. its serializable that you may lose until adding a fair bit of time to this.
At Controller Level
Assuming you are implementing this in an applicatino with MVC setup, you probably have some validation within your controller. That should be able to handle these cases.
I'd recommend this approach, as you can reject any case where the Date part of the datetime does not match or is less than the original date.
You can try a extension method like so:
public static class MyDateTimeChecker
{
public static bool CheckTime(this DateTime dt, int minutes)
{
if (dt.Day > dt.AddMinutes(minutes).Day)
return false;
else
return true;
}
}
I placed Day since in your question you wanted to know if it would fall bellow 00:00 or back to the previous day at 23:59,so this is one way to go.
Then in your code you can use it like this:
DateTime dt = DateTime.Now;
if(dt.CheckTime(-1440))
{
//if true your negative number was in range
//so use it if you like.
}
else
}
//oops too much
}

Property or indexer 'System.DateTime.TimeOfDay' cannot be assigned to — it is read only

I saw lots of duplicate post of this But for me it's something different.
I have a Datetime object and get time portion & assign another time to that.When i'm going to assign it it raise those error.
in here newStartDateGroup is a DateTime Object
in here OpenTime is a TimeSpan
Property or indexer cannot be assigned to — it is read only
else if(newStartDateGroup.TimeOfDay < i.OpenTime && newEndDateGroup.TimeOfDay > i.CloseTime) // < >
{
newStartDateGroup.TimeOfDay = i.OpenTime;
DateTime struct in .NET is immutable, so instead of changing it's values you have to create new one:
newStartDateGroup = newStartDateGroup.Date.Add(i.OpenTime);
You can only create new DateTime instances.
According to your shown code, you want to set only the day time.
This should do it:
newStartDateGroup = DateTime.Today.Add(i.OpenTime);
DateTime.Today is today at 00:00.
Just assign the hours, minutes and seconds manually.
DateTime temp = DateTime(newStartDateGroup.Year, newStartDateGroup.Month, newStartDateGroup.Day, i.OpenTime.Hours, i.OpenTime.Minutes, i.OpenTime.Seconds);
newStartDateGroup = temp;

How to check if data is in range

I was wondering if there is any neat way to check is data is in allowed range. I mean in c# we can represent data from 0001-01-01 to (I think) 9999-01-01. However if we try to do something like that
DateTime result = DateTime.Parse("0001-01-01").Subtract(TimeSpan.FromDays(1))
I get an exception. Is there any neat way to check is it is possible to do DateTime operations (addition subtraction etc)
Just use the comparison operators (>, <, >=, <=, == and !=), as they are implemented in DateTime.
Example:
DateTime lowerAllowedDate = new DateTime(1,1,1); // 01/01/0001
DateTime upperAllowedDate = new DateTime(3000, 12, 31) // 31/12/3000
DateTime now = DateTime.Now
if (lowerAllowedDate <= now && now < upperAllowedDate)
{
//Do something with the date at is in within range
}
Consider these extension methods.
public static class ValidatedDateTimeOperations
{
public static bool TrySubtract (this DateTime dateTime, TimeSpan span, out DateTime result)
{
if (span < TimeSpan.Zero)
return TryAdd (dateTime, -span, out result);
if (dateTime.Ticks >= span.Ticks)
{
result = dateTime - span;
return true;
}
result = DateTime.MinValue;
return false;
}
public static bool TryAdd (this DateTime dateTime, TimeSpan span, out DateTime result)
{
if (span < TimeSpan.Zero)
return TrySubtract (dateTime, -span, out result);
if (DateTime.MaxValue.Ticks - span.Ticks >= dateTime.Ticks)
{
result = dateTime + span;
return true;
}
result = DateTime.MaxValue;
return false;
}
}
The can be called like this:
DateTime result;
if (DateTime.MinValue.TrySubtract (TimeSpan.FromDays(1), out result)
{
// Subtraction succeeded.
}
Checking for an overflow in a given operation beforehand is cumbersome and I'm not really sure it's really worth it against simply handling the exception.
You could for example do the following when subtracting:
DateTime date;
TimeSpan subtractSpan;
if ((date - DateTime.MinValue) < subtractSpan)
{
//out of range exception: date - subtractSpan
}
Worth it? Your call.
Take a look at the DateTime structure documentation in MSDN.
In particular, you can take a look at:
TryParse and TryParseExact
The comparison operators
MinValue and MaxValue
You can also put try..catch (ArgumentOutOfRangeException) around the DateTime values you are trying to use.
However, if you are consistently (or ever?) running into this kind of exception, I'd take a closer look at your design. Unless you are doing some serious date-crunching, I don't know of any instance where I would be bumping into the min and max values.

System.OutOfMemoryException thrown when computing date ranges

It might be a simple fix, but I can't for the life of me think of how to do this. I compute a bunch of StartDates and End Dates into a bunch of arrays of dates using this query:
this.Reserved = unit.Reservations.Where(r => r.Active.HasValue && r.Active.Value).SelectMany(r => Utilities.DateRangeToArray(r.StartDate, r.EndDate)).ToArray();
Utilities.DateRangeToArray() is defined as follows:
public static IEnumerable<DateTime> DateRangeToArray(DateTime start, DateTime end) {
DateTime curDate = start;
while (curDate <= end) {
yield return curDate;
curDate.AddDays(1);
}
}
Is there a way to make this less memory intensive?
Thanks!
Your code is broken - AddDays doesn't change the existing value, it returns a new value. You're ignoring that new value, thus creating an infinite loop.
Change your code to:
public static IEnumerable<DateTime> DateRangeToArray(DateTime start,
DateTime end) {
DateTime curDate = start;
while (curDate <= end) {
yield return curDate;
curDate = curDate.AddDays(1);
}
}
Another hint: unit testing can help you find this sort of problem long before you try to use the method in a LINQ query. I'd also change the name, given that it's not returning an array.
You're sure you don't have any reservations where r.StartDate > r.EndDate, right? If you do, you'll get an infinite loop, I think.
I assume the out of memory is when converting the result to the array. Two points:
The output will contain duplicate dates for overlapping reservations.
Perhaps Reserved should be a collection of date ranges (start,end) rather than containing every date?

Categories

Resources