Como posso String.Format um objeto TimeSpan com um formato personalizado no .NET?

184

Qual é a maneira recomendada de formatar TimeSpanobjetos em uma string com um formato personalizado?

Hosam Aly
fonte

Respostas:

247

Observe: esta resposta é para .Net 4.0 e superior. Se você deseja formatar um TimeSpan em .Net 3.5 ou abaixo, consulte a resposta de JohannesH .

As seqüências de caracteres personalizadas no formato TimeSpan foram introduzidas no .Net 4.0. Você pode encontrar uma referência completa dos especificadores de formato disponíveis na página Strings de formato do MSDN Custom TimeSpan .

Aqui está um exemplo de sequência de formato de período de tempo:

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

( UPDATE ) e aqui está um exemplo usando a interpolação de string C # 6:

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

Você precisa escapar do caractere ":" com um "\" (que deve ser escapado, a menos que você esteja usando uma string literal).

Este trecho da página Strings de formato de tempo personalizado do MSDN explica como escapar do ":" e "." caracteres em uma sequência de formato:

Os especificadores de formato TimeSpan personalizados não incluem símbolos separadores de espaços reservados, como os símbolos que separam dias de horas, horas de minutos ou segundos de segundos fracionários. Em vez disso, esses símbolos devem ser incluídos na string de formato personalizado como literais de string. Por exemplo, "dd.hh: mm" define um ponto (.) Como o separador entre dias e horas e dois pontos (:) como o separador entre horas e minutos.

Doctor Jones
fonte
7
@Andrei Rinea: Correto, como declarado no início do meu segundo parágrafo ".Net 4 permite que você use seqüências de formato personalizadas com o Timespan".
Doctor Jones
1
@ Edward, isso não está certo. No seu exemplo, você está escapando do primeiro me dos primeiros s, portanto, com uma entrada da myTimeSpan = new TimeSpan(15, 35, 54);instrução myTimeSpan .ToString("hh\\mm\\ss");resultará 15m35s54. Eu não acho que é isso que você pretendia, pois colocaria um m depois do seu horário e um s depois dos seus minutos.
Doctor Jones
1
@ Doutor Jones - Obrigado! Eu quis dizer myTimeSpan.ToString ("h \\ hm \\ ms \\ s"); ou myTimeSpan.ToString (@ "h \ hm \ ms \ s"); que dá 15h35m54s
Edward
2
basta ter cuidado com esta solução, porque não vai funcionar correctamente quando a parte Horas é mais do que 24
Zoltan Tirinda
1
@QuarK, não existe um especificador de formato personalizado que faça isso, todos fornecem as horas que não são contadas como parte dos dias. Você poderia fazer isso em vez disso $"{myTimeSpan.TotalHours}:{myTimeSpan:mm\\:ss}". Do ponto de vista do usuário, pode ser melhor exibir os dias, porém, ninguém quer descobrir mentalmente quantos dias existem em mais de 200 horas.
Doctor Jones
91

Para o .NET 3.5 e inferior, você pode usar:

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

Código extraído de uma resposta Jon Skeet em bytes

Para o .NET 4.0 e superior, consulte a resposta DoctaJonez .

JohannesH
fonte
Sim obrigado. Mas acho que a abordagem DateTime é mais personalizável, pois funcionaria para qualquer formato de horário suportado pelo DateTime. Essa abordagem é difícil de usar para mostrar AM / PM, por exemplo.
23909 Hosam Aly
1
Claro, o TimeSpan representa um período de tempo, não uma hora do dia (mesmo que você acredite o contrário na propriedade DateTime.Now.TimeOfDay). Se você precisar representar uma hora específica do dia, sugiro que continue usando a classe DateTime.
22909 JohannesH
7
Lembre-se de que se o TimeSpan for igual ou superior a 24 horas, você receberá uma formatação incorreta.
21420 JohannesH
31

Uma maneira é criar um DateTimeobjeto e usá-lo para formatar:

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

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

É assim que eu sei. Espero que alguém possa sugerir uma maneira melhor.

Hosam Aly
fonte
14
Isso realmente só funcionará se o TimeSpan for inferior a um dia. Essa pode não ser uma restrição tão terrível, mas impede que ela seja uma solução geral.
tvanfosson 22/02/09
Retorna o valor correto? Ts Dim Nova TimeSpan (11, 22, 30, 30): Dim sss As String = Nova datetime (ts.Ticks) .ToString ( "dd.hh: mm: ss")
NeverHopeless
10

Simples. Use TimeSpan.ToStringcom c, g ou G. Mais informações no MSDN

KKK
fonte
1
Obrigado pela sua resposta. Esse método é aparentemente novo no .NET 4 e não existia quando a pergunta foi feita. Ele também não suporta formatos personalizados. No entanto, é uma adição valiosa às respostas a essas perguntas. Obrigado novamente.
Hosam Aly
8

Eu iria com

myTimeSpan.ToString("hh\\:mm\\:ss");
Shehab Fawzy
fonte
Simples e limpo! uma alternativa é @ "hh \: mm: ss" \
Xilmiki
5

Pessoalmente, gosto desta abordagem:

TimeSpan ts = ...;
string.Format("{0:%d}d {0:%h}h {0:%m}m {0:%s}s", ts);

Você pode fazer isso da maneira que desejar, sem problemas:

string.Format("{0:%d}days {0:%h}hours {0:%m}min {0:%s}sec", ts);
string.Format("{0:%d}d {0:%h}h {0:%m}' {0:%s}''", ts);
Ninguém
fonte
5

Este é incrível:

string.Format("{0:00}:{1:00}:{2:00}",
               (int)myTimeSpan.TotalHours,
               myTimeSpan.Minutes,
               myTimeSpan.Seconds);
Harpal
fonte
1
Você precisa converter o myTimeSpan.TotalHours para um int - caso contrário, ele poderá ser arredondado. Veja a resposta de JohannesH
codeulike
3

Você também pode ir com:

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")

EDITAR:

Você também pode olhar para Strings.Format .

    Dim ts As New TimeSpan(23, 30, 59)
    Dim str As String = Strings.Format(New DateTime(ts.Ticks), "H:mm:ss")
NeverHopeless
fonte
3
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");

Todos os caracteres literais devem ser escapados.

Ryan Williams
fonte
1

Eu usei o código abaixo. É longo, mas ainda assim é uma expressão e produz uma saída muito amigável, pois não gera dias, horas, minutos ou segundos se tiverem valor zero.

Na amostra, produz saída: "4 dias 1 hora 3 segundos".

TimeSpan sp = new TimeSpan(4,1,0,3);
string.Format("{0}{1}{2}{3}", 
        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);
panpawel
fonte
Agora, há uma maneira muito melhor de escrever isso! Tente refatorar todas as operações comuns e você poderá fazer com que esse código pareça muito, muito melhor.
Hosam Aly
@Hosam Aly; Estou aprendendo o tempo todo. Você gostaria de publicar seu código aprimorado?
Panpawel
String timeComponent(int value, String name) { return value > 0 ? value + " " + name + (value > 1 ? "s" : ""); }Chame isso para cada componente (por exemplo timeComponent(sp.Days, "day")) e use String.joinpara inserir os espaços.
Hosam Aly
1

Eu uso esse método Sou belga e falo holandês, portanto, o plural de horas e minutos não está apenas adicionando 's' ao final, mas quase uma palavra diferente de singular.

Pode parecer longo, mas é muito legível, eu acho:

 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
Dabriel
fonte
Se você escreve um software que precisa ser traduzido, esse é o caminho a seguir. O TimeSpan.ToString () padrão é muito desajeitado para os usuários finais normais entenderem, especialmente quando o período é superior a um dia.
Neil
1

Essa é a abordagem que eu usei com formatação condicional. e eu posto aqui porque acho que é uma maneira limpa.

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

exemplo de saídas:

00:00 (mínimo)

1:43:04 (quando temos horas)

15:03:01 (quando o horário tiver mais de 1 dígito)

2:4:22:04 (quando tivermos dias.)

A formatação é fácil. time.Days:#0:;;\\o formato anterior ;;é para quando o valor é positivo. valores negativos são ignorados. e para valores zero, temos ;;\\para ocultá-lo em uma string formatada. observe que a barra invertida escapada é necessária, caso contrário, ela não será formatada corretamente.

M.kazem Akhgary
fonte
1

Aqui está o meu método de extensão :

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));
}

Chamada de exemplo:

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

Resultado:

3 days, 14 hours, 15 minutes, 65 milliseconds
chviLadislav
fonte
1

Isso é uma dor no VS 2010, aqui está minha solução alternativa.

 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;
                }
            } 
        }
rguez06
fonte
1

O Substringmétodo funciona perfeitamente quando você deseja apenas Horas: Minutos: Segundos. É um código simples, limpo e fácil de entender.

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

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

    Console.WriteLine(formatted);
GER
fonte
0

Aqui está a minha versão. Ele mostra apenas o necessário, lida com pluralização, negativos e tentei torná-lo leve.

Exemplos de saída

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

Código

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} day{1}", days, days == 1 ? null : "s"));
        if (hours > 0)
            parts.Add(string.Format("{0} hour{1}", hours, hours == 1 ? null : "s"));
        if (minutes > 0)
            parts.Add(string.Format("{0} minute{1}", 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("{0}{1:.###} seconds", seconds, fractionalSeconds));
        }

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

Se você deseja um formato de duração semelhante ao youtube, dado o número de segundos

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));
}

Resultado:

     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
zenny
fonte
0

Eu queria retornar uma string como "1 dia, 2 horas e 3 minutos" e também levar em consideração se, por exemplo, dias ou minutos são 0 e depois não os mostramos. graças a John Rasch por sua resposta, que a minha é apenas uma extensão do

TimeSpan timeLeft = New Timespan(0, 70, 0);
String.Format("{0}{1}{2}{3}{4}{5}",
    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 ");
Torta
fonte