I am currently want to get the date range (between time range) from the list of dates.
For example:
The time now is
2017-04-08 18:00
And I got these from and to dates:
public static string[] fromDates = new string[] { "2017-04-07 07:00", "2017-04-07 10:00", "2017-04-07 12:00", "2017-04-07 14:00", "2017-04-07 16:00" };
public static string[] toDates = new string[] { "2017-04-07 08:00", "2017-04-07 11:00", "2017-04-07 13:00", "2017-04-07 15:00", "2017-04-07 17:00" };
I am using this code:
public static bool IsInRange(this DateTime dateToCheck, string[] startDates, string[] endDates, out string StartDate, out string EndDate)
{
DateTime startDate = new DateTime();
DateTime endDate = new DateTime();
bool isWithinRange = false;
for (int i = 0; i < startDates.Length; i++)
{
startDate = Convert.ToDateTime(startDates[i]);
isWithinRange = dateToCheck >= startDate;
if (isWithinRange)
break;
}
for (int y = 0; y < endDates.Length; y++)
{
endDate = Convert.ToDateTime(endDates[y]);
isWithinRange = dateToCheck < endDate;
if (isWithinRange)
break;
}
StartDate = startDate;
EndDate = endDate;
return isWithinRange;
}
And I call it like this:
var isBetween = Convert.ToDateTime("2017-04-08 18:00").IsInRange(fromDates, toDates, out StartDate, out EndDate)
But I couldn't make it working, the StartDate in IsInRange method is always return true and it will return the first index from fromDates variable, which is wrong.
How can I make it like the time between?
I know I can do it like this:
var isBetween = dateToCheck >= startDate && dateToCheck < endDate
But it is only one date need to check, what about if it is like my situation?
Your answer much appreciated.
Thanks
I would start by converting everything into a more useful object model:
Get rid of all the strings (i.e. convert from strings to something more useful early on)
Instead of having two collections, create a new type indicating "a date/time range". You're being somewhat foiled by relating the wrong items together: the start values aren't related to each other, they're related to their corresponding end dates.
You could do this within the method if you really need to, but it would be better to move to a richer object model for as much of your code as you can. For example, suppose you have:
public sealed class DateTimeRange
{
public DateTime Start { get; }
public DateTime End { get; }
public DateTimeRange(DateTime start, DateTime end)
{
// TODO: Validate that start <= end
Start = start;
End = end;
}
public bool Contains(DateTime value) => Start <= value && value < End;
}
Then your method can look like this:
public DateTimeRange FindRange(IEnumerable<DateTimeRange> ranges, DateTime value) =>
ranges.FirstOrDefault(range => range.Contains(value));
That will return null if no ranges contain the value, or the first one that does contain a value otherwise.
(As an aside, I'd do all of this in Noda Time instead as a better date/time API, but I'm biased.)
If you want to stay with yoir design, then you should simply do everything inside one loop, instead of doing it twice, as you want always to match first element with first element, second with second etc.
public static bool IsInRange(this DateTime dateToCheck, string[] startDates, string[] endDates, out DateTime StartDate, out DateTime EndDate)
{
if (startDates.Length != endDates.Length)
{
throw new ArgumentException("The arrays must have the same length");
}
StartDate = new DateTime();
EndDate = new DateTime();
for (int i = 0; i < startDates.Length; i++)
{
StartDate = Convert.ToDateTime(startDates[i]);
EndDate = Convert.ToDateTime(endDates[i]);
if (dateToCheck >= StartDate && dateToCheck <= EndDate)
{
return true;
}
}
return false;
}
But as already stated in other answer - you should redesign your code, because it's not very maintenable and easy to understand
Related
I want to get the first day and last day of the month where a given date lies in. The date comes from a value in a UI field.
If I'm using a time picker I could say
var maxDay = dtpAttendance.MaxDate.Day;
But I'm trying to get it from a DateTime object. So if I have this...
DateTime dt = DateTime.today;
How to get first day and last day of the month from dt?
DateTime structure stores only one value, not range of values. MinValue and MaxValue are static fields, which hold range of possible values for instances of DateTime structure. These fields are static and do not relate to particular instance of DateTime. They relate to DateTime type itself.
Suggested reading: static (C# Reference)
UPDATE: Getting month range:
DateTime date = ...
var firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
var lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);
UPDATE: From comments (#KarlGjertsen & #SergeyBerezovskiy)
DateTime date = ...
var firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
var lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddSeconds(-1);
//OR
var lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddTicks(-1);
This is more a long comment on #Sergey and #Steffen's answers. Having written similar code myself in the past I decided to check what was most performant while remembering that clarity is important too.
Result
Here is an example test run result for 10 million iterations:
2257 ms for FirstDayOfMonth_AddMethod()
2406 ms for FirstDayOfMonth_NewMethod()
6342 ms for LastDayOfMonth_AddMethod()
4037 ms for LastDayOfMonth_AddMethodWithDaysInMonth()
4160 ms for LastDayOfMonth_NewMethod()
4212 ms for LastDayOfMonth_NewMethodWithReuseOfExtMethod()
2491 ms for LastDayOfMonth_SpecialCase()
Code
I used LINQPad 4 (in C# Program mode) to run the tests with compiler optimization turned on. Here is the tested code factored as Extension methods for clarity and convenience:
public static class DateTimeDayOfMonthExtensions
{
public static DateTime FirstDayOfMonth_AddMethod(this DateTime value)
{
return value.Date.AddDays(1 - value.Day);
}
public static DateTime FirstDayOfMonth_NewMethod(this DateTime value)
{
return new DateTime(value.Year, value.Month, 1);
}
public static DateTime LastDayOfMonth_AddMethod(this DateTime value)
{
return value.FirstDayOfMonth_AddMethod().AddMonths(1).AddDays(-1);
}
public static DateTime LastDayOfMonth_AddMethodWithDaysInMonth(this DateTime value)
{
return value.Date.AddDays(DateTime.DaysInMonth(value.Year, value.Month) - value.Day);
}
public static DateTime LastDayOfMonth_SpecialCase(this DateTime value)
{
return value.AddDays(DateTime.DaysInMonth(value.Year, value.Month) - 1);
}
public static int DaysInMonth(this DateTime value)
{
return DateTime.DaysInMonth(value.Year, value.Month);
}
public static DateTime LastDayOfMonth_NewMethod(this DateTime value)
{
return new DateTime(value.Year, value.Month, DateTime.DaysInMonth(value.Year, value.Month));
}
public static DateTime LastDayOfMonth_NewMethodWithReuseOfExtMethod(this DateTime value)
{
return new DateTime(value.Year, value.Month, value.DaysInMonth());
}
}
void Main()
{
Random rnd = new Random();
DateTime[] sampleData = new DateTime[10000000];
for(int i = 0; i < sampleData.Length; i++) {
sampleData[i] = new DateTime(1970, 1, 1).AddDays(rnd.Next(0, 365 * 50));
}
GC.Collect();
System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
for(int i = 0; i < sampleData.Length; i++) {
DateTime test = sampleData[i].FirstDayOfMonth_AddMethod();
}
string.Format("{0} ms for FirstDayOfMonth_AddMethod()", sw.ElapsedMilliseconds).Dump();
GC.Collect();
sw.Restart();
for(int i = 0; i < sampleData.Length; i++) {
DateTime test = sampleData[i].FirstDayOfMonth_NewMethod();
}
string.Format("{0} ms for FirstDayOfMonth_NewMethod()", sw.ElapsedMilliseconds).Dump();
GC.Collect();
sw.Restart();
for(int i = 0; i < sampleData.Length; i++) {
DateTime test = sampleData[i].LastDayOfMonth_AddMethod();
}
string.Format("{0} ms for LastDayOfMonth_AddMethod()", sw.ElapsedMilliseconds).Dump();
GC.Collect();
sw.Restart();
for(int i = 0; i < sampleData.Length; i++) {
DateTime test = sampleData[i].LastDayOfMonth_AddMethodWithDaysInMonth();
}
string.Format("{0} ms for LastDayOfMonth_AddMethodWithDaysInMonth()", sw.ElapsedMilliseconds).Dump();
GC.Collect();
sw.Restart();
for(int i = 0; i < sampleData.Length; i++) {
DateTime test = sampleData[i].LastDayOfMonth_NewMethod();
}
string.Format("{0} ms for LastDayOfMonth_NewMethod()", sw.ElapsedMilliseconds).Dump();
GC.Collect();
sw.Restart();
for(int i = 0; i < sampleData.Length; i++) {
DateTime test = sampleData[i].LastDayOfMonth_NewMethodWithReuseOfExtMethod();
}
string.Format("{0} ms for LastDayOfMonth_NewMethodWithReuseOfExtMethod()", sw.ElapsedMilliseconds).Dump();
for(int i = 0; i < sampleData.Length; i++) {
sampleData[i] = sampleData[i].FirstDayOfMonth_AddMethod();
}
GC.Collect();
sw.Restart();
for(int i = 0; i < sampleData.Length; i++) {
DateTime test = sampleData[i].LastDayOfMonth_SpecialCase();
}
string.Format("{0} ms for LastDayOfMonth_SpecialCase()", sw.ElapsedMilliseconds).Dump();
}
Analysis
I was surprised by some of these results.
Although there is not much in it the FirstDayOfMonth_AddMethod was slightly faster than FirstDayOfMonth_NewMethod in most runs of the test. However, I think the latter has a slightly clearer intent and so I have a preference for that.
LastDayOfMonth_AddMethod was a clear loser against LastDayOfMonth_AddMethodWithDaysInMonth, LastDayOfMonth_NewMethod and LastDayOfMonth_NewMethodWithReuseOfExtMethod. Between the fastest three there is nothing much in it and so it comes down to your personal preference. I choose the clarity of LastDayOfMonth_NewMethodWithReuseOfExtMethod with its reuse of another useful extension method. IMHO its intent is clearer and I am willing to accept the small performance cost.
LastDayOfMonth_SpecialCase assumes you are providing the first of the month in the special case where you may have already calculated that date and it uses the add method with DateTime.DaysInMonth to get the result. This is faster than the other versions, as you would expect, but unless you are in a desperate need for speed I don't see the point of having this special case in your arsenal.
Conclusion
Here is an extension method class with my choices and in general agreement with #Steffen I believe:
public static class DateTimeDayOfMonthExtensions
{
public static DateTime FirstDayOfMonth(this DateTime value)
{
return new DateTime(value.Year, value.Month, 1);
}
public static int DaysInMonth(this DateTime value)
{
return DateTime.DaysInMonth(value.Year, value.Month);
}
public static DateTime LastDayOfMonth(this DateTime value)
{
return new DateTime(value.Year, value.Month, value.DaysInMonth());
}
}
If you have got this far, thank you for time! Its been fun :¬). Please comment if you have any other suggestions for these algorithms.
Getting month range with .Net API (just another way):
DateTime date = ...
var firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
var lastDayOfMonth = new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month));
"Last day of month" is actually "First day of *next* month, minus 1". So here's what I use, no need for "DaysInMonth" method:
public static DateTime FirstDayOfMonth(this DateTime value)
{
return new DateTime(value.Year, value.Month, 1);
}
public static DateTime LastDayOfMonth(this DateTime value)
{
return value.FirstDayOfMonth()
.AddMonths(1)
.AddMinutes(-1);
}
NOTE:
The reason I use AddMinutes(-1), not AddDays(-1) here is because usually you need these date functions for reporting for some date-period, and when you build a report for a period, the "end date" should actually be something like Oct 31 2015 23:59:59 so your report works correctly - including all the data from last day of month.
I.e. you actually get the "last moment of the month" here. Not Last day.
OK, I'm going to shut up now.
DateTime dCalcDate = DateTime.Now;
dtpFromEffDate.Value = new DateTime(dCalcDate.Year, dCalcDate.Month, 1);
dptToEffDate.Value = new DateTime(dCalcDate.Year, dCalcDate.Month, DateTime.DaysInMonth(dCalcDate.Year, dCalcDate.Month));
Here you can add one month for the first day of current month than delete 1 day from that day.
DateTime now = DateTime.Now;
var startDate = new DateTime(now.Year, now.Month, 1);
var endDate = startDate.AddMonths(1).AddDays(-1);
If you only care about the date
var firstDay = new DateTime(date.Year, date.Month, 1, 0, 0, 0, date.Kind);
var lastDay = new DateTime(date.Year, date.Month, 1, 0, 0, 0, date.Kind).AddMonths(1).AddDays(-1);
If you want to preserve time
var firstDay = new DateTime(date.Year, date.Month, 1, date.Hour, date.Minute, date.Second, date.Kind);
var lastDay = new DateTime(date.Year, date.Month, 1, date.Hour, date.Minute, date.Second, date.Kind).AddMonths(1).AddDays(-1);
Try this one:
string strDate = DateTime.Now.ToString("MM/01/yyyy");
The accepted answer here does not take into account the Kind of the DateTime instance. For example if your original DateTime instance was a UTC Kind then by making a new DateTime instance you will be making an Unknown Kind instance which will then be treated as local time based on server settings. Therefore the more proper way to get the first and last date of the month would be this:
var now = DateTime.UtcNow;
var first = now.Date.AddDays(-(now.Date.Day - 1));
var last = first.AddMonths(1).AddTicks(-1);
This way the original Kind of the DateTime instance is preserved.
I used this in my script(works for me) but I needed a full date without the need of trimming it to only the date and no time.
public DateTime GetLastDayOfTheMonth()
{
int daysFromNow = DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month) - (int)DateTime.Now.Day;
return DateTime.Now.AddDays(daysFromNow);
}
For Persian culture
PersianCalendar pc = new PersianCalendar();
var today = pc.GetDayOfMonth(DateTime.Now);
var firstDayOfMonth = pc.GetDayOfMonth(DateTime.Now.AddDays(-(today-1)));
var lastDayOfMonth = pc.GetDayOfMonth(DateTime.Now.AddMonths(1).AddDays(-today));
Console.WriteLine("First day "+ firstDayOfMonth);
Console.WriteLine("Last day " + lastDayOfMonth);
You can do it
DateTime dt = DateTime.Now;
DateTime firstDayOfMonth = new DateTime(dt.Year, date.Month, 1);
DateTime lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);
Give this a try. It basically calculates the number of days that has passed on DateTime.Now, then subtracts one from that and uses the new value to find the first of the current month. From there it uses that DateTime and uses .AddMonths(-1) to get the first of the previous month.
Getting the last day of last month does basically the same thing except it adds one to number of days in the month and subtracts that value from DateTime.Now.AddDays, giving you the last day of the previous month.
int NumberofDays = DateTime.Now.Day;
int FirstDay = NumberofDays - 1;
int LastDay = NumberofDays + 1;
DateTime FirstofThisMonth = DateTime.Now.AddDays(-FirstDay);
DateTime LastDayOfLastMonth = DateTime.Now.AddDays(-LastDay);
DateTime CheckLastMonth = FirstofThisMonth.AddMonths(-1);
You can try this for get current month first day;
DateTime.Now.AddDays(-(DateTime.Now.Day-1))
and assign it a value.
Like this:
dateEndEdit.EditValue = DateTime.Now;
dateStartEdit.EditValue = DateTime.Now.AddDays(-(DateTime.Now.Day-1));
Create an instance of DateTime class
DateTime dateTime = DateTime.Now;
If you want to get the last day of the month you can do this
int lastDayOfMonth = DateTime.DaysInMonth(caducidadPuntos.Year, caducidadPuntos.Month);
If you want to get the first day of the month, you can do this
DateTime firstDayMonth = new DateTime(dateTime.Year, dateTime.Month, 1);
We had the requirement of being able to get the start and end of a given dates month, including times, inclusively. We ended up utilizing the aforementioned solutions, huge thanks to everyone here, and combined it into a util class to be able to get the start and end for a given month and year number combination up to the last millisecond. Including what we moved forward with in the event it helps someone else.
The util:
public class DateUtil
{
public static (DateTime startOfMonth, DateTime endOfMonth) GetStartAndEndOfMonth(int month, int year)
{
DateTime startOfMonth = GetStartOfMonth(month, year);
DateTime endOfMonth = GetEndOfMonth(month, year);
return (startOfMonth, endOfMonth);
}
public static DateTime GetStartOfMonth(int month, int year)
{
return new DateTime(year, month, 1).Date;
}
public static DateTime GetEndOfMonth(int month, int year)
{
return new DateTime(year, month, 1).Date.AddMonths(1).AddMilliseconds(-1);
}
}
Usage:
(DateTime startOfMonth, DateTime endOfMonth) = DateUtil.GetStartAndEndOfMonth(2, 2021); // February, 2021
easy way to do it
Begin = new DateTime(DateTime.Now.Year, DateTime.Now.Month,1).ToShortDateString();
End = new DataFim.Text = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month)).ToShortDateString();
DateTime dCalcDate = DateTime.Now;
var startDate = new DateTime(Convert.ToInt32(Year), Convert.ToInt32(Month), 1);
var endDate = new DateTime(Convert.ToInt32(Year), Convert.ToInt32(Month), DateTime.DaysInMonth((Convert.ToInt32(Year)), Convert.ToInt32(Month)));
int serviceday = 0;
int servicehour = 0;
int serviceminute = 0;
if (ServiceDay == null || ServiceDay == "")
{
serviceday = 0;
}
else
Convert.ToInt32(ServiceDay);
if (ServiceHour == null || ServiceHour == "")
{
servicehour = 0;
}
else
Convert.ToInt32(ServiceHour);
if (ServiceMinute == null || ServiceMinute == "")
{
serviceminute = 0;
}
else
Convert.ToInt32(serviceminute);
I am trying to add days, hours and minutes to an existing datetime but using the .addDays, .addHours and .addMinutes methods, the datetime variable stays the same. I know when ever I use the .addDays method I have to assign a new variable for the return value and even that does not change the datetime variable.
DateTime servicedatetime = new DateTime();
servicedatetime = serviceEntry.ServiceDateTime;
DateTime newdatetimeDays = new DateTime();
DateTime newdatetimeHours = new DateTime();
DateTime newdatetimeMinutes = new DateTime();
newdatetimeDays = servicedatetime.AddDays(serviceday);
newdatetimeHours = newdatetimeDays.AddHours(servicehour);
newdatetimeMinutes = newdatetimeHours.AddMinutes(serviceminute);
It looks like you want to add an arbitrary amount of days, hours and minutes but are going the long way round. You can do this all in one go:
DateTime finalDateTime = serviceEntry.ServiceDateTime
.AddDays(serviceday)
.AddHours(servicehour)
.AddMinutes(serviceminute);
You could write a lot more compact code
int serviceday;
int servicehour;
int serviceminute;
Int32.TryParse(ServiceDay, out serviceday);
Int32.TryParse(ServiceHour, out servicehour);
Int32.TryParse(ServiceMinute, out serviceminute);
DateTime finalDateTime = serviceEntry.ServiceDateTime
.AddDays(serviceday)
.AddHours(servicehour)
.AddMinutes(serviceminute);
(NOTE: The bulk of this answer is taken from DavidG one that should take the credits, just to fix that noisy block of ifs to discover if you have or not a valid value)
You need to assign the result of the Convert.ToInt32 calls, otherwise the variables are not set, eg:
serviceDay = Convert.ToInt32(ServiceDay)
I'm the head in the refacto of my code because it's a mess.
But since I'm on it from yesterday, my head look like a stone -_-"
#region DateTime foreach Currencies
static DateTime eurusd = DateTime.Now.AddHours(-1);
static DateTime eurgbp = DateTime.Now.AddHours(-1);
static DateTime eurjpy = DateTime.Now.AddHours(-1);
static DateTime usdjpy = DateTime.Now.AddHours(-1);
static DateTime gbpjpy = DateTime.Now.AddHours(-1);
static DateTime gbpusd = DateTime.Now.AddHours(-1);
static DateTime eurusdm1 = DateTime.Now.AddHours(-1);
static DateTime eurgbpm1 = DateTime.Now.AddHours(-1);
static DateTime eurjpym1 = DateTime.Now.AddHours(-1);
static DateTime usdjpym1 = DateTime.Now.AddHours(-1);
static DateTime gbpjpym1 = DateTime.Now.AddHours(-1);
static DateTime gbpusdm1 = DateTime.Now.AddHours(-1);
static DateTime eurusdh1 = DateTime.Now.AddHours(-1);
static DateTime eurgbph1 = DateTime.Now.AddHours(-1);
static DateTime eurjpyh1 = DateTime.Now.AddHours(-1);
static DateTime usdjpyh1 = DateTime.Now.AddHours(-1);
static DateTime gbpjpyh1 = DateTime.Now.AddHours(-1);
static DateTime gbpusdh1 = DateTime.Now.AddHours(-1);
static DateTime eurusdd1 = DateTime.Now.AddHours(-1);
static DateTime eurgbpd1 = DateTime.Now.AddHours(-1);
static DateTime eurjpyd1 = DateTime.Now.AddHours(-1);
static DateTime usdjpyd1 = DateTime.Now.AddHours(-1);
static DateTime gbpjpyd1 = DateTime.Now.AddHours(-1);
static DateTime gbpusdd1 = DateTime.Now.AddHours(-1);
#endregion
There are 24 different DateTime.
switch (data.Instrument)
{
case "EUR/USD":
if (CanUpdate(ref eurusd, "s5"))
{ InsertData("eurusd", data); }
if (CanUpdate(ref eurusdm1, "m1"))
{ InsertData("eurusdm1", data);}
if (CanUpdate(ref eurusdh1, "h1"))
{ InsertData("eurusdh1", data);}
if (CanUpdate(ref eurusdd1, "d1"))
{ InsertData("eurusdd1", data);}
}
A switch case with 6 Case (like the one above)
(InsertData just put some object in a collection. "x" => collection name, data => the object)
private bool CanUpdate(ref DateTime date, string timer)
{
TimeSpan result = DateTime.Now - date;
if (timer == "s5")
{
int difSeconds = result.Seconds;
if (difSeconds >= 5)
{
date = DateTime.Now;
return true;
}
}
if (timer == "m1")
{
int difMinutes = result.Minutes;
if (difMinutes >= 1)
{
date = DateTime.Now;
return true;
}
}
if (timer == "h1")
{
int difHour = result.Hours;
if (difHour >= 1)
{
date = DateTime.Now;
return true;
}
}
if (timer == "d1")
{
int difDays = result.Days;
if (difDays >= 1)
{
date = DateTime.Now;
return true;
}
}
return false;
}
My "CanUpdate" Method. Check the time between the last update and if the "if condition" are true it update the "date variable" and return true, else it return false.
As you can guess, this switchcase have more or less 70 lines. One change need to be repercuted on the whole switchcase. Ugly right ?
I would like to get rid of this switchcase and make it more maintainable.
I've think about put all of the data in different list.
So it will look like:
List<String> collectionName;
List<DateTime> dateTime;
List<String> timeFrame;
and do a foreach on the DateTime collection then do 2 nested for.
Somthing like:
foreach (DateTime dt in dateTime)
{
for(i=0; i <= collectionName.Lenght; i++)
{
for(j=0; j <= timeFrame.Lenght; j++)
{
CanUpdate(ref dt, timeFrame[j])
InsertData(collectionName[i].ToString(), data)
}
}
}
Am I in the right direction and do you even understand what I would like. Because if your head are in the same state as mine after reading this. Sorry :O
Preamble
I would (and, in fact, I did) use Dictionary to solve this problem.
I'm not a big fan of Tuple. So, for real application I would create some struct/class instead.
For the answer I assume that it's required to work with strings of formats "EUR/USD" and "eurusdm1" (see method GetInsertDataArgument()).
Of course, I haven't tested the code :) But I think it's more than enough to get the idea.
I've decided to make all the members static because you had your datetimes static in the original code. You can easily change this.
Code
class MyClass
{
private static readonly Dictionary<Tuple<string, string>, DateTime> lastUpdateDateTimes = new Dictionary<Tuple<string, string>, DateTime>();
private static readonly Dictionary<string, TimeSpan> timeIntervals = new Dictionary<string, TimeSpan>();
static MyClass()
{
timeIntervals.Add("s5", TimeSpan.FromSeconds(5));
timeIntervals.Add("m1", TimeSpan.FromMinutes(1));
timeIntervals.Add("h1", TimeSpan.FromHours(1));
timeIntervals.Add("d1", TimeSpan.FromDays(1));
}
private static string GetInsertDataArgument(string instrument, string timeIntervalName)
{
string result = instrument.Replace("/", "").ToLower();
if (timeIntervalName != "s5")
result = result + timeIntervalName;
return result;
}
private static void Update(string instrument)
{
DateTime now = DateTime.Now;
foreach (var timeInterval in timeIntervals)
{
var dateTimeKey = new Tuple<string, string>(instrument, timeInterval.Key);
if (now - lastUpdateDateTimes[dateTimeKey] < timeInterval.Value)
continue;
lastUpdateDateTimes[dateTimeKey] = now;
InsertData(GetInsertDataArgument(instrument, timeInterval.Key), data);
}
}
}
What's going on
There are 2 dictionaries:
lastUpdateDateTimes stores datetimes for each currency pair and time interval. First member of tuple stores currency pair (in "EUR/USD" format), second member stores time interval name (in "s5" format).
timeIntervals holds your time intervals. It is filled in static constructor of class.
Update method is the replacement for your switch statement and CanUpdate method.
Thanks you. Your idea are really good.
exactly what I needed especially the:
Dictionnary<Tuple<string,string>,DateTime>
I was wondering how to make all those data relational.
I just needed to delete the
if (timeIntervalName != "s5")
because
"result = result + timeIntervalName;"
should be trigger every time GetArgumentName are called.
Neded to make a
TimeSpan timeElapsed = now - lastUpdateDateTimes[dateTimeKey];
Because it didn't work in the condition even between (). Don't know why, it should.(?)
And I also needed to populate my lastUpdateDateTimes Dictionnary. (ofc)
So, thank you again. Work like a charm.
I am looking for a method of splitting a date range into a series of date ranges by chunk size of days. I am planning on using this to buffer calls to a service which if the date range is too large, the service faults.
This is what I have come up with so far. It seems to work, but I am not sure if it will exit properly. This seems like something that has probably been done several times before, but I can't find it.
public IEnumerable<Tuple<DateTime, DateTime>> SplitDateRange(DateTime start, DateTime end, int dayChunkSize)
{
var newStart = start;
var newEnd = start.AddDays(dayChunkSize);
while (true)
{
yield return new Tuple<DateTime, DateTime>(newStart, newEnd);
if (newEnd == end)
yield break;
newStart = newStart.AddDays(dayChunkSize);
newEnd = (newEnd.AddDays(dayChunkSize) > end ? end : newEnd.AddDays(dayChunkSize));
}
}
I'm looking for improvement suggestions, or "Dude, use this existing function for this!"
I think your code fails when the difference between start and end is smaller than dayChunkSize.
See this:
var singleRange = SplitDateRange(DateTime.Now, DateTime.Now.AddDays(7), dayChunkSize: 15).ToList();
Debug.Assert(singleRange.Count == 1);
Proposed solution:
public static IEnumerable<Tuple<DateTime, DateTime>> SplitDateRange(DateTime start, DateTime end, int dayChunkSize)
{
DateTime chunkEnd;
while ((chunkEnd = start.AddDays(dayChunkSize)) < end)
{
yield return Tuple.Create(start, chunkEnd);
start = chunkEnd;
}
yield return Tuple.Create(start, end);
}
There are a couple of problems with your solution:
the test newEnd == end may never be true, so the while could loop forever (I now see that this condition should always be triggered, but it wasn't obvious on first reading of the code; the while(true) feels a bit dangerous still)
AddDays is called three times for each iteration (minor performance issue)
Here is an alternative:
public IEnumerable<Tuple<DateTime, DateTime>> SplitDateRange(DateTime start, DateTime end, int dayChunkSize)
{
DateTime startOfThisPeriod = start;
while (startOfThisPeriod < end)
{
DateTime endOfThisPeriod = startOfThisPeriod.AddDays(dayChunkSize);
endOfThisPeriod = endOfThisPeriod < end ? endOfThisPeriod : end;
yield return Tuple.Create(startOfThisPeriod, endOfThisPeriod);
startOfThisPeriod = endOfThisPeriod;
}
}
Note that this truncates the last period to end on end as given in the code in the question. If that's not needed, the second line of the while could be omitted, simplifying the method. Also, startOfThisPeriod isn't strictly necessary, but I felt that was clearer than reusing start.
With respect to accepted answer you could use the short form of tuples:
private static IEnumerable<(DateTime, DateTime)> GetDateRange1(DateTime startDate, DateTime endDate, int daysChunkSize)
{
DateTime markerDate;
while ((markerDate = startDate.AddDays(daysChunkSize)) < endDate)
{
yield return (startDate, markerDate);
startDate = markerDate;
}
yield return (startDate, endDate);
}
But I prefer to use named tuples:
private static IEnumerable<(DateTime StartDate, DateTime EndDate)> GetDateRange(DateTime startDate, DateTime endDate, int daysChunkSize)
{
DateTime markerDate;
while ((markerDate = startDate.AddDays(daysChunkSize)) < endDate)
{
yield return (StartDate: startDate, EndDate: markerDate);
startDate = markerDate;
}
yield return (StartDate: startDate, EndDate: endDate);
}
Your code looks fine for me. I don't really like the idea of while(true)
But other solution would be to use enumerable.Range:
public static IEnumerable<Tuple<DateTime, DateTime>> SplitDateRange(DateTime start, DateTime end, int dayChunkSize)
{
return Enumerable
.Range(0, (Convert.ToInt32((end - start).TotalDays) / dayChunkSize +1))
.Select(x => Tuple.Create(start.AddDays(dayChunkSize * (x)), start.AddDays(dayChunkSize * (x + 1)) > end
? end : start.AddDays(dayChunkSize * (x + 1))));
}
or also, this will also work:
public static IEnumerable<Tuple<DateTime, DateTime>> SplitDateRange(DateTime start, DateTime end, int dayChunkSize)
{
var dateCount = (end - start).TotalDays / 5;
for (int i = 0; i < dateCount; i++)
{
yield return Tuple.Create(start.AddDays(dayChunkSize * i)
, start.AddDays(dayChunkSize * (i + 1)) > end
? end : start.AddDays(dayChunkSize * (i + 1)));
}
}
I do not have any objects for any of the implementations. They are practically identical.
If you know how many chunks/intervals/periods/parts you want to split your time range into, I've found the following to be helpful
You can use the DateTime.Ticks property to define your intervals, and then create a series of DateTime objects based on your defined interval:
IEnumerable<DateTime> DivideTimeRangeIntoIntervals(DateTime startTS, DateTime endTS, int numberOfIntervals)
{
long startTSInTicks = startTS.Ticks;
long endTsInTicks = endTS.Ticks;
long tickSpan = endTS.Ticks - startTS.Ticks;
long tickInterval = tickSpan / numberOfIntervals;
List<DateTime> listOfDates = new List<DateTime>();
for (long i = startTSInTicks; i <= endTsInTicks; i += tickInterval)
{
listOfDates.Add(new DateTime(i));
}
return listOfDates;
}
You can convert that listOfDates into however you want to represent a timerange (a tuple, a dedicated date range object, etc). You can also modify this function to directly return it in the form you need it.
There are a lot of corner cases that are unhandled in the answers so far. And it's not entirely clear how you would want to handle them. Do you want overlapping start/end of ranges? Is there a minimum range size? Below is some code that'll handle some of the corner cases, you'll have to think about overlapping especially and possibly push the start/end of ranges by a few seconds or maybe more depending on the data you're returning.
public static IEnumerable<(DateTime start, DateTime end)> PartitionDateRange(DateTime start,
DateTime end,
int chunkSizeInDays)
{
if (start > end)
yield break;
if (end - start < TimeSpan.FromDays(chunkSizeInDays))
{
yield return (start, end);
yield break;
}
DateTime e = start.AddDays(chunkSizeInDays);
for (;e < end; e = e.AddDays(chunkSizeInDays))
{
yield return (e.AddDays(-chunkSizeInDays), e);
}
if (e < end && end - e > TimeSpan.FromMinutes(1))
yield return (e, end);
}
Example call:
static void Main(string[] _)
{
Console.WriteLine("expected");
DateTime start = DateTime.Now - TimeSpan.FromDays(10);
DateTime end = DateTime.Now;
foreach (var range in PartitionDateRange(start, end, 2))
{
Console.WriteLine($"{range.start} to {range.end}");
}
Console.WriteLine("start > end");
start = end + TimeSpan.FromDays(1);
foreach (var range in PartitionDateRange(start, end, 2))
{
Console.WriteLine($"{range.start} to {range.end}");
}
Console.WriteLine("less than partition size");
start = end - TimeSpan.FromDays(1);
foreach (var range in PartitionDateRange(start, end, 2))
{
Console.WriteLine($"{range.start} to {range.end}");
}
}
The accepted solution looks good in most cases. If you need to take away overlap on the beginning and the end of each chunk, then this might work better.
public static IEnumerable<(DateTime FromDate, DateTime ToDate)> SplitDateRange(DateTime start, DateTime end, int dayChunkSize)
{
DateTime chunkEnd;
while ((chunkEnd = start.AddDays(dayChunkSize-1)) < end)
{
yield return (start, chunkEnd);
start = chunkEnd.AddDays(1);
}
yield return (start, end);
}
hare is an example spliced by month
IEnumerable<(DateTime, DateTime)> SplitDateRange(DateTime start, DateTime end, int monthChunkSize)
{
DateTime dateEnd=DateTime.Parse(end.ToString());
for (int i = 0;start.AddMonths(i) < dateEnd; i+=monthChunkSize)
{
end = start.AddMonths(i+monthChunkSize);
start.AddMonths(i);
yield return (start.AddMonths(i), end<dateEnd?end:dateEnd);
}
}
I'm reading the date expires cookie (2 hours) from database, and I need to check if this date has passed. What's the best way to do this?
For example:
public bool HasExpired(DateTime now)
{
string expires = ReadDateFromDataBase(); // output example: 21/10/2011 21:31:00
DateTime Expires = DateTime.Parse(expires);
return HasPassed2hoursFrom(now, Expires);
}
I'm looking for ideas as write the .HasPassed2hoursFrom method.
public bool HasPassed2hoursFrom(DateTime fromDate, DateTime expireDate)
{
return expireDate - fromDate > TimeSpan.FromHours(2);
}
bool HasPassed2hoursFrom(DateTime now, DateTime expires)
{
return (now - expires).TotalHours >= 2;
}
public bool HasExpired(DateTime now)
{
string expires = ReadDateFromDataBase(); // output example: 21/10/2011 21:31:00
DateTime Expires = DateTime.Parse(expires);
return now.CompareTo(Expires.Add(new TimeSpan(2, 0, 0))) > 0;
}
But since DateTime.Now is very fast and you don't need to pass it as function parameter...
public bool HasExpired()
{
string expires = ReadDateFromDataBase(); // output example: 21/10/2011 21:31:00
DateTime Expires = DateTime.Parse(expires);
return DateTime.Now.CompareTo(Expires.Add(new TimeSpan(2, 0, 0))) > 0;
}
Periodically check the date and see if now.CompareTo(expires) > 0
You can just use operators
boolean hasExpired = now >= Expires;
private enum DateComparisonResult
{
Earlier = -1,
Later = 1,
TheSame = 0
};
void comapre()
{
DateTime Date1 = new DateTime(2020,10,1);
DateTime Date2 = new DateTime(2010,10,1);
DateComparisonResult comparison;
comparison = (DateComparisonResult)Date1.CompareTo(Date2);
MessageBox.Show(comparison.ToString());
}
//Output is "later", means date1 is later than date2
To check if date has passed:
Source:https://msdn.microsoft.com/en-us/library/5ata5aya%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396