Browse Source

Formatter for relative dates

Change-Id: I78b307177c68c578e10101a0ee7b6306880a08f7
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
stable-1.0
Matthias Sohn 14 years ago
parent
commit
c05c6f3327
  1. 126
      org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RelativeDateFormatterTest.java
  2. 13
      org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
  3. 13
      org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
  4. 140
      org.eclipse.jgit/src/org/eclipse/jgit/util/RelativeDateFormatter.java

126
org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RelativeDateFormatterTest.java

@ -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");
}
}

13
org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties

@ -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

13
org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java

@ -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;
}

140
org.eclipse.jgit/src/org/eclipse/jgit/util/RelativeDateFormatter.java

@ -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;
}
}
Loading…
Cancel
Save