So i have a string that has different dates and I want to only find dates on or before 1998. The format in the string is dd-mm-yyyy. This is what I have so far
Regex test = new Regex(#".*\d\d-\d\d-(18|19)\d\d");
I just don't know how can I make it so that it only finds dates on or before 1998.
Years 1000 to 1999 can be matched with 1\d\d\d
Years 1000 to 1998 can be matched with (1[0-8][0-9]{2}|19[0-8][0-9]|199[0-8])
No I can't write them out that quick I used: http://gamon.webfactional.com/regexnumericrangegenerator/
Personally, I might be tempted to do in two steps, find ones in likely range (1000-1999) then actually parse them and check the actual date, particularly if your requirements get any more complex.
This one is a little more in depth, will also constrain months to 1-12, and days 1-31, will allow 01-09 for both, and goes up to 12-31-1998
#"(?<!\d)(?:1[0-2]|0?[1-9])-(?:[0-2]?[1-9]|3[0-1])-1\d(?:\d[0-8]|[0-8]\d)"
I agree with #jmargolisvt that it seems this would be best solved with the Datetime object. But, if there's some reason you must use regex ...
You can use #weston's original idea to restrict from 1000 to 1999:
.*\d{2}-\d{2}-1\d{3}
But add a negative-lookahead to eliminate 1999 explicitly:
.*\d{2}-\d{2}-(?!1999)1\d{3}
Here's another example that validates the date a little bit, not allowing month numbers or day numbers that can't exist.
(?:0[1-9]|1[0-2])-(?:0[1-9]|1\d|2\d|3[01])-(?:1\d\d[0-8])
https://regex101.com/r/Ml5WTK/1
I suggest combining regular expressions and Linq:
Regex extracts matches that can be date
Linq ensures (via DateTime.TryParseExact) that they are date and filter out years
Implementation
// Expected date; invalid date; date out the range
string source = "20-10-1950 29-02-1955 23-02-2097";
var result = Regex
.Matches(source, "[0-3][0-9]-[01][0-9]-[0-2][0-9]{3}")
.OfType<Match>()
.Select(match => {
DateTime dt;
bool valid = DateTime.TryParseExact(match.Value,
"dd-MM-yyyy", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out dt);
return new {
valid = valid,
value = dt,
};
})
.Where(item => item.valid && item.value.Year <= 1998)
.Select(item => item.value);
// 20.10.1950 12:00:00
Console.Write(string.Join(Environment.NewLine, result));
Related
I have to parse date string in C#. The dates are all ensured to start with year, month, day. But I do not know what dividers will be between the 3 parts.
Additionally the date string may also include a time part after the date.
Basically as long as the format has the year first, month second, and day third, I should parse it as a valid date regardless of which dividers are used and whether a time is included. Any other date formats should be rejected as invalid.
I can not figure out how to do this without writing a long if/else.
How do I parse a string into a C# datetime object, given the restrictions mentioned?
You can check the length of the input string is at least 10 characters, and if it is, work out what the separator should be by looking at the 5th character in the string.
Then you can use the separator to construct a format string that you pass to DateTime.TryParseExact() to parse the date. You also have to truncate the date string to 10 characters to ignore any date part at the end.
An example implementation looks like this - it returns null if the date didn't parse; otherwise, it returns the correctly parsed date:
public static DateTime? ParseDateWithUnknownDivider(string dateStr)
{
if (dateStr.Length < 10)
return null;
char divider = dateStr[4];
if (DateTime.TryParseExact(
dateStr.Substring(0, 10),
$"yyyy\\{divider}MM\\{divider}dd",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out DateTime result))
return result;
return null;
}
Note that this ignores the time part and will always return the time part as 00:00:00. If that's not what you meant, you will need to specify in your question what the time part would look like. For example, would it be separated from the date part by a space? And would it always be hh:mm:ss? And would it be 24hour clock?
try this code
string yourDateTimeString = ....;
string format="yyyy/MM/dd";
var dt = DateTime.ParseExact(yourDateTimeString,format,null,System.Globalization.DateTimeStyles.AssumeLocal);
For example I have 31.09.2017 (which is NON existent date) and that string's DateTime.ParseExact("31.09.2017", "dd.MM.yyyy", null); returns System.FormatException exception. Is there a way to turn 31.09.2017 into 30.09.2017 and do the same for all such wrong dates? For example like "round" works: to move to previous month's last day or next month's first day.
You can use the following technique :
DateTime temp;
if (!DateTime.TryParse("31.09.2017", out temp))
temp = GetValidDate("31.09.2017");
DateTime GetValidDate(string _date)
{
int day, year, month;
day = int.Parse(_date.Substring(0, 2));
month = int.Parse(_date.Substring(3, 2));
year = int.Parse(_date.Substring(6, 4));
return new DateTime(year, month, Math.Min(day, DateTime.DaysInMonth(year, month)));
}
The result may be unpredictable but your could split parts, convert to an int, then ensure that each part is within the correct range, then create a new string to parse. I suspect you only need to do this for the first 2 parts (dd and MM) and if outside of the range just set to the closest bounding value.
You can write a custom format provider and use it with DateTime.ParseExact however it may not work across cultures and may still throw exceptions like The DateTime represented by the string is not supported in calendar System.Globalization.GregorianCalendar. depending on how you implement.
DateTime.ParseExact Method (String, String, IFormatProvider)
An example.
I am a beginner in c # and I can not find the solution for my problem.
I am creating a personal project that allows me to send reminders, I have a date list and I need to do tasks between two specific dates in real life.
I found how to get the next date from today's date but I can't find how to get the previous one
Here is my sample code
void calc_x_date()
{
List<string> x_dates = new List<string>();
x_dates.Add("10/01/2017");
x_dates.Add("14/02/2017");
x_dates.Add("14/03/2017");
x_dates.Add("11/04/2017");
x_dates.Add("09/05/2017");
x_dates.Add("13/06/2017");
x_dates.Add("04/07/2017");
x_dates.Add("08/08/2017");
x_dates.Add("12/09/2017");
x_dates.Add("10/10/2017");
x_dates.Add("14/11/2017");
x_dates.Add("12/12/2017");
var allDates = x_dates.Select(DateTime.Parse).OrderBy(d => d).ToList();
var todayDate = DateTime.Today;
var nextDate = todayDate >= allDates.Last()
? allDates.Last()
: todayDate <= allDates.First()
? allDates.First()
: allDates.First(d => d >= todayDate);
string NextDate = nextDate.ToString(); // the closest next date from today
//string PreviousDate = // the closest previous date from today
}
Could someone explain me how to find my previous date please ?
Thanks in advance
I'd suggest using List<T>.BinarySearch: that will find the index of the date. If the index is 0 or more, then the exact date was found. If it's negative, then taking ~index will get you the index where the date would have been inserted.
Once you've got that information, it's easy to find the value at the previous index or the next index - but you should consider all the cases of:
Today is before all dates
Today is after all dates
Today is a date between the first and last date in the list, but isn't in the list
Today is equal to the first date
Today is equal to the last date
Today is equal to a date in the list which isn't the first or last date
As asides:
I would strongly encourage you to get in the habit of following .NET naming conventions as early as possible
I'd encourage you not to use strings to represent dates unless you really need to
If you're doing a lot of date/time work, you may find my Noda Time library easier to use correctly than DateTime
Here is a Linq approach
List<string> x_dates = new List<string>();
x_dates.Add("10/01/2017");
x_dates.Add("14/02/2017");
x_dates.Add("14/03/2017");
x_dates.Add("11/04/2017");
x_dates.Add("09/05/2017");
x_dates.Add("13/06/2017");
x_dates.Add("04/07/2017");
x_dates.Add("08/08/2017");
x_dates.Add("12/09/2017");
x_dates.Add("10/10/2017");
x_dates.Add("14/11/2017");
x_dates.Add("12/12/2017");
DateTime today = DateTime.Today;
IEnumerable<DateTime> dt_dates = x_dates.Select(DateTime.Parse);
DateTime prev = dt_dates.Where(x => x < today)
.OrderByDescending(x => x)
.First();
DateTime next = dt_dates.Where(x => x > today)
.OrderBy(x => x)
.First();
alternative solution
DateTime prev = dt_dates.Where(x => x < today).Max();
DateTime next = dt_dates.Where(x => x > today).Min();
Storing dates in string format works. It is however incredibly difficult to do date comparisons. You have to first cast it to numbers, handle the exceptions, etc.
C# has a DateTime object. You can store dates in this and ignore the time. DateTime objects can be compared using the < and > operators.
If you create a class with a start date and an end date, store these objects in a list(of tasks), would that solve your problem? You can also add a text of the task in a string to said object.
Using C#, I am trying to populate a variable with a known filename + date. The catch is that the date must always be the last day of the previous month. It must also have no slashes or hyphens, and the year must be two digits. For example, if it is now November 27 2015, I need my filename to be: Foobar_103115.txt
As a programmer who still has much to learn, I have written the clunky code below and it does achieve my desired result, even though it will obviously break after the end of this century. My code is written this way because I could not figure out a more direct syntax for getting the date I want, complete with the specified formatting.
My question is this: What would be a more elegant and efficient way of recreating the below code?
I have commented all the code for any novice programmers who might be interested in this. I know the experts I'm asking for help from don't need it.
public void Main()
{
String Filename
DateTime date = DateTime.Today;
var FirstDayOfThisMonth = DateTime.Today.AddDays(-(DateTime.Today.Day - 1)); //Gets the FIRST DAY of each month
var LastDayOfLastMonth = FirstDayOfThisMonth.AddDays(-1); //Subtracts one day from the first day of each month to give you the last day of the previous month
String outputDate = LastDayOfLastMonth.ToShortDateString(); //Reformats a long date string to a shorter one like 01/01/2015
var NewDate = outputDate.Replace("20", ""); //Gives me a two-digit year which will work until the end of the century
var NewDate2 = NewDate.Replace("/", ""); //Replaces the slashes with nothing so the format looks this way: 103115 (instead of 10/31/15)
Filename = "Foobar_" + NewDate2 + ".txt"; //concatenates my newly formatted date to the filename and assigns to the variable
Sounds like you want something more like:
// Warning: you should think about time zones...
DateTime today = DateTime.Today;
DateTime startOfMonth = new DateTime(today.Year, today.Month, 1);
DateTime endOfPreviousMonth = startOfMonth.AddDays(-1);
string filename = string.Format(CultureInfo.InvariantCulture,
"FooBar_{0:MMddyy}.txt", endOfPreviousMonth);
I definitely wouldn't use ToShortDateString here - you want a very specific format, so express it specifically. The results of ToShortDateString will vary based on the current thread's culture.
Also note how my code only evaluates DateTime.Today once - this is a good habit to get into, as otherwise if the clock "ticks" into the next day between two evaluations of DateTime.Today, your original code could give some pretty odd results.
I want to extract date from the string.
String : _21_BT_Txn_Details_1-Aug-2015_1031389ACF6.zip
How to do it?
Try this:
\d{1,2}-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-\d{4}
Demo and explanation of regex symbols: https://regex101.com/r/lW9yI3/2
Assumptions:
Day could be one or two digits
Year is always four digits
Standard three-letter abbreviations are use for the month
You can use the following code that can extract more than 1 dates inside the string like the one you provided:
var txt = "_21_BT_Txn_Details_1-Aug-2015_1031389ACF6.zip";
DateTime dt;
var res = txt.Split('_').Where(p => DateTime.TryParse(p, out dt)).ToList();
Or, if you always have the date in the above format (day-MON-year), use
DateTime.TryParseExact(p, "d-MMM-yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out dt)
There is no need for a regex here.