Comment puis-je String.Format un objet TimeSpan avec un format personnalisé dans .NET?

184

Quelle est la méthode recommandée pour mettre en forme des TimeSpanobjets dans une chaîne avec un format personnalisé?

Hosam Aly
la source

Réponses:

247

Remarque: cette réponse concerne .Net 4.0 et supérieur. Si vous souhaitez formater un TimeSpan en .Net 3.5 ou inférieur, veuillez consulter la réponse de JohannesH .

Les chaînes de format TimeSpan personnalisées ont été introduites dans .Net 4.0. Vous pouvez trouver une référence complète des spécificateurs de format disponibles sur la page MSDN Custom TimeSpan Format Strings .

Voici un exemple de chaîne de format de période:

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

( UPDATE ) et voici un exemple utilisant l'interpolation de chaîne C # 6:

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

Vous devez échapper le caractère ":" avec un "\" (qui lui-même doit être échappé sauf si vous utilisez une chaîne textuelle).

Cet extrait de la page MSDN Custom TimeSpan Format Strings explique comment échapper à ":" et "". caractères dans une chaîne de format:

Les spécificateurs de format TimeSpan personnalisés n'incluent pas de symboles de séparation d'espace réservé, tels que les symboles qui séparent les jours des heures, les heures des minutes ou les secondes des fractions de seconde. Au lieu de cela, ces symboles doivent être inclus dans la chaîne de format personnalisée en tant que littéraux de chaîne. Par exemple, "jj.hh: mm" définit un point (.) Comme séparateur entre les jours et les heures et un deux-points (:) comme séparateur entre les heures et les minutes.

Docteur Jones
la source
7
@Andrei Rinea: Correct, comme indiqué au début de mon deuxième paragraphe ".Net 4 vous permet d'utiliser des chaînes de format personnalisées avec Timespan".
Doctor Jones
1
@Edward, ce n'est pas tout à fait vrai. Dans votre exemple, vous échappez le premier m et le premier s, donc avec une entrée de myTimeSpan = new TimeSpan(15, 35, 54);l'instruction myTimeSpan .ToString("hh\\mm\\ss");se traduira par 15m35s54. Je ne pense pas que ce soit ce que vous vouliez, car cela placera un m après vos heures et un s après vos minutes.
Doctor Jones
1
@Doctor Jones - Merci! Je voulais dire myTimeSpan.ToString ("h \\ hm \\ ms \\ s"); ou myTimeSpan.ToString (@ "h \ hm \ ms \ s"); ce qui donne 15h35m54s
Edward
2
soyez prudent avec cette solution, car elle ne fonctionnera pas correctement lorsque la partie Heures est supérieure à 24
Zoltan Tirinda
1
@QuarK, il n'y a pas de spécificateur de format personnalisé qui fait cela, ils vous donnent tous les heures qui ne sont pas comptées comme faisant partie des jours. Vous pouvez le faire à la place $"{myTimeSpan.TotalHours}:{myTimeSpan:mm\\:ss}". Du point de vue de l'utilisateur, il serait peut-être préférable d'afficher les jours, personne ne veut mentalement savoir combien de jours sont dans plus de 200 heures.
Doctor Jones
91

Pour .NET 3.5 et versions antérieures, vous pouvez utiliser:

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

Code tiré d'une réponse de Jon Skeet sur les octets

Pour .NET 4.0 et supérieur, consultez la réponse de DoctaJonez .

JohannesH
la source
Oui merci. Mais je pense que l'approche DateTime est plus personnalisable, car elle fonctionnerait pour tout format d'heure pris en charge par DateTime. Cette approche est difficile à utiliser pour afficher AM / PM par exemple.
Hosam Aly
1
Bien sûr, TimeSpan est censé représenter une période de temps, pas une heure de la journée (même si la propriété DateTime.Now.TimeOfDay vous ferait croire le contraire). Si vous avez besoin de représenter une heure spécifique de la journée, je vous suggère de continuer à utiliser la classe DateTime.
JohannesH
7
N'oubliez pas que si le TimeSpan est égal ou supérieur à 24 heures, vous obtiendrez un formatage incorrect.
JohannesH
31

Une façon est de créer un DateTimeobjet et de l'utiliser pour le formatage:

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

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

C'est comme ça que je sais. J'espère que quelqu'un pourra suggérer une meilleure solution.

Hosam Aly
la source
14
Cela ne fonctionnera vraiment que si le TimeSpan est inférieur à un jour. Ce n’est peut-être pas une restriction si terrible, mais cela l’empêche d’être une solution générale.
tvanfosson le
Renvoie-t-il une valeur correcte? Dim ts As New TimeSpan (11, 22, 30, 30): Dim sss As String = New DateTime (ts.Ticks) .ToString ("dd.hh: mm: ss")
NeverHopeless
10

Facile. Utiliser TimeSpan.ToStringavec c, g ou G. Plus d'informations sur MSDN

KKK
la source
1
Merci pour votre réponse. Cette méthode est apparemment nouvelle dans .NET 4 et n'existait pas lorsque la question a été posée. Il ne prend pas non plus en charge les formats personnalisés. Néanmoins, c'est un ajout précieux aux réponses à ces questions. Merci encore.
Hosam Aly
8

J'irais avec

myTimeSpan.ToString("hh\\:mm\\:ss");
Shehab Fawzy
la source
Simple et propre! une alternative est @ "hh \: mm \: ss"
Xilmiki
5

Personnellement, j'aime cette approche:

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

Vous pouvez le personnaliser comme vous le souhaitez sans aucun problème:

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);
Personne
la source
5

C'est génial:

string.Format("{0:00}:{1:00}:{2:00}",
               (int)myTimeSpan.TotalHours,
               myTimeSpan.Minutes,
               myTimeSpan.Seconds);
Harpal
la source
1
Vous devez convertir myTimeSpan.TotalHours en un entier, sinon il risque d'être arrondi. Voir la réponse de JohannesH
codeulike
3

Vous pouvez également utiliser:

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

ÉDITER:

Vous pouvez également consulter Strings.Format .

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

Tous les caractères littéraux doivent être échappés.

Ryan Williams
la source
1

J'ai utilisé le code ci-dessous. Il est long, mais il s'agit toujours d'une expression et produit une sortie très conviviale, car il ne génère pas de jours, heures, minutes ou secondes s'ils ont une valeur de zéro.

Dans l'exemple, il produit une sortie: "4 jours 1 heure 3 secondes".

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
la source
Il existe maintenant une bien meilleure façon d'écrire ceci! Essayez de refactoriser toutes les opérations courantes, et vous pouvez rendre ce code beaucoup, beaucoup mieux.
Hosam Aly
@Hosam Aly; J'apprends tout le temps, voulez-vous publier votre code amélioré?
panpawel
String timeComponent(int value, String name) { return value > 0 ? value + " " + name + (value > 1 ? "s" : ""); }Appelez cela pour chaque composant (par exemple timeComponent(sp.Days, "day")), puis utilisez String.joinpour insérer les espaces.
Hosam Aly
1

J'utilise cette méthode. Je suis belge et parle néerlandais, donc le pluriel des heures et des minutes ne consiste pas seulement à ajouter des «s» à la fin, mais presque un mot différent du singulier.

Cela peut paraître long mais c'est très lisible je pense:

 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
la source
Si vous écrivez un logiciel qui doit être traduit, c'est à peu près la voie à suivre. Le TimeSpan.ToString () standard est tout simplement trop maladroit pour que les utilisateurs finaux normaux le comprennent, en particulier lorsque l'intervalle dépasse une journée.
Neil
1

C'est l'approche que j'ai utilisée moi-même avec la mise en forme conditionnelle. et je le poste ici parce que je pense que c'est une manière propre.

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

exemple de sorties:

00:00 (le minimum)

1:43:04 (quand on a des heures)

15:03:01 (lorsque les heures sont supérieures à 1 chiffre)

2:4:22:04 (quand nous avons des jours.)

Le formatage est simple. time.Days:#0:;;\\le format avant ;;est pour quand la valeur est positive. les valeurs négatives sont ignorées. et pour les valeurs nulles, nous avons ;;\\afin de le cacher dans une chaîne formatée. notez que la barre oblique inverse échappée est nécessaire sinon elle ne se formatera pas correctement.

M.kazem Akhgary
la source
1

Voici ma méthode d'extension :

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

Exemple d'appel:

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

Production:

3 days, 14 hours, 15 minutes, 65 milliseconds
chviLadislav
la source
1

C'est une douleur dans VS 2010, voici ma solution de contournement.

 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
la source
1

La Substringméthode fonctionne parfaitement lorsque vous ne voulez que les heures: minutes: secondes. C'est un code simple, propre et facile à comprendre.

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

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

    Console.WriteLine(formatted);
GER
la source
0

Voici ma version. Il ne montre que ce qui est nécessaire, gère la pluralisation, les négatifs et j'ai essayé de le rendre léger.

Exemples de sortie

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

Code

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
la source
0

Si vous voulez un format de durée similaire à YouTube, étant donné le nombre de secondes

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

Production:

     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
la source
0

Je voulais renvoyer une chaîne telle que "1 jour 2 heures 3 minutes" et aussi prendre en compte si par exemple les jours ou les minutes sont à 0 et ne pas les afficher. merci à John Rasch pour sa réponse dont la mienne est à peine une extension 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 ");
Tarte
la source