diff --git a/core-java-modules/core-java-date-operations-2/pom.xml b/core-java-modules/core-java-date-operations-2/pom.xml
index f60c7b7fc0..461ba70b62 100644
--- a/core-java-modules/core-java-date-operations-2/pom.xml
+++ b/core-java-modules/core-java-date-operations-2/pom.xml
@@ -30,11 +30,29 @@
hirondelle-date4j
${hirondelle-date4j.version}
+
+ net.time4j
+ time4j-base
+ ${time4j-base.version}
+
+
+ net.time4j
+ time4j-sqlxml
+ ${time4j-sqlxml.version}
+
+
+ org.ocpsoft.prettytime
+ prettytime
+ ${prettytime.version}
+
2.10
1.5.1
+ 3.2.7.Final
+ 5.9
+ 5.8
\ No newline at end of file
diff --git a/core-java-modules/core-java-date-operations-2/src/main/java/com/baeldung/timeago/version7/TimeAgoCalculator.java b/core-java-modules/core-java-date-operations-2/src/main/java/com/baeldung/timeago/version7/TimeAgoCalculator.java
new file mode 100644
index 0000000000..e09880f1ee
--- /dev/null
+++ b/core-java-modules/core-java-date-operations-2/src/main/java/com/baeldung/timeago/version7/TimeAgoCalculator.java
@@ -0,0 +1,94 @@
+package com.baeldung.timeago.version7;
+
+import java.util.Date;
+import java.util.TimeZone;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.Period;
+import org.joda.time.format.PeriodFormat;
+import org.joda.time.format.PeriodFormatter;
+import org.joda.time.format.PeriodFormatterBuilder;
+
+public class TimeAgoCalculator {
+
+ public static String calculateTimeAgoByTimeGranularity(Date pastTime, TimeGranularity granularity) {
+ Date currentTime = new Date();
+ long timeDifferenceInMillis = currentTime.getTime() - pastTime.getTime();
+ return timeDifferenceInMillis / granularity.toMillis() + " " + granularity.name()
+ .toLowerCase() + " ago";
+ }
+
+ public static String calculateHumanFriendlyTimeAgo(Date pastTime) {
+ Date currentTime = new Date();
+ long timeDifferenceInMillis = currentTime.getTime() - pastTime.getTime();
+ if (timeDifferenceInMillis / TimeGranularity.DECADES.toMillis() > 0)
+ return "several decades ago";
+ else if (timeDifferenceInMillis / TimeGranularity.YEARS.toMillis() > 0)
+ return "several years ago";
+ else if (timeDifferenceInMillis / TimeGranularity.MONTHS.toMillis() > 0)
+ return "several months ago";
+ else if (timeDifferenceInMillis / TimeGranularity.WEEKS.toMillis() > 0)
+ return "several weeks ago";
+ else if (timeDifferenceInMillis / TimeGranularity.DAYS.toMillis() > 0)
+ return "several days ago";
+ else if (timeDifferenceInMillis / TimeGranularity.HOURS.toMillis() > 0)
+ return "several hours ago";
+ else if (timeDifferenceInMillis / TimeGranularity.MINUTES.toMillis() > 0)
+ return "several minutes ago";
+ else
+ return "moments ago";
+ }
+
+ public static String calculateExactTimeAgoWithJodaTime(Date pastTime) {
+ Period period = new Period(new DateTime(pastTime.getTime()), new DateTime());
+ PeriodFormatter formatter = new PeriodFormatterBuilder().appendYears()
+ .appendSuffix(" year ", " years ")
+ .appendSeparator("and ")
+ .appendMonths()
+ .appendSuffix(" month ", " months ")
+ .appendSeparator("and ")
+ .appendWeeks()
+ .appendSuffix(" week ", " weeks ")
+ .appendSeparator("and ")
+ .appendDays()
+ .appendSuffix(" day ", " days ")
+ .appendSeparator("and ")
+ .appendHours()
+ .appendSuffix(" hour ", " hours ")
+ .appendSeparator("and ")
+ .appendMinutes()
+ .appendSuffix(" minute ", " minutes ")
+ .appendSeparator("and ")
+ .appendSeconds()
+ .appendSuffix(" second", " seconds")
+ .toFormatter();
+ return formatter.print(period);
+ }
+
+ public static String calculateHumanFriendlyTimeAgoWithJodaTime(Date pastTime) {
+ Period period = new Period(new DateTime(pastTime.getTime()), new DateTime());
+ if (period.getYears() != 0)
+ return "several years ago";
+ else if (period.getMonths() != 0)
+ return "several months ago";
+ else if (period.getWeeks() != 0)
+ return "several weeks ago";
+ else if (period.getDays() != 0)
+ return "several days ago";
+ else if (period.getHours() != 0)
+ return "several hours ago";
+ else if (period.getMinutes() != 0)
+ return "several minutes ago";
+ else
+ return "moments ago";
+ }
+
+ public static String calculateZonedTimeAgoWithJodaTime(Date pastTime, TimeZone zone) {
+ DateTimeZone dateTimeZone = DateTimeZone.forID(zone.getID());
+ Period period = new Period(new DateTime(pastTime.getTime(), dateTimeZone), new DateTime(dateTimeZone));
+ return PeriodFormat.getDefault()
+ .print(period);
+ }
+
+}
diff --git a/core-java-modules/core-java-date-operations-2/src/main/java/com/baeldung/timeago/version7/TimeGranularity.java b/core-java-modules/core-java-date-operations-2/src/main/java/com/baeldung/timeago/version7/TimeGranularity.java
new file mode 100644
index 0000000000..0cdb9729e2
--- /dev/null
+++ b/core-java-modules/core-java-date-operations-2/src/main/java/com/baeldung/timeago/version7/TimeGranularity.java
@@ -0,0 +1,41 @@
+package com.baeldung.timeago.version7;
+
+import java.util.concurrent.TimeUnit;
+
+public enum TimeGranularity {
+ SECONDS {
+ public long toMillis() {
+ return TimeUnit.SECONDS.toMillis(1);
+ }
+ }, MINUTES {
+ public long toMillis() {
+ return TimeUnit.MINUTES.toMillis(1);
+ }
+ }, HOURS {
+ public long toMillis() {
+ return TimeUnit.HOURS.toMillis(1);
+ }
+ }, DAYS {
+ public long toMillis() {
+ return TimeUnit.DAYS.toMillis(1);
+ }
+ }, WEEKS {
+ public long toMillis() {
+ return TimeUnit.DAYS.toMillis(7);
+ }
+ }, MONTHS {
+ public long toMillis() {
+ return TimeUnit.DAYS.toMillis(30);
+ }
+ }, YEARS {
+ public long toMillis() {
+ return TimeUnit.DAYS.toMillis(365);
+ }
+ }, DECADES {
+ public long toMillis() {
+ return TimeUnit.DAYS.toMillis(365 * 10);
+ }
+ };
+
+ public abstract long toMillis();
+}
diff --git a/core-java-modules/core-java-date-operations-2/src/main/java/com/baeldung/timeago/version8/TimeAgoCalculator.java b/core-java-modules/core-java-date-operations-2/src/main/java/com/baeldung/timeago/version8/TimeAgoCalculator.java
new file mode 100644
index 0000000000..392cfa54fa
--- /dev/null
+++ b/core-java-modules/core-java-date-operations-2/src/main/java/com/baeldung/timeago/version8/TimeAgoCalculator.java
@@ -0,0 +1,44 @@
+package com.baeldung.timeago.version8;
+
+import java.time.Duration;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.Period;
+import java.time.ZoneId;
+import java.util.Date;
+import java.util.Locale;
+
+import org.ocpsoft.prettytime.PrettyTime;
+
+public class TimeAgoCalculator {
+
+ public static String calculateTimeAgoWithPeriodAndDuration(LocalDateTime pastTime, ZoneId zone) {
+ Period period = Period.between(pastTime.toLocalDate(), LocalDate.now(zone));
+ Duration duration = Duration.between(pastTime, LocalDateTime.now(zone));
+ if (period.getYears() != 0)
+ return "several years ago";
+ else if (period.getMonths() != 0)
+ return "several months ago";
+ else if (period.getDays() != 0)
+ return "several days ago";
+ else if (duration.toHours() != 0)
+ return "several hours ago";
+ else if (duration.toMinutes() != 0)
+ return "several minutes ago";
+ else if (duration.getSeconds() != 0)
+ return "several seconds ago";
+ else
+ return "moments ago";
+ }
+
+ public static String calculateTimeAgoWithPrettyTime(Date pastTime) {
+ PrettyTime prettyTime = new PrettyTime();
+ return prettyTime.format(pastTime);
+ }
+
+ public static String calculateTimeAgoWithTime4J(Date pastTime, ZoneId zone, Locale locale) {
+ return net.time4j.PrettyTime.of(locale)
+ .printRelative(pastTime.toInstant(), zone);
+ }
+
+}
diff --git a/core-java-modules/core-java-date-operations-2/src/test/java/com/baeldung/timeago/version7/TimeAgoCalculatorUnitTest.java b/core-java-modules/core-java-date-operations-2/src/test/java/com/baeldung/timeago/version7/TimeAgoCalculatorUnitTest.java
new file mode 100644
index 0000000000..53975e2d83
--- /dev/null
+++ b/core-java-modules/core-java-date-operations-2/src/test/java/com/baeldung/timeago/version7/TimeAgoCalculatorUnitTest.java
@@ -0,0 +1,55 @@
+package com.baeldung.timeago.version7;
+
+import java.util.Date;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TimeAgoCalculatorUnitTest {
+
+ @Test
+ public void timeAgoByTimeGranularityTest() {
+ long DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
+ Assert.assertEquals("5 seconds ago", TimeAgoCalculator.calculateTimeAgoByTimeGranularity(new Date(System.currentTimeMillis() - (5 * 1000)), TimeGranularity.SECONDS));
+ Assert.assertEquals("5 minutes ago", TimeAgoCalculator.calculateTimeAgoByTimeGranularity(new Date(System.currentTimeMillis() - (5 * 60 * 1000)), TimeGranularity.MINUTES));
+ Assert.assertEquals("5 hours ago", TimeAgoCalculator.calculateTimeAgoByTimeGranularity(new Date(System.currentTimeMillis() - (5 * 60 * 60 * 1000)), TimeGranularity.HOURS));
+ Assert.assertEquals("5 days ago", TimeAgoCalculator.calculateTimeAgoByTimeGranularity(new Date(System.currentTimeMillis() - (5 * DAY_IN_MILLIS)), TimeGranularity.DAYS));
+ Assert.assertEquals("5 months ago", TimeAgoCalculator.calculateTimeAgoByTimeGranularity(new Date(System.currentTimeMillis() - (5 * DAY_IN_MILLIS * 30)), TimeGranularity.MONTHS));
+ Assert.assertEquals("5 weeks ago", TimeAgoCalculator.calculateTimeAgoByTimeGranularity(new Date(System.currentTimeMillis() - (5 * DAY_IN_MILLIS * 7)), TimeGranularity.WEEKS));
+ Assert.assertEquals("5 years ago", TimeAgoCalculator.calculateTimeAgoByTimeGranularity(new Date(System.currentTimeMillis() - (5 * DAY_IN_MILLIS * 365)), TimeGranularity.YEARS));
+ Assert.assertEquals("5 decades ago", TimeAgoCalculator.calculateTimeAgoByTimeGranularity(new Date(System.currentTimeMillis() - (5 * DAY_IN_MILLIS * 365 * 10)), TimeGranularity.DECADES));
+ }
+
+ @Test
+ public void humanFriendlyTimeAgoTest() {
+ long DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
+ Assert.assertEquals("moments ago", TimeAgoCalculator.calculateHumanFriendlyTimeAgo(new Date(System.currentTimeMillis() - (5 * 1000))));
+ Assert.assertEquals("several minutes ago", TimeAgoCalculator.calculateHumanFriendlyTimeAgo(new Date(System.currentTimeMillis() - (5 * 60 * 1000))));
+ Assert.assertEquals("several hours ago", TimeAgoCalculator.calculateHumanFriendlyTimeAgo(new Date(System.currentTimeMillis() - (5 * 60 * 60 * 1000))));
+ Assert.assertEquals("several days ago", TimeAgoCalculator.calculateHumanFriendlyTimeAgo(new Date(System.currentTimeMillis() - (5 * DAY_IN_MILLIS))));
+ Assert.assertEquals("several months ago", TimeAgoCalculator.calculateHumanFriendlyTimeAgo(new Date(System.currentTimeMillis() - (5 * DAY_IN_MILLIS * 30))));
+ Assert.assertEquals("several weeks ago", TimeAgoCalculator.calculateHumanFriendlyTimeAgo(new Date(System.currentTimeMillis() - (3 * DAY_IN_MILLIS * 7))));
+ Assert.assertEquals("several years ago", TimeAgoCalculator.calculateHumanFriendlyTimeAgo(new Date(System.currentTimeMillis() - (5 * DAY_IN_MILLIS * 365))));
+ Assert.assertEquals("several decades ago", TimeAgoCalculator.calculateHumanFriendlyTimeAgo(new Date(System.currentTimeMillis() - (5 * DAY_IN_MILLIS * 365 * 10))));
+ }
+
+ @Test
+ public void calculateExactTimeAgoWithJodaTimeTest() {
+ Assert.assertEquals("5 hours and 15 minutes and 3 seconds", TimeAgoCalculator.calculateExactTimeAgoWithJodaTime(new Date(System.currentTimeMillis() - (5 * 60 * 60 * 1000 + 15 * 60 * 1000 + 3 * 1000))));
+ Assert.assertEquals("5 hours and 1 minute and 1 second", TimeAgoCalculator.calculateExactTimeAgoWithJodaTime(new Date(System.currentTimeMillis() - (5 * 60 * 60 * 1000 + 1 * 60 * 1000 + 1 * 1000))));
+ Assert.assertEquals("2 days and 1 minute and 1 second", TimeAgoCalculator.calculateExactTimeAgoWithJodaTime(new Date(System.currentTimeMillis() - (2 * 24 * 60 * 60 * 1000 + 1 * 60 * 1000 + 1 * 1000))));
+ }
+
+ @Test
+ public void calculateHumanFriendlyTimeAgoWithJodaTimeTest() {
+ long DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
+ Assert.assertEquals("moments ago", TimeAgoCalculator.calculateHumanFriendlyTimeAgoWithJodaTime(new Date(System.currentTimeMillis() - (5 * 1000))));
+ Assert.assertEquals("several minutes ago", TimeAgoCalculator.calculateHumanFriendlyTimeAgoWithJodaTime(new Date(System.currentTimeMillis() - (5 * 60 * 1000))));
+ Assert.assertEquals("several hours ago", TimeAgoCalculator.calculateHumanFriendlyTimeAgoWithJodaTime(new Date(System.currentTimeMillis() - (5 * 60 * 60 * 1000))));
+ Assert.assertEquals("several days ago", TimeAgoCalculator.calculateHumanFriendlyTimeAgoWithJodaTime(new Date(System.currentTimeMillis() - (5 * DAY_IN_MILLIS))));
+ Assert.assertEquals("several months ago", TimeAgoCalculator.calculateHumanFriendlyTimeAgoWithJodaTime(new Date(System.currentTimeMillis() - (5 * DAY_IN_MILLIS * 30))));
+ Assert.assertEquals("several weeks ago", TimeAgoCalculator.calculateHumanFriendlyTimeAgoWithJodaTime(new Date(System.currentTimeMillis() - (3 * DAY_IN_MILLIS * 7))));
+ Assert.assertEquals("several years ago", TimeAgoCalculator.calculateHumanFriendlyTimeAgoWithJodaTime(new Date(System.currentTimeMillis() - (5 * DAY_IN_MILLIS * 365))));
+ }
+
+}
diff --git a/core-java-modules/core-java-date-operations-2/src/test/java/com/baeldung/timeago/version8/TimeAgoCalculatorUnitTest.java b/core-java-modules/core-java-date-operations-2/src/test/java/com/baeldung/timeago/version8/TimeAgoCalculatorUnitTest.java
new file mode 100644
index 0000000000..0412b3c2fd
--- /dev/null
+++ b/core-java-modules/core-java-date-operations-2/src/test/java/com/baeldung/timeago/version8/TimeAgoCalculatorUnitTest.java
@@ -0,0 +1,23 @@
+package com.baeldung.timeago.version8;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TimeAgoCalculatorUnitTest {
+
+ @Test
+ public void calculateTimeAgoWithPeriodAndDurationTest() {
+ long DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
+ Assert.assertEquals("moments ago", TimeAgoCalculator.calculateTimeAgoWithPeriodAndDuration(LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis()), ZoneId.systemDefault()), ZoneId.systemDefault()));
+ Assert.assertEquals("several seconds ago", TimeAgoCalculator.calculateTimeAgoWithPeriodAndDuration(LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis() - (5 * 1000)), ZoneId.systemDefault()), ZoneId.systemDefault()));
+ Assert.assertEquals("several minutes ago", TimeAgoCalculator.calculateTimeAgoWithPeriodAndDuration(LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis() - (5 * 60 * 1000)), ZoneId.systemDefault()), ZoneId.systemDefault()));
+ Assert.assertEquals("several hours ago", TimeAgoCalculator.calculateTimeAgoWithPeriodAndDuration(LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis() - (5 * 60 * 60 * 1000)), ZoneId.systemDefault()), ZoneId.systemDefault()));
+ Assert.assertEquals("several days ago", TimeAgoCalculator.calculateTimeAgoWithPeriodAndDuration(LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis() - (5 * DAY_IN_MILLIS)), ZoneId.systemDefault()), ZoneId.systemDefault()));
+ Assert.assertEquals("several months ago", TimeAgoCalculator.calculateTimeAgoWithPeriodAndDuration(LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis() - (5 * DAY_IN_MILLIS * 30)), ZoneId.systemDefault()), ZoneId.systemDefault()));
+ Assert.assertEquals("several years ago", TimeAgoCalculator.calculateTimeAgoWithPeriodAndDuration(LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis() - (5 * DAY_IN_MILLIS * 365)), ZoneId.systemDefault()), ZoneId.systemDefault()));
+ }
+}