TimeSpan.ParseExact 有超过 59 秒的解决方法吗?
Any workaround to TimeSpan.ParseExact with more than 59 seconds?
我正在开发一个跟踪时间的应用程序,用户必须能够根据需要设置应用程序时间格式,例如:
"ss:fff" (seconds:miliseconds)
"ss\s\. fff mils\." (seconds s. miliseconds mils.)
"dd:hh:mm" (days:hours:minutes)
etc...
我存储的时间尽可能长,所以通过简单的 TimeSpan 格式,我可以用用户配置的格式显示它们,简单且实际工作。
当我开始实现“手动”时间添加时出现了问题(用户在 TextBox 中键入一个新时间,它以配置的时间格式添加到时间列表中)。
就在用户引入新时间后,我必须将引入的时间从字符串转换为长整数,目的是存储它(TimeSpan.TryParseExact 提供配置的时间格式即可),除了一个问题: 如果我们有像 mm:ss 这样的格式并且解析时间是 90:32,解析失败,因为解析时间不应该 > 59 分钟。
我制作了一个小型控制台应用程序示例来帮助重现我的问题:
static void Main(string[] args)
{
string TimeFormat = @"ss\:fff";
long[] SampleTimes = new long[] { 1000, 5000, 59666 };
List<long> times = new List<long>(SampleTimes);
string input;
long aux;
do
{
ShowTimes(times, TimeFormat);
Console.Write(">");
input = Console.ReadLine();
if (TryParseTime(input, TimeFormat, out aux))
times.Add(aux);
else
Console.WriteLine("Failed parsing");
} while (input != "Exit");
}
static void ShowTimes(IEnumerable<long> times, string format)
{
Console.WriteLine("-----");
foreach (long time in times)
Console.WriteLine(TimeSpan.FromMilliseconds(time).ToString(format));
Console.WriteLine("-----");
}
static bool TryParseTime(string time, string format, out long parsed)
{
TimeSpan result;
bool ok = TimeSpan.TryParseExact(time, format, null, out result);
parsed = ok ? (long)result.TotalMilliseconds : -1;
return ok;
}
在另一篇文章 [[=14=]] 中引用了相同的问题,他们解决了这个问题,将引入时间的各个部分分开并通过代码计算:
//From first post
var temp = "113388";
s_Time = DateTime.ParseExact(temp.Substring(0, 4
), "HHmm", null).AddSeconds(int.Parse(temp.Substring(4)));
//From second post
public static decimal Hours(string s)
{
decimal r;
if (decimal.TryParse(s, out r))
return r;
var parts = s.Split(':');
return (decimal)new TimeSpan(int.Parse(parts[0]), int.Parse(parts[1]),0).TotalHours;
}
但是我无法按照这种方式进行,因为没有时间格式可以作为拆分介绍的时间格式的参考,它可以随时更改。
此时我唯一的想法是创建一个TimeSpan.TryParseExact扩展方法,通过正则表达式获取最大的时间单位并通过单独的解析...
有更好的方法吗?
我觉得很合理;让您的用户输入如下字符串:
T-{hour}h{min}m{sec}s [baby!]
用正则表达式转义它,然后用字符串替换它(例如"{hour}"
-> "(?<h>\d+)"
)成为正则表达式:
T-(?<h>\d+)h(?<m>\d+)m(?<s>\d+)s \[baby!\]
还有字符串替换成时间跨度输出格式:
'T-'hh'h'mm'm'ss's [baby!]'
然后你有你的捕获组..他们可以输入他们奇怪的时间格式,你可以解析它,你可以输出它..
好的,我最终使用了这个自定义方法来完成这项工作。
不是连续执行很多次的方法,因为它会有巨大的性能问题,但是从前端解析引入的数据是可以接受的:
/// <summary>
/// Given a time and a format it creates a <see cref="TimeSpan"/> ignoring the format digit limitations.
/// The format is not validated, so better ensure a correct one is provided ;)
/// </summary>
/// <param name="time"></param>
/// <param name="format"></param>
/// <param name="timeSpan"></param>
/// <returns></returns>
public static bool TryParseTime(string time, string format, out TimeSpan timeSpan)
{
// Regex to match the components of the time format (ss:fff matches ss and fff)
var formatRegex = new Regex(@"(?<=(?<!\)(?:\{2})*)(%?([fFsmhd])(*))");
var matches = formatRegex.Matches(format);
if (matches.Count > 0)
{
// We build a big regex to extract the values from time
string formatExtractionRegex = string.Empty;
int pivot = 0;
foreach (Match match in matches)
{
if (match.Success)
{
char c = match.Value.ToLower()[0];
formatExtractionRegex += $@"{format.Substring(pivot, match.Index - pivot)}(?<{c}>\d+)";
pivot = match.Index + match.Length;
}
}
var timeParts = new Regex(formatExtractionRegex).Match(time);
int d, h, m, s, f;
int.TryParse(timeParts.Groups["d"].ToString(), out d);
int.TryParse(timeParts.Groups["h"].ToString(), out h);
int.TryParse(timeParts.Groups["m"].ToString(), out m);
int.TryParse(timeParts.Groups["s"].ToString(), out s);
int.TryParse(timeParts.Groups["f"].ToString(), out f);
timeSpan = new TimeSpan(d, h, m, s, f);
return true;
}
timeSpan = default;
return false;
}
该方法通过构建一个替换正则表达式 \d+
的数字类型的大正则表达式从时间中提取数据,因此我们 select 整个数字组比格式指定。
如果我们提供时间 100:100:5000
和格式 mm\:ss\:fff
,生成的正则表达式将为 (?<m>\d+)\:(?<s>\d+)\:(?<f>\d+)
.
最后我们解析匹配的组,我们解析它们以提供给 TimeSpan Constructor。
我正在开发一个跟踪时间的应用程序,用户必须能够根据需要设置应用程序时间格式,例如:
"ss:fff" (seconds:miliseconds)
"ss\s\. fff mils\." (seconds s. miliseconds mils.)
"dd:hh:mm" (days:hours:minutes)
etc...
我存储的时间尽可能长,所以通过简单的 TimeSpan 格式,我可以用用户配置的格式显示它们,简单且实际工作。
当我开始实现“手动”时间添加时出现了问题(用户在 TextBox 中键入一个新时间,它以配置的时间格式添加到时间列表中)。
就在用户引入新时间后,我必须将引入的时间从字符串转换为长整数,目的是存储它(TimeSpan.TryParseExact 提供配置的时间格式即可),除了一个问题: 如果我们有像 mm:ss 这样的格式并且解析时间是 90:32,解析失败,因为解析时间不应该 > 59 分钟。
我制作了一个小型控制台应用程序示例来帮助重现我的问题:
static void Main(string[] args)
{
string TimeFormat = @"ss\:fff";
long[] SampleTimes = new long[] { 1000, 5000, 59666 };
List<long> times = new List<long>(SampleTimes);
string input;
long aux;
do
{
ShowTimes(times, TimeFormat);
Console.Write(">");
input = Console.ReadLine();
if (TryParseTime(input, TimeFormat, out aux))
times.Add(aux);
else
Console.WriteLine("Failed parsing");
} while (input != "Exit");
}
static void ShowTimes(IEnumerable<long> times, string format)
{
Console.WriteLine("-----");
foreach (long time in times)
Console.WriteLine(TimeSpan.FromMilliseconds(time).ToString(format));
Console.WriteLine("-----");
}
static bool TryParseTime(string time, string format, out long parsed)
{
TimeSpan result;
bool ok = TimeSpan.TryParseExact(time, format, null, out result);
parsed = ok ? (long)result.TotalMilliseconds : -1;
return ok;
}
在另一篇文章 [[=14=]] 中引用了相同的问题,他们解决了这个问题,将引入时间的各个部分分开并通过代码计算:
//From first post
var temp = "113388";
s_Time = DateTime.ParseExact(temp.Substring(0, 4
), "HHmm", null).AddSeconds(int.Parse(temp.Substring(4)));
//From second post
public static decimal Hours(string s)
{
decimal r;
if (decimal.TryParse(s, out r))
return r;
var parts = s.Split(':');
return (decimal)new TimeSpan(int.Parse(parts[0]), int.Parse(parts[1]),0).TotalHours;
}
但是我无法按照这种方式进行,因为没有时间格式可以作为拆分介绍的时间格式的参考,它可以随时更改。
此时我唯一的想法是创建一个TimeSpan.TryParseExact扩展方法,通过正则表达式获取最大的时间单位并通过单独的解析...
有更好的方法吗?
我觉得很合理;让您的用户输入如下字符串:
T-{hour}h{min}m{sec}s [baby!]
用正则表达式转义它,然后用字符串替换它(例如"{hour}"
-> "(?<h>\d+)"
)成为正则表达式:
T-(?<h>\d+)h(?<m>\d+)m(?<s>\d+)s \[baby!\]
还有字符串替换成时间跨度输出格式:
'T-'hh'h'mm'm'ss's [baby!]'
然后你有你的捕获组..他们可以输入他们奇怪的时间格式,你可以解析它,你可以输出它..
好的,我最终使用了这个自定义方法来完成这项工作。
不是连续执行很多次的方法,因为它会有巨大的性能问题,但是从前端解析引入的数据是可以接受的:
/// <summary>
/// Given a time and a format it creates a <see cref="TimeSpan"/> ignoring the format digit limitations.
/// The format is not validated, so better ensure a correct one is provided ;)
/// </summary>
/// <param name="time"></param>
/// <param name="format"></param>
/// <param name="timeSpan"></param>
/// <returns></returns>
public static bool TryParseTime(string time, string format, out TimeSpan timeSpan)
{
// Regex to match the components of the time format (ss:fff matches ss and fff)
var formatRegex = new Regex(@"(?<=(?<!\)(?:\{2})*)(%?([fFsmhd])(*))");
var matches = formatRegex.Matches(format);
if (matches.Count > 0)
{
// We build a big regex to extract the values from time
string formatExtractionRegex = string.Empty;
int pivot = 0;
foreach (Match match in matches)
{
if (match.Success)
{
char c = match.Value.ToLower()[0];
formatExtractionRegex += $@"{format.Substring(pivot, match.Index - pivot)}(?<{c}>\d+)";
pivot = match.Index + match.Length;
}
}
var timeParts = new Regex(formatExtractionRegex).Match(time);
int d, h, m, s, f;
int.TryParse(timeParts.Groups["d"].ToString(), out d);
int.TryParse(timeParts.Groups["h"].ToString(), out h);
int.TryParse(timeParts.Groups["m"].ToString(), out m);
int.TryParse(timeParts.Groups["s"].ToString(), out s);
int.TryParse(timeParts.Groups["f"].ToString(), out f);
timeSpan = new TimeSpan(d, h, m, s, f);
return true;
}
timeSpan = default;
return false;
}
该方法通过构建一个替换正则表达式 \d+
的数字类型的大正则表达式从时间中提取数据,因此我们 select 整个数字组比格式指定。
如果我们提供时间 100:100:5000
和格式 mm\:ss\:fff
,生成的正则表达式将为 (?<m>\d+)\:(?<s>\d+)\:(?<f>\d+)
.
最后我们解析匹配的组,我们解析它们以提供给 TimeSpan Constructor。