How to Convert ISO 8601 Duration to TimeSpan in VB.Net?
Asked Answered
J

3

16

Is there a standard library method that converts a string that has duration in the standard ISO 8601 Duration (also used in XSD for its duration type) format into the .NET TimeSpan object?

For example, P0DT1H0M0S which represents a duration of one hour, is converted into New TimeSpan(0,1,0,0,0).

A Reverse converter does exist which works as follows: Xml.XmlConvert.ToString(New TimeSpan(0,1,0,0,0)) The above expression will return P0DT1H0M0S.

Jumpy answered 15/9, 2008 at 13:22 Comment(0)
G
27

This will convert from xs:duration to TimeSpan:

System.Xml.XmlConvert.ToTimeSpan("P0DT1H0M0S")

See http://msdn.microsoft.com/en-us/library/system.xml.xmlconvert.totimespan.aspx

Gun answered 15/9, 2008 at 14:9 Comment(2)
-1 because this post lacks a warning about durations with months or yearsOctonary
@Trajan, This conversion does support month durations and year durations without a problem, so I don't think it warrants a downvote. TimeSpan itself has a limitation that it doesn't natively support months and years (converting them to 31 or 365 days), but this is a limitation of the OP's requested destination so you'd have to assume that wasn't a scenario they cared about or they had some other solution for those scenarios (or they discovered it after releasing ;)Cramped
D
20

One minor word of caution - XmlConvert.ToTimeSpan() is a little funny when working with months and years. The TimeSpan class does not have month or year members, probably because their length varies. However, ToTimeSpan() will happily accept a duration string with month or year values in it and guess at a duration, instead of throwing an exception. Observe:

PS C:\Users\troll> [Reflection.Assembly]::LoadWithPartialName("System.Xml")

GAC    Version        Location
---    -------        --------
True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\System.Xml\2.0.0.0__b77a5c561934e089\System.Xml.dll


PS C:\Users\troll> [System.Xml.XmlConvert]::ToTimeSpan("P1M")


Days              : 30
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 0
Ticks             : 25920000000000
TotalDays         : 30
TotalHours        : 720
TotalMinutes      : 43200
TotalSeconds      : 2592000
TotalMilliseconds : 2592000000



PS C:\Users\troll> [System.Xml.XmlConvert]::ToTimeSpan("P1Y")


Days              : 365
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 0
Ticks             : 315360000000000
TotalDays         : 365
TotalHours        : 8760
TotalMinutes      : 525600
TotalSeconds      : 31536000
TotalMilliseconds : 31536000000



PS C:\Users\troll>
Daryl answered 22/4, 2011 at 22:57 Comment(1)
Funfact: Months are weird. P11M => 330 days but P12M => 365 daysOctonary
A
0

As @ima dirty troll said TimeSpan translates always years as 365 days and months as 30 days.

TimeSpan ts = System.Xml.XmlConvert.ToTimeSpan("P5Y");
DateTime now = new DateTime(2008,2,29);
Console.WriteLine(now + ts); // 27/02/2013 0:00:00

To address it you should add each field individually rather than using TimeSpan.

DateTime now = new DateTime (2008, 2, 29);
string duration = "P1Y";
Regex expr = 
    new Regex (@"(-?)P((\d{1,4})Y)?((\d{1,4})M)?((\d{1,4})D)?(T((\d{1,4})H)?((\d{1,4})M)?((\d{1,4}(\.\d{1,3})?)S)?)?", RegexOptions.Compiled | RegexOptions.CultureInvariant);
bool positiveDuration = false == (input [0] == '-');

MatchCollection matches = expr.Matches (duration);
var g = matches [0];
Func<int,int> getNumber = x => {
    if (g.Groups.Count < x || string.IsNullOrEmpty (g.Groups [x].ToString ())) {
        return 0;
    }

    int a = int.Parse (g.Groups [x].ToString ());

    return PositiveDuration ? a : a * -1;

};
now.AddYears (getNumber (3));
now.AddMonths (getNumber (5));
now.AddDays (getNumber (7));
now.AddHours (getNumber (10));
now.AddMinutes (getNumber (12));
now.AddSeconds (getNumber (14));
Console.WriteLine (now); // 28/02/2012 0:00:00
Airdrop answered 12/8, 2012 at 15:52 Comment(1)
There's no rule that says the values have to no larger than 4 digits, by the way. P10000YT20000H is a perfectly valid xs:duration, but your regular expression would not accept it.Breathe

© 2022 - 2024 — McMap. All rights reserved.