]> source.dussan.org Git - jgit.git/commitdiff
Formatter for relative dates 61/3361/4
authorMatthias Sohn <matthias.sohn@sap.com>
Mon, 9 May 2011 12:46:38 +0000 (14:46 +0200)
committerMatthias Sohn <matthias.sohn@sap.com>
Mon, 9 May 2011 12:46:38 +0000 (14:46 +0200)
Change-Id: I78b307177c68c578e10101a0ee7b6306880a08f7
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RelativeDateFormatterTest.java [new file with mode: 0644]
org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
org.eclipse.jgit/src/org/eclipse/jgit/util/RelativeDateFormatter.java [new file with mode: 0644]

diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RelativeDateFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RelativeDateFormatterTest.java
new file mode 100644 (file)
index 0000000..4b45209
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2011, Matthias Sohn <matthias.sohn@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.eclipse.jgit.util.RelativeDateFormatter.YEAR_IN_MILLIS;
+import static org.eclipse.jgit.util.RelativeDateFormatter.SECOND_IN_MILLIS;
+import static org.eclipse.jgit.util.RelativeDateFormatter.MINUTE_IN_MILLIS;
+import static org.eclipse.jgit.util.RelativeDateFormatter.HOUR_IN_MILLIS;
+import static org.eclipse.jgit.util.RelativeDateFormatter.DAY_IN_MILLIS;
+
+import java.util.Date;
+
+import org.eclipse.jgit.util.RelativeDateFormatter;
+import org.junit.Test;
+
+public class RelativeDateFormatterTest {
+
+       private void assertFormat(long ageFromNow, long timeUnit,
+                       String expectedFormat) {
+               Date d = new Date(System.currentTimeMillis() - ageFromNow * timeUnit);
+               String s = RelativeDateFormatter.format(d);
+               assertEquals(expectedFormat, s);
+       }
+
+       @Test
+       public void testFuture() {
+               assertFormat(-100, YEAR_IN_MILLIS, "in the future");
+               assertFormat(-1, SECOND_IN_MILLIS, "in the future");
+       }
+
+       @Test
+       public void testFormatSeconds() {
+               assertFormat(1, SECOND_IN_MILLIS, "1 seconds ago");
+               assertFormat(89, SECOND_IN_MILLIS, "89 seconds ago");
+       }
+
+       @Test
+       public void testFormatMinutes() {
+               assertFormat(90, SECOND_IN_MILLIS, "2 minutes ago");
+               assertFormat(3, MINUTE_IN_MILLIS, "3 minutes ago");
+               assertFormat(60, MINUTE_IN_MILLIS, "60 minutes ago");
+               assertFormat(89, MINUTE_IN_MILLIS, "89 minutes ago");
+       }
+
+       @Test
+       public void testFormatHours() {
+               assertFormat(90, MINUTE_IN_MILLIS, "2 hours ago");
+               assertFormat(149, MINUTE_IN_MILLIS, "2 hours ago");
+               assertFormat(35, HOUR_IN_MILLIS, "35 hours ago");
+       }
+
+       @Test
+       public void testFormatDays() {
+               assertFormat(36, HOUR_IN_MILLIS, "2 days ago");
+               assertFormat(13, DAY_IN_MILLIS, "13 days ago");
+       }
+
+       @Test
+       public void testFormatWeeks() {
+               assertFormat(14, DAY_IN_MILLIS, "2 weeks ago");
+               assertFormat(69, DAY_IN_MILLIS, "10 weeks ago");
+       }
+
+       @Test
+       public void testFormatMonths() {
+               assertFormat(70, DAY_IN_MILLIS, "2 months ago");
+               assertFormat(75, DAY_IN_MILLIS, "3 months ago");
+               assertFormat(364, DAY_IN_MILLIS, "12 months ago");
+       }
+
+       @Test
+       public void testFormatYearsMonths() {
+               assertFormat(366, DAY_IN_MILLIS, "1 year, 0 month ago");
+               assertFormat(380, DAY_IN_MILLIS, "1 year, 1 month ago");
+               assertFormat(410, DAY_IN_MILLIS, "1 year, 2 months ago");
+               assertFormat(2, YEAR_IN_MILLIS, "2 years, 0 month ago");
+               assertFormat(1824, DAY_IN_MILLIS, "4 years, 12 months ago");
+       }
+
+       @Test
+       public void testFormatYears() {
+               assertFormat(5, YEAR_IN_MILLIS, "5 years ago");
+               assertFormat(60, YEAR_IN_MILLIS, "60 years ago");
+       }
+}
index a19c9c41dd96e7fa73a45612363a83e31e903683..1f4e9a13729e24df5bf2095f338c327a9cf3c8ed 100644 (file)
@@ -136,6 +136,7 @@ createNewFileFailed=Could not create new file {0}
 credentialPassword=Password
 credentialUsername=Username
 daemonAlreadyRunning=Daemon already running
+daysAgo={0} days ago
 deleteBranchUnexpectedResult=Delete branch returned unexpected result {0}
 deleteFileFailed=Could not delete file {0}
 deletingNotSupported=Deleting {0} not supported.
@@ -203,6 +204,7 @@ flagIsDisposed={0} is disposed.
 flagNotFromThis={0} not from this.
 flagsAlreadyCreated={0} flags already created.
 funnyRefname=funny refname
+hoursAgo={0} hours ago
 hugeIndexesAreNotSupportedByJgitYet=Huge indexes are not supported by jgit, yet
 hunkBelongsToAnotherFile=Hunk belongs to another file
 hunkDisconnectedFromFile=Hunk disconnected from file
@@ -221,6 +223,7 @@ indexWriteException=Modified index could not be written
 integerValueOutOfRange=Integer value {0}.{1} out of range
 internalRevisionError=internal revision error
 interruptedWriting=Interrupted writing {0}
+inTheFuture=in the future
 invalidAdvertisementOf=invalid advertisement of {0}
 invalidAncestryLength=Invalid ancestry length
 invalidBooleanValue=Invalid boolean value: {0}.{1}={2}
@@ -268,6 +271,7 @@ mergeConflictOnNonNoteEntries=Merge conflict on non-note entries: base = {0}, ou
 mergeStrategyAlreadyExistsAsDefault=Merge strategy "{0}" already exists as a default strategy
 mergeStrategyDoesNotSupportHeads=merge strategy {0} does not support {1} heads to be merged into HEAD
 mergeUsingStrategyResultedInDescription=Merge of revisions {0} with base {1} using strategy {2} resulted in: {3}. {4}
+minutesAgo={0} minutes ago
 missingAccesskey=Missing accesskey.
 missingConfigurationForKey=No value for key {0} found in configuration
 missingDeltaBase=delta base
@@ -279,6 +283,9 @@ missingSecretkey=Missing secretkey.
 mixedStagesNotAllowed=Mixed stages not allowed
 mkDirFailed=Creating directory {0} failed
 mkDirsFailed=Creating directories for {0} failed
+month=month
+months=months
+monthsAgo={0} months ago
 multipleMergeBasesFor=Multiple merge bases for:\n  {0}\n  {1} found:\n  {2}\n  {3}
 need2Arguments=Need 2 arguments
 needPackOut=need packOut
@@ -384,6 +391,7 @@ resultLengthIncorrect=result length incorrect
 rewinding=Rewinding to commit {0}
 searchForReuse=Finding sources
 searchForSizes=Getting sizes
+secondsAgo={0} seconds ago
 sequenceTooLargeForDiffAlgorithm=Sequence too large for difference algorithm.
 serviceNotEnabledNoName=Service not enabled
 serviceNotPermitted={0} not permitted
@@ -465,6 +473,7 @@ uriNotFound={0} not found
 userConfigFileInvalid=User config file {0} invalid {1}
 walkFailure=Walk failure.
 wantNotValid=want {0} not valid
+weeksAgo={0} weeks ago
 windowSizeMustBeLesserThanLimit=Window size must be < limit
 windowSizeMustBePowerOf2=Window size must be power of 2
 writeTimedOut=Write timed out
@@ -474,3 +483,7 @@ writingNotSupported=Writing {0} not supported.
 writingObjects=Writing objects
 wrongDecompressedLength=wrong decompressed length
 wrongRepositoryState=Wrong Repository State: {0}
+year=year
+years=years
+yearsAgo={0} years ago
+yearsMonthsAgo={0} {1}, {2} {3} ago
index f86a8e5446c189582807a845bc71ede3d5a64b55..845608440ed96016cb2084de4d1626aba39f5932 100644 (file)
@@ -196,6 +196,7 @@ public class JGitText extends TranslationBundle {
        /***/ public String credentialPassword;
        /***/ public String credentialUsername;
        /***/ public String daemonAlreadyRunning;
+       /***/ public String daysAgo;
        /***/ public String deleteBranchUnexpectedResult;
        /***/ public String deleteFileFailed;
        /***/ public String deletingNotSupported;
@@ -263,6 +264,7 @@ public class JGitText extends TranslationBundle {
        /***/ public String flagNotFromThis;
        /***/ public String flagsAlreadyCreated;
        /***/ public String funnyRefname;
+       /***/ public String hoursAgo;
        /***/ public String hugeIndexesAreNotSupportedByJgitYet;
        /***/ public String hunkBelongsToAnotherFile;
        /***/ public String hunkDisconnectedFromFile;
@@ -281,6 +283,7 @@ public class JGitText extends TranslationBundle {
        /***/ public String integerValueOutOfRange;
        /***/ public String internalRevisionError;
        /***/ public String interruptedWriting;
+       /***/ public String inTheFuture;
        /***/ public String invalidAdvertisementOf;
        /***/ public String invalidAncestryLength;
        /***/ public String invalidBooleanValue;
@@ -328,6 +331,7 @@ public class JGitText extends TranslationBundle {
        /***/ public String mergeStrategyAlreadyExistsAsDefault;
        /***/ public String mergeStrategyDoesNotSupportHeads;
        /***/ public String mergeUsingStrategyResultedInDescription;
+       /***/ public String minutesAgo;
        /***/ public String missingAccesskey;
        /***/ public String missingConfigurationForKey;
        /***/ public String missingDeltaBase;
@@ -339,6 +343,9 @@ public class JGitText extends TranslationBundle {
        /***/ public String mixedStagesNotAllowed;
        /***/ public String mkDirFailed;
        /***/ public String mkDirsFailed;
+       /***/ public String month;
+       /***/ public String months;
+       /***/ public String monthsAgo;
        /***/ public String multipleMergeBasesFor;
        /***/ public String need2Arguments;
        /***/ public String needPackOut;
@@ -444,6 +451,7 @@ public class JGitText extends TranslationBundle {
        /***/ public String rewinding;
        /***/ public String searchForReuse;
        /***/ public String searchForSizes;
+       /***/ public String secondsAgo;
        /***/ public String sequenceTooLargeForDiffAlgorithm;
        /***/ public String serviceNotEnabledNoName;
        /***/ public String serviceNotPermitted;
@@ -525,6 +533,7 @@ public class JGitText extends TranslationBundle {
        /***/ public String userConfigFileInvalid;
        /***/ public String walkFailure;
        /***/ public String wantNotValid;
+       /***/ public String weeksAgo;
        /***/ public String windowSizeMustBeLesserThanLimit;
        /***/ public String windowSizeMustBePowerOf2;
        /***/ public String writeTimedOut;
@@ -534,4 +543,8 @@ public class JGitText extends TranslationBundle {
        /***/ public String writingObjects;
        /***/ public String wrongDecompressedLength;
        /***/ public String wrongRepositoryState;
+       /***/ public String year;
+       /***/ public String years;
+       /***/ public String yearsAgo;
+       /***/ public String yearsMonthsAgo;
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RelativeDateFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RelativeDateFormatter.java
new file mode 100644 (file)
index 0000000..bcbcd80
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2011, Matthias Sohn <matthias.sohn@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.util;
+
+import java.text.MessageFormat;
+import java.util.Date;
+
+import org.eclipse.jgit.JGitText;
+
+/**
+ * Formatter to format timestamps relative to the current time using time units
+ * in the format defined by {@code git log --relative-date}.
+ */
+public class RelativeDateFormatter {
+       final static long SECOND_IN_MILLIS = 1000;
+
+       final static long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS;
+
+       final static long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS;
+
+       final static long DAY_IN_MILLIS = 24 * HOUR_IN_MILLIS;
+
+       final static long WEEK_IN_MILLIS = 7 * DAY_IN_MILLIS;
+
+       final static long MONTH_IN_MILLIS = 30 * DAY_IN_MILLIS;
+
+       final static long YEAR_IN_MILLIS = 365 * DAY_IN_MILLIS;
+
+       /**
+        * @param when
+        *            {@link Date} to format
+        * @return age of given {@link Date} compared to now formatted in the same
+        *         relative format as returned by {@code git log --relative-date}
+        */
+       @SuppressWarnings("boxing")
+       public static String format(Date when) {
+
+               long ageMillis = (System.currentTimeMillis() - when.getTime());
+
+               // shouldn't happen in a perfect world
+               if (ageMillis < 0)
+                       return JGitText.get().inTheFuture;
+
+               // seconds
+               if (ageMillis < upperLimit(MINUTE_IN_MILLIS))
+                       return MessageFormat.format(JGitText.get().secondsAgo,
+                                       round(ageMillis, SECOND_IN_MILLIS));
+
+               // minutes
+               if (ageMillis < upperLimit(HOUR_IN_MILLIS))
+                       return MessageFormat.format(JGitText.get().minutesAgo,
+                                       round(ageMillis, MINUTE_IN_MILLIS));
+
+               // hours
+               if (ageMillis < upperLimit(DAY_IN_MILLIS))
+                       return MessageFormat.format(JGitText.get().hoursAgo,
+                                       round(ageMillis, HOUR_IN_MILLIS));
+
+               // up to 14 days use days
+               if (ageMillis < 14 * DAY_IN_MILLIS)
+                       return MessageFormat.format(JGitText.get().daysAgo,
+                                       round(ageMillis, DAY_IN_MILLIS));
+
+               // up to 10 weeks use weeks
+               if (ageMillis < 10 * WEEK_IN_MILLIS)
+                       return MessageFormat.format(JGitText.get().weeksAgo,
+                                       round(ageMillis, WEEK_IN_MILLIS));
+
+               // months
+               if (ageMillis < YEAR_IN_MILLIS)
+                       return MessageFormat.format(JGitText.get().monthsAgo,
+                                       round(ageMillis, MONTH_IN_MILLIS));
+
+               // up to 5 years use "year, months" rounded to months
+               if (ageMillis < 5 * YEAR_IN_MILLIS) {
+                       long years = ageMillis / YEAR_IN_MILLIS;
+                       String yearLabel = (years > 1) ? JGitText.get().years : //
+                                       JGitText.get().year;
+                       long months = round(ageMillis % YEAR_IN_MILLIS, MONTH_IN_MILLIS);
+                       String monthLabel = (months > 1) ? JGitText.get().months : //
+                                       JGitText.get().month;
+                       return MessageFormat.format(JGitText.get().yearsMonthsAgo,
+                                       new Object[] { years, yearLabel, months, monthLabel });
+               }
+
+               // years
+               return MessageFormat.format(JGitText.get().yearsAgo,
+                               round(ageMillis, YEAR_IN_MILLIS));
+       }
+
+       private static long upperLimit(long unit) {
+               long limit = unit + unit / 2;
+               return limit;
+       }
+
+       private static long round(long n, long unit) {
+               long rounded = (n + unit / 2) / unit;
+               return rounded;
+       }
+}
\ No newline at end of file