public DateTimeOffset Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
        {
            var str   = reader.ReadStringSegmentUnsafe();
            var array = str.Array;
            var i     = str.Offset;
            var len   = str.Count;
            var to    = str.Offset + str.Count;

            // YYYY
            if (len == 4)
            {
                var y = (array[i++] - (byte)'0') * 1000 + (array[i++] - (byte)'0') * 100 + (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0');
                return(new DateTimeOffset(y, 1, 1, 0, 0, 0, TimeSpan.Zero));
            }

            // YYYY-MM
            if (len == 7)
            {
                var y = (array[i++] - (byte)'0') * 1000 + (array[i++] - (byte)'0') * 100 + (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0');
                if (array[i++] != (byte)'-')
                {
                    goto ERROR;
                }
                var m = (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0');
                return(new DateTimeOffset(y, m, 1, 0, 0, 0, TimeSpan.Zero));
            }

            // YYYY-MM-DD
            if (len == 10)
            {
                var y = (array[i++] - (byte)'0') * 1000 + (array[i++] - (byte)'0') * 100 + (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0');
                if (array[i++] != (byte)'-')
                {
                    goto ERROR;
                }
                var m = (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0');
                if (array[i++] != (byte)'-')
                {
                    goto ERROR;
                }
                var d = (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0');
                return(new DateTimeOffset(y, m, d, 0, 0, 0, TimeSpan.Zero));
            }

            // range-first section requires 19
            if (array.Length < 19)
            {
                goto ERROR;
            }

            var year = (array[i++] - (byte)'0') * 1000 + (array[i++] - (byte)'0') * 100 + (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0');

            if (array[i++] != (byte)'-')
            {
                goto ERROR;
            }
            var month = (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0');

            if (array[i++] != (byte)'-')
            {
                goto ERROR;
            }
            var day = (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0');

            if (array[i++] != (byte)'T')
            {
                goto ERROR;
            }

            var hour = (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0');

            if (array[i++] != (byte)':')
            {
                goto ERROR;
            }
            var minute = (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0');

            if (array[i++] != (byte)':')
            {
                goto ERROR;
            }
            var second = (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0');

            int ticks = 0;

            if (i < to && array[i] == '.')
            {
                i++;

                // *7.
                if (!(i < to) || !NumberConverter.IsNumber(array[i]))
                {
                    goto END_TICKS;
                }
                ticks += (array[i] - (byte)'0') * 1000000;
                i++;

                if (!(i < to) || !NumberConverter.IsNumber(array[i]))
                {
                    goto END_TICKS;
                }
                ticks += (array[i] - (byte)'0') * 100000;
                i++;

                if (!(i < to) || !NumberConverter.IsNumber(array[i]))
                {
                    goto END_TICKS;
                }
                ticks += (array[i] - (byte)'0') * 10000;
                i++;

                if (!(i < to) || !NumberConverter.IsNumber(array[i]))
                {
                    goto END_TICKS;
                }
                ticks += (array[i] - (byte)'0') * 1000;
                i++;

                if (!(i < to) || !NumberConverter.IsNumber(array[i]))
                {
                    goto END_TICKS;
                }
                ticks += (array[i] - (byte)'0') * 100;
                i++;

                if (!(i < to) || !NumberConverter.IsNumber(array[i]))
                {
                    goto END_TICKS;
                }
                ticks += (array[i] - (byte)'0') * 10;
                i++;

                if (!(i < to) || !NumberConverter.IsNumber(array[i]))
                {
                    goto END_TICKS;
                }
                ticks += (array[i] - (byte)'0') * 1;
                i++;

                // others, lack of precision
                while (i < to && NumberConverter.IsNumber(array[i]))
                {
                    i++;
                }
            }

END_TICKS:

            if (i < to && array[i] == '-' || array[i] == '+')
            {
                if (!(i + 5 < to))
                {
                    goto ERROR;
                }

                var minus = array[i++] == '-';

                var h = (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0');
                i++;
                var m = (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0');

                var offset = new TimeSpan(h, m, 0);
                if (minus)
                {
                    offset = offset.Negate();
                }

                return(new DateTimeOffset(year, month, day, hour, minute, second, offset).AddTicks(ticks));
            }

            return(new DateTimeOffset(year, month, day, hour, minute, second, TimeSpan.Zero).AddTicks(ticks));

ERROR:
            throw new InvalidOperationException("invalid datetime format. value:" + StringEncoding.UTF8.GetString(str.Array, str.Offset, str.Count));
        }
        public TimeSpan Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
        {
            var str   = reader.ReadStringSegmentUnsafe();
            var array = str.Array;
            var i     = str.Offset;
            var len   = str.Count;
            var to    = str.Offset + str.Count;

            // check day exists
            bool hasDay = false;
            {
                bool foundDot   = false;
                bool foundColon = false;
                for (int j = i; j < str.Count; j++)
                {
                    if (array[j] == '.')
                    {
                        if (foundColon)
                        {
                            break;
                        }
                        foundDot = true;
                    }
                    else if (array[j] == ':')
                    {
                        if (foundDot)
                        {
                            hasDay = true;
                        }
                        foundColon = true;
                    }
                }
            }

            // check sign
            var minus = false;

            if (array[i] == '-')
            {
                minus = true;
                i++;
            }

            var day = 0;

            if (hasDay)
            {
                var poolArray = JsonSerializer.MemoryPool.Rent();
                try
                {
                    for (; array[i] != '.'; i++)
                    {
                        poolArray[day++] = array[i];
                    }
                    day = new JsonReader(poolArray).ReadInt32();
                    i++; // skip '.'
                }
                finally
                {
                    JsonSerializer.MemoryPool.Return(poolArray);
                }
            }

            var hour = (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0');

            if (array[i++] != (byte)':')
            {
                goto ERROR;
            }
            var minute = (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0');

            if (array[i++] != (byte)':')
            {
                goto ERROR;
            }
            var second = (array[i++] - (byte)'0') * 10 + (array[i++] - (byte)'0');

            int ticks = 0;

            if (i < to && array[i] == '.')
            {
                i++;

                // *7.
                if (!(i < to) || !NumberConverter.IsNumber(array[i]))
                {
                    goto END_TICKS;
                }
                ticks += (array[i] - (byte)'0') * 1000000;
                i++;

                if (!(i < to) || !NumberConverter.IsNumber(array[i]))
                {
                    goto END_TICKS;
                }
                ticks += (array[i] - (byte)'0') * 100000;
                i++;

                if (!(i < to) || !NumberConverter.IsNumber(array[i]))
                {
                    goto END_TICKS;
                }
                ticks += (array[i] - (byte)'0') * 10000;
                i++;

                if (!(i < to) || !NumberConverter.IsNumber(array[i]))
                {
                    goto END_TICKS;
                }
                ticks += (array[i] - (byte)'0') * 1000;
                i++;

                if (!(i < to) || !NumberConverter.IsNumber(array[i]))
                {
                    goto END_TICKS;
                }
                ticks += (array[i] - (byte)'0') * 100;
                i++;

                if (!(i < to) || !NumberConverter.IsNumber(array[i]))
                {
                    goto END_TICKS;
                }
                ticks += (array[i] - (byte)'0') * 10;
                i++;

                if (!(i < to) || !NumberConverter.IsNumber(array[i]))
                {
                    goto END_TICKS;
                }
                ticks += (array[i] - (byte)'0') * 1;
                i++;

                // others, lack of precision
                while (i < to && NumberConverter.IsNumber(array[i]))
                {
                    i++;
                }
            }

END_TICKS:

            // be careful to overflow
            var ts = new TimeSpan(day, hour, minute, second);
            var tk = TimeSpan.FromTicks(ticks);

            return((minus)
                ? ts.Negate().Subtract(tk)
                : ts.Add(tk));

ERROR:
            throw new InvalidOperationException("invalid datetime format. value:" + StringEncoding.UTF8.GetString(str.Array, str.Offset, str.Count));
        }