Skip to content

Commit f9aa929

Browse files
committed
fix(#93189): Fix functionality of "ms:format-time" function.
1 parent 52ad5da commit f9aa929

File tree

11 files changed

+188
-62
lines changed

11 files changed

+188
-62
lines changed

src/libraries/System.Private.Xml/src/System.Private.Xml.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,12 @@
4545
<Compile Include="System\Xml\Schema\DateAndTime\Converters\DateAndTimeConverter.cs" />
4646
<Compile Include="System\Xml\Schema\DateAndTime\Helpers\DateAndTimeInfo.cs" />
4747
<Compile Include="System\Xml\Schema\DateAndTime\Helpers\DateInfo.cs" />
48+
<Compile Include="System\Xml\Schema\DateAndTime\Helpers\TimeInfo.cs" />
4849
<Compile Include="System\Xml\Schema\DateAndTime\Specifications\DateTimeTypeCode.cs" />
4950
<Compile Include="System\Xml\Schema\DateAndTime\Specifications\XsdDateAndTimeFlags.cs" />
5051
<Compile Include="System\Xml\Schema\DateAndTime\Specifications\XsdDateTimeKind.cs" />
5152
<Compile Include="System\Xml\Schema\DateAndTime\XsdDate.cs" />
53+
<Compile Include="System\Xml\Schema\DateAndTime\XsdTime.cs" />
5254
<Compile Include="System\Xml\Xsl\Runtime\XsltFunctionNames.cs" />
5355
<Compile Include="System\Xml\Xsl\Runtime\XsltMethods.cs" />
5456
<None Include="System\Xml\Core\HtmlEncodedRawTextWriter.tt">

src/libraries/System.Private.Xml/src/System/Xml/Schema/DateAndTime/Converters/DateAndTimeConverter.cs

Lines changed: 62 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,31 @@ public static bool TryParse(string text, out DateInfo parsedValue)
104104
return false;
105105
}
106106

107+
public static bool TryParse(string text, out TimeInfo parsedValue)
108+
{
109+
const int start = 0;
110+
111+
DateAndTimeInfo rawParsedValue;
112+
string normalizedText = text.Trim();
113+
114+
if (TryParseAsTime(normalizedText, XsdDateAndTimeFlags.Time, start, out rawParsedValue))
115+
{
116+
parsedValue = rawParsedValue.Time;
117+
return true;
118+
}
119+
else
120+
{
121+
if (TryParseAsDateTime(normalizedText, XsdDateAndTimeFlags.DateTime | XsdDateAndTimeFlags.XdrDateTime, start, out rawParsedValue))
122+
{
123+
parsedValue = rawParsedValue.Time;
124+
return true;
125+
}
126+
}
127+
128+
parsedValue = default;
129+
return false;
130+
}
131+
107132
private static bool TryParseAsDate(
108133
string text,
109134
XsdDateAndTimeFlags kinds,
@@ -118,14 +143,14 @@ private static bool TryParseAsDate(
118143
{
119144
if (TryParseZoneAndWhitespace(text, start + s_lzyyyy_MM_dd, out kind, out zoneHour, out zoneMinute))
120145
{
121-
parsedValue = new DateAndTimeInfo(date, 0, 0, kind.Value, 0, 0, DateTimeTypeCode.Date, zoneHour.Value, zoneMinute.Value);
146+
parsedValue = new DateAndTimeInfo(date, kind.Value, default, DateTimeTypeCode.Date, zoneHour.Value, zoneMinute.Value);
122147
return true;
123148
}
124149

125150
if (ParseChar(text, start + s_lzyyyy_MM_dd, 'T')
126-
&& TryParseTimeAndZoneAndWhitespace(text, start + s_lzyyyy_MM_ddT, out _, out _, out _, out _, out kind, out zoneHour, out zoneMinute))
151+
&& TryParseTimeAndZoneAndWhitespace(text, start + s_lzyyyy_MM_ddT, out kind, out _, out zoneHour, out zoneMinute))
127152
{
128-
parsedValue = new DateAndTimeInfo(date, 0, 0, kind.Value, 0, 0, DateTimeTypeCode.Date, zoneHour.Value, zoneMinute.Value);
153+
parsedValue = new DateAndTimeInfo(date, kind.Value, default, DateTimeTypeCode.Date, zoneHour.Value, zoneMinute.Value);
129154
return true;
130155
}
131156
}
@@ -145,32 +170,34 @@ private static bool TryParseAsDateTime(
145170
| XsdDateAndTimeFlags.XdrDateTimeNoTz;
146171

147172
DateInfo date;
148-
int? hour, minute, second, fraction, zoneHour, zoneMinute;
173+
TimeInfo time;
174+
int? zoneHour, zoneMinute;
149175
XsdDateTimeKind? kind;
150176

151177
// Choose format starting from the most common and trying not to reparse the same thing too many times
152178
if (Test(kinds, dateTimeVariants) && TryParseDate(text, start, out date))
153179
{
154180
if (Test(kinds, XsdDateAndTimeFlags.DateTime) &&
155181
ParseChar(text, start + s_lzyyyy_MM_dd, 'T') &&
156-
TryParseTimeAndZoneAndWhitespace(text, start + s_lzyyyy_MM_ddT, out hour, out minute, out second, out fraction, out kind, out zoneHour, out zoneMinute))
182+
TryParseTimeAndZoneAndWhitespace(text, start + s_lzyyyy_MM_ddT, out kind, out time, out zoneHour, out zoneMinute))
157183
{
158-
parsedValue = new DateAndTimeInfo(date, fraction.Value, hour.Value, kind.Value, minute.Value, second.Value, DateTimeTypeCode.DateTime, zoneHour.Value, zoneMinute.Value);
184+
parsedValue = new DateAndTimeInfo(date, kind.Value, time, DateTimeTypeCode.DateTime, zoneHour.Value, zoneMinute.Value);
159185
return true;
160186
}
161187

162188
if (Test(kinds, XsdDateAndTimeFlags.XdrDateTime))
163189
{
164190
if (TryParseZoneAndWhitespace(text, start + s_lzyyyy_MM_dd, out kind, out zoneHour, out zoneMinute))
165191
{
166-
parsedValue = new DateAndTimeInfo(date, 0, 0, kind.Value, 0, 0, DateTimeTypeCode.XdrDateTime, zoneHour.Value, zoneMinute.Value);
192+
time = default;
193+
parsedValue = new DateAndTimeInfo(date, kind.Value, time, DateTimeTypeCode.XdrDateTime, zoneHour.Value, zoneMinute.Value);
167194
return true;
168195
}
169196

170197
if (ParseChar(text, start + s_lzyyyy_MM_dd, 'T')
171-
&& TryParseTimeAndZoneAndWhitespace(text, start + s_lzyyyy_MM_ddT, out hour, out minute, out second, out fraction, out kind, out zoneHour, out zoneMinute))
198+
&& TryParseTimeAndZoneAndWhitespace(text, start + s_lzyyyy_MM_ddT, out kind, out time, out zoneHour, out zoneMinute))
172199
{
173-
parsedValue = new DateAndTimeInfo(date, fraction.Value, hour.Value, kind.Value, minute.Value, second.Value, DateTimeTypeCode.XdrDateTime, zoneHour.Value, zoneMinute.Value);
200+
parsedValue = new DateAndTimeInfo(date, kind.Value, time, DateTimeTypeCode.XdrDateTime, zoneHour.Value, zoneMinute.Value);
174201
return true;
175202
}
176203
}
@@ -179,15 +206,16 @@ private static bool TryParseAsDateTime(
179206
{
180207
if (ParseChar(text, start + s_lzyyyy_MM_dd, 'T'))
181208
{
182-
if (ParseTimeAndWhitespace(text, start + s_lzyyyy_MM_ddT, out hour, out minute, out second, out fraction))
209+
if (ParseTimeAndWhitespace(text, start + s_lzyyyy_MM_ddT, out time))
183210
{
184-
parsedValue = new DateAndTimeInfo(date, fraction.Value, hour.Value, XsdDateTimeKind.Unspecified, minute.Value, second.Value, DateTimeTypeCode.XdrDateTime, 0, 0);
211+
parsedValue = new DateAndTimeInfo(date, XsdDateTimeKind.Unspecified, time, DateTimeTypeCode.XdrDateTime, 0, 0);
185212
return true;
186213
}
187214
}
188215
else
189216
{
190-
parsedValue = new DateAndTimeInfo(date, 0, 0, XsdDateTimeKind.Unspecified, 0, 0, DateTimeTypeCode.XdrDateTime, 0, 0);
217+
time = default;
218+
parsedValue = new DateAndTimeInfo(date, XsdDateTimeKind.Unspecified, time, DateTimeTypeCode.XdrDateTime, 0, 0);
191219
return true;
192220
}
193221
}
@@ -203,9 +231,10 @@ private static bool TryParseAsTime(
203231
int start,
204232
out DateAndTimeInfo parsedValue)
205233
{
206-
if (Test(kinds, XsdDateAndTimeFlags.Time) && TryParseTimeAndZoneAndWhitespace(text, start, out int? hour, out int? minute, out int? second, out int? fraction, out XsdDateTimeKind? kind, out int? zoneHour, out int? zoneMinute))
234+
if (Test(kinds, XsdDateAndTimeFlags.Time)
235+
&& TryParseTimeAndZoneAndWhitespace(text, start, out XsdDateTimeKind? kind, out TimeInfo time, out int? zoneHour, out int? zoneMinute))
207236
{
208-
parsedValue = new DateAndTimeInfo(DateInfo.DefaultValue, fraction.Value, hour.Value, kind.Value, minute.Value, second.Value, DateTimeTypeCode.Time, zoneHour.Value, zoneMinute.Value);
237+
parsedValue = new DateAndTimeInfo(DateInfo.DefaultValue, kind.Value, time, DateTimeTypeCode.Time, zoneHour.Value, zoneMinute.Value);
209238
return true;
210239
}
211240

@@ -219,9 +248,9 @@ private static bool TryParseAsXdrTimeNoTz(
219248
int start,
220249
out DateAndTimeInfo parsedValue)
221250
{
222-
if (Test(kinds, XsdDateAndTimeFlags.XdrTimeNoTz) && ParseTimeAndWhitespace(text, start, out int? hour, out int? minute, out int? second, out int? fraction))
251+
if (Test(kinds, XsdDateAndTimeFlags.XdrTimeNoTz) && ParseTimeAndWhitespace(text, start, out TimeInfo time))
223252
{
224-
parsedValue = new DateAndTimeInfo(DateInfo.DefaultValue, fraction.Value, hour.Value, XsdDateTimeKind.Unspecified, minute.Value, second.Value, DateTimeTypeCode.Time, default, default);
253+
parsedValue = new DateAndTimeInfo(DateInfo.DefaultValue, XsdDateTimeKind.Unspecified, time, DateTimeTypeCode.Time, default, default);
225254
return true;
226255
}
227256

@@ -249,19 +278,19 @@ private static bool TryParseAsGYearOrGYearMonth(
249278
TryParseZoneAndWhitespace(text, start + s_lzyyyy_MM, out kind, out zoneHour, out zoneMinute))
250279
{
251280
date = new DateInfo(DateInfo.FirstDay, month.Value, year.Value);
252-
parsedValue = new DateAndTimeInfo(date, default, default, kind.Value, default, default, DateTimeTypeCode.GYearMonth, zoneHour.Value, zoneMinute.Value);
281+
parsedValue = new DateAndTimeInfo(date, kind.Value, default, DateTimeTypeCode.GYearMonth, zoneHour.Value, zoneMinute.Value);
253282
return true;
254283
}
255284

256285
if (Test(kinds, XsdDateAndTimeFlags.GYear) && TryParseZoneAndWhitespace(text, start + s_lzyyyy, out kind, out zoneHour, out zoneMinute))
257286
{
258287
date = new DateInfo(DateInfo.FirstDay, DateInfo.FirstMonth, year.Value);
259-
parsedValue = new DateAndTimeInfo(date, default, default, kind.Value, default, default, DateTimeTypeCode.GYear, zoneHour.Value, zoneMinute.Value);
288+
parsedValue = new DateAndTimeInfo(date, kind.Value, default, DateTimeTypeCode.GYear, zoneHour.Value, zoneMinute.Value);
260289
return true;
261290
}
262291
}
263292

264-
parsedValue = new DateAndTimeInfo(default, default, default, XsdDateTimeKind.Unspecified, default, default, default, default, default);
293+
parsedValue = new DateAndTimeInfo(default, XsdDateTimeKind.Unspecified, default, default, default, default);
265294
return false;
266295
}
267296

@@ -286,7 +315,7 @@ private static bool TryParseAsGMonthOrGMonthDay(
286315
TryParseZoneAndWhitespace(text, start + s_lz__mm_dd, out kind, out zoneHour, out zoneMinute))
287316
{
288317
date = new DateInfo(day.Value, month.Value, DateInfo.LeapYear);
289-
parsedValue = new DateAndTimeInfo(date, default, default, kind.Value, default, default, DateTimeTypeCode.GMonthDay, zoneHour.Value, zoneMinute.Value);
318+
parsedValue = new DateAndTimeInfo(date, kind.Value, default, DateTimeTypeCode.GMonthDay, zoneHour.Value, zoneMinute.Value);
290319
return true;
291320
}
292321

@@ -297,7 +326,7 @@ private static bool TryParseAsGMonthOrGMonthDay(
297326
TryParseZoneAndWhitespace(text, start + s_lz__mm__, out kind, out zoneHour, out zoneMinute)))
298327
{
299328
date = new DateInfo(DateInfo.FirstDay, month.Value, DateInfo.LeapYear);
300-
parsedValue = new DateAndTimeInfo(date, default, default, kind.Value, default, default, DateTimeTypeCode.GMonth, zoneHour.Value, zoneMinute.Value);
329+
parsedValue = new DateAndTimeInfo(date, kind.Value, default, DateTimeTypeCode.GMonth, zoneHour.Value, zoneMinute.Value);
301330
return true;
302331
}
303332
}
@@ -326,7 +355,7 @@ private static bool TryParseAsGDay(
326355
TryParseZoneAndWhitespace(text, start + s_lz___dd, out kind, out zoneHour, out zoneMinute))
327356
{
328357
date = new DateInfo(day.Value, DateInfo.FirstMonth, DateInfo.LeapYear);
329-
parsedValue = new DateAndTimeInfo(date, default, default, kind.Value, default, default, DateTimeTypeCode.GDay, zoneHour.Value, zoneMinute.Value);
358+
parsedValue = new DateAndTimeInfo(date, kind.Value, default, DateTimeTypeCode.GDay, zoneHour.Value, zoneMinute.Value);
330359

331360
return true;
332361
}
@@ -422,11 +451,10 @@ private static bool TryParseDate(
422451
private static bool TryParseTime(
423452
string rawValue,
424453
ref int start,
425-
[NotNullWhen(true)] out int? hour,
426-
[NotNullWhen(true)] out int? minute,
427-
[NotNullWhen(true)] out int? second,
428-
[NotNullWhen(true)] out int? fraction)
454+
out TimeInfo time)
429455
{
456+
int? fraction, hour, minute, second;
457+
430458
if (
431459
ParseTwoDigits(rawValue, start, out hour) && hour < 24 &&
432460
ParseChar(rawValue, start + s_lzHH, ':') &&
@@ -475,6 +503,7 @@ private static bool TryParseTime(
475503
{
476504
if (fractionDigits == 0)
477505
{
506+
time = default;
478507
return false; // cannot end with .
479508
}
480509
fraction *= Power10[MaxFractionDigits - fractionDigits];
@@ -493,47 +522,40 @@ private static bool TryParseTime(
493522
fraction = 0;
494523
}
495524

525+
time = new TimeInfo(fraction.Value, hour.Value, minute.Value, second.Value);
496526
return true;
497527
}
498528

499-
hour = default;
500-
minute = default;
501-
second = default;
502-
fraction = default;
529+
time = default;
503530
return false;
504531
}
505532

506533
private static bool ParseTimeAndWhitespace(
507534
string rawValue,
508535
int start,
509-
[NotNullWhen(true)] out int? hour,
510-
[NotNullWhen(true)] out int? minute,
511-
[NotNullWhen(true)] out int? second,
512-
[NotNullWhen(true)] out int? fraction)
536+
out TimeInfo time)
513537
{
514-
if (TryParseTime(rawValue, ref start, out hour, out minute, out second, out fraction))
538+
if (TryParseTime(rawValue, ref start, out time))
515539
{
516540
while (start < rawValue.Length)
517541
{
518542
start++;
519543
}
520544
return start == rawValue.Length;
521545
}
546+
522547
return false;
523548
}
524549

525550
private static bool TryParseTimeAndZoneAndWhitespace(
526551
string rawValue,
527552
int start,
528-
[NotNullWhen(true)] out int? hour,
529-
[NotNullWhen(true)] out int? minute,
530-
[NotNullWhen(true)] out int? second,
531-
[NotNullWhen(true)] out int? fraction,
532553
[NotNullWhen(true)] out XsdDateTimeKind? kind,
554+
out TimeInfo time,
533555
[NotNullWhen(true)] out int? zoneHour,
534556
[NotNullWhen(true)] out int? zoneMinute)
535557
{
536-
if (TryParseTime(rawValue, ref start, out hour, out minute, out second, out fraction))
558+
if (TryParseTime(rawValue, ref start, out time))
537559
{
538560
if (TryParseZoneAndWhitespace(rawValue, start, out kind, out zoneHour, out zoneMinute))
539561
{

src/libraries/System.Private.Xml/src/System/Xml/Schema/DateAndTime/Helpers/DateAndTimeInfo.cs

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,46 +8,34 @@ namespace System.Xml.Schema.DateAndTime.Helpers
88
internal struct DateAndTimeInfo
99
{
1010
public DateInfo Date { get; }
11-
public int Fraction { get; }
12-
public int Hour { get; }
1311
public XsdDateTimeKind Kind { get; }
14-
public int Minute { get; }
15-
public int Second { get; }
12+
public TimeInfo Time { get; }
1613
public DateTimeTypeCode TypeCode { get; }
1714
public int ZoneHour { get; }
1815
public int ZoneMinute { get; }
1916

2017
public DateAndTimeInfo(
2118
DateInfo date,
22-
int fraction,
23-
int hour,
2419
XsdDateTimeKind kind,
25-
int minute,
26-
int second,
20+
TimeInfo time,
2721
DateTimeTypeCode typeCode,
2822
int zoneHour,
2923
int zoneMinute)
3024
{
3125
Date = date;
32-
Fraction = fraction;
33-
Hour = hour;
3426
Kind = kind;
35-
Minute = minute;
36-
Second = second;
27+
Time = time;
3728
TypeCode = typeCode;
3829
ZoneHour = zoneHour;
3930
ZoneMinute = zoneMinute;
4031
}
4132

4233
public DateAndTimeInfo()
4334
: this(
44-
default,
45-
default,
4635
default,
4736
XsdDateTimeKind.Unspecified,
4837
default,
4938
default,
50-
default,
5139
0,
5240
0)
5341
{
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace System.Xml.Schema.DateAndTime.Helpers
5+
{
6+
internal struct TimeInfo
7+
{
8+
public int Fraction { get; }
9+
public int Hour { get; }
10+
public int Microsecond
11+
{
12+
get
13+
{
14+
return Convert.ToInt32((Fraction % TimeSpan.TicksPerMillisecond) / TimeSpan.TicksPerMicrosecond);
15+
}
16+
}
17+
18+
public int Millisecond
19+
{
20+
get
21+
{
22+
return Convert.ToInt32(Fraction / TimeSpan.TicksPerMillisecond);
23+
}
24+
}
25+
26+
public int Minute { get; }
27+
public int Second { get; }
28+
29+
public TimeInfo(int fraction, int hour, int minute, int second)
30+
{
31+
Fraction = fraction;
32+
Hour = hour;
33+
Minute = minute;
34+
Second = second;
35+
}
36+
}
37+
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,10 @@ private XsdDateTime(DateAndTimeInfo parsedValue) : this()
7474

7575
private void InitiateXsdDateTime(DateAndTimeInfo parsedValue)
7676
{
77-
_dt = new DateTime(parsedValue.Date.Year, parsedValue.Date.Month, parsedValue.Date.Day, parsedValue.Hour, parsedValue.Minute, parsedValue.Second);
78-
if (parsedValue.Fraction != 0)
77+
_dt = new DateTime(parsedValue.Date.Year, parsedValue.Date.Month, parsedValue.Date.Day, parsedValue.Time.Hour, parsedValue.Time.Minute, parsedValue.Time.Second);
78+
if (parsedValue.Time.Fraction != 0)
7979
{
80-
_dt = _dt.AddTicks(parsedValue.Fraction);
80+
_dt = _dt.AddTicks(parsedValue.Time.Fraction);
8181
}
8282
_extra = (uint)(((int)parsedValue.TypeCode << TypeShift) | ((int)parsedValue.Kind << KindShift) | (parsedValue.ZoneHour << ZoneHourShift) | parsedValue.ZoneMinute);
8383
}

0 commit comments

Comments
 (0)