Regular expression for matching short date pattern in any culture - c#

I need a custom regex to math short date pattern(etc. "M/dd/yyyy", "dd/mm/yyyy", "yyyy/mm/dd",...depend on culture..) then I can get day,month,year to parse that format like this:
string input = "03/24/2013";
Match m = Regex.Match(input, #"^(?<day>\d{1,2})/(?<month>\d{1,2})/(?<year>\d{4})$");
if( m.Success )
{
DateTime d = new DateTime(int.Parse(m.Groups["year"].Value),
int.Parse(m.Groups["month"].Value),
int.Parse(m.Groups["day"].Value));
}
but! this expression not support all various formats so
require :
Accept only '/' as separators.
Valid all short date pattern(Date only,also Time would be great) in various cultures (etc, en-US, th-TH, jp-JP,...). Is this possible, actually I just need to know which part is year, month or day but I couldn't find it anywhere. Please help me...
Valid common range: day [1-31]{1-2}, month [1-12]{1-2}, year[1600-9999]{4}

As Jim and I suggested, you can just iterate over all cultures:
foreach (var culture in CultureInfo.GetCultures(CultureTypes.SpecificCultures))
{
DateTime result;
if (DateTime.TryParseExact(text, "d", culture, DateTimeStyles.None,
out result))
{
// Valid - return, or whatever
}
}
Note that "d" is the standard format string for a short date pattern. Alternatively, you could use culture.DateTimeFormat.ShortDatePattern.

Related

How to get DateTime Format String of actual date-time?

I'd like to take a datetime string, and get the string version of its format:
e.g. 2017-01-02 would yield YYYY-MM-dd for logging purposes if a datetime is parsed incorrectly due to a parsing exception.
Is this possible with System.DateTime or any other built-in?
"2017-01-02" would yield "YYYY-MM-dd"
It's not possible. You are asking for magic, making something out of nothing.
There is no way without meta information for a computer (or a human) to decide if that is the 2nd January or the 1st February. Both is possible.
There is no reliable way of getting that format string based on a string that might look like a date.
The closest hack that I can come up with is iterating over all cultures and then trying to ParseExact the predefined DateTime patterns that are found in each culturinfo's DateTimeFormat property.
The hack would look like this:
var date = "2017-01-02";
var formats = new Dictionary<string,int>();
// go over all cultures
foreach(var ci in CultureInfo.GetCultures(CultureTypes.SpecificCultures))
{
// each culture has a DateTimeFormatInfo that holds date and time patterns
foreach(var p in typeof(DateTimeFormatInfo)
.GetProperties()
.Where(prop => prop.Name.EndsWith("Pattern")))
{
// get a pattern property from the cultureinfo DateTimeFormat
var fmt = (string) p.GetValue(ci.DateTimeFormat, null);
try
{
// try to parse the date
DateTime.ParseExact(date,fmt , ci);
// add the format
if (formats.Keys.Contains(fmt))
{
formats[fmt]++; // how often this was a valid date
}
else
{
formats.Add(fmt, 1); // first time we found it
}
}
catch
{
// no date in this culture for this format, ignore
}
}
}
// output the formats that were parsed as a date
foreach(var kv in formats)
{
// Dump is a linqPad extension, use Console.WriteLine if that bothers you
kv.Value.Dump(kv.Key);
}
and on my box this returns
yyyy-MM-dd
40
yyyy-M-d
1
Now for 01/02/2017 it would return:
dd/MM/yyyy
255
d/M/yyyy
93
M/d/yyyy
20
d/MM/yyyy
11
MM/dd/yyyy
2
which shows a bit of the concerns raised in the comments. Without knowing the culture a date string was generated it is a guess what the date format was. Worst case it was a string with numbers and dashes between them.

ParseExact string to DateTime

I try to parse my string to datetime, but I get a FormatException.
string date = "27.02.2017 13:03:16 Uhr";
action.Date =
DateTime.ParseExact(date, "dd.MM.yyyy HH:mm:ss",CultureInfo.InvariantCulture);
Does somebody has any idea?
You have to escape Uhr suffix:
string date = "27.02.2017 13:03:16 Uhr";
action.Date = DateTime.ParseExact(date, "dd.MM.yyyy HH:mm:ss 'Uhr'",
CultureInfo.InvariantCulture);
The problem is the "Uhr" at the end of the input. If you specify your format like this, the input must match the format.
After some people add "Uhr" and some people don't, I recommend to extract the relevant part using a regex.
using System.Text.RegularExpressions;
Match m = Regex.Match( #"/([\d]{2}\.[\d]{2}\.[\d]{4} +[\d]{2}\:[\d]{2}\:[\d]{2})/", date);
if (m != null)
date = m.Groups[0].Value;
else
// Something wrong with the input here
And then (unless m is null) you can use this string as a valid input for your ParseExact-part in most cases. But be aware that the regex does not perform any range-checks, so an input like Montag, 99.99.9999 99:99:99 Uhr would lead to the date string 99.99.9999 99:99:99 which matches the regex, but is not a valid DateTime anyway.

Convert unusual string to DateTime w/out foreach

Update My Solution:
var rowsToAdd = (from t in dtEntry.AsEnumerable().Cast<DataRow>()
let startDate = (
t.Field<string>("StartDate").Length > 0)
? DateTime.Parse(t.Field<string>("StartDate").Split(new Char [] {'('})[0], CultureInfo.InvariantCulture)
: DateTime.Today.AddMonths(-3)
where startDate > filterDate
select t);
Original Question:
I get a DateTime string from an external API that looks like this:
10/14/2014 8:30 AM (America/Los Angeles)
I have all the data in a datatable called dtEntry which I'm using below.
None of the built-in c# DateTime conversion functions seem to work. They all result in format exeptions. Does anyone know how I could do this? The other catch is that I'm using LINQ (see below).
DataRow[] rows = (from t in dtEntry.AsEnumerable().Cast<DataRow>()
let startDate = (
t.Field<string>("StartDate").Length > 0)
? DateTime.Parse(t.Field<string>("StartDate"))
: DateTime.Today.AddMonths(-3)
where startDate > filterDate
select t).ToArray();
Any ideas? I've got the ternary operator in there because I need to handle empty strings as well.
You can split your string based on space and then Take(3) elements from the result array, Join them back using string.Join and then use DateTime.ParseExact or DateTime.TryParseExact like:
string str = "10/14/2014 8:30 AM (America/Los Angeles)";
string newStr = string.Join(" ", str.Split().Take(3));
DateTime parsingDateTime;
if (!DateTime.TryParseExact(newStr, "M/d/yyyy h:mm tt", CultureInfo.InvariantCulture, DateTimeStyles.None,
out parsingDateTime))
{
//invalid datetime
}
EDIT: You have to ignore (America/Los Angeles) part of string, otherwise there is no way for parsing using such string. You can find TimeZone for Region and then create DateTime for that parameter. See this: Get timezone by Country and Region
The accepted answer does not take into account the time zone part. My assumption here is the time zone is a standard time zone identifier which can be translated from the Unicode.org site. And based off this other SO Answer (.NET TimeZoneInfo from Olson time zone) which provides a helper method from the Unicode.org site, you can then parse the api time to your time:
string apiTime = "10/14/2014 8:30 AM (America/Los Angeles)";
int timeZoneStart = apiTime.IndexOf('(');
string timeZonePart = apiTime.Substring(timeZoneStart)
.Replace("(", string.Empty) // remove parenthesis
.Replace(")", string.Empty) // remove parenthesis
.Trim() // clear any other whitespace
.Replace(" ", "_"); // standard tz uses underscores for spaces
// (America/Los Angeles) will become America/Los_Angeles
string datePart = apiTime.Substring(0, timeZoneStart).Trim();
DateTime apiDate = DateTime.Parse(datePart);
TimeZoneInfo tzi = OlsonTimeZoneToTimeZoneInfo(timeZonePart);
DateTime apiDateTimeConverted = TimeZoneInfo.ConvertTime(apiDate, tzi);
The method above, OlsonTimeZoneToTimeZoneInfo, is from the SO answer linked above.

How to know if time part was present when parsing with DateTime

Say, I have two strings containing date/time in arbitrary user-provided format for an arbitrary culture:
string str1 = "6/19/2013";
string str2 = "6/19/2013 1 am";
I can parse them by doing:
DateTime dt1 = DateTime.Parse(str1);
DateTime dt2 = DateTime.Parse(str2);
But how do I know if the time part was present & parsed into the DateTime object?
What do you guys think about something like this?
public static DateTime ParseDateTimeWithTimeDifferentiation(string str, out bool bOutTimePresent)
{
//Parse date/time from 'str'
//'bOutTimePresent' = receives 'true' if time part was present
DateTime dtRes;
//Get formats for the current culture
DateTimeFormatInfo dtfi = CultureInfo.CurrentUICulture.DateTimeFormat;
DateTimeStyles dts = DateTimeStyles.AllowWhiteSpaces |
DateTimeStyles.AssumeLocal;
//Get all formats
string[] arrFmts = dtfi.GetAllDateTimePatterns();
foreach (string strFmt in arrFmts)
{
if (DateTime.TryParseExact(str, strFmt, CultureInfo.InvariantCulture, dts, out dtRes))
{
//Parsed it!
//These format codes come from here:
// http://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx
bOutTimePresent = strFmt.IndexOfAny(new char[] { 'H', 'h', 'm', 's', 'f', 'F', 't', 'z' }) != -1;
return dtRes;
}
}
//As a fall-back, just parse it as-is
dtRes = DateTime.Parse(str);
//Assume it has time, as otherwise we'd catch the date-only above
bOutTimePresent = true;
return dtRes;
}
You can try using two separate calls to DateTime.ParseExact or DateTime.TryParseExact. This will be especially easy if you know the format of the date and time parts. The code would look something like this:
DateTime dateValue;
var culture = new CultureInfo("en-US");
if (DateTime.TryParseExact(dateString, "M/d/yyyy H:mm:ss", culture,
DateTimeStyles.None, out dateValue))) {
//Using date and time. dateValue var is set.
}
else if (DateTime.TryParseExact(dateString, "M/d/yyyy", culture,
DateTimeStyles.None, out dateValue))) {
//Using just date. dateValue var is set.
}
If you cannot anticipate the exact format of the date/time string, you can either enumerate a bunch of possible possible formats, or use regular expressions to try to extract the time part. Read more about custom date and time formats here. There are also some provided standard date and time formats.
If no time was specified in the string that was parsed, the TimeOfDay property of the DateTime object will be set to midnight (00:00:00). You can then check if that's the case with something like this:
if (dt1.TimeOfDay.Equals(new TimeSpan(0,0,0))) {
//do something
} else {
//do something else
}
EDIT: Another approach could be to separate the date and time sections of the string. This is assuming some type of numerical date format is passed using dashes, commas, or anything besides spaces.
string[] dateString = str1.Split(' ');
string[] date2String = str2.Split(' ');
You'll now have a string array that can be easily used to check for special values. dateString[0], for example, should contain your entire date. dateString[1] and beyond will have any time formats, and can be recombined and parsed into a TimeSpan object. Obviously if you have only a single entity in the array, they've not entered any time.

Detect format Date

Is there a way, a good way, to test if a string than I want to transform in DateTime is dd/MM/yyyy or MM/dd/yyyy ?
Thanks,
No, because it could be both. Is 11/10/2010 November 10th or October 11th?
Yes, in some cases (if one number is above 12) it will be unambiguous - but I think it's better to force one format or the other. If you just treat anything which can be done as MM/dd/yyyy that way, and move on to dd/MM/yyyy if it fails (or the other way round) then you'll get some very surprised users.
If this is part of a web application or something similar, I would try to make it completely unambiguous by using month names instead of numbers where possible.
No, but you could try both when parsing:
DateTime result;
if (DateTime.TryParseExact(
"05/10/2010",
new[] { "MM/dd/yyyy", "dd/MM/yyyy" },
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out result)
)
{
// successfully parsed date => use the result variable
}
This problem will exist until all accepts and uses the ISO way. I'm a Swedish programmer working a lot with American and English clients and it's surprisingly hard to get these clients to use the standardized date format.
ISO 8601 - Use it!
Take a look at DateTime.ParseExact and DateTime.TryParseExact.
string date1 = "24/12/2010";
CultureInfo provider = CultureInfo.InvariantCulture;
DateTime dt1 = new DateTime(1, 1, 1);
bool dt1Valid = false;
try
{
dt1 = DateTime.ParseExact(date1, "dd/MM/yyyy", provider);
dt1Valid = true;
}
catch
{
dt1Valid = false;
}

Categories