I'm searching for a C# equivalent to the functionality provided by the boost:gregorian generators.
Specifically, I have wildcard dates and need to convert them to concrete dates.
For example a wildcard 'Third Monday in January' could be done with boost::gregorian as
typedef nth_day_of_the_week_in_month nth_dow;
nth_dow ndm(nth_dow::third, Monday,Jan);
date d = ndm.get_date(2002);
//2002-Jan-21
By using this feature, it can then be further wildcarded as 'Third Monday every month', or '.. every odd month'.
What is the best way to achieve such behaviour in C#?
It is possible to do this straight up with DateTime.DaysInMonth foreach loop then check the day, but it can be a pain so I suggest you just set up a method such as below
public static IEnumerable<DateTime> AllDatesInMonth(int year, int month)
{
int days = DateTime.DaysInMonth(year, month);
for (int day = 1; day <= days; day++)
{
yield return new DateTime(year, month, day);
}
}
Then you can do something similar to the below code:
foreach (DateTime date in AllDatesInMonth(2016,4).Where(dt => dt.DayOfWeek == DayOfWeek.Monday))
break; //This will work as it will execute the break as soon as it hits the first monday of the month
What do you mean by wildcard dates though? TimeSpans?
Related
I'm learning C# and I wanted to make a calendar to learn DateTime. I did make a calendar but now I want to see previous months days in first week like in this photo. I tried few things but I really don't remember all.
Here is my code and if there any way to improve it just say.
I'm using WinForms for ui.
DateTime now = DateTime.Now;
Month = now.Month;
Year = now.Year;
// count of days of the month
int days = DateTime.DaysInMonth(Year, Month);
// first day of the month
DateTime startofthemonth = new DateTime(Year, Month,1);
// last day of the month
var endofthemonth = startofthemonth.AddDays(-1);
// convert the startofthemonth to int
int dayoftheweek = Convert.ToInt32(startofthemonth.DayOfWeek.ToString("d")) + 6;
// convert the endofthemonth to int
int lastdayinmonth = Convert.ToInt32(endofthemonth.DayOfWeek.ToString("d"));
// blank user control
for (int i = lastdayinmonth; i <= lastdayinmonth; i--)
{
UCBlankDay uCBlankDay = new UCBlankDay();
uCBlankDay.BlankDays(i);
flowLayoutPanel1.Controls.Add(uCBlankDay);
}
// day user control
for (int i = 1; i <= days; i++)
{
UCDay uCDay = new UCDay();
uCDay.Days(i);
flowLayoutPanel1.Controls.Add(uCDay);
}
}
to be clear code works without showing previous months days in first week
I've recently done a calendar and I found that when you are in the month view, it's better to divide your month in weeks first instead of days right away.
I would use your 1st day of the month and find the first day of the week doing something like this:
public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek)
{
int diff = (7 + (dt.DayOfWeek - startOfWeek)) % 7;
return dt.AddDays(-1 * diff).Date;
}
startofthemonth = date.StartOfMonth().StartOfWeek(startDay);
I would then iterate by weeks instead of days like this
do
{
Weeks.Add(new UCBlankWeek(**Your day logic will be inside this constructor**));
startofthemonth = startofthemonth.AddDays(7);
currentMonth = startofthemonth.Month;
}
while (currentMonth == MonthNumber);
You can loop 7 times inside the constructor of everyweek and put you logic in there.
Hope this helps with your issue!
This question talks about validating a string representing a date, and in it folks mention that it's good to avoid using Exceptions for regular flow logic. And TryParse() is great for that. But TryParse() takes a string, and in in my case i've already got the year month and day as integers. I want to validate the month/day/year combination. For example February 30th.
It's pretty easy to just put a try/catch around new DateTime(int, int, int), but I'm wondering if there's a way to do it without relying on exceptions.
I'd also feel silly composing these ints into a string and then using TryParse().
The following will check for valid year/month/day combinations in the range supported by DateTime, using a proleptic Gregorian calendar:
public bool IsValidDate(int year, int month, int day)
{
return year >= 1 && year <= 9999
&& month >= 1 && month <= 12
&& day >= 1 && day <= DateTime.DaysInMonth(year, month);
}
If you need to work with other calendar systems, then expand it as follows:
public bool IsValidDate(int year, int month, int day, Calendar cal)
{
return year >= cal.GetYear(cal.MinSupportedDateTime)
&& year <= cal.GetYear(cal.MaxSupportedDateTime)
&& month >= 1 && month <= cal.GetMonthsInYear(year)
&& day >= 1 && day <= cal.GetDaysInMonth(year, month);
}
Use String Interpolation
int year = 2017;
int month = 2;
int day = 28;
DateTime dt;
DateTime.TryParse($"{month}/{day}/{year}", out dt);
As far as I know, there's no easy way to check for a DateTime's int's validity besides concatenating the ints into a correctly formatted string beforehand.
To avoid try/catch-ing, I would write a static utility class which utilizes DateTime.TryParse:
using System;
public static class DateTimeUtilities
{
public static bool TryParse(int year, int month, int day, out DateTime result)
{
return DateTime.TryParse(
string.Format("{0}/{1}/{2}", year, month, day), out result);
}
}
Usage:
DateTime dateTime;
if (DateTimeUtilities.TryParse(2017, 2, 30, out dateTime))
{
// success
}
else
{
// fail, dateTime = DateTime.MinValue
}
Pending the needs of your application, e.g. culture (thanks #Matt Johnson), I would also look into DateTime.TryParseExact.
Look at it this way. Any code that you write:
Will have to check month ranges 1-12
Will have to check day ranges by month, which means you'll have to hard code an array
Will have to account for leap years, which can be a pain the the rear
Rather than doing ALL that, and reinventing the wheel, and potentially getting it wrong -- why don't you keep it simple and just wrap the DateTime constructor in a try-catch and keep it moving? Let the nerds up in Redmond do all the hard work for this common task. The best solution is one that any developer following you can understand and rely upon quickly.
I'd bet money that under the hood, TryParse and the DateTime constructor are using the exact same validators, except that the latter throws an exception while the former does not. TryParse, for this, is overkill with all the extra string manipulation involved.
I have something odd requirement. I have Current Date and List of Week Days. And I want next all possible date till the target date.
For i.e. Today, its 22-04-2014 And Tuesday. Target date is 15-05-2014I have 2 week days, Monday and Thursday. So code should find near by Week Day, which will be Thursday here. So It should return date of Thursday which is 24-04-2014. Now, next turn is of Monday which comes from List. So now, It should return date of Monday which is 28-04-2014.
It should keep repeating till the target date.
So, final result will be
24-04-2014,
28-04-2014,
1-05-2014,
5-05-2014,
8-05-2014,
12-05-2014
Please help me to get this type of result. Here, Monday and Thursday is not fixed. It can be any Day and any number of Day.
Update : Link to the working example - Example
You can try this code, i have tested it and working correctly
private List<DateTime> ProcessDate(DateTime dtStartDate, DateTime targetDate)
{
DateTime dtLoop = dtStartDate;
//dtRequiredDates to hold required dates
List<DateTime> dtRequiredDates = new List<DateTime>();
for (int i = dtStartDate.DayOfYear; i < targetDate.DayOfYear; i++)
{
if (dtLoop.DayOfWeek == DayOfWeek.Monday || dtLoop.DayOfWeek == DayOfWeek.Thursday)
{
dtRequiredDates.Add(dtLoop);
}
dtLoop = dtLoop.AddDays(1);
}
return dtRequiredDates;
}
You may have to enhance this codes so that it doesn't throw any exception based on the requirement.
UPDATE 2:
You can have another method which will accept the days of week as follows
private List<DateTime> ProcessDate(DateTime dtStartDate, DateTime targetDate, List<DayOfWeek> daysOfWeek)
{
DateTime dtLoop = dtStartDate;
List<DateTime> dtRequiredDates = new List<DateTime>();
for (int i = dtStartDate.DayOfYear; i < targetDate.DayOfYear; i++)
{
foreach (DayOfWeek day in daysOfWeek)
{
if (dtLoop.DayOfWeek == day)
{
dtRequiredDates.Add(dtLoop);
}
}
dtLoop = dtLoop.AddDays(1);
}
return dtRequiredDates;
}
Here is the Example
Hence you can pass any number of week days as you wish.
Hope this helps
You could try something like this:
List<DayOfWeek> listOfDays = new List<DayOfWeek>{DayOfWeek.Monday, DayOfWeek.Thursday};
var end = new DateTime(2014,05,15);
var day = DateTime.Now.Date;
while (day < end)
{
day.AddDays(1); // adds +1 days to "day"
if (listOfDays.Contains(day.DayOfWeek)) Console.WriteLine(day.Date.ToString());
}
(I can't test the code right now, so maybe you need to modify a little ;-)
What I need is to get logic on how to get monthname-year between two dates.
Dictionary<Monthname,year> GetMonthsandYear(Datetime d1,Datetime d2)
or
List<Tuple<string,int> GetMonthsandYear(Datetime d1,Datetime d2)
example : jan-1-2013 to mar-3-2013
should return January-2013,february-2013,march-2013 or in reverse format by list.reverse
If your actual requirement is "the previous 24 months" then it's much simpler. Just start off with the current month, and get the 1st day of it - then iterate and add 1 month 24 times.
Personally I'd return an IEnumerable<DateTime> rather than anything else - you can format each element however you want - but it's pretty simple:
public static IEnumerable<DateTime> GetMonths(int count)
{
// Note: this uses the system local time zone. Are you sure that's what
// you want?
var today = DateTime.Today;
// Always return the 1st of the month, so we don't need to worry about
// what "March 30th - 1 month" means
var startOfMonth = new DateTime(today.Year, today.Month, 1);
for (int i = 0; i < count; i++)
{
yield return startOfMonth;
startOfMonth = startOfMonth.AddMonths(-1);
}
}
Then if you want a List<string> of these values as "February 2014" etc for example, you could have:
var monthYears = GetMonths(24).Select(dt => dt.ToString("MMMM yyyy"))
.ToList();
Note that a Dictionary<...> would not be appropriate unless you really don't care about the order - and I suspect you do. You shouldn't rely on the order in which items are returned from a Dictionary<TKey, TValue> when you view it as a sequence - it's not intended to be an ordered collection.
I don't understand why you need Dictionary or List<Tuple<string,int> but one solution could be;
DateTime dt1 = new DateTime(2013, 1, 1);
DateTime dt2 = new DateTime(2013, 3, 3);
while (dt1 < dt2)
{
Console.WriteLine(dt1.ToString("MMMM-yyyy"));
dt1 = dt1.AddMonths(1);
}
Result will be;
January-2013
February-2013
March-2013
Even if you need, you can add these values to a List<string> in while loop.
But be carefull about what Jon said, this solution will generate only January and February if your dt1.Day is greater than dt2.Day.
I have application that needs to be run on working days, and within working hours.
In application configuration, I've set start time in format
Monday-Friday
9:00AM-5:30PM
Now, I have a problem how to check if current day is within day boundare is (for the time is easy - parse time with DateTime.ParseExact and simple branch will do), but I don't know how to parse days.
I've tried with:
DayOfWeek day = DateTime.Now.DayOfWeek;
if (day >= (DayOfWeek)Enum.Parse(typeof(DayOfWeek), sr.start_day) &&
day <= (DayOfWeek)Enum.Parse(typeof(DayOfWeek), sr.end_day))
{ /* OK */ }
sr.start_day and sr.end_day are strings
but the problem occurred during weekend testing - apparently, in DayOfWeek enum, Sunday is first day of the week (refering to the comments on MSDN page
I suppose I could do some gymnastics with current code, but I am looking for the most readable code available.
Edit
Sorry for the misunderstanding - working days are not from Monday to Friday - they are defined as strings in config file, and they can be even from Friday to Saturday - which breaks my original code.
if ((day >= DayOfWeek.Monday) && (day <= DayOfWeek.Friday))
{
// action
}
From Hans Passant's comment on my original question:
Just add 7 to the end day if it is less than the start day. Similarly,
add 7 to day if it is less than the start day.
DayOfWeek day = DateTime.Now.DayOfWeek;
DayOfWeek start_day = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), sr.start_day);
DayOfWeek end_day = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), sr.end_day);
if (end_day < start_day)
end_day += 7;
if (day < start_day)
day += 7;
if (day >= start_day && day <= end_day)
{
//Action
}
extention for DateTime
public static bool IsWeekend(this DateTime date)
{
return new[] {DayOfWeek.Sunday, DayOfWeek.Saturday}.Contains(date.DayOfWeek);
}
This is an elegant solution for the problem. It's a class that can easily be imported into other projects. The coding allows the programmer to dynamically assign what days to check for and pass them as a string array to the class. The data can come from a database or be hard coded when you pass it to an instance of this class for processing. It returns the values of True if you're off work and False if you're working that day. Below the class I provided a simple example of implementation. This class features: Dynamic allocation of what days you have off, Simple error handler by setting strings to lowercase before comparing them, Easily integrated with a database that has your work schedule where your days off may not always be the same. Easily integrated as a hard coded number of days off.
// The Class To Check If You're Off Work
class DayOffChecker
{
public bool CheckDays(List<string> DaysOff)
{
string CurrentDay = DateTime.Now.DayOfWeek.ToString();
CurrentDay.ToLower();
foreach (string DayCheck in DaysOff)
{
DayCheck.ToLower();
if (CurrentDay == DayCheck)
{
return (true);
}
}
return (false);
}
}
// Example usage code:
class Program
{
List<string> DaysOff = List<string>();
DaysOff.Add("Saturday"); // Add some values to our list.
DaysOff.Add("Sunday");
DayOffChecker CheckToday = new DayOffChecker();
if(CheckToday.CheckDays(DaysOff))
{
Console.WriteLine("You're Off Today!!!");
}
}
We can also follow similar approach of checking if a given hour is between two hours. Following is the algorithm
checkIfFallsInRange(index,start,end)
bool normalPattern = start <= end ;
if ( normalPattern)
return index>=start && index<=end;
else
return index>=start || index <=end;
My simple solution to determining if the current day is a workday or not is:
public static bool IsWorkDay(this DateTime dt)
{
return IsWorkDay(dt, DayOfWeek.Sunday, DayOfWeek.Saturday);
}
public static bool IsWorkDay(this DateTime dt, params DayOfWeek[] noneWorkDays)
{
return !noneWorkDays.Contains(dt.DayOfWeek);
}
It assumes Sunday / Saturday are non-work days. Otherwise the user can specify the non-work days. And is an extension for easy of use.
Note: To avoid a loop could created a bit flag.
DayOfWeek Day = DateTime.Today.DayOfWeek;
int Time = DateTime.Now.Hour;
if (Day != DayOfWeek.Saturday && Day != DayOfWeek.Sunday)
{
if (Time >= 8 && Time <= 16)
{
//It is Weekdays work hours from 8 AM to 4 PM
{
}
else
{
// It is Weekend
}
You can use the DayOfWeek enumeration in order to see if a date is Sunday or Saturday. http://msdn.microsoft.com/en-us/library/system.dayofweek.aspx I hope this can help.
The line below will return "Sunday"
string nameOfTheDay = DateTime.Now.ToString("dddd", new System.Globalization.CultureInfo("en-GB")).ToLower();
if(nameOfTheDay != "sunday" && nameOfTheDay != "saturday")
{
//Do Stuff
}
public bool IsWeekend(DateTime dateToCheck)
{
DayOfWeek day = (DayOfWeek) dateToCheck.Day;
return ((day == DayOfWeek.Saturday) || (day == DayOfWeek.Sunday));
}