Reputation: 471
I have the following list of times from today for our jenkins builds, and I want to parse this list and return the date\time of the build.
List (Time since build)
-----------------------
1 day 21 hr
1 day 22 hr
1 mo 14 days
1 mo 14 days
27 days
1 day 6 hr
1 mo 14 days
6 min 13 sec
For example: 1 day 21 hr --> Time from today should return 05/22/2016 09:00:00PM
I put together the following regex version...but it feels very hacky and brittle.
How can I parse this text better.
using System;
using System.Text.RegularExpressions;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string txt = "1 day 21 hr";
string regexday = "(\\d\\s+day)"; // 1 day
string regexdaymultiple = "(\\d\\d\\s+day)"; //10 days
string regexhr = "(\\s+\\d\\s+hr)"; // 1 hr
string regexhrmultiple = "(\\s+\\d\\d\\s+hr)"; // 21 hr
Regex regexdaymatch = new Regex(regexday, RegexOptions.IgnoreCase | RegexOptions.Singleline);
Match matchday = regexdaymatch.Match(txt);
if (matchday.Success)
{
String d1 = matchday.Groups[1].ToString();
DateTime now = DateTime.Now;
Console.Write("("+now.AddDays(-Convert.ToInt32(d1.Replace(" day", "")))+")" + "\n");
}
Regex regexdaymutliplesmatch = new Regex(regexdaymultiple, RegexOptions.IgnoreCase | RegexOptions.Singleline);
Match matchdaymultiple = regexdaymutliplesmatch.Match(txt);
if (matchdaymultiple.Success)
{
String d1 = matchdaymultiple.Groups[1].ToString();
DateTime now = DateTime.Now;
Console.Write("(" + now.AddDays(-Convert.ToInt32(d1.Replace(" day", ""))) + ")" + "\n");
}
Regex regexhrmatch = new Regex(regexhr, RegexOptions.IgnoreCase | RegexOptions.Singleline);
Match matchhr = regexhrmatch.Match(txt);
if (matchhr.Success)
{
String d1 = matchhr.Groups[1].ToString();
DateTime now = DateTime.Now;
Console.Write("(" + now.AddHours(-Convert.ToInt32(d1.Replace(" hr", ""))) + ")" + "\n");
}
Regex regexhrmultiplematch = new Regex(regexhrmultiple, RegexOptions.IgnoreCase | RegexOptions.Singleline);
Match matchhrmultiple = regexhrmultiplematch.Match(txt);
if (matchhrmultiple.Success)
{
String d1 = matchhrmultiple.Groups[1].ToString();
DateTime now = DateTime.Now;
Console.Write("(" + now.AddHours(-Convert.ToInt32(d1.Replace(" hr", ""))) + ")" + "\n");
}
Console.ReadLine();
}
}
}
Upvotes: 0
Views: 167
Reputation: 5660
Here's a class that doesn't use Regex. If you are very familiar with Regex then your solution works fine.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
public class TimeSinceBuildsReader
{
public virtual IReadOnlyList<DateTime> TimesWhenBuildsOccurred(string timeSinceBuildsFilePath)
{
if (!File.Exists(timeSinceBuildsFilePath))
{
return new List<DateTime>();
}
var lines = File.ReadAllLines(timeSinceBuildsFilePath);
var list = new List<DateTime>(lines.Length);
var now = DateTime.Now;
foreach (var line in lines)
{
var split = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select(l => l.Trim()).ToArray();
if (split.Length % 2 != 0)
{
continue;
}
switch (split.Length)
{
case 2:
list.Add(now.Subtract(getTimePassed(now, split[0], split[1])));
break;
case 4:
var firstDuration = getTimePassed(now, split[0], split[1]);
var secondDuration = getTimePassed(now, split[2], split[3]);
list.Add(now.Subtract(firstDuration).Subtract(secondDuration));
break;
}
}
return list;
}
private static TimeSpan getTimePassed(DateTime now, string number, string durationType)
{
var num = int.Parse(number);
if (durationType.Contains("month") || durationType.Contains("mo"))
{
var numberOfDays = now.DayOfYear - now.AddMonths(num * -1).DayOfYear;
return TimeSpan.FromDays(numberOfDays);
}
if (durationType.Contains("day"))
{
return TimeSpan.FromDays(num);
}
if (durationType.Contains("hour") || durationType.Contains("hr"))
{
return TimeSpan.FromHours(num);
}
if (durationType.Contains("minute") || durationType.Contains("min"))
{
return TimeSpan.FromMinutes(num);
}
if (durationType.Contains("second") || durationType.Contains("sec"))
{
return TimeSpan.FromSeconds(num);
}
throw new NotImplementedException("Could not parse duration type from input " + durationType);
}
}
The method is virtual to allow a fake of this class to be created by e.g. FakeItEasy and injected as a dependency into another class (in case you wish to extend your console app).
Here is the modified Main
method:
private static void Main()
{
foreach (var timestamp in new TimeSinceBuildsReader().TimesWhenBuildsOccurred("time-since-builds.txt"))
{
Console.WriteLine(timestamp);
}
Console.ReadKey();
}
Upvotes: 1
Reputation: 6876
This sounds to me like your parsing the HTML presented for the build and trying to work backwards to the time. I feel that this approach is flawed. I'd recommend researching the Jenkins API and pulling your data that way.
https://media.readthedocs.org/pdf/jenkinsapi/latest/jenkinsapi.pdf
Seems the api returns the data in proper timestamps.
Upvotes: 1