I have a small problem because I do not always understand how to use the lessons of the day, for example, I want the time from the list to have the day time recalculated from that date, but if I have a new time, that conversion counts from the new time. It works fine for me if I only have one time, but if I have two times, foreach the loop calculates me both times for the day.
This is my code:
public TimeSpan GetHoursForDay(DateTime day) {
TimeSpan time = TimeSpan.Zero;
foreach (var times in shouldWorkTime)
{
if (times.Valid_from > day) //here's the real problem for me, do i want the hours to count from that date, for example: for 1.1.2020 it doesn't need to take hours from 1.12.2019
continue;
if (day.DayOfWeek == DayOfWeek.Monday)
{
time += times.ShouldWorkMonday;
}
if (day.DayOfWeek == DayOfWeek.Tuesday)
{
time += times.ShouldWorkTuesday;
}
if (day.DayOfWeek == DayOfWeek.Wednesday)
{
time += times.ShouldWorkWednesday;
}
if (day.DayOfWeek == DayOfWeek.Thursday)
{
time += times.ShouldWorkThursday;
}
if (day.DayOfWeek == DayOfWeek.Friday)
{
time += times.ShouldWorkFriday;
}
if (day.DayOfWeek == DayOfWeek.Saturday)
{
time += times.ShouldWorkSaturday;
}
if (day.DayOfWeek == DayOfWeek.Sunday)
{
time += times.ShouldWorkSunday;
}
}
return time;
}
}
These are the values I get in the list:
var shouldWorkTime = new List<ShouldWorkTime>
{
new ShouldWorkTime
{
Valid_from = new DateTime(2019, 12, 01, 0, 0, 0),
ShouldWorkMonday = new TimeSpan(8,0,0),
ShouldWorkTuesday= new TimeSpan(7,0,0),
ShouldWorkWednesday= new TimeSpan(6,0,0),
ShouldWorkThursday= new TimeSpan(5,0,0),
ShouldWorkFriday= new TimeSpan(8,0,0),
ShouldWorkSaturday = new TimeSpan(0,0,0),
ShouldWorkSunday = new TimeSpan(0,0,0)
},
new ShouldWorkTime
{
Valid_from = new DateTime(2020, 01, 01, 0, 0, 0),
ShouldWorkMonday = new TimeSpan(4,0,0),
ShouldWorkTuesday= new TimeSpan(3,0,0),
ShouldWorkWednesday= new TimeSpan(6,0,0),
ShouldWorkThursday= new TimeSpan(5,0,0),
ShouldWorkFriday= new TimeSpan(9,0,0),
ShouldWorkSaturday = new TimeSpan(0,0,0),
ShouldWorkSunday = new TimeSpan(0,0,0)
}
};
for the day value, I always get for the current day from the calendar, so I want to be counted in this case for the days of 1.1.2020 values for the days that fall in the second count, and until then the values that fall in the first count.
so i need to return how many hours for a particular day a worker needs to make, but valid from the last date from (Valid_From).
How can I correct this? thank you all very much for your help
eg:
input 1.1.2020 output = 6,0,0;
input 1.12.2019 output = 0,0,0;
Here is a little modification of the Data structure.
Instead of a brunch of property The TimeSpan will be mapped to the DayOfWeek using a Dictionary. This will remove the need for a switch-case or a lot of If.
Using DayOfWeek as dictionary key ensure that only one TimeSpan is defined for a day.
public class WorkingTimeScheldure
{
public DateTime Start { get; set; }
public DateTime End { get; set; }
public Dictionary<DayOfWeek, TimeSpan> Scheldure { get; set; }
}
That way I can ask for the TimeSpan of a day using : Scheldure[test.DayOfWeek]
Note the addition of an End property. It may be usefull to select the right thing.
eg: 30/01/2020 is superior to 01/12/2019 and 01/01/2020..
So in order to take the most recent, I assume the list in ordered on Start date and pick the last one: .Last(x => x.Start <= day).
Online Demo
public class Program
{
static List<ShouldWork> WorkTimeScheldure;
public static void Main()
{
WorkTimeScheldure = new List<ShouldWork>
{
new ShouldWork
{
Start = new DateTime(2019, 12, 01, 0, 0, 0),
Scheldure= new Dictionary<DayOfWeek, TimeSpan>()
{
{(DayOfWeek)0, new TimeSpan(0,0,0)},
{(DayOfWeek)1, new TimeSpan(8,0,0)},
{(DayOfWeek)2, new TimeSpan(7,0,0)},
{(DayOfWeek)3, new TimeSpan(6,0,0)},
{(DayOfWeek)4, new TimeSpan(5,0,0)},
{(DayOfWeek)5, new TimeSpan(8,0,0)},
{(DayOfWeek)6, new TimeSpan(0,0,0)}
}
},
new ShouldWork
{
Start = new DateTime(2020, 01, 01, 0, 0, 0),
Scheldure = new Dictionary<DayOfWeek, TimeSpan>()
{
{(DayOfWeek)0, new TimeSpan(0,0,0)},
{(DayOfWeek)1, new TimeSpan(4,0,0)},
{(DayOfWeek)2, new TimeSpan(3,0,0)},
{(DayOfWeek)3, new TimeSpan(6,0,0)},
{(DayOfWeek)4, new TimeSpan(5,0,0)},
{(DayOfWeek)5, new TimeSpan(9,0,0)},
{(DayOfWeek)6, new TimeSpan(0,0,0)}
}
}
};
var testValues = new[] {
new DateTime(2019, 12, 01, 0, 0, 0),
new DateTime(2019, 12, 02, 0, 0, 0),
new DateTime(2019, 12, 03, 0, 0, 0),
new DateTime(2019, 12, 04, 0, 0, 0),
new DateTime(2019, 12, 05, 0, 0, 0),
new DateTime(2019, 12, 06, 0, 0, 0),
new DateTime(2019, 12, 07, 0, 0, 0),
new DateTime(2019, 12, 08, 0, 0, 0),
new DateTime(2020, 01, 01, 0, 0, 0),
new DateTime(2020, 01, 02, 0, 0, 0),
new DateTime(2020, 01, 03, 0, 0, 0),
new DateTime(2020, 01, 05, 0, 0, 0),
new DateTime(2020, 01, 05, 0, 0, 0),
new DateTime(2020, 01, 06, 0, 0, 0),
new DateTime(2020, 01, 07, 0, 0, 0),
new DateTime(2020, 01, 08, 0, 0, 0),
};
foreach (var test in testValues) {
// Perhaps there is many possible, so I took the Last.
var workingTime = WorkTimeScheldure.Last(x => x.Start <= day);
//Please handle the case where there is no matching scheludre for this date.
var houtToWork = workingTime.Scheldure[day.DayOfWeek].Hours;
Console.WriteLine(
$"{day.ToShortDateString()} , it's a {day.DayOfWeek}" +
$" I have to work {houtToWork} Hour{(houtToWork>1?"s":"")}!"
);
}
}
}
Result :
12/01/2019 , it's a Sunday I have to work 0 Hour!
12/02/2019 , it's a Monday I have to work 8 Hours!
12/03/2019 , it's a Tuesday I have to work 7 Hours!
12/04/2019 , it's a Wednesday I have to work 6 Hours!
12/05/2019 , it's a Thursday I have to work 5 Hours!
12/06/2019 , it's a Friday I have to work 8 Hours!
12/07/2019 , it's a Saturday I have to work 0 Hour!
12/08/2019 , it's a Sunday I have to work 0 Hour!
01/01/2020 , it's a Wednesday I have to work 6 Hours!
01/02/2020 , it's a Thursday I have to work 5 Hours!
01/03/2020 , it's a Friday I have to work 9 Hours!
01/04/2020 , it's a Saturday I have to work 0 Hour!
01/05/2020 , it's a Sunday I have to work 0 Hour!
01/06/2020 , it's a Monday I have to work 4 Hours!
01/07/2020 , it's a Tuesday I have to work 3 Hours!
01/08/2020 , it's a Wednesday I have to work 6 Hours!
The test times.Valid_from > day is false for all element in shouldWorkTime if the value of day is big enough. That why time can be incremented multiple time.
If you want to increment only once and on the first/last acceptable value of Valid_from, you should ensure that shouldWorkTime is sorted in increasing/decreasing order and ensure that the increment is done only once.
In fact you didn't need increment but just return the corresponding TimeSpan:
public TimeSpan GetHoursForDay(DateTime day) {
// shouldWorkTime should have been sorted once for all at creation.
// This code use the first acceptable Valid_from
// By using OrderByDescending we take the last (in date) entry
var math = shouldWorkTime
.Where(v => day >= v.Valid_from) // We take only valid entry
.OrderByDescending(v => v.Valid_from) // We sort only on valid entry
.FirstOrDefault(); // we take the last (in date) valid entry
if (match == null)
return TimeSpan.Zero;
switch (day.DayOfWeek)
{
case DayOfWeek.Monday:
return match.ShouldWorkMonday;
case DayOfWeek.Tuesday:
return match.ShouldWorkTuesday;
case DayOfWeek.Wednesday:
return match.ShouldWorkWednesday;
case DayOfWeek.Thursday:
return match.ShouldWorkThursday;
case DayOfWeek.Friday:
return match.ShouldWorkFriday;
case DayOfWeek.Saturday:
return match.ShouldWorkSaturday;
case DayOfWeek.Sunday:
return match.ShouldWorkSunday;
}
}
Edit:
To avoid code duplication, the ShouldWorkTime class may provide a GetWorkTimeForDayOfWeek:
public TimeSpan GetWorkTimeForDayOfWeek(DayOfWeek dayOfWeek) {
...
}
And instead of storing seven values in seven fields, you may take a look to collections. I will choose a Dictionary<DayOfWeek, TimeSpan>.
I have a list of DateTime values with dates that contain hours and minutes:
List<DateTime> times = times = new List<DateTime>()
{
new DateTime(2019, 01, 01, 17, 00, 00),
new DateTime(2019, 01, 01, 18, 45, 00),
new DateTime(2019, 01, 01, 19, 00, 00),
new DateTime(2019, 01, 01, 19, 30, 00),
new DateTime(2019, 01, 01, 22, 30, 00)
};
DateTime current = DateTime.Now;
I put them all in a ComboBox, and I want to make some sort of algorithm so when I load my form, it will check for the current time and find the closest value to the current time and select the ComboBox item that contains that hour.
How can I achieve this? I tried to loop through them all and check for the least hour, but that doesn't seem to work. Is there a smarter way to do it?
For example: If the current time is 17:32, it will choose 17:00, because that's the closest. But, if the current time is 18:20, it will choose 18:45 and so on.
Compare to the Ticks property of DateTime (MSDN). It can be seen as a linear representation of the whole date and timestamp and is sortable.
Do something like
comboBox.SelectedItem = times.OrderBy(t => Math.Abs(t.Ticks - current.Ticks)).First()
You could take the difference with DateTime.Now for all your datetimes, order by this difference and take the first result.
times.OrderBy(m => Math.Abs((DateTime.Now - m).TotalMilliseconds)).First();
You would have to select an instance of DateTime which minimizes the temporal distance to the current time. You could use an extension method for IEnumerable<T> to do that as follows.
public static T ArgMin<T, R>(T t1, T t2, Func<T, R> f)
where R : IComparable<R>
{
return f(t1).CompareTo(f(t2)) > 0 ? t2 : t1;
}
public static T ArgMin<T, R>(this IEnumerable<T> Sequence, Func<T, R> f)
where R : IComparable<R>
{
return Sequence.Aggregate((t1, t2) => ArgMin<T, R>(t1, t2, f));
}
var iNow = DateTime.Now;
var iResult = times.ArgMin(iTime => Math.Abs((iTime - iNow).Ticks));
Although very generic, this implementation does not involve any sorting.
You are looking for ArgMax which is not implemented in standard Linq, but can be emulated via Aggreagte
List<DateTime> times = new List<DateTime>() {
new DateTime(2019, 01, 01, 17, 00, 00),
new DateTime(2019, 01, 01, 18, 45, 00),
new DateTime(2019, 01, 01, 19, 00, 00),
new DateTime(2019, 01, 01, 19, 30, 00),
new DateTime(2019, 01, 01, 22, 30, 00),
};
DateTime toFind = new DateTime(2019, 5, 8, 18, 20, 0);
var closestTime = times
.Aggregate((best, item) => Math.Abs((item.TimeOfDay - toFind.TimeOfDay).Ticks) <
Math.Abs((best.TimeOfDay - toFind.TimeOfDay).Ticks)
? item
: best);
Please, note, that if we should find the closest time, we have to get rid of date part - TimeOfDay. If date part should be count, just remove TimeOfDay -
var closestDateAndTime = times
.Aggregate((best, item) => Math.Abs((item - toFind).Ticks) <
Math.Abs((best - toFind).Ticks)
? item
: best);
One option is to use MoreLinq's MinBy:
var actualNow = DateTime.Now;
// set `current` up however you need it
var current = new DateTime(2019, 01, 01, actualNow.Hour, actualNow.Minute, actualNow.Minute, actualNow.Millisecond); // set this up however you need it
var min = times.MinBy(z => Math.Abs((current - z).Ticks)).First();
It avoids the memory pressure of the OrderBy based solutions (since it avoids allocating a list to store the entire set of times).
Note you may want to check whether times is empty before using this (or other) solutions. If you don't wish to do that, consider using:
var min = times.MinBy(z => Math.Abs((current - z).Ticks)).Cast<DateTime?>().FirstOrDefault();
which will return null (rather than default(DateTime), or throw an exception) if times is empty.
I am trying to predict tide in c# using a formula given below:
The program was already done in excel, however when translating it to c# I am encountering some problems.
This is the formula in excel:
=PI()*((C18)/(C19)+1)
Cells C18 and C19 just contain subtraction of two date cells each
In C# I am having trouble finding A.
There are 2 problematic areas in finding A.
Problem1: When I do (t - t1)/(t2 - t1) the answer in C# is 0, but in excel the answer is 1.
Problem 2: the +1 at the end of equation. How do you add a number to timeSpan(in excel this works, maybe because they parse it as a datetime value?)
Here is my code:
DateTime t = new DateTime(2016, 6, 21, 13, 41, 00);
DateTime t1 = new DateTime(2016, 6, 21, 7, 13, 00);
DateTime t2 = new DateTime(2016, 6, 21, 13, 57, 00);
double h1 = 1.421;
double h2 = 2.337;
double h2_minus_h1 = h2 - h1;
TimeSpan t_minus_t1 = TimeSpan.FromTicks(t.Subtract(t1).Ticks);
TimeSpan t2_minus_t1 = TimeSpan.FromTicks(t2.Subtract(t1).Ticks);
TimeSpan MainCalculationForA = TimeSpan.FromTicks((t.Subtract(t1).Ticks) / (t2.Subtract(t1).Ticks));
MessageBox.Show(MainCalculationForA.ToString());
The answer for this which is (t - t1)/(t2 - t1) is: 00:00:00, while in excel it is just 1
Note:The formula is taken from here
tide prediction pdf
note:(t - t1) and (t2 - t1) both have exactly the same answer in both C# and excel. So why the difference in answer upon division?
From the PDF provided, t, t1 and t2 are in decimal hours, so you need .TotalHours instead
DateTime t = new DateTime(2016, 6, 21, 13, 41, 00);
DateTime t1 = new DateTime(2016, 6, 21, 7, 13, 00);
DateTime t2 = new DateTime(2016, 6, 21, 13, 57, 00);
double h1 = 1.421;
double h2 = 2.337;
double t_min_t1 = (t - t1).TotalHours;
double t2_min_t1 = (t2 - t1).TotalHours;
double A = Math.PI*(t_min_t1/t2_min_t1 + 1);
double h = h1 + (h2 - h1)*((Math.Cos(A) + 1)/2);
MessageBox.Show(h.ToString());
The end result is 2.33345960154238.
Does someone knows how to calculate the total hours between 2 times?
For example if a worker clocks in at 8:00 and out at 16:00, I would like to know that in decimal it's 8.0 hours and it's 8:00 hours.
I'm using C# framework 2.0.
The variables that hold the in and out time are of type string.
TY
DateTime start = new DateTime(2010, 8, 25, 8, 0, 0);
DateTime end = new DateTime(2010, 8, 25, 16, 0, 0);
Console.WriteLine((end - start).TotalHours);
for strings:
DateTime start = DateTime.Parse("8:00");
DateTime end = DateTime.Parse("16:00");
Console.WriteLine((end - start).TotalHours);
I came up with this daylight saving time safe method. The function is correct for both UTC and local timezones. If the DateTimeKind is Unspecified on either of the inputs then the return value is undefined (which is a fancy way of saying it could be incorrect).
private double TotalHours(DateTime earliest, DateTime latest)
{
earliest = (earliest.Kind == DateTimeKind.Local) ? earliest.ToUniversalTime() : earliest;
latest = (latest.Kind == DateTimeKind.Local) ? latest.ToUniversalTime() : latest;
return (latest - earliest).TotalHours;
}
System.DateTime punchIn = new System.DateTime(2010, 8, 25, 8, 0, 0);
System.DateTime punchOut = new System.DateTime(2010, 8, 25, 16, 0, 0);
System.TimeSpan diffResult = punchOut.Subtract(punchIn);
Check out TimeSpan.TotalHours:
TimeSpan difference = datetime2 - datetime1;
double totalHours = difference.TotalHours;
You can do it by subtracting two datetimes and using the TotalHours property of the resulting Timespan. Heres an example:
DateTime start = new DateTime(2010, 8, 25, 8, 0, 0);
DateTime end = new DateTime(2010, 8, 25, 16, 0, 0);
int hours = end.Subtract(start).TotalHours;