[java] Java에서 “시간 전”을 계산하는 방법은 무엇입니까?

Ruby on Rails에는 날짜를 가져 와서 “오래된”날짜를 인쇄 할 수있는 기능이 있습니다.

예를 들면 다음과 같습니다.

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

Java로 이것을 쉽게 할 수있는 방법이 있습니까?



답변

PrettyTime 라이브러리를 살펴보십시오 .

사용이 매우 간단합니다.

import org.ocpsoft.prettytime.PrettyTime;

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

국제화 된 메시지를 위해 로케일을 전달할 수도 있습니다.

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

의견에서 언급했듯이 Android에는이 기능이 내장되어 있습니다. android.text.format.DateUtils 클래스에 있습니다.


답변

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


답변

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


답변

  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 @ 지속 시간을 밀리 초 단위로 사람이 읽을 수있는 형식으로 지정


답변

이것은 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 

그리고 누군가가 필요로하는 경우 위와 같은 문자열 을 밀리 초로 다시 변환하는 클래스가 있습니다. 사람들이 읽을 수있는 텍스트로 다양한 것들의 타임 아웃을 지정할 수 있도록하는 데 매우 유용합니다.


답변

이를 수행하는 간단한 방법이 있습니다.

20 분 전에 시간을 원한다고 가정 해 봅시다.

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

그게 다야 ..


답변

내장 된 솔루션 :

Java는 상대 시간 형식 지정을 기본적으로 지원하지 않으며 Java-8 및 새 패키지도 지원하지 않습니다. java.time . 영어와 다른 것이 필요하지 않고 손으로 만든 솔루션 만 허용되는 경우 @RealHowTo의 답변을 참조하십시오 (즉석 델타를 현지 시간으로 변환하는 시간대를 고려하지 않는 것이 큰 단점이 있지만) 단위!). 어쨌든, 특히 다른 로케일에 대해 집에서 만든 복잡한 해결 방법을 피하려면 외부 라이브러리가 필요합니다.

후자의 경우 내 라이브러리 Time4J (또는 Android의 Time4A) 를 사용하는 것이 좋습니다 . 그것은 제공하는 최고의 유연성과 가장 국제화 파워 . net.time4j.PrettyTime 클래스 에는 printRelativeTime...(...)이 목적을위한 7 가지 메소드 가 있습니다. 테스트 클럭을 시간 소스로 사용하는 예 :

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.Instantjava.time.Period.

단점이 있습니까? 단 두개.

  • 라이브러리는 작지 않습니다 (큰 i18n 데이터 저장소 때문에).
  • API는 잘 알려져 있지 않으므로 커뮤니티 지식과 지원은 제공되지 않지만 제공된 문서는 매우 상세하고 포괄적입니다.

(소형) 대안 :

더 작은 솔루션을 찾고 많은 기능이 필요하지 않고 i18n-data와 관련된 가능한 품질 문제를 용인하려는 경우 :

  • 나는 ocpsoft / PrettyTime (실제로 32 가지 언어 (곧 34?) java.util.Date만 지원하는 것이 좋습니다 -@ataylor의 답변 참조)을 권장합니다. 커뮤니티 규모가 큰 업계 표준 CLDR (유니 코드 컨소시엄)은 불행히도 i18n 데이터의 기반이 아니므로 데이터의 추가 향상 또는 개선에 시간이 오래 걸릴 수 있습니다.

  • Android에있는 경우 도우미 클래스 android.text.format.DateUtils 는 슬림 한 내장 대안입니다 (여기에서 몇 달 동안 지원되지 않는다는 단점이있는 여기의 다른 의견 및 답변 참조). 이 도우미 클래스의 API 스타일을 좋아하는 사람은 거의 없습니다.

  • Joda-Time 의 팬이라면 PeriodFormat 클래스 (버전 v2.9.4에서 14 개 언어 지원, 반대편 에서)를 볼 수 있습니다 . Joda-Time도 확실히 컴팩트하지 않으므로 여기에 언급했습니다. 완전성). 이 라이브러리는 상대 시간이 전혀 지원되지 않기 때문에 실제 답변이 아닙니다. 최소한 문자 그대로 “ago”를 추가해야합니다 (그리고 생성 된 목록 형식에서 모든 하위 단위를 수동으로 제거-어색함). Time4J 또는 Android-DateUtils와 달리 약어 또는 상대 시간에서 절대 시간 표현으로의 자동 전환을 특별히 지원하지 않습니다. PrettyTime과 마찬가지로 Java 커뮤니티의 개인 구성원이 i18n 데이터에 확인하지 않은 기여도에 전적으로 의존합니다.