Search code examples
perliso8601

Converting ISO8601 duration (P3Y6M...) to seconds in perl


I'm trying to convert an ISO 8601 time duration format, P3Y6M4DT12H30M5S, to seconds in perl. Is there a library that does this? I've tried searching, but I've only found a js library. Looking through DateTime and Time::Moment didn't provide a solution either.


Solution

  • There is DateTime::Format::Duration::ISO8601, which can convert the ISO 8601 duration string into a DateTime::Duration object. You can then convert that to seconds.

    use DateTime::Duration;
    use DateTime::Format::Duration::ISO8601;
    
    my $format = DateTime::Format::Duration::ISO8601->new;
    my $d = $format->parse_duration('P3Y6M4DT12H30M5S');
    

    However, the DateTime::Duration format cannot be used to convert a year to seconds, as explained in the docs here.

    The last example demonstrates that there will not be any conversion between units which don't have a fixed conversion rate. The only conversions possible are:

    years <=> months
    weeks <=> days
    hours <=> minutes
    seconds <=> nanoseconds
    

    For the explanation of why this is the case, please see the How DateTime Math Works section of the DateTime.pm documentation

    You can use DateTime::Format::Duration with the %s pattern to circumvent that. A complete implementation might look like this.

    use DateTime::Duration;
    use DateTime::Format::Duration::ISO8601;
    use DateTime::Format::Duration;
    
    my $format = DateTime::Format::Duration::ISO8601->new;
    my $d      = $format->parse_duration('P3Y6M4DT12H30M5S');
    
    my $output_format = DateTime::Format::Duration->new( pattern => '%s' );
    print $output_format->format_duration($d);
    

    Or, in short if you only need it once.

    use DateTime::Duration;
    use DateTime::Format::Duration::ISO8601;
    use DateTime::Format::Duration;
    
    print DateTime::Format::Duration->new( pattern => '%s' )
        ->format_duration(
        DateTime::Format::Duration::ISO8601->new->parse_duration('P3Y6M4DT12H30M5S') );
    

    Both of these will print

    109254605