【问题标题】:Specifying DateTime.ToString("o") decimal precision指定 DateTime.ToString("o") 十进制精度
【发布时间】:2018-03-01 05:56:06
【问题描述】:

我有一个 DateTime 对象,我正在尝试以符合 ISO 8601 的格式将其输出到 xml 文件中,以便在两个系统之间传输 - 我们无法控制接收者。 .net round trip format 大部分满足此要求,但将精度强制为 7dp。

有什么方法可以指定吗?例如"o:0" 完全省略毫秒小数位或"o:3" 将其设置为3dp。我知道我可以使用yyyy'-'MM'-'dd'T'HH':'mm':'ssK(或yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffK)作为自定义格式说明符自己格式化输出,以便在必要时控制小数位,但想看看我是否错过了一种简单地将小数精度传递给@的方法987654326@ 格式说明符。

【问题讨论】:

  • 如果文档是可以参考的,我不这么认为
  • 可悲的是,这就是我所怀疑的。我将深入研究 github 上的 corefx 源代码,看看是否有任何未记录的内容。只是想我会在这里问,以防有人以前处理过这个问题。如果我找到任何东西,我一定会更新/回答,以防其他人将来需要这个

标签: c# datetime formatting


【解决方案1】:

似乎答案是否定的,没有任何东西(至少在核心框架中)可以自定义它。查看source code for DateTime roundtrip formatting,这是硬编码为 7dp:

// Omitted for brevity
...
AppendHHmmssTimeOfDay(result, dateTime);
result.Append('.');

long fraction = dateTime.Ticks % TimeSpan.TicksPerSecond;
AppendNumber(result, fraction, 7);

如果有人感兴趣,我的解决方案是实现一个自定义的ToFormattedString() 扩展方法来处理这个问题,如果需要,它将用自定义的格式字符串替换格式字符串,并用它调用ToString

using System.Globalization; // System.Globalization needed for IFormatProvider overload
using System.Text; // System.Text required for StringBuilder class

namespace Extensions
{
    public static class DateTimeFormatExtension
    {
        // Consts for building the custom format string
        private const string ROUNDTRIP_FORMAT_PREFIX = "yyyy'-'MM'-'dd'T'HH':'mm':'ss";
        private const char ROUNDTRIP_FORMAT_FRACTION = 'f';
        private const char ROUNDTRIP_FORMAT_SUFFIX = 'K';

        // Appending the 'f' custom format string maxes out at "fffffff"(7 dp) and will throw an exception if given more 
        private const int DATETIME_MAX_DP = 7;

        private static int GetRoundtripLength(int decimalPlaces) =>
            ROUNDTRIP_FORMAT_PREFIX.Length + decimalPlaces + 2; // +2 to account for the '.' and the 'K' suffix

        public static string ToFormattedString(this DateTime input) => input.ToString();

        public static string ToFormattedString(this DateTime input, string format)
        {
            var provider = DateTimeFormatInfo.CurrentInfo;
            return input.ToFormattedString(format, provider);
        }

                    public static string ToFormattedString(this DateTime input, string format, IFormatProvider provider)
        {
            string parsedFormat = format;
            if (!string.IsNullOrWhiteSpace(format))
            {
                switch (format[0])
                {
                    case 'o':
                    case 'O':
                        var precision = format.Substring(1);
                        // Only do this if we have a custom 'o' string, otherwise us the base functionality
                        if (!string.IsNullOrWhiteSpace(precision))
                        {
                            // If the custom addition to the format string is an integer, use that to determine dp
                            if (int.TryParse(precision, out int decimalCount))
                            {
                                // Build the format string
                                var formatBuilder = new StringBuilder(GetRoundtripLength(decimalCount));
                                formatBuilder.Append(ROUNDTRIP_FORMAT_PREFIX);

                                // Append '.' and 'f' chars to format string (Append nothing if 0 dp)
                                if (decimalCount > 0)
                                {
                                    formatBuilder
                                        .Append('.')
                                        .Append(ROUNDTRIP_FORMAT_FRACTION,
                                            // Cap max dp length to avoid exceptions
                                            Math.Min(decimalCount, DATETIME_MAX_DP));
                                }

                                // Append 'K' suffix
                                formatBuilder.Append(ROUNDTRIP_FORMAT_SUFFIX);
                                parsedFormat = formatBuilder.ToString();
                            }
                        }
                        break;
                    default:
                        break;
                }
            }
            return input.ToString(parsedFormat, provider);
        }
    }
}

然后可以这样使用:

using Extensions;
...
DateTime currentDateTime = DateTime.UtcNow;
string result;
result = currentDateTime.ToFormattedString(); // empty just calls the default ToString()
// result: "2018-03-02 12:31:17 AM"
result = currentDateTime.ToFormattedString("dd/MM/yy ssmmhh"); // custom format strings still work
// result: "02-03-18 173112"
result = currentDateTime.ToFormattedString("d"); // standard format strings still work
// result: "2018-03-02"
result = currentDateTime.ToFormattedString("D");
// result: "Friday, March 2, 2018"
result = currentDateTime.ToFormattedString("F");
// result: "Friday, March 2, 2018 12:31:17 AM"
result = currentDateTime.ToFormattedString("o"); // standard format specifier uses default ToString("o") behaviour
// result: "2018-03-02T00:31:17.9818727Z"
result = currentDateTime.ToFormattedString("o0"); // no decimal places
// result: "2018-03-02T00:31:17Z"
result = currentDateTime.ToFormattedString("o3"); // 3 decimal places
// result: "2018-03-02T00:31:17.981Z"
result = currentDateTime.ToFormattedString("o100"); // too many decimals cap at 7
// result: "2018-03-02T00:31:17.9818727Z"

编辑:更新以从格式字符串中删除“:”以保持与 numeric formatting 上指定的小数精度方式一致:

标准数字格式字符串采用Axx 的形式,其中:

A 是一个称为格式说明符的单个字母字符。

...

xx 是一个可选整数,称为精度说明符

【讨论】:

  • 虽然我喜欢您的自定义格式,但对于许多人来说,拥有完整的自定义格式字符串也可以。它是:"yyyy'-'MM'-'dd'T'HH':'mm':'ss" 并会导致 "2021-06-10T20:05:00" var myDt = DateTime.UtcNow; var myFmt = myDt.ToString(""yyyy'-'MM'-'dd'T'HH':'mm':'ss"")
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-08
  • 1970-01-01
  • 1970-01-01
  • 2018-01-20
  • 1970-01-01
相关资源
最近更新 更多