<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def string_to_timestamp(value, second_digits=3, default_utc_offset=None, rounding_method=TimestampRoundingMethod.ceiling):
 """ Return the ISO 8601 date time that corresponds to the specified string representation. When the required precision is lower than microsecond, the function rounds the sub-second time to the specified number of digits. By default, when possible, it returns the smallest value greater than or equal to the sub-second time for specified precision, also know as "ceiling conversion"; for instance, the function converts ``918845`` microseconds to ``919`` milliseconds. Otherwise, the function return the largest value equal to the sub-second time for specified precision, also known as "floor conversion"; for instance, the function converts ``999916`` microseconds to ``999`` milliseconds. @param value: string representation of an ISO date time. @param second_digits: indicate the number of second digits after the decimal point, to determine the time precision. For instance, 3 digits corresponds to milliseconds, 6 digits corresponds to microseconds. @param default_utc_offset: if the specified string representation doesn't mention any time zone, use this offset as the time zone of the corresponding ISO 8601 date time, or use the time zone of the machine this code is running on as the default time zone. @param rounding_method: one of the rounding method, as declared in ``TimestampRoundingMethod``, when the required precision is lower than microsecond. @return: an instance ``ISO8601DateTime`` represented by the string. @raise ValueError: if the specified number of second digits after the decimal point is not in 0..6, or if the string representation doesn't correspond to a valid ISO 8601 date time. """

    if second_digits < 0 or second_digits > 6:
        raise ValueError('The number of second digits after the decimal point must be in 0..6')

    if not value:
        return None

    if isinstance(value, datetime.date):
        pydatetime = ISO8601DateTime.from_datetime(value)

    else:
        if default_utc_offset is not None:
            value = '%s%s%02d' % (value[:19], '-' if default_utc_offset < 0 else '+', default_utc_offset)

        d = dateutil.parser.parse(value)

        if second_digits >= 6:
            microsecond = d.microsecond
        else:
            f = 10 ** (6 - second_digits)
            if rounding_method == TimestampRoundingMethod.ceiling:
                m = int(math.ceil(float(d.microsecond) / f))
                if m >= f:
                    m -= 1
            else:
                m = int(math.floor(float(d.microsecond) / f))

            microsecond = m * f

        pydatetime = ISO8601DateTime(
                d.year, d.month, d.day,
                d.hour, d.minute, d.second, microsecond,
                d.tzinfo or dateutil.tz.tzlocal())

    pydatetime.set_second_digits(second_digits)

    return pydatetime