C# 如何将时间 [ms, s, m, h, d] 作为字符串转换为秒作为 int

C# How to convert time [ms, s, m, h, d] as string to seconds as int

我想知道是否有一种快速的方法(可能使用 TimeSpan)将持续时间格式的字符串(如 17.24s20.79m1.3h 转换为秒格式,如17 代表 17.24s1260 代表 20.79m4699 代表 1.3h

感谢您的建议。

Timespan 确实有一个接受字符串参数的 Parse 方法,但它使用的输入格式与您想要的不同。在此处查看更多信息:https://docs.microsoft.com/en-us/dotnet/api/system.timespan.parse?view=netcore-3.1

您必须创建自己的解析方法以将字符串输入转换为 Timespan 并使用 TotalSeconds 属性 获取秒数。

如果你走这条路,我建议首先编写一个单元测试,其中包含所有被认为有效的输入及其预期结果。从那里你可以实现你的解析方法并验证它。

namespace Whosebug
{
    public class Tests
    {
        [Fact]
        public void TestTimespanStringConversion()
        {
            // Test cases go here...
            Assert.Equal(TimeSpan.FromHours(1.2), "1.20h".AsTimeSpan())
        }
    }

    public static class StringExtensions
    {
        public static TimeSpan AsTimeSpan(this string input)
        {
            // Conversion logic goes here...
            return new TimeSpan();
        }
    }
}

让我们从数学开始:

  20.79 minutes == 20.79 * 60 seconds == 1247 seconds
    1.3 hours   == 1.3 * 3600 seconds == 4680 seconds

我看不到 12604699,这就是为什么我会坚持简单的数学,我会用 \TODO: 标记应该修改的代码,如果你坚持不同的逻辑。

对于不同的后缀,让我们提取模型:

private static Dictionary<string, Func<double, TimeSpan>> s_Builders =
  new Dictionary<string, Func<double, TimeSpan>>(StringComparer.OrdinalIgnoreCase) {
    
    {  "", x => TimeSpan.FromSeconds(x)},
    { "s", x => TimeSpan.FromSeconds(x)},
    //TODO: if you insist on 1260, put required logic here
    { "m", x => TimeSpan.FromMinutes(x)},
    //TODO: if you insist on 4699, put required logic here
    { "h", x => TimeSpan.FromHours(x)},
    { "d", x => TimeSpan.FromDays(x)},
}; 

实施 TryMyParse 方法的时间:

public static bool TryMyParse(string value, out TimeSpan result) {
  result = default;

  if (string.IsNullOrWhiteSpace(value))
    return false;

  string suffix = s_Builders
    .Keys
    .OrderByDescending(key => key.Length)
    .FirstOrDefault(key => value.EndsWith(key, StringComparison.OrdinalIgnoreCase));

  if (null == suffix)
    return false;
  else if (double.TryParse(value.Substring(0, value.Length - suffix.Length), 
                           out double number)) {
    try {
      result = s_Builders[suffix](number);

      return true;
    }
    catch (OverflowException) {
      return false;
    }
    catch (ArgumentException) {
      return false;
    }
  }

  return false;
}

最后,MyParse很简单:

public static TimeSpan MyParse(string value) =>
  TryMyParse(value, out var result)
    ? result
    : throw new FormatException($"{value} is not a valid time");

演示:

  string[] tests = new string[] {
    "17.24s",
    "20.79m", 
    "1.3h",
  };

  string demo = string.Join(Environment.NewLine, tests
    .Select(test => $"{test,6} :: {Math.Round(MyParse(test).TotalSeconds),4}"));

  Console.Write(demo);

结果:

  17.24s ::   17
  20.79m :: 1247
    1.3h :: 4680

谢谢大家的建议!我很感激。这是适合我的解决方案:

    private int timeParser(string pTime) {
        int iResult = 0;
        double dTime = 0.0;
        NumberStyles style = NumberStyles.Number;
        CultureInfo culture = CultureInfo.InvariantCulture;
        
        if(pTime.Contains("ms")) {
            if(Double.TryParse(pTime.Trim().Replace("ms", ""), style, culture, out dTime)) {
                iResult = (int)Math.Round(TimeSpan.FromMilliseconds(dTime).TotalSeconds);
            } else {
                throw new FormatException("Unable to convert " + pTime);
            }
        } else if(pTime.Contains("s")) {
            if(Double.TryParse(pTime.Trim().Replace("s", ""), style, culture, out dTime)) {
                iResult = (int)Math.Round(TimeSpan.FromSeconds(dTime).TotalSeconds);
            } else {
                throw new FormatException("Unable to convert " + pTime);
            }                   
        } else if(pTime.Contains("m")) {
            if(Double.TryParse(pTime.Trim().Replace("m", ""), style, culture, out dTime)) {
                iResult = (int)Math.Round(TimeSpan.FromMinutes(dTime).TotalSeconds);
            } else {
                throw new FormatException("Unable to convert " + pTime);
            }
        } else if(pTime.Contains("h")) {
            if(Double.TryParse(pTime.Trim().Replace("h", ""), style, culture, out dTime)) {
                iResult = (int)Math.Round(TimeSpan.FromHours(dTime).TotalSeconds);
            } else {
                throw new FormatException("Unable to convert " + pTime);
            }
        } else if(pTime.Contains("d")) {
            if(Double.TryParse(pTime.Trim().Replace("d", ""), style, culture, out dTime)) {
                iResult = (int)Math.Round(TimeSpan.FromDays(dTime).TotalSeconds);
            } else {
                throw new FormatException("Unable to convert " + pTime);
            }
        } else {
            throw new FormatException(pTime + " is not a valid timeformat");
        }
        return iResult;
    }