Как рассчитать «время назад» в Java? - PullRequest
114 голосов
/ 05 октября 2010

В Ruby on Rails есть функция, которая позволяет вам взять любую дату и распечатать, как давно она была.

Например:

8 minutes ago
8 hours ago
8 days ago
8 months ago
8 years ago

Есть ли простой способ сделать это на Java?

Ответы [ 25 ]

160 голосов
/ 05 октября 2010

Взгляните на библиотеку PrettyTime .

Это довольно просто в использовании:

import org.ocpsoft.prettytime.PrettyTime;

PrettyTime p = new PrettyTime();
System.out.println(p.format(new Date()));
// prints "moments ago"

Вы также можете передать локаль для интернационализированных сообщений:1008 *

PrettyTime p = new PrettyTime(new Locale("fr"));
System.out.println(p.format(new Date()));
// prints "à l'instant"

Как отмечается в комментариях, в Android эта функциональность встроена в класс android.text.format.DateUtils.

69 голосов
/ 05 октября 2010

Рассматривали ли вы перечисление TimeUnit ? Это может быть очень полезно для такого рода вещей

    try {
        SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");
        Date past = format.parse("01/10/2010");
        Date now = new Date();

        System.out.println(TimeUnit.MILLISECONDS.toMillis(now.getTime() - past.getTime()) + " milliseconds ago");
        System.out.println(TimeUnit.MILLISECONDS.toMinutes(now.getTime() - past.getTime()) + " minutes ago");
        System.out.println(TimeUnit.MILLISECONDS.toHours(now.getTime() - past.getTime()) + " hours ago");
        System.out.println(TimeUnit.MILLISECONDS.toDays(now.getTime() - past.getTime()) + " days ago");
    }
    catch (Exception j){
        j.printStackTrace();
    }
42 голосов
/ 22 апреля 2014

Я беру ответы RealHowTo и Ben J и делаю свою собственную версию:

public class TimeAgo {
public static final List<Long> times = Arrays.asList(
        TimeUnit.DAYS.toMillis(365),
        TimeUnit.DAYS.toMillis(30),
        TimeUnit.DAYS.toMillis(1),
        TimeUnit.HOURS.toMillis(1),
        TimeUnit.MINUTES.toMillis(1),
        TimeUnit.SECONDS.toMillis(1) );
public static final List<String> timesString = Arrays.asList("year","month","day","hour","minute","second");

public static String toDuration(long duration) {

    StringBuffer res = new StringBuffer();
    for(int i=0;i< TimeAgo.times.size(); i++) {
        Long current = TimeAgo.times.get(i);
        long temp = duration/current;
        if(temp>0) {
            res.append(temp).append(" ").append( TimeAgo.timesString.get(i) ).append(temp != 1 ? "s" : "").append(" ago");
            break;
        }
    }
    if("".equals(res.toString()))
        return "0 seconds ago";
    else
        return res.toString();
}
public static void main(String args[]) {
    System.out.println(toDuration(123));
    System.out.println(toDuration(1230));
    System.out.println(toDuration(12300));
    System.out.println(toDuration(123000));
    System.out.println(toDuration(1230000));
    System.out.println(toDuration(12300000));
    System.out.println(toDuration(123000000));
    System.out.println(toDuration(1230000000));
    System.out.println(toDuration(12300000000L));
    System.out.println(toDuration(123000000000L));
}}

, который напечатает следующее

0 second ago
1 second ago
12 seconds ago
2 minutes ago
20 minutes ago
3 hours ago
1 day ago
14 days ago
4 months ago
3 years ago
42 голосов
/ 05 октября 2010
  public class TimeUtils {

      public final static long ONE_SECOND = 1000;
      public final static long SECONDS = 60;

      public final static long ONE_MINUTE = ONE_SECOND * 60;
      public final static long MINUTES = 60;

      public final static long ONE_HOUR = ONE_MINUTE * 60;
      public final static long HOURS = 24;

      public final static long ONE_DAY = ONE_HOUR * 24;

      private TimeUtils() {
      }

      /**
       * converts time (in milliseconds) to human-readable format
       *  "<w> days, <x> hours, <y> minutes and (z) seconds"
       */
      public static String millisToLongDHMS(long duration) {
        StringBuffer res = new StringBuffer();
        long temp = 0;
        if (duration >= ONE_SECOND) {
          temp = duration / ONE_DAY;
          if (temp > 0) {
            duration -= temp * ONE_DAY;
            res.append(temp).append(" day").append(temp > 1 ? "s" : "")
               .append(duration >= ONE_MINUTE ? ", " : "");
          }

          temp = duration / ONE_HOUR;
          if (temp > 0) {
            duration -= temp * ONE_HOUR;
            res.append(temp).append(" hour").append(temp > 1 ? "s" : "")
               .append(duration >= ONE_MINUTE ? ", " : "");
          }

          temp = duration / ONE_MINUTE;
          if (temp > 0) {
            duration -= temp * ONE_MINUTE;
            res.append(temp).append(" minute").append(temp > 1 ? "s" : "");
          }

          if (!res.toString().equals("") && duration >= ONE_SECOND) {
            res.append(" and ");
          }

          temp = duration / ONE_SECOND;
          if (temp > 0) {
            res.append(temp).append(" second").append(temp > 1 ? "s" : "");
          }
          return res.toString();
        } else {
          return "0 second";
        }
      }


      public static void main(String args[]) {
        System.out.println(millisToLongDHMS(123));
        System.out.println(millisToLongDHMS((5 * ONE_SECOND) + 123));
        System.out.println(millisToLongDHMS(ONE_DAY + ONE_HOUR));
        System.out.println(millisToLongDHMS(ONE_DAY + 2 * ONE_SECOND));
        System.out.println(millisToLongDHMS(ONE_DAY + ONE_HOUR + (2 * ONE_MINUTE)));
        System.out.println(millisToLongDHMS((4 * ONE_DAY) + (3 * ONE_HOUR)
            + (2 * ONE_MINUTE) + ONE_SECOND));
        System.out.println(millisToLongDHMS((5 * ONE_DAY) + (4 * ONE_HOUR)
            + ONE_MINUTE + (23 * ONE_SECOND) + 123));
        System.out.println(millisToLongDHMS(42 * ONE_DAY));
        /*
          output :
                0 second
                5 seconds
                1 day, 1 hour
                1 day and 2 seconds
                1 day, 1 hour, 2 minutes
                4 days, 3 hours, 2 minutes and 1 second
                5 days, 4 hours, 1 minute and 23 seconds
                42 days
         */
    }
}

more @ Форматирование длительности в миллисекундах в удобочитаемый формат

9 голосов
/ 14 февраля 2013

есть простой способ сделать это:

допустим, вы хотите время 20 минут назад:

Long minutesAgo = new Long(20);
Date date = new Date();
Date dateIn_X_MinAgo = new Date (date.getTime() - minutesAgo*60*1000);

вот и все ..

9 голосов
/ 21 февраля 2011

Это основано на ответе RealHowTo, поэтому, если вам это нравится, подарите ему / ей некоторую любовь.

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

Он также обрабатывает часть "и" немного по-другому.Я часто нахожу, что при соединении строк с разделителем бывает проще пропустить сложную логику и просто удалить последний разделитель, когда вы закончите.

import java.util.concurrent.TimeUnit;
import static java.util.concurrent.TimeUnit.MILLISECONDS;

public class TimeUtils {

    /**
     * Converts time to a human readable format within the specified range
     *
     * @param duration the time in milliseconds to be converted
     * @param max      the highest time unit of interest
     * @param min      the lowest time unit of interest
     */
    public static String formatMillis(long duration, TimeUnit max, TimeUnit min) {
        StringBuilder res = new StringBuilder();

        TimeUnit current = max;

        while (duration > 0) {
            long temp = current.convert(duration, MILLISECONDS);

            if (temp > 0) {
                duration -= current.toMillis(temp);
                res.append(temp).append(" ").append(current.name().toLowerCase());
                if (temp < 2) res.deleteCharAt(res.length() - 1);
                res.append(", ");
            }

            if (current == min) break;

            current = TimeUnit.values()[current.ordinal() - 1];
        }

        // clean up our formatting....

        // we never got a hit, the time is lower than we care about
        if (res.lastIndexOf(", ") < 0) return "0 " + min.name().toLowerCase();

        // yank trailing  ", "
        res.deleteCharAt(res.length() - 2);

        //  convert last ", " to " and"
        int i = res.lastIndexOf(", ");
        if (i > 0) {
            res.deleteCharAt(i);
            res.insert(i, " and");
        }

        return res.toString();
    }
}

Небольшой код, чтобы вызвать его:

import static java.util.concurrent.TimeUnit.*;

public class Main {

    public static void main(String args[]) {
        long[] durations = new long[]{
            123,
            SECONDS.toMillis(5) + 123,
            DAYS.toMillis(1) + HOURS.toMillis(1),
            DAYS.toMillis(1) + SECONDS.toMillis(2),
            DAYS.toMillis(1) + HOURS.toMillis(1) + MINUTES.toMillis(2),
            DAYS.toMillis(4) + HOURS.toMillis(3) + MINUTES.toMillis(2) + SECONDS.toMillis(1),
            DAYS.toMillis(5) + HOURS.toMillis(4) + MINUTES.toMillis(1) + SECONDS.toMillis(23) + 123,
            DAYS.toMillis(42)
        };

        for (long duration : durations) {
            System.out.println(TimeUtils.formatMillis(duration, DAYS, SECONDS));
        }

        System.out.println("\nAgain in only hours and minutes\n");

        for (long duration : durations) {
            System.out.println(TimeUtils.formatMillis(duration, HOURS, MINUTES));
        }
    }

}

, который выдаст следующее:

0 seconds
5 seconds 
1 day and 1 hour 
1 day and 2 seconds 
1 day, 1 hour and 2 minutes 
4 days, 3 hours, 2 minutes and 1 second 
5 days, 4 hours, 1 minute and 23 seconds 
42 days 

Again in only hours and minutes

0 minutes
0 minutes
25 hours 
24 hours 
25 hours and 2 minutes 
99 hours and 2 minutes 
124 hours and 1 minute 
1008 hours 

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

5 голосов
/ 28 мая 2013

Если вы ищете простое «Сегодня», «Вчера» или «x дней назад».

private String getDaysAgo(Date date){
    long days = (new Date().getTime() - date.getTime()) / 86400000;

    if(days == 0) return "Today";
    else if(days == 1) return "Yesterday";
    else return days + " days ago";
}
5 голосов
/ 22 октября 2015

О встроенных решениях:

Java не имеет встроенной поддержки форматирования относительного времени, также как и Java-8 и его новый пакет java.time. Если вам нужен только английский язык и ничего больше, то и только тогда может быть приемлемым решение, созданное вручную - см. Ответ @RealHowTo (хотя он имеет серьезный недостаток - не принимать во внимание часовой пояс для перевода мгновенных дельт в местное время). единицы!). В любом случае, если вы хотите избежать сложного обходного пути, специально разработанного для других языков, вам нужна внешняя библиотека.

В последнем случае я рекомендую использовать мою библиотеку Time4J (или Time4A на Android). Он предлагает наибольшую гибкость и большую мощность i18n . Класс net.time4j.PrettyTime имеет семь методов printRelativeTime...(...) для этой цели. Пример использования тестовых часов в качестве источника времени:

TimeSource<?> clock = () -> PlainTimestamp.of(2015, 8, 1, 10, 24, 5).atUTC();
Moment moment = PlainTimestamp.of(2015, 8, 1, 17, 0).atUTC(); // our input
String durationInDays =
  PrettyTime.of(Locale.GERMAN).withReferenceClock(clock).printRelative(
    moment,
    Timezone.of(EUROPE.BERLIN),
    TimeUnit.DAYS); // controlling the precision
System.out.println(durationInDays); // heute (german word for today)

Другой пример использования java.time.Instant в качестве ввода:

String relativeTime = 
  PrettyTime.of(Locale.ENGLISH)
    .printRelativeInStdTimezone(Moment.from(Instant.EPOCH));
System.out.println(relativeTime); // 45 years ago

Эта библиотека поддерживает в своей последней версии (v4.17) 80 языков , а также некоторые локали для конкретных стран (особенно для испанского, английского, арабского, французского). Данные i18n в основном основаны на новейшей CLDR-версии v29 . Другими важными причинами использования этой библиотеки являются хорошая поддержка множественных правил (которые часто отличаются от английского в других локалях), сокращенный стиль формата (например: "1 секунда назад" ) и выразительные способы для с учетом часовых поясов . Time4J даже знает о таких экзотических деталях, как високосных секунд в расчетах относительного времени (не очень важно, но он формирует сообщение, связанное с горизонтом ожидания). Совместимость с Java-8 существует благодаря легко доступным методам преобразования для таких типов, как java.time.Instant или java.time.Period.

Есть ли недостатки? Только два.

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

(Компакт) альтернативы:

Если вы ищете решение меньшего размера и вам не нужно так много функций, и вы готовы терпеть возможные проблемы с качеством, связанные с i18n-data, тогда:

  • Я бы порекомендовал ocpsoft / PrettyTime (поддержка фактически 32 языков (скоро 34?), Подходящих для работы только с java.util.Date - см. Ответ @ataylor). К сожалению, промышленный стандарт CLDR (от консорциума Unicode) с большим опытом работы в сообществе не является базой данных i18n, поэтому дальнейшее усовершенствование или улучшение данных может занять некоторое время ...

  • Если вы работаете на Android, тогда вспомогательный класс android.text.format.DateUtils представляет собой тонкую встроенную альтернативу (см. Другие комментарии и ответы здесь, с тем недостатком, что он не имеет поддержка в течение многих лет и месяцев. И я уверен, что очень немногим нравится стиль API этого вспомогательного класса.

  • Если вы являетесь поклонником Joda-Time , тогда вы можете взглянуть на его класс PeriodFormat (поддержка 14 языков в выпуске v2.9.4, с другой стороны : Joda-Time, конечно, тоже не компактен, поэтому я упомяну это здесь только для полноты). Эта библиотека не является реальным ответом, потому что относительное время вообще не поддерживается. Вам нужно будет как минимум добавить литерал «назад» (и вручную убирать все нижние единицы из сгенерированных форматов списка - неудобно). В отличие от Time4J или Android-DateUtils, он не имеет специальной поддержки сокращений или автоматического переключения с относительного времени на представление абсолютного времени. Как и PrettyTime, он полностью зависит от неподтвержденных вкладов частных членов Java-сообщества в свои данные i18n.

5 голосов
/ 21 июня 2015

java.time

Использование платформы java.time , встроенной в Java 8 и более поздние версии.

LocalDateTime t1 = LocalDateTime.of(2015, 1, 1, 0, 0, 0);
LocalDateTime t2 = LocalDateTime.now();
Period period = Period.between(t1.toLocalDate(), t2.toLocalDate());
Duration duration = Duration.between(t1, t2);

System.out.println("First January 2015 is " + period.getYears() + " years ago");
System.out.println("First January 2015 is " + period.getMonths() + " months ago");
System.out.println("First January 2015 is " + period.getDays() + " days ago");
System.out.println("First January 2015 is " + duration.toHours() + " hours ago");
System.out.println("First January 2015 is " + duration.toMinutes() + " minutes ago");
4 голосов
/ 01 апреля 2011

Я создал простой Java timeago порт для jquery-timeago плагина, который делает то, что вы просите.

TimeAgo time = new TimeAgo();
String minutes = time.timeAgo(System.currentTimeMillis() - (15*60*1000)); // returns "15 minutes ago"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...