I'm trying to create a list based on another list. The list looks like -
{T1, T1, T2, T2, T3, T3} however the integer is subject to change based on user inputs. I am trying to assign a TimeSpan value on a new list based on the index of the old list, and the integers will vary the result. For example, if the start time is given as 11:00, and the time gap given by the user is 5 (minutes), the new list should look like - {11:00, 11:00, 11:05, 11:05, 11:10; 11:10}
Here is my current function:
public List<string> TimeGet(List<string> heatList, TimeSpan startTimeSpan, TimeSpan timeGap)
{
List<string> timeList = new List<string>();
string timeToAddString;
for (int i = 0; i < heatList.Count; i++)
{
if (heatList[i].Contains("1"))
{
TimeSpan timeToAdd = startTimeSpan;
timeToAddString = Convert.ToString(timeToAdd);
timeList.Add(timeToAddString);
}
else
{
string resultString = Regex.Match(heatList[i], #"\d+").Value;
int resultInt = Int32.Parse(resultString);
timeGap = TimeSpan.FromMinutes(timeGap.Minutes * resultInt);
TimeSpan timeToAdd = startTimeSpan + timeGap;
timeToAddString = Convert.ToString(timeToAdd);
timeList.Add(timeToAddString);
}
}
}
I would like the new list to correspond to the values on heatlist depending on the integer based in each string on the list, and produce a timespan or time of day to correspond with it on a new list.
This function can be made much simpler, assuming every element in heatList follows that T\d+ pattern.
public IEnumerable<string> TimeGet(List<string> heatList, TimeSpan startTimeSpan, TimeSpan timeGap)
{
foreach (var element in heatList)
{
var input = element.Substring(1); // Takes everything from index 1 = the first digit in the string
int multiplier = Int32.Parse(input) - 1;
var additionalTime = new TimeSpan(0, (int)(timeGap.Minutes * multiplier), 0);
yield return (startTimeSpan + additionalTime).ToString();
}
}
Sample usage:
string[] sBaseList = { "T1", "T1", "T2", "T2", "T4", "T6" };
var sList = sBaseList.ToList();
TimeSpan startSpan = new TimeSpan(11, 0, 0);
TimeSpan gapSpan = new TimeSpan(0, 5, 0);
var result = TimeGet(sList, startSpan, gapSpan);
foreach (var s in result)
Console.WriteLine(s);
Console.ReadKey();
Result: 11:00:00, 11:00:00, 11:05:00, 11:05:00, 11:15:00, 11:25:00.
namespace Test
{
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
class MainClass
{
public static void Main (string[] args)
{
List<string> inputdata = new List<string> ();
List<TimeSpan> outputdata = new List<TimeSpan> ();
string input = null;
while ((input = Console.ReadLine ()) != string.Empty) {
inputdata.Add (input);
TimeSpan t = new TimeSpan (11, 0, 0) + new TimeSpan (0, Convert.ToInt32 (Regex.Match (input, "\\d+").ToString ()), 0);
outputdata.Add (t);
}
for (int i = 0; i < inputdata.Count; i++) {
Console.WriteLine ("Inputdata: {0}, Outputdata: {1}", inputdata [i], outputdata [i].ToString ());
}
}
}
}
Use LINQ
return heatList
.Select(t => TimeSpan.FromTicks(
(Int32.Parse(t.Substring(1))-1) * timeGap.Ticks
)
+ startTimeSpan).ToString("hh:mm")
.ToList();
Explanation:
.Select(t => ...) enumerates the strings from the heatList assigning each temperature to t.
t.Substring(1) skips the "T" in "T123".
Int32.Parse(t.Substring(1)) - 1 creates the number range 0, 1, 2 ...
* timeGap.Ticks gives the offset for a given temperature in ticks.
TimeSpan.FromTicks(...) + startTimeSpan) yields the resulting time by adding timespans.
.ToString("hh:mm") converts the resulting timespan into a string.
.ToList(); creates a new list.
Ticks is the unit TimeSpan uses to store time spans internally.
you can do that with simple linq statement
public static List<string> TimeGet(List<string> heatList, TimeSpan startTimeSpan, TimeSpan timeGap)
{
return heatList
.Select(x =>
startTimeSpan.Add(TimeSpan.FromMinutes(timeGap.Minutes*(int.Parse(x.Substring(1)) - 1)))
.ToString(#"hh\:mm")).ToList();
}
this will select an item from heatList one by one, parse the number in it and subtract 1 from it (so T1 result is 0 and T2 in 1 and ...), now add timeGap times the resulted number to startTimeSpan and format it in hh:mm format.
Related
I have a datasource that returns dates and I have to find where the months falls within the month and day range buckets. The months and day range buckets are predefined so I put it in a Dictionary (not sure if that is even a good idea). I am using linq to find the min and Max dates and extracting the month from them. I need to find month from the dictionary where that month extracted falls within the range. For Example
Dictionary<int, int> MonthDayBuckets = new Dictionary<int, int>() { { 3,31 }, { 6,30 }, { 9,30 }, { 12,31 } };
var MinyDate = _dataSource.Min(x => x.values[0]);
var MaxDate = _dataSource.Max(x => x.values[0]);
var startMonth = Convert.ToDateTime(MinyDate).ToString("MM");
var endMonth = Convert.ToDateTime(MaxDate).ToString("MM");
Say startmonth return Jan so I want to be able to go to the dictionary and return only march (03.31) and if I get 10 for the Max (October) I am trying to return (12,31) December
If my understanding is correct, your MonthDayBuckets variable is meant to represent date ranges:
3/31 - 6/30
6/30 - 9/30
9/30 - 12/31
12/31 - 3/31
...and given a month, you're wanting to see what the end date is of the interval that the first of that month falls between? Like you gave the example of October returning 12/31.
This problem can be simplified since you'll get the same result saying "what's the next occurring date after this given date?" The next occurring date for 10/01 would be 12/31. So here's how you could rearrange your data:
var availableDates = new List<string> { "03/31", "06/30", "09/30", "12/31" };
Now you'll be able to find a match by finding the index of the first one that's greater than your given date. Note how I made the month/day combos lexicographical orderable.
var startMonth = Convert.ToDateTime(MinyDate).ToString("MM");
var startDate = startMonth + "/01";
var endMonth = Convert.ToDateTime(MaxDate).ToString("MM");
var endDate = endMonth + "/01";
// Wrap around to the first date if this falls after the end
var nextStartDate = availableDates.FirstOrDefault(d => d.CompareTo(startDate) >= 1) ?? availableDates[0];
var nextEndDate = availableDates.FirstOrDefault(d => d.CompareTo(endDate) >= 1) ?? availableDates[0];
You could use Linq for the purpose. For example,
var nearestKey = MonthDayBuckets.Keys.Where(x => x >= endMonth.Month).Min();
var nearestDate = new DateTime(DateTime.Now.Year,nearestKey,MonthDayBuckets[nearestKey]); // or whatever the year it needs to be represent
Though above query would get you the result, I would suggest you define a structure to store the Range itself, rather than using Dictionary
For example,
public class Range
{
public MonthDate StartRange{get;set;}
public MonthDate EndRange{get;set;}
public Range(MonthDate startRange,MonthDate endRange)
{
StartRange = startRange;
EndRange = endRange;
}
}
public class MonthDate
{
public MonthDate(int month,int date)
{
Month = month;
Date = date;
}
public int Month{get;set;}
public int Date{get;set;}
//Depending on if your Ranges are inclusive or not,you need to decide how to compare
public static bool operator >=(MonthDate source, MonthDate comparer)
{
return source.Month>= comparer.Month && source.Date>=comparer.Date;
}
public static bool operator <=(MonthDate source, MonthDate comparer)
{
return source.Month<= comparer.Month && source.Date<=comparer.Date;
}
}
Now you could define ranges as
var dateRanges = new Range[]
{
new Range(new MonthDate(12,31),new MonthDate(3,31)),
new Range(new MonthDate(3,31),new MonthDate(6,30)),
new Range(new MonthDate(6,30),new MonthDate(12,31)),
};
var result = dateRanges.First(x=>x.StartRange <= new MonthDate(endMonth.Month,endMonth.Day) && x.EndRange >= new MonthDate(endMonth.Month,endMonth.Day));
I have the following code:
Dictionary<DayOfWeek, List<TimeSpan>> Daily = new Dictionary<DayOfWeek, List<TimeSpan>>();
The idea is that i can add a day with a time to the Daily dictionary. But a job can execute on the same day more than once.
so Daily can look like this:
{ "Monday" : [{"Hour":10, "Minute": 15}, {"Hour": 8, "Minute":5}] }
Now i would like to get the next execution datetime.
private void UpdateNextExecutionTime()
{
TimeSpan t = new TimeSpan(15, 15, 0);
DayOfWeek current = DateTime.Now.DayOfWeek;
DayOfWeek tmp = current;
TimeSpan time = new TimeSpan(DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second);
int cur = (int)current;
for(int i = 0; i < 7; i++) {
if(Daily.ContainsKey((DayOfWeek)cur)) {
tmp = (DayOfWeek)cur;
/* stuck */
continue;
}
cur++;
if (cur >= 7)
cur = 0;
}
}
I managed to get the first upcoming day (including today) in the Daily dictionary but i'm stuck on the getting the time.
How would i be able to do this?
Assuming t is the time you're using as the point at which you'd like to find the next execution time, put this where you have 'stuck' (edit; just realized you changed to using TimeSpan in your list):
var execTimes = Daily[tmp];
if (execTimes != null) {
var nextTime = execTimes.OrderBy(x => x).FirstOrDefault(x => x > t);
if (nextTime != default(TimeSpan)) {
// do something...
}
}
You can also have the day in the TimeSpan,
Here is a simple example the finds the next date:
var schedule = new List<TimeSpan>{
new TimeSpan(0,16,30,0),
new TimeSpan(1,16,30,0),
new TimeSpan(5,16,30,0)
};
var monday = DateTime.Today.AddDays(-(int)DateTime.Today.DayOfWeek + 1);
var fromMonday = DateTime.Now - monday;
var next = schedule.OrderBy(t => t).FirstOrDefault(t => t > fromMonday);
Console.Write(monday + next - DateTime.Now);
I want to be able to calculate the time in hours and minutes elapsed between, say, 12:35pm 02/13/2016 to 1:45pm 02/14/2016, but can't figure out the correct format to input it.
EDIT: Should add that the span between the times will be stored in an arraylist, one span per customer.
Basically, you need something like this:
var dateA = new DateTime(2016,2,13,12,35,0);
var dateB = new DateTime(2016,2,14,1,45,0);
var timespan = dateB - dateA;
var hours = timespan.Hours;
bar minutes = timespan.Minutes;
Here's how I would go about it:
Func<string, DateTime?> tryParse = t =>
{
DateTime output;
if (DateTime.TryParseExact(
t,
new [] { "h:mmtt MM/dd/yyyy" },
System.Globalization.CultureInfo.CurrentCulture,
System.Globalization.DateTimeStyles.AssumeLocal,
out output))
{
return output;
}
return null;
};
var dt1 = tryParse("12:35pm 02/13/2016");
var dt2 = tryParse("1:45pm 02/14/2016");
TimeSpan? ts = null;
if (dt1.HasValue && dt2.HasValue)
{
ts = dt2.Value.Subtract(dt1.Value);
}
if (ts.HasValue)
{
Console.WriteLine(
String.Format(
"{0} hours & {1} minutes",
ts.Value.Hours,
ts.Value.Minutes));
}
I'm trying to plot a flot chart. I want the x axis to be months from january to december and y axis to be account balances. I have the income and expense of the account over 12 months time but subtracting them would only give me the difference for the month it does not add the balance from the previous month.
Here is how I obtained the income and expenses over the range:
var monthsToDate = Enumerable.Range(1, 12)
.Select(m => new DateTime(DateTime.Today.Year, m, 1))
.ToList();
var sums = from month in monthsToDate
select new
{
month = month,
income = (from account in household.Accounts
from transaction in account.Transactions
where transaction.IsIncome && transaction.Created.Month == month.Month
select transaction.Amount).DefaultIfEmpty().Sum(),
expense = (from account in household.Accounts
from transaction in account.Transactions
where !transaction.IsIncome && transaction.Created.Month == month.Month
select transaction.Amount).DefaultIfEmpty().Sum(),
};
what I get back is this
.
.
.
[4] = { month = {5/1/2015 12:00:00 AM}, income = 3000, expense = 1804.75 }
[5] = { month = {6/1/2015 12:00:00 AM}, income = 2500, expense = 1560 }
[6] = { month = {7/1/2015 12:00:00 AM}, income = 0, expense = 550 }
.
.
.
You can add this reusable extension method to your code:
internal static class CollectionExtensions
{
/// <summary>
/// Returns a sequence whose first element is the first element of the source sequence (if any),
/// and every subsequent element is the result of applying a specified accumulator function to the
/// previous element of the resulting sequence and the next member of the source sequence.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="accumulator"></param>
/// <returns></returns>
public static IEnumerable<T> Accumulate<T>(this IEnumerable<T> source, Func<T, T, T> accumulator)
{
if (source == null) throw new ArgumentNullException("source");
if (accumulator == null) throw new ArgumentNullException("accumulator");
return source.AccumulateImpl(accumulator);
}
private static IEnumerable<T> AccumulateImpl<T>(this IEnumerable<T> source, Func<T, T, T> accumulator)
{
using (var enumerator = source.GetEnumerator())
{
T accumulation;
T next;
if (enumerator.MoveNext())
accumulation = enumerator.Current;
else yield break;
yield return accumulation;
if (enumerator.MoveNext())
next = enumerator.Current;
else yield break;
while (true)
{
accumulation = accumulator(accumulation, next);
yield return accumulation;
if (enumerator.MoveNext())
next = enumerator.Current;
else yield break;
}
}
}
}
Example usage:
var range = Enumerable.Range(0, 5); // 0, 1, 2, 3, 4
var accumulated = range.Accumulate((x, y) => x + y); // 0, 1, 3, 6, 10
Now, if you change your select to return a named type, instead of an anonymous one (I'm assuming you're using decimal for money - if not, you can adapt this code):
internal class MonthlyIncomeAndExpenses
{
public DateTime Month { get; set; }
public decimal Income { get; set; }
public decimal Expenses { get; set; }
}
var sums = from month in monthsToDate
select new MonthlyIncomeAndExpenses
{
Month = month,
Income = ..., // what you already have
Expense = ..., // what you already have
};
Then you only have to add one easy line:
var accumulated = sums.Accumulate((previous, next) => new MonthlyIncomeAndExpenses
{
Month = next.Month,
Income = previous.Income + next.Income,
Expense = previous.Expense + next.Expense,
});
Since you need an array of arrays for flot, you could just run a loop over your array and sum up the income and expense from all previous month. Something like this (after your existing code):
var flotDataAsList = new List<double[]>();
double balance = 0.0;
for (int i = 0; i <= 12; i++)
{
DateTime thisMonth = new DateTime(year, i, 1);
balance += sums.Where(m => m.month == thisMonth).Sum(m => m.income - m.expense);
flotDataAsList .Add(new double[] { GetJavascriptTimestamp(thisMonth), balance });
}
var flotDataAsArray = flotDataAsList.ToArray();
GetJavascriptTimestamp() method can be taken from flot documentation:
public static int GetJavascriptTimestamp(System.DateTime input)
{
System.TimeSpan span = new System.TimeSpan(System.DateTime.Parse("1/1/1970").Ticks);
System.DateTime time = input.Subtract(span);
return (long)(time.Ticks / 10000);
}
I have a problem calculating difference between two timestamps, when minutes are represented with 3 digits, e.g. 180:22 = 180 minutes and 22 seconds.
So, can you help me how can I get difference between timestamps like:
180:22 and 122:11
or
232:21 and 31:34
etc.
UPDATE: I need to get difference between two times, defined as strings. What makes a problem is that minutes in those strings (times) are larger than 60, and they are over the limit. So I need to know how to find difference like in above examples (180:22 and 122:11, and 232:21 and 31:34)
Use System.TimeSpan structures:
var seconds=(new TimeSpan(0, 180, 22)-new TimeSpan(0, 122, 11)).TotalSeconds;
var minutes=(new TimeSpan(0, 232, 21)-new TimeSpan(0, 31, 34)).TotalMinutes;
Here's a class that will do this stuff:
public class CrazyTime
{
public TimeSpan TimeSpanRepresentation { get; set; }
public CrazyTime(TimeSpan timeSpan)
{
this.TimeSpanRepresentation = timeSpan;
}
public CrazyTime(string crazyTime)
{
// No error checking. Add if so desired
var pieces = crazyTime.Split(new[] { ':' });
var minutes = int.Parse(pieces[0]);
var seconds = int.Parse(pieces[1]);
TimeSpanRepresentation = new TimeSpan(0, minutes, seconds);
}
public static CrazyTime operator-(CrazyTime left, CrazyTime right)
{
var newValue = left.TimeSpanRepresentation - right.TimeSpanRepresentation;
return new CrazyTime(newValue);
}
public override string ToString()
{
// How should negative Values look?
return ((int)Math.Floor(TimeSpanRepresentation.TotalMinutes)).ToString() + ":" + TimeSpanRepresentation.Seconds.ToString();
}
}
Here's how it might be used:
var a = new CrazyTime("123:22");
var b = new CrazyTime("222:11");
var c = b - a;
Console.WriteLine(c);
This works:
string time1 = "180:22";
string time2 = "122:11";
TimeSpan span1 = getTimespan(time1);
TimeSpan span2 = getTimespan(time2);
TimeSpan diff = span1 - span2;
getTimespan just has to correctly parse the string. I decided on Regex to do that, but you could go any route, particularly if the delimiter ":" isn't ever going to change.
private static TimeSpan getTimespan(string time1)
{
Regex reg = new Regex(#"\d+");
MatchCollection matches = reg.Matches(time1);
if (matches.Count == 2)
{
int minutes = int.Parse(matches[0].Value);
int seconds = int.Parse(matches[1].Value);
return new TimeSpan(0, minutes, seconds);
}
return TimeSpan.Zero;
}