¿Cómo puedo String.Format un objeto TimeSpan con un formato personalizado en .NET?

Respuestas:

247

Tenga en cuenta: esta respuesta es para .Net 4.0 y superior. Si desea formatear un TimeSpan en .Net 3.5 o inferior, consulte la respuesta de JohannesH .

Se introdujeron cadenas de formato TimeSpan personalizadas en .Net 4.0. Puede encontrar una referencia completa de los especificadores de formato disponibles en la página de cadenas de formato de TimeSpan personalizado de MSDN .

Aquí hay una cadena de formato de intervalo de tiempo de ejemplo:

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

( ACTUALIZACIÓN ) y aquí hay un ejemplo usando la interpolación de cadenas C # 6:

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

Debe escapar del carácter ":" con un "\" (que se debe escapar a menos que esté utilizando una cadena literal).

Este extracto de la página de cadenas de formato de TimeSpan personalizado de MSDN explica cómo escapar de ":" y "." caracteres en una cadena de formato:

Los especificadores de formato de TimeSpan personalizados no incluyen símbolos de separador de marcador de posición, como los símbolos que separan días de horas, horas de minutos o segundos de segundos fraccionarios. En cambio, estos símbolos deben incluirse en la cadena de formato personalizado como literales de cadena. Por ejemplo, "dd.hh: mm" define un punto (.) Como el separador entre días y horas, y dos puntos (:) como el separador entre horas y minutos.

Doctor jones
fuente
77
@Andrei Rinea: Correcto, como se indicó al comienzo de mi segundo párrafo ".Net 4 le permite usar cadenas de formato personalizado con Timespan".
Doctor Jones
1
@ Edward, eso no está del todo bien. En su ejemplo, está escapando el primer my el primer s, por lo que con una entrada demyTimeSpan = new TimeSpan(15, 35, 54); la declaración myTimeSpan .ToString("hh\\mm\\ss");dará como resultado 15m35s54. No creo que sea lo que pretendías, ya que colocará una m después de tus horas y una s después de tus minutos.
Doctor Jones
1
@Doctor Jones - ¡Gracias! Me refería a myTimeSpan.ToString ("h \\ hm \\ ms \\ s"); o myTimeSpan.ToString (@ "h \ hm \ ms \ s"); que da 15h35m54s
Edward
2
solo tenga cuidado con esta solución, porque no funcionará correctamente cuando la parte Horas sea más de 24
Zoltan Tirinda
1
@QuarK, no hay un especificador de formato personalizado que lo haga, todos te dan las horas que no se cuentan como parte de los días. Sin embargo, podrías hacer esto $"{myTimeSpan.TotalHours}:{myTimeSpan:mm\\:ss}". Desde el punto de vista del usuario, podría ser mejor mostrar los días, sin embargo, nadie quiere calcular mentalmente cuántos días hay en más de 200 horas.
Doctor Jones
91

Para .NET 3.5 y versiones anteriores, puede usar:

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

Código tomado de una respuesta de Jon Skeet en bytes

Para .NET 4.0 y superior, vea DoctaJonez respuesta de .

JohannesH
fuente
Si, gracias. Pero creo que el enfoque DateTime es más personalizable, ya que funcionaría para cualquier formato de hora compatible con DateTime. Este enfoque es difícil de usar para mostrar AM / PM, por ejemplo.
Hosam Aly
1
Claro, TimeSpan está destinado a representar un período de tiempo, no una hora del día (aunque la propiedad DateTime.Now.TimeOfDay le haría creer lo contrario). Si necesita representar una hora específica del día, le sugiero que continúe usando la clase DateTime.
JohannesH
77
Solo recuerde que si el TimeSpan es igual o superior a 24 horas, obtendrá un formato incorrecto.
JohannesH
31

Una forma es crear un DateTimeobjeto y usarlo para formatear:

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

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

Así es como lo sé. Espero que alguien pueda sugerir una mejor manera.

Hosam Aly
fuente
14
Esto realmente solo funcionará si TimeSpan es menos de un día. Puede que no sea una restricción tan terrible, pero evita que sea una solución general.
tvanfosson
¿Devuelve el valor correcto? Dim ts As New TimeSpan (11, 22, 30, 30): Dim sss As String = New DateTime (ts.Ticks) .ToString ("dd.hh: mm: ss")
NeverHopeless
10

Sencillo. Usar TimeSpan.ToStringcon c, go G. Más información en MSDN

KKK
fuente
1
Gracias por su respuesta. Este método es aparentemente nuevo en .NET 4 y no existía cuando se hizo la pregunta. Tampoco admite formatos personalizados. Sin embargo, es una valiosa adición a las respuestas a estas preguntas. Gracias de nuevo.
Hosam Aly
8

Yo iria con

myTimeSpan.ToString("hh\\:mm\\:ss");
Shehab Fawzy
fuente
¡Simple y limpio! una alternativa es @ "hh \: mm \: ss"
Xilmiki
5

Personalmente, me gusta este enfoque:

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

Puedes hacer esto tan personalizado como quieras sin 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);
Ninguno
fuente
5

Este es uno increíble:

string.Format("{0:00}:{1:00}:{2:00}",
               (int)myTimeSpan.TotalHours,
               myTimeSpan.Minutes,
               myTimeSpan.Seconds);
Harpal
fuente
1
Debe transmitir myTimeSpan.TotalHours a un int; de lo contrario, podría redondearse. Vea la respuesta de JohannesH
codeulike
3

También puedes ir con:

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:

También puedes mirar Strings.Format .

    Dim ts As New TimeSpan(23, 30, 59)
    Dim str As String = Strings.Format(New DateTime(ts.Ticks), "H:mm:ss")
Nunca sin esperanza
fuente
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 los caracteres literales deben escapar.

Ryan Williams
fuente
1

Usé el código a continuación. Es largo, pero aún así es una expresión, y produce una salida muy amigable, ya que no genera días, horas, minutos o segundos si tienen un valor de cero.

En la muestra produce resultados: "4 días 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
fuente
¡Ahora hay una forma mucho mejor de escribir esto! Intente refactorizar todas las operaciones comunes, y puede hacer que este código se vea mucho, mucho mejor.
Hosam Aly
@Hosam Aly; Estoy aprendiendo todo el tiempo, ¿te importa publicar tu código mejorado?
panpawel 01 de
String timeComponent(int value, String name) { return value > 0 ? value + " " + name + (value > 1 ? "s" : ""); }Llame a eso para cada componente (por ejemplo timeComponent(sp.Days, "day")), luego use String.joinpara insertar los espacios.
Hosam Aly
1

Yo uso este metodo Soy belga y hablo holandés, así que plural de horas y minutos no es solo agregar 's' al final, sino casi una palabra diferente de singular.

Puede parecer largo, pero creo que es muy legible:

 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
fuente
Si escribe software que necesita ser traducido, este es el camino a seguir. El TimeSpan.ToString () estándar es demasiado torpe para que lo entiendan los usuarios finales normales, especialmente cuando el lapso es de más de un día.
Neil
1

Este es el enfoque que utilicé con formato condicional. y lo publico aquí porque creo que es una forma limpia.

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

ejemplo de salidas:

00:00 (mínimo)

1:43:04 (cuando tenemos horas)

15:03:01 (cuando las horas son más de 1 dígito)

2:4:22:04 (cuando tenemos días)

El formateo es fácil. time.Days:#0:;;\\El formato anterior ;;es para cuando el valor es positivo. Los valores negativos se ignoran. y para valores cero tenemos ;;\\para ocultarlo en una cadena con formato. tenga en cuenta que la barra invertida escapada es necesaria; de lo contrario, no se formateará correctamente.

M.kazem Akhgary
fuente
1

Aquí está mi método de extensión :

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

Llamada de ejemplo:

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

Salida:

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

Esto es una molestia en VS 2010, aquí está mi solución 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
fuente
1

El Substringmétodo funciona perfectamente cuando solo quieres las Horas: Minutos: Segundos. Es un código simple, limpio y 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
fuente
0

Aquí está mi versión. Muestra solo lo necesario, maneja la pluralización, los negativos, y traté de hacerlo liviano.

Ejemplos de salida

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
fuente
0

Si desea un formato de duración similar al de YouTube, dada la cantidad 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));
}

Salida:

     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
fuente
0

Quería devolver una cadena como "1 día 2 horas 3 minutos" y también tener en cuenta si, por ejemplo, los días o los minutos son 0 y luego no se muestran. gracias a John Rasch por su respuesta, que la mía es apenas una extensión de

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 ");
Piees
fuente