如何在 C#/.NET 中解析 ISO 8601 的"PnDTnHnMn.nS"格式?



我在windows控制台应用程序中有一个日期值,比如"P0Y0M0DT23H43M52.103S",我想在c#中解析这个日期时间值,以获得过期的分钟数。使用new java.time.Duration.parse("P0Y0M0DT23H43M52.103S").toMinutes()可以很容易地在Java中完成。

Duration.parse(CharSequence text)的文档中写道:

这将解析持续时间的文本表示,包括toString()生成的字符串。接受的格式基于ISO-8601持续时间格式PnDTnHnMn.nS,天数被视为正好是24小时。

我正在.net c#中寻找类似的功能,它可以帮助我准确地以分钟为单位锻炼时间,而无需循环和拆分。

谢谢

您可以使用我的Noda Time库及其Period类型:

using NodaTime;
using NodaTime.Text;
var parseResult = PeriodPattern.NormalizingIso.Parse("P0Y0M0DT23H43M52.103S");
if (parseResult.Success)
{
Period period = parseResult.Value;
Console.WriteLine($"Success! Parsed to {period}");
}

请注意,即使在Java中,将ISO-8601周期解析为Duration也有些不合适。。。持续时间应该是固定的时间长度,而如果你有一个ISO-8601周期;P1M";,不是一个固定的时间长度(与年相同(。这就是为什么Duration.parse方法要求它只有天、小时、分钟等

使用XmlConvert.ToTimeSpan():

TimeSpan duration = XmlConvert.ToTimeSpan("P0Y0M0DT23H43M52.103S");

此正则表达式将ISO-8601持续时间字符串(例如P1DT2H(解析为命名组,以便于提取。

(?n)^P(?=.)(((?<CY>d+)Y)?((?<CM>d+)M)?((?<CW>d+)W)?((?<CD>d+)D)?)?(T(?=.+)(((?<TH>d+)H)?((?<TM>d+)M)?((?<TS>d+)S)?)?)?$

请参阅下面的PowerShell脚本以了解如何生成此脚本。

PowerShell

此PowerShell脚本使用regex解析ISO8601持续时间,然后将基于日历的时间增量应用于指定日期。

<#
.SYNOPSIS
Adds an ISO-8601 calendar-based duration to a date.
.PARAM Duration
The duration to add
.PARAM Date
The reference date, defaults to Now.
.EXAMPLES 
P1Y: 1 year 
P1Y2M3W4D: 1 year, 2months, 3 weeks, 4 days 
PT5H6M7S:  5 hours, 6 minutes, 7 seconds
P1Y2M3W4DT5H6M7S: 1 year, 2months, 3 weeks, 4 days, 5 hours, 6 minutes, 7 seconds
#>
function Add-Duration {
param (
[Parameter(Mandatory)]
[string]$Duration,
[DateTime]$Date = (Get-Date),
[Switch]$Past
)
# Compute ISO-8601 duration from the anchor
$ReCalendarParts=("YMWD".ToCharArray()  | %{ "((?<C$_>d+)$_)?" }) -join ""
$ReTimeParts=("HMS".ToCharArray()  | %{ "((?<T$_>d+)$_)?" }) -join ""
$ReISO8601P = "(?n)^P(?=.)($ReCalendarParts)?(T(?=.+)($ReTimeParts)?)?$"
$m = ([Regex]$ReISO8601P).Match($Duration)
if (-Not $m.Success) { throw "Unexpected -Duration value $Duration. Expected P30D or similar ISO-8601 duration" }
foreach ($group in $m.Groups) {
if ($group.Name -ne "0" -and $group.Success) {
$interval = [int]$group.Value
if ($Past) { $interval *= -1 }
$Date = switch -exact ($group.Name) {
CY { $Date.AddYears($interval) }
CM { $Date.AddMonths($interval) }
CW { $Date.AddDays($interval * 7) }
CD { $Date.AddDays($interval) }
TH { $Date.AddHours($interval) }
TM { $Date.AddMinutes($interval) }
TS { $Date.AddSeconds($interval) }
default { throw "Unsupported interval $($Group.Name)" }
}
}
}
$Date
}

C#

此C#代码使用regex解析ISO8601持续时间,然后将基于日历的时间增量应用于指定日期。

有关Regex模式的构造方式,请参阅上面的PowerShell示例。


Console.WriteLine(DateTime.Now.AddDuration("PT1H"));
internal static class DateTimeExtensions
{
const string Iso8601DurationPattern = @"(?n)^P(?=.)(((?<CY>d+)Y)?((?<CM>d+)M)?((?<CW>d+)W)?((?<CD>d+)D)?)?(T(?=.+)(((?<TH>d+)H)?((?<TM>d+)M)?((?<TS>d+)S)?)?)?$";
private static readonly Regex Iso8601DurationRegex = new Regex(Iso8601DurationPattern, RegexOptions.Compiled);
public static DateTime AddDuration(this DateTime value, string duration, bool past = false)
{
var match = Iso8601DurationRegex.Match(duration);
if (!match.Success)
{
throw new ArgumentOutOfRangeException(nameof(duration));
}
foreach (Group group in match.Groups)
{
if (group.Success && group.Name != "0")
{
var interval = Int32.Parse(group.Value);
if (past) { interval *= -1; }
value = group.Name switch
{
"CY" => value.AddYears(interval),
"CM" => value.AddMonths(interval),
"CW" => value.AddDays(interval * 7),
"CD" => value.AddDays(interval),
"TH" => value.AddHours(interval),
"TM" => value.AddMinutes(interval),
"TS" => value.AddSeconds(interval),
_ => throw new NotSupportedException("Unsupported duration element $($Group.Name)")
};
}
}
return value;
}
}

相关内容

  • 没有找到相关文章

最新更新