My application automatically bills subscribed customers using a payment processing vendor. I keep only information on the expiry date of the card and the vendors reference number for auto-billing. Everyday I want to check members whose cards will expire within 30 days. I am running a background process to send email reminders. I am struggling getting Linq to accept my query. The expiry date is stored in the database as a string eg 0319 for March 2019. I am wondering if I have any chance of getting this to work. Please assist if you can. My last resort might be having to format expiry dates currently stored as strings mmyy in database to proper dates.
int mon = DateTime.Now.Month;
int yr = DateTime.Now.Year;
int days = DateTime.DaysInMonth(yr, mon);
int dy = DateTime.Now.Day;
var allCardExpiring = db.DirectDebits.Include(i => i.Customer).Where(a =>a.DdOk && a.Customer.PassOk && DateTime.DaysInMonth(Convert.ToInt32(a.DateExpiry.Substring(2, 4)), Convert.ToInt32(a.DateExpiry.Substring(0, 2)))+days-dy < 30).Select(a => a.DirectDebitId).Distinct().ToList();
This is a good example that shows that you should not form the database into the format that operators use to enter their input. If your database would have had the ExpiryDate as a DateTime, you wouldn't have had this problem. Of course with the cost that when operators enter their expiry date you would have to convert it to a DateTime, but (1) that is easier than converting back and (2) What do you more often: query the ExpiryDate or update it?
If you are stuck with this database, then my advice would be to create a query where you split your MonthYear property into a a Month and a Year, using DbFunctions.Left and DbFunctions.Right then convert it in your query to a proper DateTime using DbFunctions.CreateDateTime
If you need this conversion for other functions, consider creating separate IQueryable functions for this, so you can re-use it.
As extension function for your DirectDebit that takes an input sequence of DirectDebits and returns a DateTimeDirectDebit sequence:
public static IQueryable<DateTimeDirectDebit> ToDateTimeDirectDebits(
this IQueryable<DirectDebit> directDebits)
{
return directDebits.Select(directDebit => new
{
// split ExpiryDate into a Month and a Year
ExpiryDate = new
{
Month = DbFunctions.Left(directDebit.DateExpire, 2),
Year = DbFunctions.Right(directDebit.DateExpire, 2),
}
DirectDebit = directDebit,
})
.Select(directDebit => new DateTimeDirectDebit
{
// create the ExpiryDate as DateTime
ExpiryDate = DbFunctions.CreateDateTime(
directDebit.ExpiryDate.Year,
directDebit.ExpiryDate.Mnth,
1, // first day of the month
...),
DirectDebit = directDebit.DirectDebit,
});
}
You also need a function that returns the DateTimeDirectDebits that expire within a certain amount of days. Again as an extension method:
public static IQueryable<DateTimeDirectDebit> WhereExpiresWithinDays(
this IQueryable<DateTimeDirectDebit> source,
int nrOfDays)
{
DateTime now = DateTime.now;
DateTime limitDate = now.AddDays(nrOfDays);
return source.Where(directDebit => directDebit.ExpiryDate < limitDate);
}
Similarly you might want a function that returns all directDebits that expire next Month use DbFunctions.DiffMonths for this.
Usage:
using (var dbContext = new ...)
{
var directDebitsThatExpireNextMonth = dbContext.DirectDebits
.ToDateTimeDirectDebits
.WhereExpiresWithinDays(30)
.Select(...);
}
The nice thing is, that by using these LINQ-like extension methods, you can hide how your database is structured, especially the parts that you are not happy about. This way, your code does not have to be restructures so much if your database changes internally, especially those functions that don't use these changes.
Of course, for a proper hiding of the database structure, class DateTimeDirectDebits should not expose property DirectDebits, but only the properties you want to show to the outside world.
Related
I can really use some help wrapping my head around a problem I'm having querying data according to a SQL Date field.
I am storing the Date in UTC format using the following code:
objFitCalendarDto.Day = objFitCalendarDto.Day.ToUniversalTime();
That line assigns the date to the model that is inserted into the db through Entity Framework.
Now, my query is supposed to retrieve a row based on a date. So, I should be able to get the row for today, tomorrow, yesterday, and so on.
To do this, I'm using the method to search between two dates, a start date and an end date as follows:
DateTime dayBegin = DateTime.Today.Date.AddDays(dayOffset);
DateTime dayEnd = DateTime.Today.Date.AddDays(dayOffset + 1);
The purpose of dayOffset is to specify which day. If Offset is 0, then I am searching for Today. If dayOffset is 1, then I am searching for rows with tomorrow's date.
Now, since I stored the data originally in UTC, I am assuming that I must search for it in UTC as well. So before executing my query, I convert the dates to UTC like so:
dayBegin = TimeZoneInfo.ConvertTimeToUtc(dayBegin);
dayEnd = TimeZoneInfo.ConvertTimeToUtc(dayEnd);
Then I execute my query like so:
var query = (from f in Db.FitCalendars
where f.FitProgramId == programId &&
f.DayAsDate >= dayBegin && f.DayAsDate < dayEnd
select f);
problem is, it doesn't work. I have a row with the date, "2016-01-26" when I look at it in SQL Manager. However, it only returns from a query on yesterday's date. Today is 2016-01-26, by the way. Clearly I'm not getting this UTC concept. Can anyone see what I'm doing wrong here? I was assuming that if I stored everything as UTC and then before querying I converted my dates for the query to UTC, that everything should work.
UPDATE
Let's try like this:
As soon as you are storing only date part (SQL 'date' type), you
need to compare also only dates.
Instead of
DateTime dayBegin = DateTime.Today.Date.AddDays(dayOffset);
dayBegin = TimeZoneInfo.ConvertTimeToUtc(dayBegin);
let's just do
DateTime dayBegin = DateTime.UtcNow.Date.AddDays(dayOffset);
dayBegin in that case will be date with time anyway (time is 12:00:00 AM). It means, we need to truncate it with DbFunctions. We need equality check here.
var query = (from f in Db.FitCalendars
where f.FitProgramId == programId &&
f.DayAsDate == DbFunctions.TruncateTime(dayBegin)
select f);
END OF UPDATE
I believe that problem is that you comparing dates with times. In your case you need to compare only dates, as far as I understand. As a solution - use DbFunctions TruncateTime function. It can be used within linq queries - like in your code.
https://msdn.microsoft.com/en-us/library/system.data.entity.dbfunctions.truncatetime(v=vs.113).aspx
So, complete solution would be
var query = (from f in Db.FitCalendars
where f.FitProgramId == programId &&
DbFunctions.TruncateTime(f.DayAsDate) >= DbFunctions.TruncateTime(dayBegin) && DbFunctions.TruncateTime(f.DayAsDate) < DbFunctions.TruncateTime(dayEnd)
select f);
I want to query the update timestamp _ts on documents to get documents that haven't mutated since a certain amount of time.
When I create a select query in the azure portal this works:
SELECT TOP 10 c.id FROM c WHERE c._ts < 6.35909919217878E+17
The wierd number is the Ticks created with a datetime object, see below.
But when I try to create it through LINQ it won't do because you don't have _ts but a Timestamp as a DateTime object. When I try to enter a full DateTime object to compare to the Timestamp it crashes saying it doesn't support it. So I try this:
DocRepo.Get(x => x.Timestamp.Ticks < CloseDate.Ticks);
This results to nothing and when I watch the query executed it has this as a select query:
SELECT * FROM root WHERE root[\"_ts\"][\"Ticks\"] < 6.35909943137688E+17
Is it possible to query on the _ts timestamp or do i have to have an extra updatedAt field to do it, which seems redundant.
You have a couple of problems with your queries. In your first query, you are comparing "Ticks" (one ten millionth of a second - see here) to the _ts value which will most likely return all the documents in your collection because the _ts value is a POSIX (Unix) time measured in seconds see here. They also aren't based on the same epoch. The Unix value starts at midnight 1,1,1970 where the Ticks start at midnight 1,1,0001 Therefore, the _ts value will always be much smaller than the Ticks value (not to mention off by 1,969 years!). You will need to convert your dates to their Unix time value. You could create an Extension method to help you do this:
public static long ToUnixTime(this DateTime date)
{
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
return (long)(date - epoch).TotalSeconds;
}
As for the Linq statement, you can't (unfortunately) put a DateTime into a Linq query because a DateTime value won't convert to a const (which is the error you are getting). So, in both cases, you can't compare either the _ts value or TimeStamp value very easily.
So what to do? Well, in looking at the DocumentDB SDK, if you look at the definition of TimeStamp you will see the following:
// Summary:
// Gets the last modified timestamp associated with the resource.
[JsonConverter(typeof(UnixDateTimeConverter))]
[JsonProperty(PropertyName = "_ts")]
public virtual DateTime Timestamp { get; internal set; }
So by default, the SDK is converting the _ts value to a DateTime and exposing it through TimeStamp field. There are a couple of things you could do depending on what type your DocRepo is returning. If it is the default Document type, you could create a new class and inherit from the Docment type like this:
public class MyDocument : Document
{
public long _ts
{
get; set;
}
}
If it is your own custom class, then just add the _ts field to your class. Either way, if the _ts field is present, DocumentDB will populate the field. Then, if you add the ToUnixTime extension method you could compose your Linq query like this:
DocRepo.Get(x => x._ts < CloseDate.ToUnixTime());
It may not be an elegant solution and someone (hopefully) might come up with a better solution, but I have verified that it works against my own DocumentDB collection.
Hope this helps.
This is what i use to convert Unix timestamp into DateTime format we can easily understand in C#:
public DateTime TimeStamp
{
get
{
return DateTimeOffset.FromUnixTimeSeconds(int.Parse(_ts)).DateTime;
}
}
Hope this helps.
I'm using asp.net MVC in my project. My database table includes some records. The table has datetime column for records. I want to get records of last week adding. So the LastlyRecords is:
DateTime.Now = 04.04.2015
LastWeekDateTime = 28.04.2015
LastWeekDateTime < LastlyRecords < DateTime.Now
Have a method that accepts a "Start Date" and "End Date" as parameters.
Call it with a start/end date like:
GetRecords(DateTime.Now.AddWeeks(-1), DateTime.Now);
For there, you can have a stored procedure fetch records between that date range (or do something similar for Entity Framework or whatever you're using).
You can do something similar in T-SQL via GETDATE() and DATEADD(), but it's arguably better to do the range calculation in the calling code (because it's more a business logic thing than a data access thing).
you have to write logic on your query or you can modify below as per your datetime range.
var lastweek = DateTime.Now.AddDays(7);
var records = d in db.Persons
where DateTime.Compare(lastweek, d.DateRecordColumn)
and DateTime.Compare(d.DateRecordColumn, DateTime.Now)
select d;
hope this help to resolve your query!!!
string dateTime = "01 April 2015 Wednesday 16:23";
DateTime time1 = Convert.ToDateTime(dateTime);
if (DateTime.Compare(DateTime.Now, time1) == 1 && // DateTime.Now > time1
DateTime.Compare(time1, DateTime.Today.AddDays((int)-7)) == 1 // time1 >= DateTime.Now-7days
)
{
#("True.")
}
else
{
#("False.")
}
The result is true, so it is in last week.
I have a view model in which I am storing a DateTime but in my view using a JQUERY datetimepicker, time only:
ViewModel
[DataType(DataType.Time)]
public DateTime? MondayFrom { get; set; }
[DataType(DataType.Time)]
public DateTime? MondayTo { get; set; }
As it stands, when the Create method gets called it is using todays date plus the time selected from the timepicker.
As I am not particularly concerned with the Date part of the DateTime, I want to change the day, month & year to 01/01/1900 or something less specific than the date the record was written, before the record is created, this is purely to avoid any confusion in the future.
I don't want to get bogged down on whether this is the right thing to do or not.
I'm struggling to get a handle on the Date part of the DateTime, see below:
public void CreateClub(Club club)
{
foreach (var item in club.MeetingDays)
{
// change Date part to 01/01/1900, leave the time as is..
}
_repository.CreateClub(club);
}
How might I floor the date part of the item but leave the time well alone?
Just use the TimeOfDay property to extract the time within the day, and add that to the date you want:
private static readonly DateTime BaseDate = new DateTime(1900, 1, 1);
var updatedDateTime = BaseDate + otherDateTime.TimeOfDay;
You could even write an extension method or two:
public static DateTime WithDate(this DateTime start, DateTime date)
{
// No need to use the Date property if you already know
// it will be midnight...
return date.Date + start.TimeOfDay;
}
public static DateTime WithFloorDate(this DateTime start)
{
return start.WithDate(FloorDate);
}
Of course, I'd suggest you use Noda Time where you can specify dates, times and date/time values (with or without a time zone or UTC offset0 separately, but that's a different conversation.
DateTime is immutable - you cant just change part of it. You can extract the time and add it to a "base" date:
for(int i=0; i < club.MeetingDays.Count; i++)
{
club.MeetingDays[i] = new DateTime(1900, 1, 1) + club.MeetingDays[i].TimeOfDay;
}
Note that you need a for loop so you can place the new value back in the collection. You could also use Linq:
club.MeetingDays = club.MeetingDays
.Select(t => new DateTime(1900, 1, 1) + t.TimeOfDay)
.ToList();
Assuming that club.MeetingDays is a List<Datetime>
I'm looking for the most efficient way to suck out a series of monthly counts of records in my database, but adjusting for time zone, since the times are actually stored as UTC. I would like my result set to be a series of objects that include month, year and count.
I have LINQ to SQL objects that looks something like this:
public class MyRecord {
public int ID { get; set; }
public DateTime TimeStamp { get; set; }
public string Data { get; set; }
}
I'm not opposed to using straight SQL, but LINQ to SQL would at least keep the code a lot more clean. The time zone adjustment is available as an integer (-5, for example). Again, the result set what I'm looking for is objects containing the month, year and count, all integers.
Any suggestions? I can think of several ways to do it straight, but not with a time zone adjustment.
EDIT: The answer below got me headed in the right direction. This is what I ultimately ended up with:
var counts = _context.MyRecord
.Select(r => new {original = r.TimeStamp, adjusted = TimeAdjust.GetAdjustedTime(Config.TimeZoneAdjustment, r.TimeStamp)}).ToArray()
.GroupBy(r => new {r.adjusted.Month, r.adjusted.Year})
.Select(g => new MonthCount { Count = g.Count(), Year = g.Key.Year, Month = g.Key.Month })
.OrderByDescending(g => g.Year).ThenByDescending(g => g.Month);
Basically I'm pulling all of the date down, which works OK given the limited scope of this app. The TimeAdjust function gets "real" adjusted times, accounting for DLS. The ToArray() call is made to avoid the lazy execution that pisses of SQL because of the time adjustment function.
context.MyRecords
// tz adjusted, projection
.Select(r => new {original = r.TimeStamp, adjusted = r.TimeStamp.AddHours(tz)})
// group by start of month
.GroupBy (r => r.adjusted.Date.AddDays(-r.Day))
// final projection from groups to values asked for
.Select (g => new {count = g.Count(), year = g.Key.Year, month = g.Key.Month})
Note: As #dana points out, if you want to account for daylight savings time too this is a whole lot more complex. Grouping by offset times ignoring DST will get a good enough answer for most reports because the only values that will be wrongly accounted for are on month boundaries, around midnight for a few months of the year.
IF you do want to account for DST it's more complex than just looking at the date range and adding an extra hour. Historically both the boundaries and the offsets of timezones have changed, so to do this right you need a historical record of these changes and you need to know the location of the user. A simple TZ offset does not get you this.
IF you account for DST you also need to make sure you don't lose an hour or double count an hour twice a year.
It's nigh on impossible to get this 100% right if you account for DST which is why the TZ offset only approach is the better bet.
If you were using MySql, then you could use the function:
CONVERT_TZ(dt,from_tz,to_tz)
converts a datetime value dt from the time zone given by from_tz to the time zone given by to_tz and returns the resulting value. Time zones are specified as described in Section 9.7, “MySQL Server Time Zone Support”