2019-08-23 11:56:54 -07:00
|
|
|
// ==++==
|
|
|
|
//
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
//
|
|
|
|
// ==--==
|
|
|
|
// <OWNER>[....]</OWNER>
|
|
|
|
//
|
|
|
|
namespace System.Globalization {
|
|
|
|
using System.Text;
|
|
|
|
using System;
|
|
|
|
using System.Diagnostics.Contracts;
|
|
|
|
using System.Globalization;
|
2020-04-29 06:40:03 -07:00
|
|
|
using System.Collections;
|
2019-08-23 11:56:54 -07:00
|
|
|
|
2020-03-09 06:34:16 -07:00
|
|
|
static class TimeSpanFormat
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
private static void IntToString(int n, int digits, String outStr)
|
|
|
|
{
|
|
|
|
((int32)n).[Friend]ToString(outStr, digits);
|
|
|
|
}
|
|
|
|
|
2020-03-09 06:34:16 -07:00
|
|
|
protected static readonly FormatLiterals PositiveInvariantFormatLiterals = TimeSpanFormat.FormatLiterals.[Friend]InitInvariant(false /*isNegative*/) ~ _.Dispose();
|
|
|
|
protected static readonly FormatLiterals NegativeInvariantFormatLiterals = TimeSpanFormat.FormatLiterals.[Friend]InitInvariant(true /*isNegative*/) ~ _.Dispose();
|
2019-08-23 11:56:54 -07:00
|
|
|
|
2020-03-09 06:34:16 -07:00
|
|
|
protected enum Pattern {
|
2019-08-23 11:56:54 -07:00
|
|
|
None = 0,
|
|
|
|
Minimum = 1,
|
|
|
|
Full = 2,
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Format
|
|
|
|
//
|
|
|
|
// Actions: Main method called from TimeSpan.ToString
|
|
|
|
//
|
2020-03-09 06:34:16 -07:00
|
|
|
protected static Result<void> Format(TimeSpan value, StringView format, IFormatProvider formatProvider, String outStr)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
var format;
|
|
|
|
|
|
|
|
if (format.IsNull || format.Length == 0)
|
|
|
|
format = "c";
|
|
|
|
|
|
|
|
// standard formats
|
|
|
|
if (format.Length == 1) {
|
|
|
|
char8 f = format[0];
|
|
|
|
|
|
|
|
if (f == 'c' || f == 't' || f == 'T')
|
|
|
|
{
|
|
|
|
FormatStandard(value, true, format, Pattern.Minimum, outStr);
|
|
|
|
return .Ok;
|
|
|
|
}
|
|
|
|
if (f == 'g' || f == 'G') {
|
|
|
|
Pattern pattern;
|
|
|
|
DateTimeFormatInfo dtfi = DateTimeFormatInfo.GetInstance(formatProvider);
|
|
|
|
|
|
|
|
if ((int64)value < 0)
|
2020-03-09 06:34:16 -07:00
|
|
|
format = dtfi.[Friend]FullTimeSpanNegativePattern;
|
2019-08-23 11:56:54 -07:00
|
|
|
else
|
2020-03-09 06:34:16 -07:00
|
|
|
format = dtfi.[Friend]FullTimeSpanPositivePattern;
|
2019-08-23 11:56:54 -07:00
|
|
|
if (f == 'g')
|
|
|
|
pattern = Pattern.Minimum;
|
|
|
|
else
|
|
|
|
pattern = Pattern.Full;
|
|
|
|
|
|
|
|
return FormatStandard(value, false, format, pattern, outStr);
|
|
|
|
}
|
|
|
|
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
|
|
|
return .Err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FormatCustomized(value, format, DateTimeFormatInfo.GetInstance(formatProvider), outStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// FormatStandard
|
|
|
|
//
|
|
|
|
// Actions: Format the TimeSpan instance using the specified format.
|
|
|
|
//
|
|
|
|
private static Result<void> FormatStandard(TimeSpan value, bool isInvariant, StringView format, Pattern pattern, String outStr)
|
|
|
|
{
|
|
|
|
int32 day = (int32)((int64)value / TimeSpan.TicksPerDay);
|
|
|
|
int64 time = (int64)value % TimeSpan.TicksPerDay;
|
|
|
|
|
|
|
|
if ((int64)value < 0) {
|
|
|
|
day = -day;
|
|
|
|
time = -time;
|
|
|
|
}
|
|
|
|
int hours = (int)(time / TimeSpan.TicksPerHour % 24);
|
|
|
|
int minutes = (int)(time / TimeSpan.TicksPerMinute % 60);
|
|
|
|
int seconds = (int)(time / TimeSpan.TicksPerSecond % 60);
|
|
|
|
int fraction = (int)(time % TimeSpan.TicksPerSecond);
|
|
|
|
|
|
|
|
FormatLiterals literal;
|
|
|
|
if (isInvariant) {
|
|
|
|
if ((int64)value < 0)
|
|
|
|
literal = NegativeInvariantFormatLiterals;
|
|
|
|
else
|
|
|
|
literal = PositiveInvariantFormatLiterals;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
literal = FormatLiterals();
|
2020-03-09 06:34:16 -07:00
|
|
|
literal.[Friend]Init(format, pattern == Pattern.Full);
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
if (fraction != 0) { // truncate the partial second to the specified length
|
2020-03-09 06:34:16 -07:00
|
|
|
fraction = (int)((int64)fraction / (int64)Math.Pow(10, DateTimeFormat.[Friend]MaxSecondsFractionDigits - literal.[Friend]ff));
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Pattern.Full: [-]dd.hh:mm:ss.fffffff
|
|
|
|
// Pattern.Minimum: [-][d.]hh:mm:ss[.fffffff]
|
|
|
|
|
2020-03-09 06:34:16 -07:00
|
|
|
outStr.Append(literal.[Friend]Start); // [-]
|
2019-08-23 11:56:54 -07:00
|
|
|
if (pattern == Pattern.Full || day != 0) { //
|
|
|
|
day.ToString(outStr); // [dd]
|
2020-03-09 06:34:16 -07:00
|
|
|
outStr.Append(literal.[Friend]DayHourSep); // [.]
|
2019-08-23 11:56:54 -07:00
|
|
|
} //
|
2020-03-09 06:34:16 -07:00
|
|
|
IntToString(hours, literal.[Friend]hh, outStr); // hh
|
|
|
|
outStr.Append(literal.[Friend]HourMinuteSep); // :
|
|
|
|
IntToString(minutes, literal.[Friend]mm, outStr); // mm
|
|
|
|
outStr.Append(literal.[Friend]MinuteSecondSep); // :
|
|
|
|
IntToString(seconds, literal.[Friend]ss, outStr); // ss
|
2019-08-23 11:56:54 -07:00
|
|
|
if (!isInvariant && pattern == Pattern.Minimum) {
|
2020-03-09 06:34:16 -07:00
|
|
|
int effectiveDigits = literal.[Friend]ff;
|
2019-08-23 11:56:54 -07:00
|
|
|
while (effectiveDigits > 0) {
|
|
|
|
if (fraction % 10 == 0) {
|
|
|
|
fraction = fraction / 10;
|
|
|
|
effectiveDigits--;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (effectiveDigits > 0) {
|
2020-03-09 06:34:16 -07:00
|
|
|
outStr.Append(literal.[Friend]SecondFractionSep); // [.FFFFFFF]
|
|
|
|
(fraction).ToString(outStr, DateTimeFormat.[Friend]fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture);
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (pattern == Pattern.Full || fraction != 0) {
|
2020-03-09 06:34:16 -07:00
|
|
|
outStr.Append(literal.[Friend]SecondFractionSep); // [.]
|
|
|
|
IntToString(fraction, literal.[Friend]ff, outStr); // [fffffff]
|
2019-08-23 11:56:54 -07:00
|
|
|
} //
|
2020-03-09 06:34:16 -07:00
|
|
|
outStr.Append(literal.[Friend]End); //
|
2019-08-23 11:56:54 -07:00
|
|
|
return .Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// FormatCustomized
|
|
|
|
//
|
|
|
|
// Actions: Format the TimeSpan instance using the specified format.
|
|
|
|
//
|
2020-03-09 06:34:16 -07:00
|
|
|
protected static Result<void> FormatCustomized(TimeSpan value, StringView format, DateTimeFormatInfo dtfi, String result)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
|
|
|
|
Contract.Assert(dtfi != null, "dtfi == null");
|
|
|
|
|
|
|
|
int day = (int)((int64)value / TimeSpan.TicksPerDay);
|
|
|
|
int64 time = (int64)value % TimeSpan.TicksPerDay;
|
|
|
|
|
|
|
|
if ((int64)value < 0) {
|
|
|
|
day = -day;
|
|
|
|
time = -time;
|
|
|
|
}
|
|
|
|
int hours = (int)(time / TimeSpan.TicksPerHour % 24);
|
|
|
|
int minutes = (int)(time / TimeSpan.TicksPerMinute % 60);
|
|
|
|
int seconds = (int)(time / TimeSpan.TicksPerSecond % 60);
|
|
|
|
int fraction = (int)(time % TimeSpan.TicksPerSecond);
|
|
|
|
|
|
|
|
int64 tmp = 0;
|
|
|
|
int i = 0;
|
|
|
|
int tokenLen = 0;
|
|
|
|
|
|
|
|
while (i < format.Length) {
|
|
|
|
char8 ch = format[i];
|
|
|
|
int nextChar;
|
|
|
|
switch (ch) {
|
|
|
|
case 'h':
|
2020-03-09 06:34:16 -07:00
|
|
|
tokenLen = DateTimeFormat.[Friend]ParseRepeatPattern(format, i, ch);
|
2019-08-23 11:56:54 -07:00
|
|
|
if (tokenLen > 2)
|
|
|
|
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
|
|
|
return .Err;
|
2020-03-09 06:34:16 -07:00
|
|
|
DateTimeFormat.[Friend]FormatDigits(result, hours, tokenLen);
|
2019-08-23 11:56:54 -07:00
|
|
|
break;
|
|
|
|
case 'm':
|
2020-03-09 06:34:16 -07:00
|
|
|
tokenLen = DateTimeFormat.[Friend]ParseRepeatPattern(format, i, ch);
|
2019-08-23 11:56:54 -07:00
|
|
|
if (tokenLen > 2)
|
|
|
|
return .Err;
|
|
|
|
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
2020-03-09 06:34:16 -07:00
|
|
|
DateTimeFormat.[Friend]FormatDigits(result, minutes, tokenLen);
|
2019-08-23 11:56:54 -07:00
|
|
|
break;
|
|
|
|
case 's':
|
2020-03-09 06:34:16 -07:00
|
|
|
tokenLen = DateTimeFormat.[Friend]ParseRepeatPattern(format, i, ch);
|
2019-08-23 11:56:54 -07:00
|
|
|
if (tokenLen > 2)
|
|
|
|
return .Err;
|
|
|
|
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
2020-03-09 06:34:16 -07:00
|
|
|
DateTimeFormat.[Friend]FormatDigits(result, seconds, tokenLen);
|
2019-08-23 11:56:54 -07:00
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
//
|
|
|
|
// The fraction of a second in single-digit precision. The remaining digits are truncated.
|
|
|
|
//
|
2020-03-09 06:34:16 -07:00
|
|
|
tokenLen = DateTimeFormat.[Friend]ParseRepeatPattern(format, i, ch);
|
|
|
|
if (tokenLen > DateTimeFormat.[Friend]MaxSecondsFractionDigits)
|
2019-08-23 11:56:54 -07:00
|
|
|
return .Err;
|
|
|
|
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
|
|
|
|
|
|
|
tmp = (int64)fraction;
|
2020-03-09 06:34:16 -07:00
|
|
|
tmp /= (int64)Math.Pow(10, DateTimeFormat.[Friend]MaxSecondsFractionDigits - tokenLen);
|
|
|
|
(tmp).ToString(result, DateTimeFormat.[Friend]fixedNumberFormats[tokenLen - 1], CultureInfo.InvariantCulture);
|
2019-08-23 11:56:54 -07:00
|
|
|
break;
|
|
|
|
case 'F':
|
|
|
|
//
|
|
|
|
// Displays the most significant digit of the seconds fraction. Nothing is displayed if the digit is zero.
|
|
|
|
//
|
2020-03-09 06:34:16 -07:00
|
|
|
tokenLen = DateTimeFormat.[Friend]ParseRepeatPattern(format, i, ch);
|
|
|
|
if (tokenLen > DateTimeFormat.[Friend]MaxSecondsFractionDigits)
|
2019-08-23 11:56:54 -07:00
|
|
|
return .Err;
|
|
|
|
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
|
|
|
|
|
|
|
tmp = (int64)fraction;
|
2020-03-09 06:34:16 -07:00
|
|
|
tmp /= (int64)Math.Pow(10, DateTimeFormat.[Friend]MaxSecondsFractionDigits - tokenLen);
|
2019-08-23 11:56:54 -07:00
|
|
|
int effectiveDigits = tokenLen;
|
|
|
|
while (effectiveDigits > 0) {
|
|
|
|
if (tmp % 10 == 0) {
|
|
|
|
tmp = tmp / 10;
|
|
|
|
effectiveDigits--;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (effectiveDigits > 0) {
|
2020-03-09 06:34:16 -07:00
|
|
|
(tmp).ToString(result, DateTimeFormat.[Friend]fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture);
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
//
|
|
|
|
// tokenLen == 1 : Day as digits with no leading zero.
|
|
|
|
// tokenLen == 2+: Day as digits with leading zero for single-digit days.
|
|
|
|
//
|
2020-03-09 06:34:16 -07:00
|
|
|
tokenLen = DateTimeFormat.[Friend]ParseRepeatPattern(format, i, ch);
|
2019-08-23 11:56:54 -07:00
|
|
|
if (tokenLen > 8)
|
|
|
|
return .Err;
|
|
|
|
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
2020-03-09 06:34:16 -07:00
|
|
|
DateTimeFormat.[Friend]FormatDigits(result, day, tokenLen, true);
|
2019-08-23 11:56:54 -07:00
|
|
|
break;
|
|
|
|
case '\'':
|
|
|
|
case '\"':
|
|
|
|
//StringBuilder enquotedString = new StringBuilder();
|
2020-03-09 06:34:16 -07:00
|
|
|
tokenLen = DateTimeFormat.[Friend]ParseQuoteString(format, i, result);
|
2019-08-23 11:56:54 -07:00
|
|
|
//result.Append(enquotedString);
|
|
|
|
break;
|
|
|
|
case '%':
|
|
|
|
// Optional format character.
|
|
|
|
// For example, format string "%d" will print day
|
|
|
|
// Most of the cases, "%" can be ignored.
|
2020-03-09 06:34:16 -07:00
|
|
|
nextChar = DateTimeFormat.[Friend]ParseNextChar(format, i);
|
2019-08-23 11:56:54 -07:00
|
|
|
// nextChar will be -1 if we already reach the end of the format string.
|
|
|
|
// Besides, we will not allow "%%" appear in the pattern.
|
|
|
|
if (nextChar >= 0 && nextChar != (int)'%')
|
|
|
|
{
|
|
|
|
TimeSpanFormat.FormatCustomized(value, StringView((char8*)&nextChar, 1), dtfi, result);
|
|
|
|
tokenLen = 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// This means that '%' is at the end of the format string or
|
|
|
|
// "%%" appears in the format string.
|
|
|
|
//
|
|
|
|
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
|
|
|
return .Err;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
// Escaped character. Can be used to insert character into the format string.
|
|
|
|
// For example, "\d" will insert the character 'd' into the string.
|
|
|
|
//
|
2020-03-09 06:34:16 -07:00
|
|
|
nextChar = DateTimeFormat.[Friend]ParseNextChar(format, i);
|
2019-08-23 11:56:54 -07:00
|
|
|
if (nextChar >= 0)
|
|
|
|
{
|
|
|
|
result.Append(((char8)nextChar));
|
|
|
|
tokenLen = 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// This means that '\' is at the end of the formatting string.
|
|
|
|
//
|
|
|
|
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
|
|
|
return .Err;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
|
|
|
return .Err;
|
|
|
|
}
|
|
|
|
i += tokenLen;
|
|
|
|
}
|
|
|
|
return .Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-03-09 06:34:16 -07:00
|
|
|
protected struct FormatLiterals {
|
|
|
|
String Start {
|
2019-08-23 11:56:54 -07:00
|
|
|
get {
|
|
|
|
return literals[0];
|
|
|
|
}
|
|
|
|
}
|
2020-03-09 06:34:16 -07:00
|
|
|
String DayHourSep {
|
2019-08-23 11:56:54 -07:00
|
|
|
get {
|
|
|
|
return literals[1];
|
|
|
|
}
|
|
|
|
}
|
2020-03-09 06:34:16 -07:00
|
|
|
String HourMinuteSep {
|
2019-08-23 11:56:54 -07:00
|
|
|
get {
|
|
|
|
return literals[2];
|
|
|
|
}
|
|
|
|
}
|
2020-03-09 06:34:16 -07:00
|
|
|
String MinuteSecondSep {
|
2019-08-23 11:56:54 -07:00
|
|
|
get {
|
|
|
|
return literals[3];
|
|
|
|
}
|
|
|
|
}
|
2020-03-09 06:34:16 -07:00
|
|
|
String SecondFractionSep {
|
2019-08-23 11:56:54 -07:00
|
|
|
get {
|
|
|
|
return literals[4];
|
|
|
|
}
|
|
|
|
}
|
2020-03-09 06:34:16 -07:00
|
|
|
String End {
|
2019-08-23 11:56:54 -07:00
|
|
|
get {
|
|
|
|
return literals[5];
|
|
|
|
}
|
|
|
|
}
|
2020-03-09 06:34:16 -07:00
|
|
|
String AppCompatLiteral;
|
|
|
|
int dd;
|
|
|
|
int hh;
|
|
|
|
int mm;
|
|
|
|
int ss;
|
|
|
|
int ff;
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
private String[] literals;
|
|
|
|
private List<String> ownedStrs;
|
|
|
|
|
|
|
|
public void Dispose() mut
|
|
|
|
{
|
|
|
|
DeleteAndNullify!(literals);
|
|
|
|
if (ownedStrs != null)
|
|
|
|
{
|
|
|
|
DeleteContainerAndItems!(ownedStrs);
|
|
|
|
ownedStrs = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
String AddOwnedStr(String str) mut
|
|
|
|
{
|
|
|
|
if (ownedStrs == null)
|
|
|
|
ownedStrs = new List<String>();
|
|
|
|
ownedStrs.Add(str);
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* factory method for static invariant FormatLiterals */
|
2020-03-09 06:34:16 -07:00
|
|
|
static FormatLiterals InitInvariant(bool isNegative) {
|
2019-08-23 11:56:54 -07:00
|
|
|
FormatLiterals x = FormatLiterals();
|
|
|
|
x.literals = new String[6];
|
|
|
|
x.literals[0] = isNegative ? "-" : String.Empty;
|
|
|
|
x.literals[1] = ".";
|
|
|
|
x.literals[2] = ":";
|
|
|
|
x.literals[3] = ":";
|
|
|
|
x.literals[4] = ".";
|
|
|
|
x.literals[5] = String.Empty;
|
|
|
|
x.AppCompatLiteral = ":."; // MinuteSecondSep+SecondFractionSep;
|
|
|
|
x.dd = 2;
|
|
|
|
x.hh = 2;
|
|
|
|
x.mm = 2;
|
|
|
|
x.ss = 2;
|
2020-03-09 06:34:16 -07:00
|
|
|
x.ff = DateTimeFormat.[Friend]MaxSecondsFractionDigits;
|
2019-08-23 11:56:54 -07:00
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
|
|
|
// For the "v1" TimeSpan localized patterns, the data is simply literal field separators with
|
|
|
|
// the constants guaranteed to include DHMSF ordered greatest to least significant.
|
|
|
|
// Once the data becomes more complex than this we will need to write a proper tokenizer for
|
|
|
|
// parsing and formatting
|
2020-03-09 06:34:16 -07:00
|
|
|
void Init(StringView format, bool useInvariantFieldLengths) mut
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
literals = new String[6];
|
|
|
|
for (int i = 0; i < literals.Count; i++)
|
|
|
|
literals[i] = "";
|
|
|
|
dd = 0;
|
|
|
|
hh = 0;
|
|
|
|
mm = 0;
|
|
|
|
ss = 0;
|
|
|
|
ff = 0;
|
|
|
|
|
|
|
|
String sb = scope String();
|
|
|
|
bool inQuote = false;
|
|
|
|
char8 quote = '\'';
|
|
|
|
int field = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < format.Length; i++) {
|
|
|
|
switch (format[i]) {
|
|
|
|
case '\'':
|
|
|
|
case '\"':
|
|
|
|
if (inQuote && (quote == format[i])) {
|
|
|
|
/* we were in a quote and found a matching exit quote, so we are outside a quote now */
|
|
|
|
Contract.Assert(field >= 0 && field <= 5, "field >= 0 && field <= 5");
|
|
|
|
if (field >= 0 && field <= 5) {
|
|
|
|
literals[field] = AddOwnedStr(new String(sb));
|
|
|
|
sb.Length = 0;
|
|
|
|
inQuote = false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return; // how did we get here?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!inQuote) {
|
|
|
|
/* we are at the start of a new quote block */
|
|
|
|
quote = format[i];
|
|
|
|
inQuote = true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* we were in a quote and saw the other type of quote character, so we are still in a quote */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '%':
|
|
|
|
Contract.Assert(false, "Unexpected special token '%', Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
|
|
|
sb.Append(format[i]);
|
|
|
|
case '\\':
|
|
|
|
if (!inQuote) {
|
|
|
|
i++; /* skip next character that is escaped by this backslash or percent sign */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
sb.Append(format[i]);
|
|
|
|
case 'd':
|
|
|
|
if (!inQuote) {
|
|
|
|
Contract.Assert((field == 0 && sb.Length == 0) || field == 1,
|
|
|
|
"field == 0 || field == 1, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
|
|
|
field = 1; // DayHourSep
|
|
|
|
dd++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
if (!inQuote) {
|
|
|
|
Contract.Assert((field == 1 && sb.Length == 0) || field == 2,
|
|
|
|
"field == 1 || field == 2, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
|
|
|
field = 2; // HourMinuteSep
|
|
|
|
hh++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'm':
|
|
|
|
if (!inQuote) {
|
|
|
|
Contract.Assert((field == 2 && sb.Length == 0) || field == 3,
|
|
|
|
"field == 2 || field == 3, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
|
|
|
field = 3; // MinuteSecondSep
|
|
|
|
mm++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
if (!inQuote) {
|
|
|
|
Contract.Assert((field == 3 && sb.Length == 0) || field == 4,
|
|
|
|
"field == 3 || field == 4, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
|
|
|
field = 4; // SecondFractionSep
|
|
|
|
ss++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
case 'F':
|
|
|
|
if (!inQuote) {
|
|
|
|
Contract.Assert((field == 4 && sb.Length == 0) || field == 5,
|
|
|
|
"field == 4 || field == 5, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
|
|
|
field = 5; // End
|
|
|
|
ff++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
sb.Append(format[i]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Contract.Assert(field == 5);
|
2020-04-28 07:41:56 -07:00
|
|
|
AppCompatLiteral = AddOwnedStr(new String(MinuteSecondSep, SecondFractionSep));
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
Contract.Assert(0 < dd && dd < 3, "0 < dd && dd < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
|
|
|
Contract.Assert(0 < hh && hh < 3, "0 < hh && hh < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
|
|
|
Contract.Assert(0 < mm && mm < 3, "0 < mm && mm < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
|
|
|
Contract.Assert(0 < ss && ss < 3, "0 < ss && ss < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
|
|
|
Contract.Assert(0 < ff && ff < 8, "0 < ff && ff < 8, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
|
|
|
|
|
|
|
if (useInvariantFieldLengths) {
|
|
|
|
dd = 2;
|
|
|
|
hh = 2;
|
|
|
|
mm = 2;
|
|
|
|
ss = 2;
|
2020-03-09 06:34:16 -07:00
|
|
|
ff = DateTimeFormat.[Friend]MaxSecondsFractionDigits;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (dd < 1 || dd > 2) dd = 2; // The DTFI property has a problem. let's try to make the best of the situation.
|
|
|
|
if (hh < 1 || hh > 2) hh = 2;
|
|
|
|
if (mm < 1 || mm > 2) mm = 2;
|
|
|
|
if (ss < 1 || ss > 2) ss = 2;
|
|
|
|
if (ff < 1 || ff > 7) ff = 7;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} //end of struct FormatLiterals
|
|
|
|
}
|
|
|
|
}
|