Skip to content

Commit 4ef3b50

Browse files
Copilotjeffhandley
andcommitted
Support ISO 8601 24:00:00 (endOfDayFrag) in XsdDateTime parsing
Co-authored-by: jeffhandley <1031940+jeffhandley@users.noreply.github.com>
1 parent 2da4665 commit 4ef3b50

File tree

1 file changed

+39
-9
lines changed

1 file changed

+39
-9
lines changed

src/libraries/System.Private.Xml/src/System/Xml/Schema/XsdDateTime.cs

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -132,22 +132,40 @@ public XsdDateTime(string text, XsdDateTimeFlags kinds) : this()
132132
{
133133
throw new FormatException(SR.Format(SR.XmlConvert_BadFormat, text, kinds));
134134
}
135-
InitiateXsdDateTime(parser);
135+
if (!TryInitiateXsdDateTime(parser))
136+
{
137+
throw new FormatException(SR.Format(SR.XmlConvert_BadFormat, text, kinds));
138+
}
136139
}
137140

138-
private XsdDateTime(Parser parser) : this()
141+
private bool TryInitiateXsdDateTime(Parser parser)
139142
{
140-
InitiateXsdDateTime(parser);
141-
}
143+
// Per ISO 8601, 24:00:00 represents end of a calendar day
144+
// (same instant as next day's 00:00:00). Set hour to 0 and add one day.
145+
int hour = parser.hour;
146+
bool isEndOfDay = hour == 24;
147+
if (isEndOfDay)
148+
{
149+
hour = 0;
150+
}
142151

143-
private void InitiateXsdDateTime(Parser parser)
144-
{
145-
_dt = new DateTime(parser.year, parser.month, parser.day, parser.hour, parser.minute, parser.second);
152+
_dt = new DateTime(parser.year, parser.month, parser.day, hour, parser.minute, parser.second);
146153
if (parser.fraction != 0)
147154
{
148155
_dt = _dt.AddTicks(parser.fraction);
149156
}
157+
158+
if (isEndOfDay)
159+
{
160+
if (_dt.Ticks > DateTime.MaxValue.Ticks - TimeSpan.TicksPerDay)
161+
{
162+
return false;
163+
}
164+
_dt = _dt.AddDays(1);
165+
}
166+
150167
_extra = (uint)(((int)parser.typeCode << TypeShift) | ((int)parser.kind << KindShift) | (parser.zoneHour << ZoneHourShift) | parser.zoneMinute);
168+
return true;
151169
}
152170

153171
internal static bool TryParse(string text, XsdDateTimeFlags kinds, out XsdDateTime result)
@@ -158,7 +176,11 @@ internal static bool TryParse(string text, XsdDateTimeFlags kinds, out XsdDateTi
158176
result = default;
159177
return false;
160178
}
161-
result = new XsdDateTime(parser);
179+
result = default;
180+
if (!result.TryInitiateXsdDateTime(parser))
181+
{
182+
return false;
183+
}
162184
return true;
163185
}
164186

@@ -917,7 +939,7 @@ private bool ParseTimeAndWhitespace(int start)
917939
private bool ParseTime(ref int start)
918940
{
919941
if (
920-
Parse2Dig(start, ref hour) && hour < 24 &&
942+
Parse2Dig(start, ref hour) && hour <= 24 &&
921943
ParseChar(start + s_lzHH, ':') &&
922944
Parse2Dig(start + s_lzHH_, ref minute) && minute < 60 &&
923945
ParseChar(start + s_lzHH_mm, ':') &&
@@ -977,6 +999,14 @@ private bool ParseTime(ref int start)
977999
fraction += round;
9781000
}
9791001
}
1002+
1003+
// Per ISO 8601, 24:00:00 represents end of a calendar day
1004+
// (same instant as next day's 00:00:00), but only when minute, second, and fraction are all zero.
1005+
if (hour == 24 && (minute != 0 || second != 0 || fraction != 0))
1006+
{
1007+
return false;
1008+
}
1009+
9801010
return true;
9811011
}
9821012
// cleanup - conflict with gYear

0 commit comments

Comments
 (0)