I have the chart with three precisions.
Per hour,
Per 30 minutes,
Per 15 minutes.
Table with my data looks like this:
When i generating my chart i starting from the special date time for example from current date time
For example. When I starting from 18:00, and my preccision is per 15 minutes i need data from this times
18:00
17:45
17:30
17:15
17:00
...
In my data table I have data maximum per 3 minutes, so when i would like to get data from 17:15 my lambda query returns null because i have data only from 17:13 and 17:16.
So i need the query whitch return data nearest my data time. In upper example it's need to return data from 17:16.
I try DiffHours Method but it's don't work on MySQL. I need method working on MySQL and MSSQL
My current method looks like this:
var report = _reportRepository.FindBy(a => a.Fridge.FridgeIdentity == fridgeIdentity && a.CreatedDate.Year == fromTime.Year && a.CreatedDate.Month == fromTime.Month && a.CreatedDate.Day == fromTime.Day && a.CreatedDate.Hour == fromTime.Hour).FirstOrDefault();
but it's work only for per hour precision.
Thanks for help!
How about this, to get the closest time to a particular interval:
var fromTime = new DateTime(2016, 05, 20, 9, 0, 0);
var report = _reportRepository
.OrderBy(m =>m.CreatedDate > fromTime
? m.CreatedDate - fromTime
: fromTime - m.CreatedDate)
.Take(1);
You showed only limited code and weren't utterly specific about some points even after several questions about it, so I will assume the following:
you are able to create a working report for hourly precision, that means you are able to generate the list of desired times, e.g. 18:00, 18:15, 18:30 for a 15 minute interval (you just can't get the correct data for it) and these times are in the variable fromTime
you always have round times as report times, so e.g. 18:00, never 17:48
your closest entries can be both before and after the query time
if you do e.g. a 15 minutes report, and there is no value in your database with a datetime between 17:45:00 and 18:14:59, the report will not have any result for 18:00 (since your data covers every 3 minutes, it shouldn't be a problem anyway, except for pauses)
You have to use different queries for the 3 interval times. For 15 minutes use (assuming your table is called a):
select *
from
(select *,
convert(timestamp(date(date_add(CreatedDate, INTERVAL '7:30' MINUTE_SECOND)),
maketime(hour(date_add(CreatedDate, INTERVAL '7:30' MINUTE_SECOND)),
round(minute(date_add(CreatedDate, INTERVAL '7:30' MINUTE_SECOND)) div 15)
* 15, 0)), datetime) as filtertime
from a
) as withfilter
order by filtertime, abs(timediff(filtertime, CreatedDate)) ;
For the other intervals, you have to replace the interval accordingly (so replace 7:30 by half the interval time in minutes, and 15 by the interval time in minutes, so for 30 minute interval it would be:
select *
from
(select *,
convert(timestamp(date(date_add(CreatedDate, INTERVAL '15:00' MINUTE_SECOND)),
maketime(hour(date_add(CreatedDate, INTERVAL '15:00' MINUTE_SECOND)),
round(minute(date_add(CreatedDate, INTERVAL '15:00' MINUTE_SECOND)) div 30)
* 30, 0)), datetime) as filtertime
from a
) as withfilter
order by filtertime, abs(timediff(filtertime, CreatedDate)) ;
(and 30:00 and 60 for the hourly interval).
This will basically round your CreatedDate to the closest whole 15/30/60 minutes and order it by the timedifference to that. It will always round up, so a CreatedDate 2016-05-20 09:15:00 will be rounded to 2016-05-20 09:30:00 for a 30 minute interval, not to 2016-05-20 09:00:00.
You might want to take a direct look at the result to understand the final view; for your example data, e.g. 2016-05-20 09:18:40, it will first calculate the filtertimes 2016-05-20 09:15:00, 2016-05-20 09:30:00 and 2016-05-20 09:00:00 for the 3 queries 15 minutes, 30 minutes and 1 hour. It will then order it according to their distance to these times (3:40min, 11:20min and 18:40min).
Your reportfilter will have to use the filtertime instead of the CreatedDate time, and you have to add the minutes to compare to fromtime:
var report = _reportRepository.FindBy(a => a.Fridge.FridgeIdentity == fridgeIdentity
&& a.filtertime.Year == fromTime.Year
&& a.filtertime.Month == fromTime.Month
&& a.filtertime.Day == fromTime.Day
&& a.filtertime.Hour == fromTime.Hour
&& a.filtertime.Minute == fromTime.Minute).FirstOrDefault();
Related
I am working on a stock market software. Where I am having a candle every 5 minutes. So whenever a time-frame of say 30 minutes is selected, what we do is -
long val = (long)(D * 24 * 60) / 30; //D is datetime of candle converted in OA date in double.
//The above code never create problem as (24*60)%30 == 0.
The above line returns same value for every half an hour chunk i. e. candle of 10:35, 10:40.....11:00. With that we can easily find out chunks of half an hour, whenever val is changed.
Now, We have a challange to implement the chunk of 75-Mins in the same way. Our market start from 9:15 and ends at 3:30. Suppose date for which 75-Mins needs to be calculated is 22-9-2018. For that I will need to have exactly 5 candle of below time -
22-9-2018 10:30 (9:15 to 10:30 = 75 mins)
22-9-2018 11:45
22-9-2018 1:00
22-9-2018 2:15
22-9-2018 3:30
I need to have same kind of code as metioned above which will calculate same value for these five chunks.
Problem I found is, If we start 75 from 12:00, then the chunk in market time will be at 8:45 to 10:00 while we require from 9:15 to 10:30 first chunk.
Also, (24*60)%75 = 15, So 15 Mins difference everyday disturbs the next day calculation too.
UPDATE -
To clear the question, For a chunk from 10:35 to 11:45, I will have candles like 10:35, 10:40, 10:45..... 11:45. For all these datetimes, I need a same numeric return value. As soon as the candle of 11:50 comes, the returned numeric value will get changed and my new 75 Min chunk will start. It will give same value till 1:00.
You can use a loop or a linq query like this:
var startTime = new DateTime(2018, 09, 22, 9, 15, 0);
var times = Enumerable.Range(1, 5).Select(x => startTime.AddMinutes(x * 75)).ToList();
Example
Here is also another example about how to split a date range. In the following example, i included the start time also as part of the result:
IEnumerable<DateTime> Split(DateTime start, DateTime end, int minutes)
{
if (minutes <= 0)
throw new ArgumentException(
$"'{nameof(minutes)}' should be greater than 0.",
nameof(minutes));
var result = start;
while (result <= end)
{
yield return result;
result = result.AddMinutes(minutes);
}
}
And here is the usage:
var startTime = new DateTime(2018, 09, 22, 9, 15, 0);
var endTime = startTime.AddHours(7);
var times = Split(startTime, endTime, 75).ToList();
I am using Highcharts to give visual displays of information. As of right now, I am grouping all of the database table records by the hour:
var lstGroupSummariesByHour =
lstAllSummaries.Where(x => x.DateTimeProperty.Year == year && !x.deleted)
.GroupBy(x => x.DateTimeProperty.Hour)
.Select(x => new object[] {x.Count()}).ToArray();
This is one line on my line chart.. but I am looking to create a new line where it shows all summaries for every half hour. Is there a simple LINQ lambda way to achieve this?
According to the this question if I wanted 30 minute intervals my code would look something like this since in the second answer he divides the minute by 12 to get 5 minute intervals?
var lstGroupSummariesByHalfHour =
lstAllSummaries.Where(x => x.DateTimeProperty.Year == year && !x.deleted)
.GroupBy(
x =>
new DateTime(x.DateTimeProperty.Year, x.DateTimeProperty.Month, x.DateTimeProperty.Day, x.DateTimeProperty.Hour,
x.DateTimeProperty.Minute / 2, 0))
.Select(x => new object[] {x.Count()}).ToArray();
This is returning right under 3500 records which is causing a Highcharts error of not enough ticks on the X-Axis.. which is 0 through 24 (based on hour).. so shouldn't it be returning 48 since there are 48 half-hours in a day? 24 * 2 (which would still return the highcharts error, but I will deal with that later)?
How do I fix the code above to get results for every half hour?
UPDATE
What I'm looking for (for example), is how many summaries are between 0100 & 0130, 0131 - 0200, 0201 - 0230.. and so on and so on.
Here is what my graph currently looks like:
I want to get the count of summaries for the tick that is in between each number (Hour).. 0030.. 0130.. 0230..
My graph currently is based on the entire year.. so throughout the year.. 'x' number of summaries during the 0 hour.. 'y' number of summaries during the 1 hour.. and so on and so on.. so I'm looking for a total count of number of summaries for the entire year that happened between 0000 - 0030, 0031-0100, 0101 - 0130.. and so on and so on.
you need to quantize the minutes to either 0 or 30
new DateTime(
x.DateTimeProperty.Year,
x.DateTimeProperty.Month,
x.DateTimeProperty.Day,
x.DateTimeProperty.Hour,
x.DateTimeProperty.Minute > 30 ? 30 : 0,
0));
I feel like this is something really simple, but my Google Fu is letting me down as I keep finding difference calculations.
I have a time (e.g. 1800 hours) stored in a DateTime object. The date is null and immaterial. All I want to know is how many milliseconds until the NEXT occurrence of that time.
So, if I run the calculation at 0600 - it will return 12 hours (in ms). At 1750, it will return ten minutes (in ms) and at 1900 it will return 24 hours (in ms).
All the things I can find show me how to calculate differences, which doesn't work once you're past the time.
Here is what I tried, but fails once you're past the time and gives negative values:
DateTime nowTime = DateTime.Now;
TimeSpan difference = _shutdownTime.TimeOfDay - nowTime.TimeOfDay;
double result = difference.TotalMilliseconds;
You're already doing everything you should, except for one thing: handling negative results.
If the result is negative, it means the time you want to calculate the duration until has already passed, and then you want it to mean "tomorrow" instead, and get a positive value.
In this case, simply add 24 hours:
DateTime nowTime = DateTime.Now;
TimeSpan difference = _shutdownTime.TimeOfDay - nowTime.TimeOfDay;
double result = difference.TotalMilliseconds;
if (result < 0)
result += TimeSpan.FromHours(24).TotalMilliseconds;
The next thing to consider is this: If the time you want to calculate the duration until is 19:00 hours, and the current time is exactly 19:00 hours, do you want it to return 0 (zero) or 24 hours worth of time? Meaning, do you really want the next such occurrence?
If so, then change the above if-statement to use <=:
DateTime nowTime = DateTime.Now;
TimeSpan difference = _shutdownTime.TimeOfDay - nowTime.TimeOfDay;
double result = difference.TotalMilliseconds;
if (result <= 0)
result += TimeSpan.FromHours(24).TotalMilliseconds;
However, note that this will be prone to the usual problems with floating point values. If the current time is 18:59:59.9999999, do you still want it to return the current time (a minuscule portion of time) until 19:00 today, or do you want it to flip to tomorrow? If so, change the comparison to be slightly different:
DateTime nowTime = DateTime.Now;
TimeSpan difference = _shutdownTime.TimeOfDay - nowTime.TimeOfDay;
double result = difference.TotalMilliseconds;
if (result <= -0.0001)
result += TimeSpan.FromHours(24).TotalMilliseconds;
where -0.0001 is a value that corresponds to "the range of inaccuracy you're prepared to accept being tomorrow instead of today in terms of milliseconds".
When doing calculations like this it is important to take possible DST changes under consideration so that your results remain correct.
Suppose your operational parameters are:
var shutdownTime = TimeSpan.FromHours(18);
// just to illustrate, in Europe there is a DST change on 2013-10-27
// normally you 'd just use DateTime.Now here
var now = new DateTime(2013, 10, 26, 20, 00, 00);
// do your calculations in local time
var nextShutdown = now.Date + shutdownTime;
if (nextShutdown < now) {
nextShutdown = nextShutdown.AddDays(1);
}
// when you want to calculate time spans in absolute time
// (vs. wall clock time) always convert to UTC first
var remaining = nextShutdown.ToUniversalTime() - now.ToUniversalTime();
Console.WriteLine(remaining);
The answer to your question would now be remaining.TotalMilliseconds.
long ticks = new DateTime(2012, 1, 31).ToLocalTime().Ticks; // 634635684000000000
But how to do this without DateTime constructor ?
edit
What I actually want is to keep only the years, months and days from the ticks.
long ALL_Ticks = DateTime.Now.Ticks; // 634636033446495283
long Only_YearMonthDay = 634635684000000000; // how to do this ?
I want to use this in a linq-sql query using Linq.Translations.
If you only want the ticks for the date portion of the current datetime you could use:
long Only_YearMonthDay = DateTime.Now.Date.Ticks; //634635648000000000
//DateTime.Now.Date.Ticks + DateTime.Now.TimeOfDay.Ticks == DateTime.Now.Ticks
You could find out how many days are in the calculation and then multiply by 864,000,000,000 (which is how many ticks are in a day). Is that what you are looking for? Bit of documentation here : http://msdn.microsoft.com/en-us/library/system.timespan.ticksperday.aspx.
Happy coding,
Cheers,
Chris.
OK - didn't think this through properly! Ticks represent the amount of 100 nanosecond intervals since 12:00:00 midnight, January 1, 0001. You would need to calculate how many days have passed since that date and then multiply it by the ticks per day value!
If I understand you right, you are not worried about the ticks up to a particular time of the day?! So, it would be something along the lines of :
var ticksToDate = (DateTime.UtcNow - DateTime.MinValue).Days * 864000000000;
Does that answer your question??
That is going to be rather difficult unless you have some other way of getting the current date and time. According to MSDN:
A single tick represents one hundred nanoseconds or one ten-millionth of a second. There are 10,000 ticks in a millisecond.
The value of this property represents the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001, which represents DateTime.MinValue. It does not include the number of ticks that are attributable to leap seconds.
Now, if you know the current date and time, you can calculate how many days have passed since January 1, 0001 and use that to calculate the number of ticks.
I understand you dont want the hour parts of the date. If you use Date, then you only get the day (for example: 01/01/2012 00:00:00)
long ticks = new DateTime(2012, 1, 31).Date.Ticks;
And with any DateTime object already created is the same of course.
long ticks = dateObject.Date.Ticks;
You already have the answer there in your post:
long ALL_Ticks = DateTime.Now.Ticks;
// that's the ticks (from DateTime.MinValue) until 'now' (this very moment)
long ticks = new DateTime(2012, 1, 31).ToLocalTime().Ticks;
// or
long ticks = DateTime.Now.Date.Ticks;
// that's the ticks until the beginning of today
long yearmonthticks = new DateTime(2012, 1, 1).ToLocalTime().Ticks;
// that's the ticks until the beginning of the month
// etc..., the rest is simple subtractions
Since your question doesn't specify any reason not to use the DateTime constructor, this is the best solution for what seems like your problem.
I had a use case where I couldn't use DateTime but needed Years/Months from Ticks.
I used the source behind DateTime to figure out how. To go the other way you can look at the constructor, one of which calls the following code.
private static long DateToTicks(int year, int month, int day) {
if (year >= 1 && year <= 9999 && month >= 1 && month <= 12) {
int[] days = IsLeapYear(year)? DaysToMonth366: DaysToMonth365;
if (day >= 1 && day <= days[month] - days[month - 1]) {
int y = year - 1;
int n = y * 365 + y / 4 - y / 100 + y / 400 + days[month - 1] + day - 1;
return n * TicksPerDay;
}
}
throw new ArgumentOutOfRangeException(null, Environment.GetResourceString("ArgumentOutOfRange_BadYearMonthDay"));
}
This can be found in link below, of course you will need to re-write to suit your needs and look up the constants and IsLeapYear function too.
https://referencesource.microsoft.com/#mscorlib/system/datetime.cs,602
Given two datetimes. What is the best way to calculate the number of working hours between them. Considering the working hours are Mon 8 - 5.30, and Tue-Fri 8.30 - 5.30, and that potentially any day could be a public holiday.
This is my effort, seem hideously inefficient but in terms of the number of iterations and that the IsWorkingDay method hits the DB to see if that datetime is a public holiday.
Can anyone suggest any optimizations or alternatives.
public decimal ElapsedWorkingHours(DateTime start, DateTime finish)
{
decimal counter = 0;
while (start.CompareTo(finish) <= 0)
{
if (IsWorkingDay(start) && IsOfficeHours(start))
{
start = start.AddMinutes(1);
counter++;
}
else
{
start = start.AddMinutes(1);
}
}
decimal hours;
if (counter != 0)
{
hours = counter/60;
}
return hours;
}
Before you start optimizing it, ask yourself two questions.
a) Does it work?
b) Is it too slow?
Only if the answer to both question is "yes" are you ready to start optimizing.
Apart from that
you only need to worry about minutes and hours on the start day and end day. Intervening days will obviously be a full 9/9.5 hours, unless they are holidays or weekends
No need to check a weekend day to see if it's a holiday
Here's how I'd do it
// Normalise start and end
while start.day is weekend or holiday, start.day++, start.time = 0.00am
if start.day is monday,
start.time = max(start.time, 8am)
else
start.time = max(start.time, 8.30am)
while end.day is weekend or holiday, end.day--, end.time = 11.59pm
end.time = min(end.time, 5.30pm)
// Now we've normalised, is there any time left?
if start > end
return 0
// Calculate time in first day
timediff = 5.30pm - start.time
day = start.day + 1
// Add time on all intervening days
while(day < end.day)
// returns 9 or 9.30hrs or 0 as appropriate, could be optimised to grab all records
// from the database in 1 or 2 hits, by counting all intervening mondays, and all
// intervening tue-fris (non-holidays)
timediff += duration(day)
// Add time on last day
timediff += end.time - 08.30am
if end.day is Monday then
timediff += end.time - 08.00am
else
timediff += end.time - 08.30am
return timediff
You could do something like
SELECT COUNT(DAY) FROM HOLIDAY WHERE HOLIDAY BETWEEN #Start AND #End GROUP BY DAY
to count the number of holidays falling on Monday, Tuesday, Wednesday, and so forth. Probably a way of getting SQL to count just Mondays and non-Mondays, though can't think of anything at the moment.
especially considering the IsWorkingDay method hits the DB to see if that day is a public holiday
If the problem is the number of queries rather than the amount of data, query the working day data from the data base for the entire day range you need at the beginning instead of querying in each loop iteration.
There's also the recursive solution. Not necessarily efficient, but a lot of fun:
public decimal ElapseddWorkingHours(DateTime start, DateTime finish)
{
if (start.Date == finish.Date)
return (finish - start).TotalHours;
if (IsWorkingDay(start.Date))
return ElapsedWorkingHours(start, new DateTime(start.Year, start.Month, start.Day, 17, 30, 0))
+ ElapsedWorkingHours(start.Date.AddDays(1).AddHours(DateStartTime(start.Date.AddDays(1)), finish);
else
return ElapsedWorkingHours(start.Date.AddDays(1), finish);
}
Take a look at the TimeSpan Class. That will give you the hours between any 2 times.
A single DB call can also get the holidays between your two times; something along the lines of:
SELECT COUNT(*) FROM HOLIDAY WHERE HOLIDAY BETWEEN #Start AND #End
Multiply that count by 8 and subtract it from your total hours.
-Ian
EDIT: In response to below, If you're holiday's are not a constant number of hours. you can keep a HolidayStart and a HolidayEnd Time in your DB and and just return them from the call to the db as well. Do an hour count similar to whatever method you settle on for the main routine.
Building on what #OregonGhost said, rather than using an IsWorkingDay() function at accepts a day and returns a boolean, have a HolidayCount() function that accepts a range and returns an integer giving the number of Holidays in the range. The trick here is if you're dealing with a partial date for your boundry beginning and end days you may still need to determine if those dates are themselves holidays. But even then, you could use the new method to make sure you needed at most three calls the to DB.
Try something along these lines:
TimeSpan = TimeSpan Between Date1 And Date2
cntDays = TimeSpan.Days
cntNumberMondays = Iterate Between Date1 And Date2 Counting Mondays
cntdays = cntdays - cntnumbermondays
NumHolidays = DBCall To Get # Holidays BETWEEN Date1 AND Date2
Cntdays = cntdays - numholidays
numberhours = ((decimal)cntdays * NumberHoursInWorkingDay )+((decimal)cntNumberMondays * NumberHoursInMondayWorkDay )
Use #Ian's query to check between dates to find out which days are not working days. Then do some math to find out if your start time or end time falls on a non-working day and subtract the difference.
So if start is Saturday noon, and end is Monday noon, the query should give you back 2 days, from which you calculate 48 hours (2 x 24). If your query on IsWorkingDay(start) returns false, subtract from 24 the time from start to midnight, which would give you 12 hours, or 36 hours total non-working hours.
Now, if your office hours are the same for every day, you do a similar thing. If your office hours are a bit scattered, you'll have more trouble.
Ideally, make a single query on the database that gives you all of the office hours between the two times (or even dates). Then do the math locally from that set.
The most efficient way to do this is to calculate the total time difference, then subtract the time that is a weekend or holiday. There are quite a few edge cases to consider, but you can simplify that by taking the first and last days of the range and calculating them seperately.
The COUNT(*) method suggested by Ian Jacobs seems like a good way to count the holidays. Whatever you use, it will just handle the whole days, you need to cover the start and end dates separately.
Counting the weekend days is easy; if you have a function Weekday(date) that returns 0 for Monday through 6 for Sunday, it looks like this:
saturdays = ((finish - start) + Weekday(start) + 2) / 7;
sundays = ((finish - start) + Weekday(start) + 1) / 7;
Note: (finish - start) isn't to be taken literally, replace it with something that calculates the time span in days.
Dim totalMinutes As Integer = 0
For minute As Integer = 0 To DateDiff(DateInterval.Minute, contextInParameter1, contextInParameter2)
Dim d As Date = contextInParameter1.AddMinutes(minute)
If d.DayOfWeek <= DayOfWeek.Friday AndAlso _
d.DayOfWeek >= DayOfWeek.Monday AndAlso _
d.Hour >= 8 AndAlso _
d.Hour <= 17 Then
totalMinutes += 1
Else
Dim test = ""
End If
Next minute
Dim totalHours = totalMinutes / 60
Piece of Cake!
Cheers!