Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extract minutes from time string in "jira notation"

Tags:

c#

timespan

jira

I am trying to extract the number of minutes from user input that is entered in "jira time notation".

For example, I would like to achieve the following results

  • Input: "30m" / Output: 30
  • Input: "1h 20m" / Output: 80
  • Input: "3h" / Output 180

From research, I have found TimeSpan.ParseExact but I can't figure out how to use this to achieve what I need.

All help would be very much appreciated.

My code so far:

public static int TextToMins(string TimeText)
    {
        CultureInfo culture = new CultureInfo("en-IE");
        string[] formats = { "What goes here?" };
        TimeSpan ts = TimeSpan.ParseExact(TimeText.Trim().ToLower(), formats, culture);
        return Convert.ToInt32(ts.TotalMinutes);
    }
like image 784
user1515364 Avatar asked Oct 22 '13 21:10

user1515364


3 Answers

I'd probably do something like this and avoid mucking about with Timespan's built-in parsing, given that days per [work] week and [working] hours per [work] day are configurable values in Jira (On our system they are configured as 5 days per week and 8 hours per day, respectively.)

class JiraTimeDurationParser
{

  /// <summary>
  /// Jira's configured value for [working] days per week ;
  /// </summary>
  public ushort DaysPerWeek     { get ; private set ; }

  /// <summary>
  /// Jira's configured value for [working] hours per day
  /// </summary>
  public ushort HoursPerDay     { get ; private set ; }

  public JiraTimeDurationParser( ushort daysPerWeek = 5 , ushort hoursPerDay = 8 )
  {
    if ( daysPerWeek < 1 || daysPerWeek >  7 ) throw new ArgumentOutOfRangeException( "daysPerWeek"  ) ;
    if ( hoursPerDay < 1 || hoursPerDay > 24 ) throw new ArgumentOutOfRangeException( "hoursPerDay"  ) ;

    this.DaysPerWeek = daysPerWeek ;
    this.HoursPerDay = hoursPerDay ;

    return ;
  }

  private static Regex rxDuration = new Regex( @"
    ^                                   # drop anchor at start-of-line
      [\x20\t]* ((?<weeks>   \d+ ) w )? # Optional whitespace, followed by an optional number of weeks
      [\x20\t]* ((?<days>    \d+ ) d )? # Optional whitesapce, followed by an optional number of days
      [\x20\t]* ((?<hours>   \d+ ) h )? # Optional whitespace, followed by an optional number of hours
      [\x20\t]* ((?<minutes> \d+ ) m )  # Optional whitespace, followed by a mandatory number of minutes
      [\x20\t]*                         # Optional trailing whitespace
    $                                   # followed by end-of-line
    " ,
    RegexOptions.IgnorePatternWhitespace
    ) ;

  public TimeSpan Parse( string jiraDuration )
  {
    if ( string.IsNullOrEmpty( jiraDuration ) ) throw new ArgumentOutOfRangeException("jiraDuration");

    Match m = rxDuration.Match( jiraDuration ) ;
    if ( !m.Success ) throw new ArgumentOutOfRangeException("jiraDuration") ;

    int weeks   ; bool hasWeeks   = int.TryParse( m.Groups[ "weeks"   ].Value , out weeks   ) ;
    int days    ; bool hasDays    = int.TryParse( m.Groups[ "days"    ].Value , out days    ) ;
    int hours   ; bool hasHours   = int.TryParse( m.Groups[ "hours"   ].Value , out hours   ) ;
    int minutes ; bool hasMinutes = int.TryParse( m.Groups[ "minutes" ].Value , out minutes ) ;

    bool isValid = hasWeeks|hasDays|hasHours|hasMinutes ;
    if ( !isValid ) throw new ArgumentOutOfRangeException("jiraDuration") ;

    TimeSpan duration = new TimeSpan( weeks*DaysPerWeek*HoursPerDay + days*HoursPerDay + hours , minutes , 0 );
    return duration ;

  }

  public bool TryParse( string jiraDuration , out TimeSpan timeSpan )
  {
    bool success ;
    try
    {
      timeSpan = Parse(jiraDuration) ;
      success = true ;
    }
    catch
    {
      timeSpan = default(TimeSpan) ;
      success  = false ;
    }
    return success ;
  }

}
like image 145
Nicholas Carey Avatar answered Oct 10 '22 15:10

Nicholas Carey


Bit of a sledgehammer approach, but how about:

public static int TextToMins(string timeText)
{
    var total = 0;
    foreach (var part in timeText.Split(' '))
    {
        if (part[part.Length - 1] == 'h')
        {
            total += 60 * int.Parse(part.Trim('h'));
        }
        else
        {
            total += int.Parse(part.Trim('m'));
        }
    }
    return total;
}
like image 2
David Arno Avatar answered Oct 10 '22 15:10

David Arno


The answer to "what goes here" is, a string built out of the options from Custom Timespan Format Strings. If I'm reading the documentation correctly, characters not in that list -- including whitespace -- have to be escaped with the \ character or surrounded with single quotation marks.

For example, try m\m to parse "1m", and h\h m\m to parse "1h 10m". So your code would be:

string[] formats = { "m\m", "h\h\ m\m" }; 

Caveat: I haven't tried parsing TimeSpan objects. But I have done DateTime objects, and it is very similar. So I think this should work.

like image 2
Katie Kilian Avatar answered Oct 10 '22 13:10

Katie Kilian