/* -----------------------------------------------------
*
* File: TimeSpanParser.cs
* Author: sven.kopacz@keysight.com
* Created: 21.10.2016
*
* -----------------------------------------------------
*
* Description: See class summary
*
* -----------------------------------------------------
*/
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace OpenTap
{
///
/// A time span parser can parse a text input (which would typically be a user input) for
/// timespan information like "1 hour 2 minutes" or "1 week 250ms" and sum them all up into a
/// TimeSpan. Currently only English units are supported.
///
public static class TimeSpanParser
{
///
/// Parses a string for TimeSpan Information like "1 week 2 days" or "2h12m3s". Currently only
/// English units are accepted. Note that "month" is not a valid unit, since it does not
/// correspond to a determined number of days and so is "year" because of leap years. However you
/// can have both units parsed to hardwired values using the parameter.
/// Note that Microseconds are accepted, but the .NET TimeSpan only resolves milliseconds. Thus only
/// multiples of thousands of Microseconds will affect the result, e.g. "2000µs", when reformatted, will
/// evaluate to "2ms" whereas "200µs" will simply return TimeSpan.Zero.
///
///
/// Thrown when one or more arguments have unsupported or
/// illegal values.
/// Thrown when a unit is unknown.
///
/// The input string.
/// The acceptable number styles.
/// The culture for number parsing. If omitted, DefaultThreadCurrentCulture is chosen.
/// true to accept months (as 30 days) and years (as 356
/// days).
///
/// A TimeSpan.
public static TimeSpan Parse(string input, NumberStyles numberParseStyles = NumberStyles.Any, CultureInfo numberParseCulture = null, bool acceptIndeterminate = false)
{
TimeSpan result = TimeSpan.Zero;
var csplit = input.Split(':');
if (csplit.Length > 1)
{ // Parse superbrief format: (dd:)(HH:)mm:ss(.fff)
// TimeSpan.Parse does not work, because it misinterprets 24:0:0 as 24 days.
if (csplit.Length > 4)
throw new FormatException("Invalid TimeSpan format.");
double[] multipliers = new double[] { 24 * 60 * 60, 60 * 60, 60, 1 };
var last = csplit[csplit.Length - 1];
double frac = 0.0;
if (last.Contains('.'))
{
var fsplit = last.Split('.');
if (fsplit.Length == 2)
{
csplit[csplit.Length - 1] = fsplit[0];
fsplit[1] = fsplit[1].Trim();
int decimals = fsplit[1].Length;
frac = double.Parse(fsplit[1]) / Math.Pow(10, decimals);
}
}
int j = multipliers.Length - 1;
for(int i = csplit.Length - 1; i >= 0; i--)
{
frac += multipliers[j] * int.Parse(csplit[i]);
j--;
}
return Time.FromSeconds(frac);
}
List> entries = NumberUnitSplitter.Split(input, numberParseStyles, numberParseCulture);
foreach (Tuple valueUnitPair in entries)
{
switch (valueUnitPair.Item2.ToLower())
{
case "µs":
case "µsec":
case "µsecond":
case "µseconds":
case "microsec":
case "microsecond":
case "microseconds":
result += Time.FromSeconds(valueUnitPair.Item1 / 1E6);
break;
case "ms":
case "millisec":
case "millisecond":
case "milliseconds":
result += Time.FromSeconds(valueUnitPair.Item1 / 1e3);
break;
case "":
case "s":
case "ss":
case "sec":
case "second":
case "seconds":
result += Time.FromSeconds(valueUnitPair.Item1);
break;
case "m":
case "mm":
case "min":
case "minute":
case "minutes":
result += Time.FromSeconds(valueUnitPair.Item1 * 60);
break;
case "h":
case "hh":
case "hour":
case "hours":
result += Time.FromSeconds(valueUnitPair.Item1 * 60 * 60);
break;
case "d":
case "dd":
case "day":
case "days":
result += Time.FromSeconds(valueUnitPair.Item1 * 60 * 60 * 24);
break;
case "w":
case "week":
case "weeks":
result += Time.FromSeconds(valueUnitPair.Item1 * 60 * 60 * 24 * 7);
break;
case "month":
case "months":
if (acceptIndeterminate)
result += Time.FromSeconds(valueUnitPair.Item1 * 60 * 60 * 24 * 30);
else
throw new ArgumentException("Unit 'month' is indeterminate");
break;
case "y":
case "year":
case "years":
if (acceptIndeterminate)
result += Time.FromSeconds(valueUnitPair.Item1 * 60 * 60 * 24 * 365);
else
throw new ArgumentException("Unit 'year' is indeterminate");
break;
default:
throw new FormatException("Unknown Unit: '" + valueUnitPair.Item2 + "'");
}
}
return result;
}
}
}