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.

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