1
0
Fork 0
mirror of https://github.com/miniflux/v2.git synced 2025-08-06 17:41:00 +00:00

refactor(processor): parse ~ISO8601 in a proper way

Instead of using an ugly (and incomplete) regex, let's use a simple for-loop to
parse ISO8601 dates, and make it explicit that we're only supporting a subset
of the spec, as we only care about youtube video durations.
This commit is contained in:
jvoisin 2025-07-07 00:24:55 +02:00 committed by Frédéric Guillot
parent 24043ece07
commit b48e6472f5

View file

@ -6,53 +6,56 @@ package processor // import "miniflux.app/v2/internal/reader/processor"
import ( import (
"errors" "errors"
"fmt" "fmt"
"regexp"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/tdewolff/minify/v2" "github.com/tdewolff/minify/v2"
"github.com/tdewolff/minify/v2/html" "github.com/tdewolff/minify/v2/html"
) )
// TODO: use something less horrible than a regex to parse ISO 8601 durations. // parseISO8601 parses a restricted subset of ISO8601 dates, mainly for youtube video durations
var (
iso8601Regex = regexp.MustCompile(`^P((?P<year>\d+)Y)?((?P<month>\d+)M)?((?P<week>\d+)W)?((?P<day>\d+)D)?(T((?P<hour>\d+)H)?((?P<minute>\d+)M)?((?P<second>\d+)S)?)?$`)
)
func parseISO8601(from string) (time.Duration, error) { func parseISO8601(from string) (time.Duration, error) {
var match []string after, ok := strings.CutPrefix(from, "PT")
if !ok {
return 0, errors.New("the period doesn't start with PT")
}
var d time.Duration var d time.Duration
num := ""
if iso8601Regex.MatchString(from) { for _, char := range after {
match = iso8601Regex.FindStringSubmatch(from) var val float64
} else { var err error
return 0, errors.New("processor: could not parse duration string")
}
for i, name := range iso8601Regex.SubexpNames() { switch char {
part := match[i] case 'Y', 'W', 'D':
if i == 0 || name == "" || part == "" { return 0, fmt.Errorf("the '%c' specifier isn't supported", char)
continue case 'H':
} if val, err = strconv.ParseFloat(num, 64); err != nil {
val, err := strconv.ParseInt(part, 10, 64)
if err != nil {
return 0, err return 0, err
} }
switch name {
case "hour":
d += time.Duration(val) * time.Hour d += time.Duration(val) * time.Hour
case "minute": num = ""
case 'M':
if val, err = strconv.ParseFloat(num, 64); err != nil {
return 0, err
}
d += time.Duration(val) * time.Minute d += time.Duration(val) * time.Minute
case "second": num = ""
case 'S':
if val, err = strconv.ParseFloat(num, 64); err != nil {
return 0, err
}
d += time.Duration(val) * time.Second d += time.Duration(val) * time.Second
num = ""
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.':
num += string(char)
continue
default: default:
return 0, fmt.Errorf("processor: unknown field %s", name) return 0, errors.New("invalid character in the period")
} }
} }
return d, nil return d, nil
} }