Как я могу рассчитать промежуток времени в Java и отформатировать вывод? - PullRequest
40 голосов
/ 11 марта 2009

Я хочу взять два раза (в секундах с начала эпохи) и показать разницу между ними в следующих форматах:

  • 2 минуты
  • 1 час, 15 минут
  • 3 часа, 9 минут
  • 1 минуту назад
  • 1 час, 2 минуты назад

Как мне это сделать ??

Ответы [ 17 ]

67 голосов
/ 09 мая 2010

Так как все кричат ​​"YOODAA !!!" но никто не публикует конкретный пример, вот мой вклад.

Вы также можете сделать это с Joda-Time . Используйте Period для представления периода. Чтобы отформатировать период в желаемом человеческом представлении, используйте PeriodFormatter, который можно построить с помощью PeriodFormatterBuilder.

Вот начальный пример:

DateTime myBirthDate = new DateTime(1978, 3, 26, 12, 35, 0, 0);
DateTime now = new DateTime();
Period period = new Period(myBirthDate, now);

PeriodFormatter formatter = new PeriodFormatterBuilder()
    .appendYears().appendSuffix(" year, ", " years, ")
    .appendMonths().appendSuffix(" month, ", " months, ")
    .appendWeeks().appendSuffix(" week, ", " weeks, ")
    .appendDays().appendSuffix(" day, ", " days, ")
    .appendHours().appendSuffix(" hour, ", " hours, ")
    .appendMinutes().appendSuffix(" minute, ", " minutes, ")
    .appendSeconds().appendSuffix(" second", " seconds")
    .printZeroNever()
    .toFormatter();

String elapsed = formatter.print(period);
System.out.println(elapsed + " ago");

Гораздо яснее и лаконичнее, не так ли?

Это печатает уже сейчас

32 years, 1 month, 1 week, 5 days, 6 hours, 56 minutes, 24 seconds ago

(Кашель, старый, кашель)

44 голосов
/ 11 марта 2009
    Date start = new Date(1167627600000L); // JANUARY_1_2007
    Date end = new Date(1175400000000L); // APRIL_1_2007


    long diffInSeconds = (end.getTime() - start.getTime()) / 1000;

    long diff[] = new long[] { 0, 0, 0, 0 };
    /* sec */diff[3] = (diffInSeconds >= 60 ? diffInSeconds % 60 : diffInSeconds);
    /* min */diff[2] = (diffInSeconds = (diffInSeconds / 60)) >= 60 ? diffInSeconds % 60 : diffInSeconds;
    /* hours */diff[1] = (diffInSeconds = (diffInSeconds / 60)) >= 24 ? diffInSeconds % 24 : diffInSeconds;
    /* days */diff[0] = (diffInSeconds = (diffInSeconds / 24));

    System.out.println(String.format(
        "%d day%s, %d hour%s, %d minute%s, %d second%s ago",
        diff[0],
        diff[0] > 1 ? "s" : "",
        diff[1],
        diff[1] > 1 ? "s" : "",
        diff[2],
        diff[2] > 1 ? "s" : "",
        diff[3],
        diff[3] > 1 ? "s" : ""));
18 голосов
/ 09 мая 2010

Да, я разбудил мертвых, которые у меня есть, но вот моя улучшенная реализация, основанная на опубликованном коде @mtim, так как эта тема почти на вершине поисков, поэтому я возиться с сонной пустотой,

    public static String getFriendlyTime(Date dateTime) {
    StringBuffer sb = new StringBuffer();
    Date current = Calendar.getInstance().getTime();
    long diffInSeconds = (current.getTime() - dateTime.getTime()) / 1000;

    /*long diff[] = new long[]{0, 0, 0, 0};
    /* sec *  diff[3] = (diffInSeconds >= 60 ? diffInSeconds % 60 : diffInSeconds);
    /* min *  diff[2] = (diffInSeconds = (diffInSeconds / 60)) >= 60 ? diffInSeconds % 60 : diffInSeconds;
    /* hours *  diff[1] = (diffInSeconds = (diffInSeconds / 60)) >= 24 ? diffInSeconds % 24 : diffInSeconds;
    /* days * diff[0] = (diffInSeconds = (diffInSeconds / 24));
     */
    long sec = (diffInSeconds >= 60 ? diffInSeconds % 60 : diffInSeconds);
    long min = (diffInSeconds = (diffInSeconds / 60)) >= 60 ? diffInSeconds % 60 : diffInSeconds;
    long hrs = (diffInSeconds = (diffInSeconds / 60)) >= 24 ? diffInSeconds % 24 : diffInSeconds;
    long days = (diffInSeconds = (diffInSeconds / 24)) >= 30 ? diffInSeconds % 30 : diffInSeconds;
    long months = (diffInSeconds = (diffInSeconds / 30)) >= 12 ? diffInSeconds % 12 : diffInSeconds;
    long years = (diffInSeconds = (diffInSeconds / 12));

    if (years > 0) {
        if (years == 1) {
            sb.append("a year");
        } else {
            sb.append(years + " years");
        }
        if (years <= 6 && months > 0) {
            if (months == 1) {
                sb.append(" and a month");
            } else {
                sb.append(" and " + months + " months");
            }
        }
    } else if (months > 0) {
        if (months == 1) {
            sb.append("a month");
        } else {
            sb.append(months + " months");
        }
        if (months <= 6 && days > 0) {
            if (days == 1) {
                sb.append(" and a day");
            } else {
                sb.append(" and " + days + " days");
            }
        }
    } else if (days > 0) {
        if (days == 1) {
            sb.append("a day");
        } else {
            sb.append(days + " days");
        }
        if (days <= 3 && hrs > 0) {
            if (hrs == 1) {
                sb.append(" and an hour");
            } else {
                sb.append(" and " + hrs + " hours");
            }
        }
    } else if (hrs > 0) {
        if (hrs == 1) {
            sb.append("an hour");
        } else {
            sb.append(hrs + " hours");
        }
        if (min > 1) {
            sb.append(" and " + min + " minutes");
        }
    } else if (min > 0) {
        if (min == 1) {
            sb.append("a minute");
        } else {
            sb.append(min + " minutes");
        }
        if (sec > 1) {
            sb.append(" and " + sec + " seconds");
        }
    } else {
        if (sec <= 1) {
            sb.append("about a second");
        } else {
            sb.append("about " + sec + " seconds");
        }
    }

    sb.append(" ago");


    /*String result = new String(String.format(
    "%d day%s, %d hour%s, %d minute%s, %d second%s ago",
    diff[0],
    diff[0] > 1 ? "s" : "",
    diff[1],
    diff[1] > 1 ? "s" : "",
    diff[2],
    diff[2] > 1 ? "s" : "",
    diff[3],
    diff[3] > 1 ? "s" : ""));*/
    return sb.toString();
}

Это, очевидно, может быть улучшено. в основном он пытается сделать промежуток времени более дружественным, хотя есть несколько ограничений, то есть он будет вести себя странно, если пройденное время (параметр) будет в будущем, и оно будет ограничено только днями, часами и секундами (месяцами и годами не обрабатывается, так что кто-то другой может; -).

Примеры выходных данных:

  • около секунды назад
  • 8 минут и 34 секунды назад
  • час и 4 минуты назад
  • день назад
  • 29 дней назад
  • год и 3 месяца назад

, ура: D

РЕДАКТИРОВАТЬ: теперь поддерживает месяцы и годы: P

12 голосов
/ 11 марта 2009

Я бы порекомендовал вам взглянуть на HumanTime

9 голосов
/ 26 февраля 2017

Избегайте устаревших классов даты и времени

Другие ответы могут быть правильными, но устарели. Утомительные старые классы даты и времени в Java теперь унаследованы, их заменили классы java.time.

Аналогичным образом, проект Joda-Time , теперь в режиме обслуживания , рекомендует перейти на классы java.time .

Использование java.time

Instant

два раза (в секундах с начала эпохи)

В java.time мы представляем моменты на временной шкале в UTC как Instant. Я предполагаю, что ваша эпоха такая же, как и у java.time, первого момента 1970 года в UTC (1970-01-01T00:00:00Z). Обратите внимание, что Instant имеет разрешение наносекунд. Удобный фабричный метод создает Instant из целых секунд, как задано в Вопросе.

Instant start = Instant.ofEpochSecond( … );
Instant stop = Instant.ofEpochSecond( … );

Здесь мы используем текущий момент и несколько минут спустя.

Instant start = Instant.now() ;
Instant stop = start.plusSeconds( TimeUnit.MINUTES.toSeconds( 7L ) ) ;  // Seven minutes as a number of seconds.

Duration

В java.time промежуток времени, не привязанный к временной шкале, представлен двумя способами. Для лет-месяцев-дней у нас есть Period. Для часов-минут-секунд у нас есть Duration.

Duration duration = Duration.between( start , stop );

Half-Open

Истекшее время рассчитывается с использованием подхода Half-Open. В этом подходе начало включительно , а окончание исключительно . Этот подход обычно используется в работе с датой и временем. Я считаю, что последовательное использование этого подхода в вашей кодовой базе поможет устранить ошибки и недоразумения из-за неоднозначностей и облегчит когнитивную нагрузку вашего программирования.

ISO 8601

Стандарт ISO 8601 определяет множество практических форматов для представления значений даты и времени в виде текста. Эти форматы разработаны для того, чтобы избежать двусмысленности, их легко анализировать на машине и интуитивно понятно для людей разных культур.

Классы java.time по умолчанию используют эти форматы при разборе и генерации строк.

Для промежутка времени, не привязанного к временной шкале, стандарт определяет формат из PnYnMnDTnHnMnS, где P обозначает начало, а T отделяет любые годы-месяцы-дни от любых часов. минуты-секунды. Таким образом, полтора часа - PT1H30M.

В нашем примере кода используется семь минут:

String outputStandard = duration.toString();

PT7M

См. Приведенный выше пример код, работающий в прямом эфире на IdeOne.com .

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

Я рекомендую никогда , используя формат часов-часов (например, 01:30 для полуторачасовых часов), поскольку этот формат совершенно неоднозначен с временем суток.

Получение деталей для построения строки

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

  • Для Period, позвоните getYears, getMonths и getDays, чтобы получить каждую часть.
  • Как ни странно, класс Duration не имел таких геттеров в Java 8 , но получил их в Java 9 и позже: toDaysPart, toHoursPart, toMinutesPart, toSecondsPart и toNanosPart.

Пример кода, простой и базовый для начала работы.

int days = duration.toDaysPart() ;
int hours = duration.toHoursPart() ;
int minutes = duration.toMinutesPart() ;
int seconds = duration.toSecondsPart() ;
int nanos = duration.toNanosPart() ;

StringBuilder sb = new StringBuilder( 100 );

if( days != 0 ) { 
    sb.append( days + " days" ) ;
};

if( hours != 0 ) { 
    sb.append( ( sb.length = 0 ) ? ( "" ) : ( ", " ) ) ;  // Append comma if any existing text.
    sb.append( hours + " hours" ) ;
};

if( minutes != 0 ) { 
    sb.append( ( sb.length = 0 ) ? ( "" ) : ( ", " ) ) ;  // Append comma if any existing text.
    sb.append( minutes + " minutes" ) ;
};

if( seconds != 0 ) { 
    sb.append( ( sb.length = 0 ) ? ( "" ) : ( ", " ) ) ;  // Append comma if any existing text.
    sb.append( seconds + " seconds" ) ;
};

if( nanos != 0 ) { 
    sb.append( ( sb.length = 0 ) ? ( "" ) : ( ", " ) ) ;  // Append comma if any existing text.
    sb.append( nanos + " nanos" ) ;
};

String output = sb.toString();

Или, возможно, вы могли бы написать более плавный код, используя DateTimeFormatterBuilder способом, показанным Joda-Time в Ответе Балуса C .


О java.time

Фреймворк java.time встроен в Java 8 и более поздние версии. Эти классы вытесняют проблемные старые устаревшие классы даты и времени, такие как java.util.Date, Calendar и & SimpleDateFormat.

Проект Joda-Time , который сейчас находится в режиме обслуживания , рекомендует выполнить переход на java.time классы.

Чтобы узнать больше, см. Oracle Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310 .

Где получить классы java.time?

Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Здесь вы можете найти несколько полезных классов, таких как Interval, YearWeek, YearQuarter, и fz more .

1 голос
/ 11 марта 2009

Я не специалист по Java, но вы можете сделать t1-t2 = t3 (в секундах), а затем разделить это на 60, это даст вам минуты, еще на 60 даст вам секунды. Тогда вам просто нужно выяснить, сколько делений вам нужно.

Надеюсь, это поможет.

1 голос
/ 19 апреля 2012

некоторый код, который играет с форматированием даты ... и времени назад.

SimpleDateFormat curFormater = new SimpleDateFormat("EEE, dd MMM yyyy kk:mm:ss"); 
    try {
        Date dateObj = curFormater.parse(pubDate);

        SimpleDateFormat postFormater = new SimpleDateFormat("MMMM dd, yyyy ss:mm:hh");              
        String newDateStr = postFormater.format(dateObj); 
        Log.v("THE NEW DATE IS",newDateStr);            
        long pubdateinmilis = dateObj.getTime();            
        pubDate = (String) DateUtils.getRelativeTimeSpanString(pubdateinmilis, System.currentTimeMillis(),DateUtils.HOUR_IN_MILLIS,DateUtils.FORMAT_ABBREV_RELATIVE);

    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } 
1 голос
/ 12 марта 2009

Если ваш промежуток времени пересекает границы летнего времени, хотите ли вы указать количество дней?

Например, с 23:00 до 23:00 следующий день - это всегда день, но может быть 23, 24 или 25 часов в зависимости от того, переходите ли вы в переход на летнее время.

Если вы заботитесь об этом, убедитесь, что вы включили его в свой выбор.

0 голосов
/ 29 октября 2017

Этот вопрос старый и уже имеет ответ, но у меня есть лучшее решение. Использовать относительный период времени от даты использования

                dateHere.setText(DateUtils.getRelativeTimeSpanString(timeInMillis,
                        System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS));

он принимает аргументы: long / int time, текущее время и как вы хотите, _month, минута, секунда и т. Д.

0 голосов
/ 11 марта 2009

Класс Calendar может обрабатывать большинство математических данных, связанных с датой. Вам нужно будет получить результат compareTo и вывести формат самостоятельно. Не существует стандартной библиотеки, которая делает именно то, что вы ищете, хотя может быть сторонняя библиотека, которая делает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...