0

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();
        }
    }
}
1
  • 1
    What you've got looks like the right path to a solution, however I might just split the entire string on every 4th space, then convert the elements in the resulting array to hours based on their contents and subtract from DateTime.Now. Commented May 25, 2016 at 20:21

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

I don't have access to an account that can query the jenkins results. I do however have access to the HTML page that is generated by jenkins and is posted on an internal web server. For this question, I have a workable solution (see the code above) I just am wondering how/if I can improve the code to make it easier to maintain.
1

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();
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.