Jak mogę String.Formatować obiekt TimeSpan z niestandardowym formatem w .NET?

184

Jaki jest zalecany sposób formatowania TimeSpanobiektów na ciąg o niestandardowym formacie?

Hosam Aly
źródło

Odpowiedzi:

247

Uwaga: ta odpowiedź dotyczy wersji .Net 4.0 i nowszych. Jeśli chcesz sformatować TimeSpan w .Net 3.5 lub niższej, zapoznaj się z odpowiedzią JohannesH .

Niestandardowe ciągi formatu TimeSpan zostały wprowadzone w .Net 4.0. Pełne odniesienie do dostępnych specyfikatorów formatu można znaleźć na stronie Ciągi formatu niestandardowego formatu czasu MSDN .

Oto przykładowy ciąg formatu okresu:

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

( UPDATE ), a oto przykład z interpolacją ciągów C # 6:

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

Musisz uciec ze znaku „:” za pomocą „\” (który sam musi być poprzedzony znakiem ucieczki, chyba że używasz pełnego ciągu).

Ten fragment strony MSDN Niestandardowe ciągi formatu formatu TimeSpan wyjaśnia, jak unikać znaków „:” i „.” znaki w ciągu formatu:

Specyfikatory niestandardowego formatu TimeSpan nie zawierają symboli zastępczych symboli zastępczych, takich jak symbole, które oddzielają dni od godzin, godzin od minut lub sekund od ułamków sekund. Zamiast tego symbole te muszą być zawarte w ciągu formatu niestandardowego jako literały ciągu. Na przykład „dd.hh: mm” definiuje kropkę (.) Jako separator między dniami a godzinami, a dwukropek (:) jako separator między godzinami a minutami.

Doktorze Jones
źródło
7
@Andrei Rinea: Prawidłowo, jak stwierdzono na początku mojego drugiego akapitu „.Net 4 pozwala na użycie ciągów formatu niestandardowego z przedziałem czasowym”.
Doktor Jones
1
@Edward, to nie do końca prawda. W twoim przykładzie unikasz pierwszych m i pierwszych s, więc wynikiem wprowadzenia myTimeSpan = new TimeSpan(15, 35, 54);instrukcji myTimeSpan .ToString("hh\\mm\\ss");będzie wynik 15m35s54. Nie sądzę, że to było to, co zamierzałeś, ponieważ będzie to m po twoich godzinach i s po twoich minutach.
Doktor Jones
1
@Doctor Jones - Dziękujemy! Miałem na myśli myTimeSpan.ToString ("h \\ hm \\ ms \\ s"); lub myTimeSpan.ToString (@ "h \ hm \ ms \ s"); co daje 15h35m54s
Edward
2
bądź ostrożny z tym rozwiązaniem, ponieważ nie będzie działać poprawnie, gdy część Godziny jest większa niż 24
Zoltan Tirinda
1
@QuarK, nie ma niestandardowego specyfikatora formatu, który to robi, wszystkie dają godziny, które nie są liczone jako dni. Możesz to zrobić zamiast tego $"{myTimeSpan.TotalHours}:{myTimeSpan:mm\\:ss}". Z punktu widzenia użytkownika może być lepiej podać liczbę dni, nikt nie chce mentalnie dowiedzieć się, ile dni zajmuje ponad 200 godzin.
Doktor Jones
91

W przypadku .NET 3.5 i niższych można użyć:

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

Kod pochodzi z Jon Skeet odpowiedź na bajty

W przypadku platformy .NET 4.0 i nowszych patrz odpowiedź DoctaJonez .

JohannesH
źródło
Tak dziękuję. Myślę jednak, że podejście DateTime jest bardziej konfigurowalne, ponieważ działałoby w każdym formacie czasu obsługiwanym przez DateTime. To podejście jest trudne do zastosowania na przykład do wyświetlania AM / PM.
Hosam Aly,
1
Jasne, TimeSpan ma reprezentować okres czasu, a nie porę dnia (nawet jeśli właściwość DateTime.Now.TimeOfDay pozwoliłaby ci sądzić inaczej). Jeśli chcesz podać konkretną porę dnia, sugeruję kontynuowanie korzystania z klasy DateTime.
JohannesH
7
Pamiętaj tylko, że jeśli TimeSpan jest równy lub dłuższy niż 24 godziny, otrzymasz nieprawidłowe formatowanie.
JohannesH
31

Jednym ze sposobów jest utworzenie DateTimeobiektu i użycie go do formatowania:

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

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

W ten sposób wiem. Mam nadzieję, że ktoś może zaproponować lepszy sposób.

Hosam Aly
źródło
14
To naprawdę zadziała tylko wtedy, gdy TimeSpan jest krótszy niż jeden dzień. To może nie być takie straszne ograniczenie, ale nie stanowi ogólnego rozwiązania.
tvanfosson
Czy zwraca prawidłową wartość? Dim ts As New TimeSpan (11, 22, 30, 30): Dim sss As String = New DateTime (ts.Ticks) .ToString ("dd.hh: mm: ss")
NeverHopeless
10

Prosty. Używaj TimeSpan.ToStringz c, g lub G. Więcej informacji na MSDN

KKK
źródło
1
Dziękuję za Twoją odpowiedź. Ta metoda jest najwyraźniej nowa w .NET 4 i nie istniała po zadaniu pytania. Nie obsługuje również niestandardowych formatów. Niemniej jednak stanowi cenny dodatek do odpowiedzi na te pytania. Dzięki jeszcze raz.
Hosam Aly,
8

Poszedłbym z

myTimeSpan.ToString("hh\\:mm\\:ss");
Shehab Fawzy
źródło
Prosty i czysty! alternatywą jest @ "hh \: mm \: ss"
Xilmiki
5

Osobiście podoba mi się to podejście:

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

Możesz to zrobić tak, jak chcesz, bez żadnych problemów:

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);
Nikt
źródło
5

To jest niesamowite:

string.Format("{0:00}:{1:00}:{2:00}",
               (int)myTimeSpan.TotalHours,
               myTimeSpan.Minutes,
               myTimeSpan.Seconds);
Harpal
źródło
1
Musisz rzutować myTimeSpan.TotalHours na int - w przeciwnym razie może zostać zaokrąglony. Zobacz odpowiedź JohannesH
codeulike
3

Możesz także skorzystać z:

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

EDYTOWAĆ:

Możesz także spojrzeć na Strings.Format .

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

Wszystkie dosłowne znaki muszą zostać usunięte.

Ryan Williams
źródło
1

Użyłem kodu poniżej. Jest długi, ale nadal jest jednym wyrażeniem i daje bardzo przyjazny wynik, ponieważ nie generuje dni, godzin, minut ani sekund, jeśli mają wartość zero.

W próbce daje wynik: „4 dni 1 godzina 3 sekundy”.

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
źródło
Teraz jest o wiele lepszy sposób na napisanie tego! Spróbuj refaktoryzować wszystkie typowe operacje i możesz sprawić, że ten kod będzie wyglądał znacznie, znacznie lepiej.
Hosam Aly
@Hosam Aly; Uczę się cały czas, czy chcesz opublikować swój ulepszony kod?
panpawel
String timeComponent(int value, String name) { return value > 0 ? value + " " + name + (value > 1 ? "s" : ""); }Nazwij to dla każdego komponentu (np. timeComponent(sp.Days, "day")), A następnie użyj, String.joinaby wstawić spacje.
Hosam Aly,
1

Używam tej metody. Jestem Belgiem i mówię po holendersku, więc liczba godzin i minut to nie tylko dodanie „s” na końcu, ale prawie inne słowo niż liczba pojedyncza.

To może wydawać się długie, ale myślę, że jest bardzo czytelne:

 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
źródło
Jeśli piszesz oprogramowanie, które musi zostać przetłumaczone, jest to całkiem dobra droga. Standardowa funkcja TimeSpan.ToString () jest zbyt niezręczna, aby zwykli użytkownicy końcowi ją rozumieli, zwłaszcza gdy rozpiętość jest większa niż jeden dzień.
Neil,
1

Takie podejście zastosowałem w przypadku formatowania warunkowego. i zamieszczam go tutaj, ponieważ uważam, że to czysty sposób.

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

przykład wyników:

00:00 (minimum)

1:43:04 (kiedy mamy godziny)

15:03:01 (gdy godziny są dłuższe niż 1 cyfra)

2:4:22:04 (kiedy mamy dni.)

Formatowanie jest łatwe. time.Days:#0:;;\\poprzedni format ;;dotyczy sytuacji, gdy wartość jest dodatnia. wartości ujemne są ignorowane. a dla wartości zerowych mamy ;;\\, aby ukryć go w sformatowanym ciągu. zwróć uwagę, że odwrócony ukośnik jest konieczny, w przeciwnym razie nie zostanie poprawnie sformatowany.

M.kazem Akhgary
źródło
1

Oto moja metoda rozszerzenia :

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

Przykładowe wywołanie:

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

Wynik:

3 days, 14 hours, 15 minutes, 65 milliseconds
chviLadislav
źródło
1

To jest problem w VS 2010, oto moje rozwiązanie obejścia.

 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
źródło
1

Ta Substringmetoda działa idealnie, gdy chcesz tylko Godziny: Minuty: Sekundy. Jest to prosty, przejrzysty kod i łatwy do zrozumienia.

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

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

    Console.WriteLine(formatted);
GER
źródło
0

Oto moja wersja. Pokazuje tylko tyle, ile jest to konieczne, radzi sobie z pluralizacją, negatywami i starałem się, aby była lekka.

Przykłady wyników

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

Kod

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
źródło
0

Jeśli chcesz format czasu trwania podobny do youtube, biorąc pod uwagę liczbę sekund

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

Wynik:

     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
źródło
0

Chciałem zwrócić ciąg znaków, taki jak „1 dzień 2 godziny 3 minuty”, a także wziąć pod uwagę, jeśli na przykład dni lub minuty są równe 0, a następnie ich nie pokazuje. dzięki Johnowi Raschowi za odpowiedź, której moje jest zaledwie przedłużeniem

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 ");
Ciasta
źródło