如何在 .NET 中使用自定义格式对 TimeSpan 对象进行 String.Format ?

Posted

技术标签:

【中文标题】如何在 .NET 中使用自定义格式对 TimeSpan 对象进行 String.Format ?【英文标题】:How can I String.Format a TimeSpan object with a custom format in .NET? 【发布时间】:2009-02-22 12:57:55 【问题描述】:

TimeSpan 对象格式化为具有自定义格式的字符串的推荐方法是什么?

【问题讨论】:

【参考方案1】:

请注意:此答案适用于 .Net 4.0 及更高版本。如果您想在 .Net 3.5 或更低版本中格式化 TimeSpan,请参阅JohannesH's answer。

.Net 4.0 中引入了自定义 TimeSpan 格式字符串。您可以在 MSDN Custom TimeSpan Format Strings 页面上找到可用格式说明符的完整参考。

这是一个示例时间跨度格式字符串:

string.Format("0:hh\\:mm\\:ss", myTimeSpan); //example output 15:36:15

(UPDATE) 这是一个使用 C# 6 字符串插值的示例:

$"myTimeSpan:hh\\:mm\\:ss"; //example output 15:36:15

您需要使用“\”转义“:”字符(除非您使用逐字字符串,否则必须对其本身进行转义)。

MSDN Custom TimeSpan Format Strings 页面的这段摘录解释了如何转义“:”和“.”。格式字符串中的字符:

自定义 TimeSpan 格式说明符不包含占位符分隔符,例如将天与小时、小时与分钟或秒与小数秒分开的符号。相反,这些符号必须作为字符串文字包含在自定义格式字符串中。例如,“dd.hh:mm”将句点 (.) 定义为天和小时之间的分隔符,将冒号 (:) 定义为小时和分钟之间的分隔符。

【讨论】:

@Andrei Rinea:正确,正如我在第二段“.Net 4 允许您使用自定义格式字符串与 Timespan”开头所述。 @Edward,这不太对。在您的示例中,您将转义第一个 m 和第一个 s,因此输入 myTimeSpan = new TimeSpan(15, 35, 54); 语句 myTimeSpan .ToString("hh\\mm\\ss"); 将导致 15m35s54。我不认为这就是你的意图,因为它会在你的小时后放置一个 m,在你的分钟后放置一个 s。 @Doctor Jones - 谢谢!我的意思是 myTimeSpan.ToString("h\\hm\\ms\\s");或 myTimeSpan.ToString(@"h\hm\ms\s");这给出了 15h35m54s 这个解决方案要小心,因为当 Hours 部分超过 24 时,它将无法正常工作 @QuarK,没有自定义格式说明符可以做到这一点,它们都会为您提供不计入天数的小时数。你可以通过$"myTimeSpan.TotalHours:myTimeSpan:mm\\:ss" 来代替。从用户的角度来看,输出天数可能会更好,但没有人想在心里算出 200 多个小时中有多少天。【参考方案2】:

对于 .NET 3.5 及更低版本,您可以使用:

string.Format ("0:00:1:00:2:00", 
               (int)myTimeSpan.TotalHours, 
                    myTimeSpan.Minutes, 
                    myTimeSpan.Seconds);

代码取自 Jon Skeet answer 字节

对于 .NET 4.0 及更高版本,请参阅 DoctJonez answer。

【讨论】:

好的,谢谢。但我认为 DateTime 方法更可定制,因为它适用于 DateTime 支持的任何时间格式。例如,这种方法很难用于显示 AM/PM。 当然,TimeSpan 旨在表示一段时间,而不是一天中的某个时间(即使 DateTime.Now.TimeOfDay 属性会让您不相信)。如果您需要表示一天中的特定时间,我建议您继续使用 DateTime 类。 请记住,如果 TimeSpan 等于或超过 24 小时,您将得到不正确的格式。【参考方案3】:

一种方法是创建一个DateTime 对象并将其用于格式化:

new DateTime(myTimeSpan.Ticks).ToString(myCustomFormat)

// or using String.Format:
String.Format("0:HHmmss", new DateTime(myTimeSpan.Ticks))

这是我知道的方式。我希望有人能提出更好的方法。

【讨论】:

这只有在 TimeSpan 小于一天的情况下才有效。这可能不是一个如此可怕的限制,但它使它不能成为一个通用的解决方案。 它返回正确的值吗? Dim ts As New TimeSpan(11, 22, 30, 30):Dim sss As String = New DateTime(ts.Ticks).ToString("dd.hh:mm:ss")【参考方案4】:

简单。将 TimeSpan.ToString 与 c、g 或 G 一起使用。更多信息请访问 MSDN

【讨论】:

感谢您的回答。这种方法显然是 .NET 4 中的新方法,并且在提出问题时不存在。它也不支持自定义格式。尽管如此,它是对这个问题的答案的一个有价值的补充。再次感谢。【参考方案5】:

我会去

myTimeSpan.ToString("hh\\:mm\\:ss");

【讨论】:

简单干净!另一种选择是@"hh\:mm\:ss"【参考方案6】:
Dim duration As New TimeSpan(1, 12, 23, 62)

DEBUG.WriteLine("Time of Travel: " + duration.ToString("dd\.hh\:mm\:ss"))

适用于框架 4

http://msdn.microsoft.com/en-us/library/ee372287.aspx

【讨论】:

【参考方案7】:

就个人而言,我喜欢这种方法:

TimeSpan ts = ...;
string.Format("0:%dd 0:%hh 0:%mm 0:%ss", ts);

您可以根据自己的喜好进行自定义,没有任何问题:

string.Format("0:%ddays 0:%hhours 0:%mmin 0:%ssec", ts);
string.Format("0:%dd 0:%hh 0:%m' 0:%s''", ts);

【讨论】:

【参考方案8】:

这太棒了:

string.Format("0:00:1:00:2:00",
               (int)myTimeSpan.TotalHours,
               myTimeSpan.Minutes,
               myTimeSpan.Seconds);

【讨论】:

您需要将 myTimeSpan.TotalHours 转换为 int - 否则可能会被四舍五入。查看 JohannesH 的回答【参考方案9】:

你也可以选择:

Dim ts As New TimeSpan(35, 21, 59, 59)  '(11, 22, 30, 30)    '
Dim TimeStr1 As String = String.Format("0:c", ts)
Dim TimeStr2 As String = New Date(ts.Ticks).ToString("dd.HH:mm:ss")

编辑:

你也可以看看Strings.Format。

    Dim ts As New TimeSpan(23, 30, 59)
    Dim str As String = Strings.Format(New DateTime(ts.Ticks), "H:mm:ss")

【讨论】:

【参考方案10】:
if (timeSpan.TotalDays < 1)
    return timeSpan.ToString(@"hh\:mm\:ss");

return timeSpan.TotalDays < 2
    ? timeSpan.ToString(@"d\ \d\a\y\ hh\:mm\:ss")
    : timeSpan.ToString(@"d\ \d\a\y\s\ hh\:mm\:ss");

所有文字字符都必须转义。

【讨论】:

【参考方案11】:

这是我自己使用条件格式的方法。我把它贴在这里是因为我认为这是一种干净的方式。

$"time.Days:#0:;;\\time.Hours:#0:;;\\time.Minutes:00:time.Seconds:00"

输出示例:

00:00(最低)

1:43:04(我们有时间的时候)

15:03:01(当小时数超过 1 位时)

2:4:22:04(当我们有日子的时候。)

格式化很简单。 time.Days:#0:;;\\ ;; 之前的格式用于值为正数时。负值被忽略。对于零值,我们有;;\\ 以便将其隐藏在格式化字符串中。请注意,转义的反斜杠是必需的,否则将无法正确格式化。

【讨论】:

:#0:;;\\ 是什么语言?我在哪里可以读到它?【参考方案12】:

这是我的extension method:

public static string ToFormattedString(this TimeSpan ts)

    const string separator = ", ";

    if (ts.TotalMilliseconds < 1)  return "No time"; 

    return string.Join(separator, new string[]
    
        ts.Days > 0 ? ts.Days + (ts.Days > 1 ? " days" : " day") : null,
        ts.Hours > 0 ? ts.Hours + (ts.Hours > 1 ? " hours" : " hour") : null,
        ts.Minutes > 0 ? ts.Minutes + (ts.Minutes > 1 ? " minutes" : " minute") : null,
        ts.Seconds > 0 ? ts.Seconds + (ts.Seconds > 1 ? " seconds" : " second") : null,
        ts.Milliseconds > 0 ? ts.Milliseconds + (ts.Milliseconds > 1 ? " milliseconds" : " millisecond") : null,
    .Where(t => t != null));

调用示例:

string time = new TimeSpan(3, 14, 15, 0, 65).ToFormattedString();

输出:

3 days, 14 hours, 15 minutes, 65 milliseconds

【讨论】:

【参考方案13】:

我使用了下面的代码。它很长,但它仍然是一个表达式,并且会产生非常友好的输出,因为它不会输出天、小时、分钟或秒(如果它们的值为零)。

在示例中,它产生输出:“4 天 1 小时 3 秒”。

TimeSpan sp = new TimeSpan(4,1,0,3);
string.Format("0123", 
        sp.Days > 0 ? ( sp.Days > 1 ? sp.ToString(@"d\ \d\a\y\s\ "): sp.ToString(@"d\ \d\a\y\ ")):string.Empty,
        sp.Hours > 0 ? (sp.Hours > 1 ? sp.ToString(@"h\ \h\o\u\r\s\ ") : sp.ToString(@"h\ \h\o\u\r\ ")):string.Empty,
        sp.Minutes > 0 ? (sp.Minutes > 1 ? sp.ToString(@"m\ \m\i\n\u\t\e\s\ ") :sp.ToString(@"m\ \m\i\n\u\t\e\ ")):string.Empty,
        sp.Seconds > 0 ? (sp.Seconds > 1 ? sp.ToString(@"s\ \s\e\c\o\n\d\s"): sp.ToString(@"s\ \s\e\c\o\n\d\s")):string.Empty);

【讨论】:

现在有一个更好的方法来写这个!尝试重构所有常见的操作,你可以让这段代码看起来好很多。 @Hosam Aly;我一直在学习,您愿意发布您改进的代码吗? String timeComponent(int value, String name) return value &gt; 0 ? value + " " + name + (value &gt; 1 ? "s" : ""); 为每个组件调用它(例如timeComponent(sp.Days, "day")),然后使用String.join 插入空格。【参考方案14】:

我使用这种方法。我是比利时人,说荷兰语,所以小时和分钟的复数不仅仅是在末尾添加“s”,而且几乎是一个与单数不同的词。

它可能看起来很长,但我认为它非常可读:

 public static string SpanToReadableTime(TimeSpan span)
    
        string[] values = new string[4];  //4 slots: days, hours, minutes, seconds
        StringBuilder readableTime = new StringBuilder();

        if (span.Days > 0)
        
            if (span.Days == 1)
                values[0] = span.Days.ToString() + " dag"; //day
            else
                values[0] = span.Days.ToString() + " dagen";  //days

            readableTime.Append(values[0]);
            readableTime.Append(", ");
        
        else
            values[0] = String.Empty;


        if (span.Hours > 0)
        
            if (span.Hours == 1)
                values[1] = span.Hours.ToString() + " uur";  //hour
            else
                values[1] = span.Hours.ToString() + " uren";  //hours

            readableTime.Append(values[1]);
            readableTime.Append(", ");

        
        else
            values[1] = string.Empty;

        if (span.Minutes > 0)
        
            if (span.Minutes == 1)
                values[2] = span.Minutes.ToString() + " minuut";  //minute
            else
                values[2] = span.Minutes.ToString() + " minuten";  //minutes

            readableTime.Append(values[2]);
            readableTime.Append(", ");
        
        else
            values[2] = string.Empty;

        if (span.Seconds > 0)
        
            if (span.Seconds == 1)
                values[3] = span.Seconds.ToString() + " seconde";  //second
            else
                values[3] = span.Seconds.ToString() + " seconden";  //seconds

            readableTime.Append(values[3]);
        
        else
            values[3] = string.Empty;


        return readableTime.ToString();
    //end SpanToReadableTime

【讨论】:

如果您编写需要翻译的软件,那么这几乎是可行的方法。标准的 TimeSpan.ToString() 对于普通最终用户来说太笨拙了,尤其是当跨度超过一天时。【参考方案15】:

这是 VS 2010 中的一个难题,这是我的解决方案。

 public string DurationString
        
            get 
            
                if (this.Duration.TotalHours < 24)
                    return new DateTime(this.Duration.Ticks).ToString("HH:mm");
                else //If duration is more than 24 hours
                
                    double totalminutes = this.Duration.TotalMinutes;
                    double hours = totalminutes / 60;
                    double minutes = this.Duration.TotalMinutes - (Math.Floor(hours) * 60);
                    string result = string.Format("0:1", Math.Floor(hours).ToString("00"), Math.Floor(minutes).ToString("00"));
                    return result;
                
             
        

【讨论】:

【参考方案16】:

当您只需要小时:分钟:秒时,Substring 方法非常有效。它是简单、干净的代码并且易于理解。

    var yourTimeSpan = DateTime.Now - DateTime.Now.AddMinutes(-2);

    var formatted = yourTimeSpan.ToString().Substring(0,8);// 00:00:00 

    Console.WriteLine(formatted);

【讨论】:

为什么不只是.ToString(@"hh\:mm\:ss")?这不是更容易理解吗? @Arad 这看起来确实容易多了!想知道我为什么写这个答案...【参考方案17】:

这是我的版本。它只显示必要的内容,处理复数形式、否定形式,并且我尝试使其轻量级。

输出示例

0 seconds
1.404 seconds
1 hour, 14.4 seconds
14 hours, 57 minutes, 22.473 seconds
1 day, 14 hours, 57 minutes, 22.475 seconds

代码

public static class TimeSpanExtensions

    public static string ToReadableString(this TimeSpan timeSpan)
    
        int days = (int)(timeSpan.Ticks / TimeSpan.TicksPerDay);
        long subDayTicks = timeSpan.Ticks % TimeSpan.TicksPerDay;

        bool isNegative = false;
        if (timeSpan.Ticks < 0L)
        
            isNegative = true;
            days = -days;
            subDayTicks = -subDayTicks;
        

        int hours = (int)((subDayTicks / TimeSpan.TicksPerHour) % 24L);
        int minutes = (int)((subDayTicks / TimeSpan.TicksPerMinute) % 60L);
        int seconds = (int)((subDayTicks / TimeSpan.TicksPerSecond) % 60L);
        int subSecondTicks = (int)(subDayTicks % TimeSpan.TicksPerSecond);
        double fractionalSeconds = (double)subSecondTicks / TimeSpan.TicksPerSecond;

        var parts = new List<string>(4);

        if (days > 0)
            parts.Add(string.Format("0 day1", days, days == 1 ? null : "s"));
        if (hours > 0)
            parts.Add(string.Format("0 hour1", hours, hours == 1 ? null : "s"));
        if (minutes > 0)
            parts.Add(string.Format("0 minute1", minutes, minutes == 1 ? null : "s"));
        if (fractionalSeconds.Equals(0D))
        
            switch (seconds)
            
                case 0:
                    // Only write "0 seconds" if we haven't written anything at all.
                    if (parts.Count == 0)
                        parts.Add("0 seconds");
                    break;

                case 1:
                    parts.Add("1 second");
                    break;

                default:
                    parts.Add(seconds + " seconds");
                    break;
            
        
        else
        
            parts.Add(string.Format("01:.### seconds", seconds, fractionalSeconds));
        

        string resultString = string.Join(", ", parts);
        return isNegative ? "(negative) " + resultString : resultString;
    

【讨论】:

【参考方案18】:

如果你想要类似于 youtube 的时长格式,给定秒数

int[] duration =  0, 4, 40, 59, 60, 61, 400, 4000, 40000, 400000 ;
foreach (int d in duration)

    Console.WriteLine("0, 6 -> 1, 10", d, d > 59 ? TimeSpan.FromSeconds(d).ToString().TrimStart("00:".ToCharArray()) : string.Format("0:0:00", d));

输出:

     0 ->       0:00
     4 ->       0:04
    40 ->       0:40
    59 ->       0:59
    60 ->       1:00
    61 ->       1:01
   400 ->       6:40
  4000 ->    1:06:40
 40000 ->   11:06:40
400000 -> 4.15:06:40

【讨论】:

【参考方案19】:

我想返回一个字符串,例如“1 天 2 小时 3 分钟”,并考虑例如天数或分钟数是否为 0,然后不显示它们。感谢John Rasch 他的回答,我的回答几乎不是

TimeSpan timeLeft = New Timespan(0, 70, 0);
String.Format("012345",
    Math.Floor(timeLeft.TotalDays) == 0 ? "" : 
    Math.Floor(timeLeft.TotalDays).ToString() + " ",
    Math.Floor(timeLeft.TotalDays) == 0 ? "" : Math.Floor(timeLeft.TotalDays) == 1 ? "day " : "days ",
    timeLeft.Hours == 0 ? "" : timeLeft.Hours.ToString() + " ",
    timeLeft.Hours == 0 ? "" : timeLeft.Hours == 1 ? "hour " : "hours ",
    timeLeft.Minutes == 0 ? "" : timeLeft.Minutes.ToString() + " ",
    timeLeft.Minutes == 0 ? "" : timeLeft.Minutes == 1 ? "minute " : "minutes ");

【讨论】:

【参考方案20】:

没有人展示过使用十进制格式说明符的方法,这是我最喜欢的一种,尤其是与字符串插值一起使用时 - https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings?redirectedfrom=MSDN#decimal-format-specifier-d

例如:

$"time.Hours:D2:time.Minutes:D2:time.Seconds:D2.time.Milliseconds:D3"
// Sample output: 00:00:09.200

你当然可以用一些辅助方法来包装它。

【讨论】:

以上是关于如何在 .NET 中使用自定义格式对 TimeSpan 对象进行 String.Format ?的主要内容,如果未能解决你的问题,请参考以下文章

vb.net 如何引用自定义类库

如何使用 VB.Net 自定义 Excel

使用JSON.NET实现对象属性的自定义化格式

如何自定义word中标题的格式

使用 Json.Net 序列化时指定自定义 DateTime 格式

如何对 ASP.NET WebApi 的每个请求应用自定义验证到 JWT 令牌?