Reputation: 956
I'm trying to parse a string formatted like so:
1900-001T00:00:00Z
into a DateTime object. The middle bit there (right after the "1900-" and before the "T") is supposed to be the day of year. I know the rest of the formatting string I would need to use would be
yyyy-XXXTHH:mm:ssZ
but what should I put in for that 'XXX'?
Upvotes: 2
Views: 1091
Reputation: 37020
I'm sure there's a fancier way, but you could write your own method to do it:
private static DateTime CustomParseDayOfYear(string input)
{
if (input == null) throw new ArgumentNullException(nameof(input));
var parts = input.Split('-', 'T');
if (parts.Length != 3) throw new FormatException(nameof(input));
var timeParts = parts[2].Trim('Z').Split(':');
if (timeParts.Length != 3) throw new FormatException(nameof(input));
int hour, minute, second, year, dayOfYear;
if (!int.TryParse(parts[0], out year))
throw new FormatException("Year must be an integer");
if (!int.TryParse(parts[1], out dayOfYear))
throw new FormatException("DayOfYear must be an integer");
if (!int.TryParse(timeParts[0], out hour))
throw new FormatException("Hour must be an integer");
if (!int.TryParse(timeParts[1], out minute))
throw new FormatException("Minute must be an integer");
if (!int.TryParse(timeParts[2], out second))
throw new FormatException("Second must be an integer");
var maxDayOfYear = new DateTime(year, 12, 31).DayOfYear;
if (year < 1 || year > 9999)
throw new ArgumentOutOfRangeException(
"Year must be greater than zero and less than 10000");
if (dayOfYear < 1 || dayOfYear > maxDayOfYear)
throw new ArgumentOutOfRangeException(
$"DayOfYear must be greater than zero and less than {maxDayOfYear + 1}");
if (hour > 23) throw new ArgumentOutOfRangeException($"Hour must be less than 24");
if (minute > 59) throw new ArgumentOutOfRangeException($"Minute must less than 60");
if (second > 59) throw new ArgumentOutOfRangeException($"Second must less than 60");
return new DateTime(year, 1, 1, hour, minute, second).AddDays(dayOfYear - 1);
}
Upvotes: 2
Reputation: 5763
Based on @AlexK's suggestion, here you go, nice and simple...
using System.Globalization;
using System.Text.RegularExpressions;
private DateTime? ParseDayOfYearDate(string value)
{
DateTime? result = null;
Regex dayOfYearDatePattern = new Regex(@"^(\d+\-)(\d+)(.+)$");
Match dayOfYearDateMatch = dayOfYearDatePattern.Match(value);
if (dayOfYearDateMatch.Success)
{
string altered = dayOfYearDateMatch.Groups[1].Value + "01-01" + dayOfYearDateMatch.Groups[3].Value;
int dayOfYear = int.Parse(dayOfYearDateMatch.Groups[2].Value); // will succeed due to the definition of the pattern
DateTime startOfYear = DateTime.ParseExact(altered, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);
result = startOfYear.AddDays(dayOfYear - 1); // since we already gave it 1st January
}
else
{
// It didn't match the pattern, will return null.
}
return result;
}
Upvotes: 1
Reputation: 51643
A self written parser could look like this:
static DateTime ToDt(string date)
{
var splitYear = date.Split(new[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
var splitDays = splitYear[1].Split(new[] { 'T' }, StringSplitOptions.RemoveEmptyEntries);
var hms = splitDays[1].TrimEnd('Z').Split(':');
var dt = new DateTime(int.Parse(splitYear[0]), 1, 1, 0, 0, 0);
dt = dt.AddDays(int.Parse(splitDays[0]) - 1);
dt = dt.AddHours(int.Parse(hms[0]));
dt = dt.AddMinutes(int.Parse(hms[1]));
dt = dt.AddSeconds(int.Parse(hms[2]));
return dt;
}
static void Main(string[] args)
{
Console.WriteLine(ToDt("1900-001T00:10:00Z"));
Console.WriteLine(ToDt("1923-180T12:11:10Z"));
Console.WriteLine(ToDt("1979-365T23:59:59Z"));
Console.WriteLine(ToDt("2017-074T18:47:10Z"));
Console.ReadLine();
}
Output:
01.01.1900 00:10:00
29.06.1923 12:11:10
31.12.1979 23:59:59
15.03.2017 18:47:10
This will throw if
IndexOutOfRangeException
FormatException
and int won't guard against "nonsensical but wellformed" inputs
'2000-999T99:99:99Z' --> 29.09.2002 04:40:39
Upvotes: 2