class Duration implements Cloneable, Serializable, Comparable<Duration>, JAXBable<JAXBDuration> {

    @SuppressWarnings("PMD.PreserveStackTrace")
    public static Duration fromISO8601String(String input) throws ParseException
    {
        if (input.length() < 3 || input.charAt(0) != 'P')
        {
            throw new ParseException("Input string is too short or does not start with 'P' [" + input + "]", 0);
        }
        Duration duration = null;
        // true when we get to the time section
        boolean foundT = false;
        for (int index = 1; index < input.length(); ++index)
        {
            int start = index;
            while (index < input.length() && (input.charAt(index) == '.' || Character.isDigit(input.charAt(index))))
            {
                ++index;
            }

            if (index == input.length())
            {
                throw new ParseException("Input string does not end with a duration specifier [" + input + "]", index);
            }
            else if (input.charAt(index) == 'T')
            {
                if (index != start || foundT)
                {
                    throw new ParseException(
                            "Missing duration specifier or extra 'T' found at index " + index + " for string [" + input + "]",
                            index);
                }
                foundT = true;
            }
            else
            {
                BigDecimal magnitude;
                try
                {
                    magnitude = new BigDecimal(input.substring(start, index));
                }
                catch (NumberFormatException e)
                {
                    if (LOGGER.isDebugEnabled())
                    {
                        LOGGER.debug(e, e);
                    }
                    throw new ParseException("Could not parse magnitude at index " + index + " for string [" + input + "]",
                            index);
                }

                Duration thisDur;
                switch (input.charAt(index))
                {
                    case 'Y':
                        thisDur = new Years(magnitude);
                        break;
                    case 'M':
                        thisDur = foundT ? new Minutes(magnitude) : new Months(magnitude);
                        break;
                    case 'W':
                        thisDur = new Weeks(magnitude);
                        break;
                    case 'D':
                        thisDur = new Days(magnitude);
                        break;
                    case 'H':
                        thisDur = new Hours(magnitude);
                        break;
                    case 'S':
                        thisDur = new Seconds(magnitude);
                        break;
                    default:
                        throw new ParseException("Unrecognized code at index " + index + " for string [" + input + "]", index);
                }
                try
                {
                    duration = duration == null ? thisDur : duration.add(thisDur);
                }
                catch (InconvertibleUnits e)
                {
                    throw new UnsupportedOperationException(
                            "Cannot represent a duration that adds " + thisDur + " to " + duration, e);
                }
            }
        }

        return duration;
    }

    public  Duration(BigDecimal magnitude);
    protected  Duration(long unscaledMagnitude, int scale);

}