I have an array of timespans that are in 15 minute intervals, e.g.:
00:00:00,
00:15:00,
00:30:00,
00:45:00,
01:00:00
etc.
I want to loop through and only show those which are of a 30 minute interval. I've tried using the .compare but this doesn't get exactly what I need.
For example:
var interval = TimeSpan.FromMinutes(30);
foreach(var t in MyArray)
{
if(TimeSpan.Compare(inteval, t.Time) == 0)
Do something
}
}
This technically works, but would only match 00:30:00. I can add the interval to the loop and use something like:
var interval = new TimeSpan(t.Hour, 30, 0)
but that only gets 00:30:00, 01:30:00.
Is there as way to make the hour like a wildcard and get every 30 min?
The output I'd want is:
00:00:00
00:30:00
01:00:00
01:30:00
etc.
What you want is a modulo operation, but since TimeSpans themselves don't define that, use TotalMinutes (or another property) to get a plain integer and then use modulo:
if ((int)t.TotalMinutes % 30 == 0)
You can also filter your array for every second item .. then use the new array and print is sequentially:
var newArray = MyArray.Where((t, index) => index % 2 == 0).ToArray();
This gives you every second item in the list.
Related
I want to write a simple program to ease my life at work, but I'm not too familiar with C# so I'm trying to read the documentation and some examples, but I don't find the documentation too good, and can't find any examples to feed off.
I have a CSV file (wireless temperature sensor that sometimes loses connection or sends multiple readouts in quick succession) and I want to compare two DateTimes (TimeSpan) from 2 subsequent rows, and depending on result remove that row, or add another in between these. If TimeSpan between 2 DateTimes is less than 10 minutes, delete that row. If it's longer than 10 minutes create a new row with time that is 10 minutes after the first one. All readouts are between themselves in multiples of 10 minutes.
Example file:
[DelimitedRecord(","), IgnoreFirst(1)]
public class CSVDataFields{
[FieldQuoted('"')] [FieldConverter(ConverterKind.Date, "MM/dd/yyyy h:mm tt")]
public DateTime Date;
[FieldQuoted('"')]
public float Value;
}
"Date","Value"
"03/19/2019 3:10 PM","20.5"
"03/19/2019 3:10 PM","20.5"
"03/19/2019 3:10 PM","20.4"
"03/19/2019 3:20 PM","20.2"
"03/19/2019 3:50 PM","20.0"
"03/19/2019 4:00 PM","19.8"
So the first check is [0] and [1], it's less than 10, so delete that row, [0] and [2] less than 10 - delete, [0] and [3] it's ok,
[3] and [4] is longer than 10 minutes, create a new row with time([3] + 10) and value average (20.2, 20.0),
new [4] and [5] is longer than 10 minutes, create a new row with time ([4] + 10) and value average (20.1, 20.0), and so on.
In the example on FileHelpers.net there is only this example. Here you can only access 1 row at a time I think, while I need to have access to 2 rows at the same time.
private void DetectDupes(ref CSVDataFields[] csv){
foreach(CSVDataFields csvData in csv){
}
}
I've also not gotten that far as how to save the new file with the modified rows.
You can accomplish this with Linq's Aggregate method:
var engine = new FileHelperEngine<CSVDataFields>();
var result = engine.ReadFile(#"c:\temp\some_source_file.txt");
List<CSVDataFields> newRows = new List<CSVDataFields>();
newRows.Add(result.First());
result.Aggregate((a, b) =>
{
var diff = Math.Abs((a.Date - b.Date).Minutes);
if (diff < 10)
{
return a;
}
else if (diff == 10)
{
newRows.Add(b);
return b;
}
else
{
var newRow = new CSVDataFields()
{
Date = a.Date.AddMinutes(10),
Value = (a.Value + b.Value) / 2
};
newRows.Add(newRow);
return newRow;
}
});
engine.WriteFile(#"C:\temp\destination_file_deduped.txt", newRows);
Output:
03/19/2019 3:10 PM, 20.5
03/19/2019 3:20 PM, 20.2
03/19/2019 3:30 PM, 20.1
03/19/2019 3:40 PM, 19.95
Explanation:
The Aggregate method iterates through an enumerable, executing a function delegate which takes as parameters the current value (a) and the next value (b) for each item in the enumerable. On each iteration, it decides if it should skip the new item b (diff < 10), add it to dedupped list (diff==10) or merge it (diff > 10). The key thing to understand here is that b is always the next item in the enumerable and the value that is returned from the current iteration becomes the current value (a) of the next iteration. In other words, a represents the result of the execution of the function for each iteration.
It's generally not a good idea to overwrite the source file (unless you have it backed up somewhere) so I am outputting to a new file but you can change that if you have to.
Also, this is a simplistic example. Make sure to account for situations like the file having no rows and what not.
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'm trying to create a downtown that will display times from 00:00 to 24:59 at a selected number of intervals (and this could change from 5 minutes to 10 minutes etc
so for example a list of
00:10
00:20
00:30
or could be
00:15
00:30
I'm using the following which works, but only for a selected number of iterations (33):
List<string> query = Enumerable
.Range(0, 33)
.Select(i => DateTime.Today
.AddHours(0)
.AddMinutes(i * (double)IntervalParameter)
.ToString())
.ToList();
*IntervalParameter = 10 for the example above.
I'm looking to adapt this so it runs the full 24 hours time frame. Just looking for the most efficient way to do this.
Why not compute the number of items?
int IntervalParameter = 5;
// .Range(0, 1440 / IntervalParameter) - see Zohar Peled's comment -
// is brief, but less readable
List<string> query = Enumerable
.Range(0, (int) (new TimeSpan(24, 0, 0).TotalMinutes / IntervalParameter))
.Select(i => DateTime.Today
.AddMinutes(i * (double)IntervalParameter) // AddHours is redundant
.ToString("HH:mm")) // Let's provide HH:mm format
.ToList();
Something like this?
public static IEnumerable<TimeSpan> Intervals(TimeSpan inclusiveStart, TimeSpan exclusiveEnd, TimeSpan increment)
{
for (var time = inclusiveStart; time < exclusiveEnd; time += increment)
yield return time;
}
Example usage:
foreach (var time in Intervals(TimeSpan.Zero, TimeSpan.FromDays(1), TimeSpan.FromMinutes(15)))
{
Console.WriteLine(time);
}
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();
I have a collection of class with a DateTime Property, I would like to get sum of Minutes and Seconds. Is there any easy way to do this using LINQ without manually summing the minutes and seconds?
I suppose you mean TimeSpan instead of DateTime? You can't add DateTime's...
To sum TimeSpan's:
list.Sum(span => span.TotalSeconds) ==> total seconds
TimeSpan.FromSeconds(...) ==> Convert total seconds back to time span
You can then use properties in TimeSpan to reformat it back to hours, minutes, seconds etc.
+1 for Stephen's answers.
Just for fun... if you really do want "to get sum of Minutes and Seconds" then:
class MyClass
{
public DateTime DateTimeMember {get;set;}
// other stuff
}
var myObjects = new List<MyClass>();
// fill list...
// 3 possible things you might be interested in
var myMinuteSum = myObjects.Sum(x => x.DateTimeMember.Minute);
var mySecondSum = myObjects.Sum(x => x.DateTimeMember.Second);
var myOddTotalOfMinutesAndSecondsInSeconds = myObjects.Sum(x => x.DateTimeMember.Minute * 60 + x.DateTimeMember.Second);