{"record":{"id":"d3053e9b4ee6bd13","repo":"briannesbitt/Carbon","slug":"invalid-iso-8601-specification-iso","errorCode":null,"errorMessage":"Invalid ISO 8601 specification: {$iso}.","messagePattern":"Invalid ISO 8601 specification: (.+?)\\.","errorType":"exception","errorClass":"InvalidPeriodParameterException","httpStatus":null,"severity":"error","filePath":"src/Carbon/CarbonPeriod.php","lineNumber":547,"sourceCode":"    {\n        $result = [];\n\n        $interval = null;\n        $start = null;\n        $end = null;\n        $dateClass = static::DEFAULT_DATE_CLASS;\n\n        foreach (explode('/', $iso) as $key => $part) {\n            if ($key === 0 && preg_match('/^R(\\d*|INF)$/', $part, $match)) {\n                $parsed = \\strlen($match[1]) ? (($match[1] !== 'INF') ? (int) $match[1] : INF) : null;\n            } elseif ($interval === null && $parsed = self::makeInterval($part)) {\n                $interval = $part;\n            } elseif ($start === null && $parsed = $dateClass::make($part)) {\n                $start = $part;\n            } elseif ($end === null && $parsed = $dateClass::make(static::addMissingParts($start ?? '', $part))) {\n                $end = $part;\n            } else {\n                throw new InvalidPeriodParameterException(\"Invalid ISO 8601 specification: $iso.\");\n            }\n\n            $result[] = $parsed;\n        }\n\n        return $result;\n    }\n\n    /**\n     * Add missing parts of the target date from the source date.\n     */\n    protected static function addMissingParts(string $source, string $target): string\n    {\n        $pattern = '/'.preg_replace('/\\d+/', '[0-9]+', preg_quote($target, '/')).'$/';\n\n        $result = preg_replace($pattern, $target, $source, 1, $count);\n\n        return $count ? $result : $target;","sourceCodeStart":529,"sourceCodeEnd":565,"githubUrl":"https://github.com/briannesbitt/Carbon/blob/b13f05955dcfd7da71d60af745fd148cbdb73505/src/Carbon/CarbonPeriod.php#L529-L565","documentation":"Strings passed to CarbonPeriod::create()/createFromISO() are treated as ISO 8601 repetition specs split on '/': an optional leading R<n>/RINF part, then at most interval, start and end segments, each of which must parse as a CarbonInterval or a date (the end may borrow missing parts from the start via addMissingParts()). When a segment cannot fill the next expected slot — too many segments, or one that is neither a valid interval nor a parseable date — the whole spec is rejected (src/Carbon/CarbonPeriod.php:547).","triggerScenarios":"CarbonPeriod::create('R4/2021-01-01/P1D/extra') (a 5th segment has no slot); CarbonPeriod::create('hello/world') ('world' is neither interval nor date); CarbonPeriod::create('P1D/not-a-date'); US m/d/Y dates like '01/31/2021' injected as a segment producing empty or unparseable parts.","commonSituations":"User-supplied recurrence strings (API query params, scheduled-task config); concatenating variables where one is empty and yields '//' segments; mis-remembering the spec order and putting the interval last after both dates.","solutions":["Pass the pieces as separate arguments instead of one ISO string: CarbonPeriod::create($start, 'P1D')->setRecurrences(4)","Fix the spec layout: R<n>/<start>/<interval>/<end>, at most 4 segments, dates before a trailing interval are fine","Validate at the boundary: regex-check the shape, then try CarbonInterval::make()/Carbon::hasFormat() per segment and reject bad input early"],"exampleFix":"// before\n$period = CarbonPeriod::create('R4/2021-01-01/P1D/nope');\n\n// after\n$period = CarbonPeriod::create('2021-01-01', 'P1D')->setRecurrences(4);\n// or a corrected spec\n$period = CarbonPeriod::create('R4/2021-01-01/P1D');","handlingStrategy":"validation","validationCode":"function isValidIsoSpec(string $iso): bool\n{\n    $parts = explode('/', $iso);\n    if (\\count($parts) > 4) {\n        return false;\n    }\n    $i = 0;\n    if (preg_match('/^R(\\d*|INF)$/', $parts[0])) {\n        $i = 1;\n    }\n    for (; $i < \\count($parts); $i++) {\n        $part = $parts[$i];\n        if (CarbonInterval::make($part) === null && Carbon::hasFormat($part, 'Y-m-d\\TH:i:s') === false && Carbon::make($part) === null) {\n            return false;\n        }\n    }\n    return true;\n}\n\nif (!isValidIsoSpec($iso)) {\n    throw new InvalidArgumentException(\"Bad ISO 8601 period spec: $iso\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    $period = CarbonPeriod::createFromISO8601String($iso);\n} catch (\\Carbon\\Exceptions\\InvalidPeriodParameterException $e) {\n    // fall back to explicit construction\n    $period = CarbonPeriod::create($defaultStart, 'P1D', $defaultEnd);\n}","preventionTips":["Treat ISO period specs as a strict external format: validate shape and segment count before use","Prefer multi-argument create($start, $interval, $end) whenever the pieces are separate variables","Add unit tests covering malformed specs from user input"],"tags":["carbon","carbon-period","iso8601","string-parsing","date-range"],"backgroundTag":"iso8601-parse-error","analyzedSha":"b13f05955dcfd7da71d60af745fd148cbdb73505","analyzedAt":"2026-08-17T04:40:56.953Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}