Parsing leap year day without year always fails in C# - c#

I'm trying to parse birthdays in the format of d/M without any year specified.
Using DateTime.TryParseExact(birthday, "d/M", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime birthdayDate) works most of the time, except when the birthday is at leap year day (aka 29/2), and the parsing never succeeds because it is default to current year. Using DatTimeStyles.NoCurrentDateDefault does not work either since it defaults to year = 1, which is not a leap year.
How do I do the parsing so it does not involve a hack (I don't want to parse it manually or manually add an arbitrary year to parse it as a full date either, they are all ugly and potentially fragile) and still work across all possible dates, including leap day? Existing questions does not help at all since nobody bothers to check if they work for leap day at all. I tried all of them and none of them worked.

A day and a month does not make a date - therefore they can't be parsed as a DateTime value without assuming a year. The .NET Framework assumes a year that is either the current year or year 1 - exactly because 2/29 is valid only on leap years - and this is a very reasonable assumption.
The .Net framework does not provide a built in way to store Day/Month values, but Noda Time does - Take a look at AnnualDate - It stores a day and a month but no year.
However, it doesn't have Parse or TryParse methods - so for that you still need to manually manipulate the input string and add a year (that is a leap year like 2016) in order to use the DateTime's TryParseExact method.
Update
As Matt Johnson wrote in his comment, Noda Time does provide a way for parsing text as an AnnualDate, using the AnnualDatePattern class.
The documentation has a page called Patterns for AnnualDate values that lists the supported patterns.

This question lacks the appropriate details, however
If you just want to parse a birth date and month including leap year dates, then just added a leap year to the end of the date.
I am not sure what you expect to do here, however you could try this
birthday = $"{birthday}/2016"; // leap year
DateTime.TryParseExact(birthday, "d/M/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime birthdayDate);
Update
my question is how to not make TryParseExact assume the year
automatically by manually overriding it in some way
To be technical here, no you have to specify the leap year in the string if you are parsing a leap year month and day exclusively as in your example
There is a lot of checks and balances the TryParseExact method does, however here are the important bits.
In short, it uses the current year or year 1, and there is no way to tell it to choose a leap year specifically
private static bool CheckDefaultDateTime(ref DateTimeResult result, ref Calendar cal, DateTimeStyles styles)
{
if ((result.flags & ParseFlags.CaptureOffset) != (ParseFlags) 0 && (result.Month != -1 || result.Day != -1) && ((result.Year == -1 || (result.flags & ParseFlags.YearDefault) != (ParseFlags) 0) && (result.flags & ParseFlags.TimeZoneUsed) != (ParseFlags) 0))
{
result.SetFailure(ParseFailureKind.Format, "Format_MissingIncompleteDate", (object) null);
return false;
}
if (result.Year == -1 || result.Month == -1 || result.Day == -1)
{
DateTime dateTimeNow = DateTimeParse.GetDateTimeNow(ref result, ref styles);
if (result.Month == -1 && result.Day == -1)
{
if (result.Year == -1)
{
if ((styles & DateTimeStyles.NoCurrentDateDefault) != DateTimeStyles.None)
{
cal = GregorianCalendar.GetDefaultInstance();
result.Year = result.Month = result.Day = 1;
}
else
{
result.Year = cal.GetYear(dateTimeNow);
result.Month = cal.GetMonth(dateTimeNow);
result.Day = cal.GetDayOfMonth(dateTimeNow);
}
}
else
{
result.Month = 1;
result.Day = 1;
}
}
else
{
if (result.Year == -1)
result.Year = cal.GetYear(dateTimeNow);
if (result.Month == -1)
result.Month = 1;
if (result.Day == -1)
result.Day = 1;
}
}
if (result.Hour == -1)
result.Hour = 0;
if (result.Minute == -1)
result.Minute = 0;
if (result.Second == -1)
result.Second = 0;
if (result.era == -1)
result.era = 0;
return true;
}

Related

C# - Validate an int-based DateTime without exceptions?

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.

How to validate DateTime for a leap year

I'm working with C# and I'm trying to find whether the given date and month is valid for a leap year. This is my code:
static void Main(string[] args)
{
Console.WriteLine("The following program is to find whether the Date and Month is Valid for an LEAP YEAR");
Console.WriteLine("Enter the Date");
int date = Convert.ToInt16(Console.ReadLine()); //User values for date and month
Console.WriteLine("Enter the Month");
int month = Convert.ToInt16(Console.ReadLine());
{
if (month == 2 && date < 30) //Determination of month and date of leap year using If-Else
Console.WriteLine("Your input is valid");
else if ((month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) && date < 32)
Console.WriteLine("Your inpput valid1");
else if (( month == 4 || month == 6 || month == 9 || month == 11 ) && date < 31)
Console.WriteLine("Your inpput valid2");
else
Console.WriteLine("Your input INvalid");
Console.ReadKey();
}
}
My question is, can I use DateTime for this program or is this a better way? Any suggestions are welcome.
I would suggest taking the input as a string and then using the DateTime.TryParse method. DateTime.TryParse accepts a string and an out DateTime (out keyword), and returns true if the string input was both correctly parsed and is a valid DateTime, and false otherwise.
From the documentation:
If s is the string representation of a leap day in a leap year in the current calendar, the method parses s successfully. If s is the string representation of a leap day in a non-leap year in the current culture's current calendar, the parse operation fails and the method returns false.
Usage example:
Console.WriteLine("Please enter a date.");
string dateString = Console.ReadLine();
DateTime dateValue;
if (DateTime.TryParse(dateString, out dateValue))
{
// Hooray, your input was recognized as having a valid date format,
// and is a valid date! dateValue now contains the parsed date
// as a DateTime.
Console.WriteLine("You have entered a valid date!");
}
else
{
// Aww, the date was invalid.
Console.WriteLine("The provided date could not be parsed.");
}
You could use DateTime.DaysInMonth with a year that is a known leap year like 2016.
if (month >= 1 && month <= 12 && date >= 1 && date <= DateTime.DaysInMonth(2016, month))
Console.WriteLine("Your input is valid");
else
Console.WriteLine("Your input is invalid");
Use a known leap year for the year part e.g. 2000 and append the month and day and year to form a string like mm-dd-2000 where mm and dd are the user entered values. Then use the DateTime.TryParse method which returns true if the date is valid.
If you're working from separate parts, then just:
try
{
new DateTime(year, month, day);
}
catch (ArgumentOutOfRangeException)
{
// it's not valid
}
Though if you prefer not to rely on exceptions, then go with juharr's answer, using DateTime.DaysInMonth.

How to compare a given date from today

I want to compare a given date to today and here is the condition: If provided date is greater than or equal to 6 months earlier from today, return true else return false
Code:
string strDate = tbDate.Text; //2015-03-29
if (DateTime.Now.AddMonths(-6) == DateTime.Parse(strDate)) //if given date is equal to exactly 6 months past from today (change == to > if date has to be less 6 months)
{
lblResult.Text = "true"; //this doesn't work with the entered date above.
}
else //otherwise give me the date which will be 6 months from a given date.
{
DateTime dt2 = Convert.ToDateTime(strDate);
lblResult.Text = "6 Months from given date is: " + dt2.AddMonths(6); //this works fine
}
If 6 months or greater than 6 months is what I would like for one
condition
If less than 6 months is another condition.
Your first problem is that you're using DateTime.Now instead of DateTime.Today - so subtracting 6 months will give you another DateTime with a particular time of day, which is very unlikely to be exactly the date/time you've parsed. For the rest of this post, I'm assuming that the value you parse is really a date, so you end up with a DateTime with a time-of-day of midnight. (Of course, in my very biased view, it would be better to use a library which supports "date" as a first class concept...)
The next problem is that you are assuming that subtracting 6 months from today and comparing it with a fixed date is equivalent to adding 6 months to the fixed date and comparing it with today. They're not the same operation - calendar arithmetic just doesn't work like that. You should work out which way you want it to work, and be consistent. For example:
DateTime start = DateTime.Parse(tbDate.Text);
DateTime end = start.AddMonths(6);
DateTime today = DateTime.Today;
if (end >= today)
{
// Today is 6 months or more from the start date
}
else
{
// ...
}
Or alternatively - and not equivalently:
DateTime target = DateTime.Parse(tbDate.Text);
DateTime today = DateTime.Today;
DateTime sixMonthsAgo = today.AddMonths(-6);
if (sixMonthsAgo >= target)
{
// Six months ago today was the target date or later
}
else
{
// ...
}
Note that you should only evaluate DateTime.Today (or DateTime.Now etc) once per set of calculations - otherwise you could find it changes between evaluations.
Try with this
DateTime s = Convert.ToDateTime(tbDate.Text);
s = s.Date;
if (DateTime.Today.AddMonths(-6) == s) //if given date is equal to exactly 6 months past from today (change == to > if date has to be less 6 months)
{
lblResult.Text = "true"; //this doesn't work with the entered date above.
}
replace == with >= or <= according to your needs

How can I check if current day is working 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));
}

Create valid datetime object with validation [duplicate]

This question already has answers here:
Wanted: DateTime.TryNew(year, month, day) or DateTime.IsValidDate(year, month, day)
(2 answers)
Closed 4 years ago.
I am creating date using following code
try
{
newdatetime = new DateTime(2012, 2, 30);
break;
}
catch (ArgumentOutOfRangeException)
{
// Try 29 Feb if not 28.
}
The catch block is to catch the invalid date like 30 Feb. Is there any way to verify if the date is valid by speciying the parameters like (year, month, day)?
Well, with months you know the valid range so you can constrain that manually. Years are obviously not constrained in the normal sense, but are instead limited by the amount that DateTime can actually hold (0001 to 9999).
With days, there is the DaysInMonth(int year, int month) method that can tell you the maximum days for the provided month. This also gives you the leap year.
With this information, you can create your own method to check the range based on the provided integers.
Something like:
public static bool AreValidDateValues(int year, int month, int day)
{
if (month < 1 || month > 12)
return false;
if (year < DateTime.MinValue.Year || year > DateTime.MaxValue.Year)
return false;
var days = DateTime.DaysInMonth(year, month);
if (day < 1 || day > days)
return false;
return true;
}
Or if you can't be bothered with that, convert the raw values into a string representation of a date and put that into DateTime.TryParse, which will give a true/false for the provided string - just be careful with culture-sensitive parsing.
You can use DateTime.TryParse to perform the check :
DateTime d;
var isValid = DateTime.TryParse(String.Format("{0}/{1}/{2}", 2, 31, 2012), out d);
Console.WriteLine(isValid);

Categories