it-swarm.com.ru

Как я могу рассчитать промежуток времени в Java и отформатировать вывод?

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

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

Как я могу сделать это ??

40
Jeremy Logan

Так как все кричат ​​"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 года, 1 месяц, 1 неделя, 5 дней, 6 часов, 56 минут, 24 секунды назад 

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

66
BalusC
    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" : ""));
44
Timour

Да, я разбудил мертвых, которые у меня есть, но вот моя улучшенная реализация, основанная на опубликованном коде @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

18
Abduliam Rehmanius

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

12
Peter Lindholm

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

Другие ответы могут быть правильными, но устарели. Проблемные старые классы даты и времени в 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. В этом подходе начало включительно, а конец эксклюзив. Этот подход обычно используется в работе с датой и временем. Я считаю, что последовательное использование этого подхода в вашей кодовой базе поможет устранить ошибки и недоразумения из-за неоднозначностей и облегчит когнитивную нагрузку вашего программирования.

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 в Ответ от Balus C .


О Java.time

Инфраструктура Java.time встроена в Java 8 и более поздние версии. Эти классы вытесняют проблемные старые классы legacy date-time, такие как Java.util.Date , Calendar , & SimpleDateFormat .

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

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

Где взять классы Java.time? 

  • Java SE 8 и SE 9 и позже
    • Встроенный. 
    • Часть стандартного Java API со встроенной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и SE 7
    • Большая часть функциональности Java.time перенесена в Java 6 & 7 в ThreeTen-Backport .
  • Android

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

9
Basil Bourque

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

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

1
TuxMeister

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

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

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

1
Adrian Pronk

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

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
OWADVL

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

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

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

0
Lucem

Я всегда начинаю с Joda Time . Работа с датами и временем в Java всегда "забавна", но Joda Time снимает с себя нагрузку. 

У них есть классы Interval и Duration, которые делают половину того, что вы ищете. Не уверен, что у них есть функция для вывода в удобочитаемом формате. Я буду продолжать искать.

НТН

0
mr_urf

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

0
Ben S

Недавно я получил такое же требование, когда мне нужен базовый метод для печати продолжительности. У меня не было возможности использовать какую-либо стороннюю библиотеку. Поэтому я просто усовершенствовал идею @mtim. 

public static void main(String[] args) throws IOException, ParseException {
        String since = "2017-02-24T04:44:05.601Z";
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        TimeZone utc = TimeZone.getTimeZone("UTC");
        dateFormat.setTimeZone(utc);

        Date sinceDate = dateFormat.parse(since);
        Date now = new Date();
        long durationInSeconds  = TimeUnit.MILLISECONDS.toSeconds(now.getTime() - sinceDate.getTime());

        long SECONDS_IN_A_MINUTE = 60;
        long MINUTES_IN_AN_HOUR = 60;
        long HOURS_IN_A_DAY = 24;
        long DAYS_IN_A_MONTH = 30;
        long MONTHS_IN_A_YEAR = 12;

        long sec = (durationInSeconds >= SECONDS_IN_A_MINUTE) ? durationInSeconds % SECONDS_IN_A_MINUTE : durationInSeconds;
        long min = (durationInSeconds /= SECONDS_IN_A_MINUTE) >= MINUTES_IN_AN_HOUR ? durationInSeconds%MINUTES_IN_AN_HOUR : durationInSeconds;
        long hrs = (durationInSeconds /= MINUTES_IN_AN_HOUR) >= HOURS_IN_A_DAY ? durationInSeconds % HOURS_IN_A_DAY : durationInSeconds;
        long days = (durationInSeconds /= HOURS_IN_A_DAY) >= DAYS_IN_A_MONTH ? durationInSeconds % DAYS_IN_A_MONTH : durationInSeconds;
        long months = (durationInSeconds /=DAYS_IN_A_MONTH) >= MONTHS_IN_A_YEAR ? durationInSeconds % MONTHS_IN_A_YEAR : durationInSeconds;
        long years = (durationInSeconds /= MONTHS_IN_A_YEAR);

        String duration = getDuration(sec,min,hrs,days,months,years);
        System.out.println(duration);
    }
    private static String getDuration(long secs, long mins, long hrs, long days, long months, long years) {
        StringBuffer sb = new StringBuffer();
        String EMPTY_STRING = "";
        sb.append(years > 0 ? years + (years > 1 ? " years " : " year "): EMPTY_STRING);
        sb.append(months > 0 ? months + (months > 1 ? " months " : " month "): EMPTY_STRING);
        sb.append(days > 0 ? days + (days > 1 ? " days " : " day "): EMPTY_STRING);
        sb.append(hrs > 0 ? hrs + (hrs > 1 ? " hours " : " hour "): EMPTY_STRING);
        sb.append(mins > 0 ? mins + (mins > 1 ? " mins " : " min "): EMPTY_STRING);
        sb.append(secs > 0 ? secs + (secs > 1 ? " secs " : " secs "): EMPTY_STRING);
        sb.append("ago");
        return sb.toString();
    }

Результатом будет промежуток времени (длительность), напечатанный словами, например, .1 day 2 hours 51 mins 41 secs ago

0
i_am_zero

Хорошо, после краткого ознакомления с API кажется, что вы можете сделать следующее:

  1. создайте некоторые ReadableInstants, представляющие время начала и время окончания.
  2. Используйте Hours.hoursBetween, чтобы получить количество часов
  3. используйте Minutes.minutesBetween, чтобы получить количество минут
  4. используйте мод 60 на минуты, чтобы получить оставшиеся минуты
  5. и вуаля!

НТН

0
mr_urf

Решение "Abduliam Rehmanius" кажется довольно приятным, или вы можете использовать какую-то библиотеку выше, или вы можете создавать новые простые вещи, см. Решение JS здесь: http://www.datejs.com/ . Это не сложно превратить в язык Java :-)

Надеюсь, моя ссылка полезна для вас!

0
Tam Vo

Время форматирования в Java всплывает для меня часто в ETL или подтягиваниях БД. Следующий фрагмент кода показывает, как я получаю пульс операции.

    SimpleDateFormat ymdhms=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    DecimalFormat formatter = new DecimalFormat("#,###"); /* ("#,###.00"); gives you the decimal fractions */
    int counter=0;
    Date beginTime0=new Date();
    while (<some end condition>) {

        <some time consuming operation>

        /*// uncomment this section durring initial exploration of the time consumer
        if(counter>100000){
            System.out.println("debug exiting due to counter="+counter);
            System.exit(0);
        }
        */

        if(0==counter%1000){
            Date now = new Date();
            long diffInSeconds = (now.getTime() - beginTime0.getTime()) / 1000;
            long diff[] = new long[] { 0, 0, 0, 0 };
            diff[3] = (diffInSeconds >= 60 ? diffInSeconds % 60 : diffInSeconds);                        /* sec   */ 
            diff[2] = (diffInSeconds = (diffInSeconds / 60)) >= 60 ? diffInSeconds % 60 : diffInSeconds; /* min   */ 
            diff[1] = (diffInSeconds = (diffInSeconds / 60)) >= 24 ? diffInSeconds % 24 : diffInSeconds; /* hours */ 
            diff[0] = (diffInSeconds = (diffInSeconds / 24));                                            /* days  */ 
            String delta = String.format(
                "%s%s%s%s"
                ,(diff[0]>0?String.format("%d day%s "    ,diff[0],(diff[0]!=1?"s":"")):"")
                ,(diff[1]>0?String.format("%2d hour%s "  ,diff[1],(diff[1]!=1?"s":" ")):"")
                ,(diff[2]>0?String.format("%2d minute%s ",diff[2],(diff[2]!=1?"s":" ")):"")
                ,           String.format("%2d second%s ",diff[3],(diff[3]!=1?"s":" "))
            );
            System.out.println(String.format(
                "%12s %s delta= %s"
                ,formatter.format(counter)
                ,ymdhms.format(new Date())
                ,delta
                )
            );
        }
        counter++;
    }
0
teserecter

Я думаю, что вы ищете что-то вроде PrettyTime
PrettyTime - библиотека форматирования времени OpenSource. Полностью настраиваемый, он создает читаемые человеком, относительные временные метки подобно тем, которые можно увидеть на Digg, Twitter и Facebook. Доступно на более чем 30 языках!
ссылка: https://github.com/ocpsoft/prettytime

Можно использовать и в Android 

Например

System.out.println(p.format(new Date(System.currentTimeMillis() + 1000*60*10)));
        //prints: "10 minutes from now"
0
Habel Philip