summaryrefslogtreecommitdiff
path: root/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.cs
blob: 37070ef5cf71d371039f26ca67958e64300d03e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable enable
using System.Diagnostics;

namespace System.Buffers.Text
{
    public static partial class Utf8Parser
    {
        /// <summary>
        /// Parses a DateTime at the start of a Utf8 string.
        /// </summary>
        /// <param name="source">The Utf8 string to parse</param>
        /// <param name="value">Receives the parsed value</param>
        /// <param name="bytesConsumed">On a successful parse, receives the length in bytes of the substring that was parsed </param>
        /// <param name="standardFormat">Expected format of the Utf8 string</param>
        /// <returns>
        /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed.
        /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0. 
        /// </returns>
        /// <remarks>
        /// Formats supported:
        ///     default       05/25/2017 10:30:15 -08:00
        ///     G             05/25/2017 10:30:15
        ///     R             Tue, 03 Jan 2017 08:08:05 GMT       (RFC 1123)
        ///     l             tue, 03 jan 2017 08:08:05 gmt       (Lowercase RFC 1123)
        ///     O             2017-06-12T05:30:45.7680000-07:00   (Round-trippable)
        /// </remarks>
        /// <exceptions>
        /// <cref>System.FormatException</cref> if the format is not valid for this data type.
        /// </exceptions>
        public static bool TryParse(ReadOnlySpan<byte> source, out DateTime value, out int bytesConsumed, char standardFormat = default)
        {
            switch (standardFormat)
            {
                case 'R':
                    {
                        if (!TryParseDateTimeOffsetR(source, NoFlipCase, out DateTimeOffset dateTimeOffset, out bytesConsumed))
                        {
                            value = default;
                            return false;
                        }
                        value = dateTimeOffset.DateTime;  // (returns a DateTimeKind.Unspecified to match DateTime.ParseExact(). Maybe better to return UtcDateTime instead?)
                        return true;
                    }

                case 'l':
                    {
                        if (!TryParseDateTimeOffsetR(source, FlipCase, out DateTimeOffset dateTimeOffset, out bytesConsumed))
                        {
                            value = default;
                            return false;
                        }
                        value = dateTimeOffset.DateTime;  // (returns a DateTimeKind.Unspecified to match DateTime.ParseExact(). Maybe better to return UtcDateTime instead?)
                        return true;
                    }

                case 'O':
                    {
                        // Emulates DateTime.ParseExact(text, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind)
                        // In particular, the formatted string "encodes" the DateTimeKind according to the following table:
                        //
                        //         2017-06-12T05:30:45.7680000       - Unspecified
                        //         2017-06-12T05:30:45.7680000+00:00 - Local
                        //         2017-06-12T05:30:45.7680000Z      - Utc

                        if (!TryParseDateTimeOffsetO(source, out DateTimeOffset dateTimeOffset, out bytesConsumed, out DateTimeKind kind))
                        {
                            value = default;
                            bytesConsumed = 0;
                            return false;
                        }

                        switch (kind)
                        {
                            case DateTimeKind.Local:
                                value = dateTimeOffset.LocalDateTime;
                                break;
                            case DateTimeKind.Utc:
                                value = dateTimeOffset.UtcDateTime;
                                break;
                            default:
                                Debug.Assert(kind == DateTimeKind.Unspecified);
                                value = dateTimeOffset.DateTime;
                                break;
                        }

                        return true;
                    }

                case default(char):
                case 'G':
                    return TryParseDateTimeG(source, out value, out _, out bytesConsumed);

                default:
                    return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
            }
        }

        /// <summary>
        /// Parses a DateTimeOffset at the start of a Utf8 string.
        /// </summary>
        /// <param name="source">The Utf8 string to parse</param>
        /// <param name="value">Receives the parsed value</param>
        /// <param name="bytesConsumed">On a successful parse, receives the length in bytes of the substring that was parsed </param>
        /// <param name="standardFormat">Expected format of the Utf8 string</param>
        /// <returns>
        /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed.
        /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0. 
        /// </returns>
        /// <remarks>
        /// Formats supported:
        ///     G  (default)  05/25/2017 10:30:15
        ///     R             Tue, 03 Jan 2017 08:08:05 GMT       (RFC 1123)
        ///     l             tue, 03 jan 2017 08:08:05 gmt       (Lowercase RFC 1123)
        ///     O             2017-06-12T05:30:45.7680000-07:00   (Round-trippable)
        /// </remarks>
        /// <exceptions>
        /// <cref>System.FormatException</cref> if the format is not valid for this data type.
        /// </exceptions>
        public static bool TryParse(ReadOnlySpan<byte> source, out DateTimeOffset value, out int bytesConsumed, char standardFormat = default)
        {
            switch (standardFormat)
            {
                case 'R':
                    return TryParseDateTimeOffsetR(source, NoFlipCase, out value, out bytesConsumed);

                case 'l':
                    return TryParseDateTimeOffsetR(source, FlipCase, out value, out bytesConsumed);

                case 'O':
                    return TryParseDateTimeOffsetO(source, out value, out bytesConsumed, out _);

                case default(char):
                    return TryParseDateTimeOffsetDefault(source, out value, out bytesConsumed);

                case 'G':
                    return TryParseDateTimeG(source, out DateTime _, out value, out bytesConsumed);

                default:
                    return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
            }
        }

        private const uint FlipCase = 0x00000020u;  // XOR mask to flip the case of a letter.
        private const uint NoFlipCase = 0x00000000u;
    }
}