My objective is to populate a combo box with time intervals of 30 min for 24 hours. I.E - 12.00am, 12.30am, 1.00am, 1.30am and so on. I need to know how to put these details into array. Thank you
Perhaps:
string[] comboboxDataSource = Enumerable.Range(0, 2 * 24)
.Select(min => DateTime.Today.AddMinutes(30 * min).ToString("h.mmtt", CultureInfo.InvariantCulture))
.ToArray();
One way is to iterate 30 minutes in a day and add this DateTime values with a specific string representation to your list. Like;
List<string> list = new List<string>();
DateTime start = DateTime.Today;
DateTime end = DateTime.Today.AddDays(1);
while (end > start)
{
list.Add(start.ToString("h.mmtt", CultureInfo.InvariantCulture));
start = start.AddMinutes(30);
}
If you wanna get them as an array, just use list.ToArray() to get it. Also time designators are in .NET Framework are mostly (I haven't check all of them) upper case. That means, you will get AM or PM when you use tt specifier, not am or pm. In such a case, you need to replace these values with their lower cases.
Don't know exactly what you mean. I would start with something like this:
private IEnumerable<Timespan> Get30MinuteIntervalls()
{
var currentValue = new Timespan(0);
while (currentValue <= Timespan.FromHours(24)
{
yield return currentValue;
currentValue = currentValue.Add(Timespan.FromMinutes(30));
}
}
var values = Get30MinuteIntervalls().ToArray();
Try:
var d = new DateTime();
d = d.Date.AddHours("0").AddMinutes("0");
for (int i = 0; i < 48; i++)
{
d.AddMinutes(30);
cbo.AddItem(d.TimeOfDay.ToString());
}
Related
I need to check if a date range is totally covered by this date range table sorted in ascending order of dFrom, both are Date type:
dFrom dTo
----- -----
10/01 10/03
10/05 10/08
10/08 10/09
10/09 10/12
10/13 10/18
10/15 10/17
10/19 10/24
range A: 10/01-10/14 is NOT totally covered because 10/04 is missing from table.
range B: 10/10-10/20 is totally covered.
What I can think of is for a given date range like A and B, to check if each day is covered in the table:
var dRangeFrom = rangeFrom.Date; // use "var" as C# has no date type
var dRangeTo = rangeTo.Date;
int DaysCovered = 0;
int HowManyDays = (dRangeTo - dRangeFrom).TotalDays()+1;
int StartFromRow = 0;
while (dRangeFrom <= dRangeTo)
{
for (int i=StartFromRow; i<table.rows.count; i++)
{
if (table.rows[i]["dFrom"] > dRangeFrom) // optimization 1: no need to continue.
break;
if (dRangeFrom >= table.rows[i]["dFrom"] && dRangeFrom <= table.rows[i]["dTo"])
{
DaysCovered++;
StartFromRow = i; // optimization 2: next day comparison simply starts from here
break;
}
}
dRangeFrom.AddDays(1);
}
if (DaysCovered == HowManyDays)
Console.Write("Totally covered");
else
Console.Write("NOT");
One way to solve it would be to write a helper method that gets all the days in a range:
public static List<DateTime> GetDaysCovered(DateTime from, DateTime to)
{
var result = new List<DateTime>();
for (var i = 0; i < (to.Date - from.Date).TotalDays; i++)
{
result.Add(from.Date.AddDays(i));
}
return result;
}
And then we can join all the ranges from the table together and see if they match the days in the range we're trying to cover:
foreach (DataRow row in table.Rows)
{
tableDates.AddRange(GetDaysCovered(
row.Field<DateTime>("dFrom").Date,
row.Field<DateTime>("dTo").Date));
}
var rangeDates = GetDaysCovered(dRangeFrom, dRangeTo);
var missingDates = rangeDates
.Where(rangeDate => !tableDates.Contains(rangeDate))
.ToList();
if (missingDates.Any())
{
Console.Write("These dates are not covered: ");
Console.Write(string.Join(",",
missingDates.Select(date => date.ToShortDateString())));
}
else
{
Console.Write("Totally covered");
}
A naive solution is to check for each date in the range whether it is covered by any row.
var totallyCovered = true;
for (var date = rangeFrom.Date; date <= rangeTo.Date; date = date.AddDays(1))
{
var covered = dates.Any(x => date >= x.dFrom && date <= x.dTo);
if (!covered)
{
totallyCovered = false;
break;
}
}
if (totallyCovered)
{
Console.WriteLine("Totally covered.");
}
else
{
Console.WriteLine("No.");
}
That's kinda long and ugly, but thankfully you can fit that into a single LINQ query:
var dateRange = Enumerable.Range(0, 1 + rangeTo.Subtract(rangeFrom).Days)
.Select(offset => rangeFrom.Date.AddDays(offset));
var totallyCovered = dateRange.All(d => dates.Any(x => d >= x.dFrom && d <= x.dTo));
Note: This has time complexity of O(|range| * |rows|), which might be too much. To fix that you'd have to employ a more sophisticated data structure that would allow you to query ranges in logarithmic time, but since your original sample also contained nested loops, I'll assume it's unnecessary.
I am creating time buckets of 60 minutes each. This is data that is returned from a Postgres query.
the duration that is being considered for creating buckets is like 1 day, 7 days.
there is an instance when there are missing buckets. to fill those I am creating a method that will identify the missing point and will be added in the series.
This method is built with C#.
to demonstrate logic, I have created a sample.
DateTimeOffset startTime = DateTime.Now.AddDays(-1), startTargetTime = DateTime.Now.AddDays(-1);
DateTimeOffset endTime = DateTimeOffset.Now;
int interval = 60;
List<DateTimeOffset> series = new List<DateTimeOffset>();
List<DateTimeOffset> target = new List<DateTimeOffset>(); // this will be the database values
int cntr = 0;
while(startTime < endTime)
{
startTime = startTime.AddMinutes(interval);
if (cntr % 2 == 0)
{
target.Add(startTime) ;
}
cntr++;
}
while (startTargetTime < endTime)
{
startTargetTime = startTargetTime.AddMinutes(interval);
series.Add(startTargetTime);
}
if(target.Count != series.Count)
{
foreach (var item in series)
{
if(!target.Exists(val => val == item))
{
target.Add(item);
}
}
}
The current problem is even though the same item exists it is still added in the target list. what could be the problem.
Are there more efficient ways to make the comparison and add the missing items?
This portion of your code, which populates series, is independent of anything going on in target. It just adds all values in the interval.
while (startTargetTime < endTime)
{
startTargetTime = startTargetTime.AddMinutes(interval);
series.Add(startTargetTime);
}
To fix it, just do a check against whether the value already exists in target:
while (startTargetTime < endTime) {
startTargetTime = startTargetTime.AddMinutes(interval);
if (!target.Contains(startTargetTime)) {
series.Add(startTargetTime);
}
}
This question already has answers here:
AddBusinessDays and GetBusinessDays
(17 answers)
Closed 3 years ago.
I am trying to calculate a finishing date when adding a duration to a start date, but skipping weekends and holidays:
DateTime start = DateTime.Now;
TimeSpan duration = TimeSpan.FromHours(100);
List<DateTime> = //List of holidays
DateTime end = ?
For example if it is 11pm on a Friday and I add 2 hours, it would end on 1am Monday morning.
Is there a neat way of doing this?
I have a temporary fix which increments the time by an hour and checks the day of the week, but it is very inefficient.
Original Idea (untested):
public static DateTime calEndDate(DateTime start, TimeSpan duration, List<DateTime> holidays)
{
var startDay = start.Day;
var i = 0;
var t = 0;
while (TimeSpan.FromHours(t) < duration)
{
var date = start.Add(TimeSpan.FromHours(i));
if (date.DayOfWeek.ToString() != "Saturday" && date.DayOfWeek.ToString() != "Sunday") //and something like !holidays.contains(start)
{
t++;
}
i++;
}
return start.Add(TimeSpan.FromHours(t));
}
}
However is needs to run over 100 times for different start dates/durations on one asp.net page load. I don't know how to benchmark it, but it doesn't seem like an elegant solution?
Here's an algorithm I'd try.
I'm on my phone, and I'll get it wrong, but you should see the logic...
var end = start;
var timeToMidnight = start.Date.AddDays(1) -start;
if ( duration < timeToMidnight ) return start + duration;
end = endMoment + timeToMidnight;
duration = duration - timeToMidnight;
//Helper method
bool IsLeisure(Datetime dt) => (dt.DayOfWeek == DayOfWeek.Saturday) || (dt.DayOfWeek == DayOfWeek.Sunday) || holidays.Any( h => h.Date == dt.Date);
//We're at the first tick of the new day. Let's move to a work day, if needed.
while(IsLeisure(end)) { end = end.AddDays(1); };
//Now let's process full days of 'duration'
while(duration >= TimeSpan.FromDays(1) ) {
end = end.AddDays(1);
if(!IsLeisure(end)) duration = duration - TimeSpan.FromDays(1);
}
//Finally, add the reminder
end = end + duration;
Note: you haven't specified the logic for when start moment is a weekend or a holiday.
Yes there is :
DateTime currentT = DateTime.Now;
DateTime _time_ = currentTime.AddHours(10);
Simple and neat.
I'm trying to write a code the generate random date and time where all values of (yyyy-mm-dd hh:mm:ss) are changed
I used this code but it change only the date and fix the time
DateTime RandomDay()
{
DateTime start = new DateTime(2013, 1, 1 , 1 , 1 ,1);
Random gen = new Random();
int range = (DateTime.Today - start).Days;
return start.AddDays(gen.Next(range));
}
I said ok if I write a code that generate a random date, and store it in a variable1, then write another code that generate random time, and store it in variable2, then pick the date only from variable1, and pick the time only from variable2, then combine variable1 and variable2 together , so I can generate random date and time
therefore, I wrote this another method to generate the time
DateTime RandomTime()
{
DateTime start = new DateTime(2013, 1, 1, 1, 1, 1);
Random gen = new Random();
int range = (DateTime.Today - start).Hours;
return start.AddHours(gen.Next(range));
}
now the problem in the RandomTime method, it generate Random hours only, doesn't change the values of the minutes and seconds,
is there any suggestion to solve the code problem? is there anyway to generate random values for all (yyyy-mm-dd hh:mm:ss) ?
Here's a method that'll randomly generate every portion of the date and time.
It uses appropriate limits so the generated date is valid (years within 1-9999, months within 1-12, a call to DateTime.DaysInMonth so we don't end up with Feb 31, etc).
public IEnumerable<DateTime> GenerateRandomDates(int numberOfDates)
{
var rnd = new Random(Guid.NewGuid().GetHashCode());
for (int i = 0; i < numberOfDates; i++)
{
var year = rnd.Next(1, 10000);
var month = rnd.Next(1, 13);
var days = rnd.Next(1, DateTime.DaysInMonth(year, month) + 1);
yield return new DateTime(year, month, days,
rnd.Next(0, 24), rnd.Next(0, 60), rnd.Next(0, 60), rnd.Next(0, 1000));
}
}
To generate 10,000 of them:
var randomDateTimes = GenerateRandomDates(10000);
Here is the one line solution. 1 line for the return part ;)
private static Random _ran = new Random();
public static DateTime RandomDateTime()
{
return
DateTime.MinValue.Add(
TimeSpan.FromTicks((long) (_ran.NextDouble()*DateTime.MaxValue.Ticks)));
}
Note 1: _ran.NextDouble() gives random value between 0 and 1. so the amount of ticks you add to minimum datetime would be between 0 and DateTime.MaxValue.Ticks. So the random date time would be between minimum and maximum date time.
Note 2: DateTime.Min.Ticks is equal to 0. so this process will never overflow even if you add maximum date time ticks.
Here is how to generate 10k random date times.
DateTime[] datetimes = new DateTime[10000];
for (int i = 0; i < datetimes.Length; i++)
{
datetimes[i] = RandomDateTime();
}
You can also use this method if you want to define ranges.
public static DateTime RandomDateTime(DateTime min, DateTime max)
{
return
DateTime.MinValue.Add(
TimeSpan.FromTicks(min.Ticks + (long) (_ran.NextDouble()*(max.Ticks - min.Ticks))));
}
Let's say we're tracking the times when a user is performing a certain action, and we want to know the average time between said actions.
For example, if the user performed this action at these times:
today, 1 PM
today, 3 PM
today, 6 PM
The result would be 2.5 hours.
I actually have solved this already, but I felt my solution was more complicated than necessary. I'll post it as an answer.
It seems that you are basically looking for Max - Min divided by Count.
public TimeSpan? Average
{
get
{
var diff = _dateTimes.Max().Subtract(_dateTimes.Min());
var avgTs = TimeSpan.FromMilliseconds(diff.TotalMilliseconds / (_dateTimes.Count() - 1));
return avgTs;
}
}
Make sure you check that there is more than one DateTime.
Update: Even more accurate if you use Ticks.
TimeSpan.FromTicks(diff.Ticks / (_dateTimes.Count() - 1));
I recently had a similar task in where I had a long running operation iterating over thousands of rows with 20-30 iterations within each.
void LongRunningOperation()
{
int r = 5000;
int sR = 20;
List<TimeSpan> timeSpanList = new List<TimeSpan>();
for (int i = 0; i < r; i++)
{
DateTime n = DateTime.Now; // Gets start time of this iteration.
for (int x = 0; x < sR; x++)
{
// DOING WORK HERE
}
timeSpanList.Add(DateTime.Now - n); // Gets the length of time of iteration and adds it to list.
double avg = timeSpanList.Select(x => x.TotalSeconds).Average(); // Use LINQ to get an average of the TimeSpan durations.
TimeSpan timeRemaining = DateTime.Now.AddSeconds((r - i) * avg) - DateTime.Now;
// Calculate time remaining by taking the total number of rows minus the number of rows done multiplied by the average duration.
UpdateStatusLabel(timeRemaining);
}
}
This is how I solved it, but I don't like it much:
public class HistoryItem
{
private IEnumerable<DateTime> _dateTimes;
public TimeSpan? Average
{
get {
TimeSpan total = default(TimeSpan);
DateTime? previous = null;
int quotient = 0;
var sortedDates = _dateTimes.OrderBy(x => x);
foreach (var dateTime in sortedDates)
{
if (previous != null)
{
total += dateTime - previous.Value;
}
++quotient;
previous = dateTime;
}
return quotient > 0 ? (TimeSpan.FromMilliseconds(total.TotalMilliseconds/quotient)) as TimeSpan? : null;
}
}
}