Как я могу String.Format объект TimeSpan с пользовательским форматом в .NET?

184

Каков рекомендуемый способ форматирования TimeSpanобъектов в строку с пользовательским форматом?

Хосам Али
источник

Ответы:

247

Обратите внимание: этот ответ предназначен для .Net 4.0 и выше. Если вы хотите отформатировать TimeSpan в .Net 3.5 или ниже, пожалуйста, смотрите ответ JohannesH .

Пользовательские строки формата TimeSpan были введены в .Net 4.0. Вы можете найти полную ссылку на доступные спецификаторы формата на странице строк формата MSDN Custom TimeSpan .

Вот пример строки формата временного интервала:

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

( ОБНОВЛЕНИЕ ) и вот пример, использующий интерполяцию строки C # 6:

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

Вы должны экранировать символ «:» с помощью символа «\» (который сам должен быть экранирован, если вы не используете дословную строку).

В этом отрывке со страницы строк формата MSDN Custom TimeSpan объясняется экранирование символов «:» и «.» символы в строке формата:

Пользовательские спецификаторы формата TimeSpan не включают символы-разделители-заполнители, такие как символы, которые отделяют дни от часов, часы от минут или секунды от дробных секунд. Вместо этого эти символы должны быть включены в строку пользовательского формата в виде строковых литералов. Например, «dd.hh: mm» определяет точку (.) Как разделитель между днями и часами, а двоеточие (:) - как разделитель между часами и минутами.

Доктор джонс
источник
7
@ Андрей Ринеа: Исправьте, как указано в начале моего второго абзаца «.Net 4 позволяет вам использовать строки произвольного формата с Timespan».
Доктор Джонс
1
@ Эдвард, это не совсем верно. В вашем примере вы экранируете первые m и первые s, поэтому при вводе myTimeSpan = new TimeSpan(15, 35, 54);оператора вы myTimeSpan .ToString("hh\\mm\\ss");получите 15m35s54. Я не думаю, что это то, что вы намеревались, так как это будет M после ваших часов и S после ваших минут.
Доктор Джонс
1
@ Доктор Джонс - Спасибо! Я имел в виду myTimeSpan.ToString ("h \\ hm \\ ms \\ s"); или myTimeSpan.ToString (@ "h \ hm \ ms \ s"); что дает 15х35м54с
Эдвард
2
просто будьте осторожны с этим решением, потому что оно не будет работать правильно, когда часть Часов больше 24
Золтан Тиринда
1
@QuarK, нет никакого специального спецификатора формата, который делает это, они все дают вам часы, которые не учитываются как часть дней. Вы могли бы сделать это вместо этого, хотя $"{myTimeSpan.TotalHours}:{myTimeSpan:mm\\:ss}". С точки зрения пользователя, было бы лучше вывести дни, хотя никто не хочет мысленно выяснить, сколько дней в 200+ часов.
Доктор Джонс
91

Для .NET 3.5 и ниже вы можете использовать:

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

Код взят из ответа Джона Скита на байты

Для .NET 4.0 и выше, см. Ответ DoctaJonez .

JohannesH
источник
Да спасибо. Но я думаю, что подход DateTime более настраиваемый, так как он будет работать для любого формата времени, поддерживаемого DateTime. Этот подход трудно использовать, например, для отображения AM / PM.
Хосам Али
1
Конечно, TimeSpan предназначен для представления периода времени, а не времени суток (хотя свойство DateTime.Now.TimeOfDay заставит вас поверить в обратное). Если вам нужно указать конкретное время суток, я предлагаю вам продолжить использовать класс DateTime.
JohannesH
7
Просто помните, что если TimeSpan равен или превышает 24 часа, вы получите неправильное форматирование.
JohannesH
31

Один из способов - создать DateTimeобъект и использовать его для форматирования:

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

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

Это способ, которым я знаю. Я надеюсь, что кто-то может предложить лучший способ.

Хосам Али
источник
14
Это действительно будет работать, только если TimeSpan меньше суток. Это не может быть таким ужасным ограничением, но оно не дает ему общего решения.
tvanfosson
Возвращает ли оно правильное значение? Dim ts As New TimeSpan (11, 22, 30, 30): Dim sss As String = Новый DateTime (ts.Ticks) .ToString ("dd.hh: mm: ss")
NeverHopeless
10

Просто. Используйте TimeSpan.ToStringс c, g или G. Дополнительная информация на MSDN

KKK
источник
1
Спасибо за ваш ответ. Этот метод, по-видимому, является новым в .NET 4 и не существовал, когда был задан вопрос. Он также не поддерживает пользовательские форматы. Тем не менее, это ценное дополнение к ответам на эти вопросы. Еще раз спасибо.
Хосам Али
8

Я бы пошел с

myTimeSpan.ToString("hh\\:mm\\:ss");
Шехаб Фаузи
источник
Просто и чисто! альтернатива @ "чч \: мм \: сс"
Xilmiki
5

Лично мне нравится такой подход:

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

Вы можете сделать это так, как вам нравится, без проблем:

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);
Ни один. Никто
источник
5

Это потрясающе:

string.Format("{0:00}:{1:00}:{2:00}",
               (int)myTimeSpan.TotalHours,
               myTimeSpan.Minutes,
               myTimeSpan.Seconds);
Harpal
источник
1
Вам необходимо привести myTimeSpan.TotalHours к int, иначе он может быть округлен. См. Ответ Йоханнеса
codeulike
3

Вы также можете пойти с:

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")
NeverHopeless
источник
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");

Все буквенные символы должны быть экранированы.

Райан Уильямс
источник
1

Я использовал код ниже. Это длинное, но все же это одно выражение, которое выдает очень дружественный вывод, поскольку не выводит дни, часы, минуты или секунды, если они имеют значение ноль.

В образце выдает результат: «4 дня 1 час 3 секунды».

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
источник
Теперь есть намного лучший способ написать это! Попробуйте реорганизовать все обычные операции, и вы можете сделать этот код намного лучше.
Хосам Али
@Hosam Aly; Я все время учусь, вы хотите опубликовать свой улучшенный код?
panpawel
String timeComponent(int value, String name) { return value > 0 ? value + " " + name + (value > 1 ? "s" : ""); }Вызовите это для каждого компонента (например timeComponent(sp.Days, "day")), затем используйте String.joinдля вставки пробелов.
Хосам Али
1

Я использую этот метод. Я бельгиец и говорю по-голландски, поэтому множественное число часов и минут - это не просто добавление «к» в конце, а почти другое слово, чем в единственном числе.

Это может показаться длинным, но это очень читабельно, я думаю:

 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
источник
Если вы пишете программное обеспечение, которое должно быть переведено, то это в значительной степени путь. Стандартный метод TimeSpan.ToString () слишком неуклюж, чтобы его могли понять обычные конечные пользователи, особенно если интервал превышает один день.
Нил
1

Именно такой подход я использовал для себя с условным форматированием. и я публикую это здесь, потому что я думаю, что это чистый путь.

$"{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:;;\\предыдущий формат ;;предназначен для положительного значения. отрицательные значения игнорируются. и для нулевых значений мы имеем ;;\\, чтобы скрыть это в форматированной строке. обратите внимание, что экранированный обратный слеш необходим, иначе он не будет правильно отформатирован.

М.Казем Ахгари
источник
1

Вот мой метод расширения :

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
chviLadislav
источник
1

Это боль в 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;
                }
            } 
        }
rguez06
источник
1

SubstringМетод отлично работает , если вы хотите только часы: минуты: секунды. Это простой, чистый код и легкий для понимания.

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

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

    Console.WriteLine(formatted);
GER
источник
0

Вот моя версия. Он показывает только столько, сколько необходимо, обрабатывает плюрализм, негативы, и я попытался сделать его легким.

Примеры вывода

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} 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
источник
0

Если вы хотите, чтобы формат продолжительности был похож на 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
zenny
источник
0

Я хотел вернуть строку типа «1 день 2 часа 3 минуты», а также принять во внимание, если, например, дни или минуты равны 0, а затем не отображать их. спасибо Джону Рашу за его ответ, который является лишь продолжением

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
источник