Few days ago I saw a post in StackoverFlow asking for finding date of birth from age in java. I thought that why don't I write a class which can manipulate Age. Here is what I came up with.
This class Age stores age in terms of
Followings are the features of this class:
You comments and suggestion is appreciable to improve it.
Happy coding.
This class Age stores age in terms of
- Years
- Months
- Days
- Hours
- Minutes
- Seconds
Followings are the features of this class:
- It returns Age for a given Date
- It is Timezone specific
- It returns age as Seconds, Minutes, Hours, Days, Weeks, Months
- Prints Age in a pretty format
- This class is Serializable, Cloneable and Comparable
- It is written using java.time api
import java.io.Serializable; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.DateTimeException; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; import java.time.temporal.ValueRange; import java.util.Date; import java.util.Objects; /** * The class <code>Age</code> represents the length of time that a person has * lived or a thing has existed. * * @author TapasB */ public class Age implements Serializable, Cloneable, Comparable<Age> { private static final long serialVersionUID = 210032925430172448L; private long years; private long months; private long days; private long hours; private long minutes; private long seconds; // Private Constructor private Age() { } /** * Creates and returns a new instance of <code>Age</code> object. * * @author TapasB * @return a new instance of <code>Age</code> */ public static Age getDefault() { return new Age(); } /** * Creates and returns a new instance of <code>Age</code> object after * setting the given years. * * @author TapasB * @param years * - years to set * @return a new instance of <code>Age</code> */ public static Age of(long years) { return getDefault().setYears(years); } /** * Creates and returns a new instance of <code>Age</code> object after * setting given years and months to it. * * @author TapasB * @param years * - years to set * @param months * - months to set * @return a new instance of <code>Age</code> */ public static Age of(long years, long months) { return of(years).setMonths(months); } /** * Creates and returns a new instance of <code>Age</code> object after * setting given years, months and days to it. * * @author TapasB * @param years * - years to set * @param months * - months to set * @param days * - days to set * @return a new instance of <code>Age</code> */ public static Age of(long years, long months, long days) { return of(years, months).setDays(days); } /** * Creates and returns a new instance of <code>Age</code> object after * setting given years, months, days and hours to it. * * @author TapasB * @param years * - years to set * @param months * - months to set * @param days * - days to set * @param hours * - hours to set * @return a new instance of <code>Age</code> */ public static Age of(long years, long months, long days, long hours) { return of(years, months, days).setHours(hours); } /** * Creates and returns a new instance of <code>Age</code> object after * setting given years, months, days, hours and minutes to it. * * @author TapasB * @param years * - years to set * @param months * - months to set * @param days * - days to set * @param hours * - hours to set * @param minutes * - minutes to set * @return a new instance of <code>Age</code> */ public static Age of(long years, long months, long days, long hours, long minutes) { return of(years, months, days, hours).setMinutes(minutes); } /** * Creates and returns a new instance of <code>Age</code> object after * setting given years, months, days, hours, minutes and seconds to it. * * @author TapasB * @param years * - years to set * @param months * - months to set * @param days * - days to set * @param hours * - hours to set * @param minutes * - minutes to set * @param seconds * - seconds to set * @return a new instance of <code>Age</code> */ public static Age of(long years, long months, long days, long hours, long minutes, long seconds) { return of(years, months, days, hours, minutes).setSeconds(seconds); } /** * Calculates and returns an instance <code>Age</code> for the given date * string and date pattern. The <code>Age</code> will be calculated with * respect to the system default time-zone. * * @author TapasB * @param date * - the birth date as string * @param pattern * - date pattern * @return an instance of <code>Age</code> * @throws ParseException * if the parsing fails */ public static Age fromDateOfBirth(String date, String pattern) throws ParseException { Objects.requireNonNull(date, "date"); Objects.requireNonNull(pattern, "pattern"); SimpleDateFormat dateFormat = new SimpleDateFormat(pattern); return fromDateOfBirth(dateFormat.parse(date)); } /** * Calculates and returns an instance of <code>Age</code> for the given * {@link Date}. The <code>Age</code> will be calculated with respect to the * system default time-zone. * * @author TapasB * @param date * - the birth date * @return an instance of <code>Age</code> */ public static Age fromDateOfBirth(Date date) { Objects.requireNonNull(date, "date"); return fromDateOfBirth(date, ZoneId.systemDefault()); } /** * Calculates and returns an instance of <code>Age</code> for the given * {@link Date} with respect to the given {@link ZoneId}. * * @author TapasB * @param date * - the birth date * @param zone * - the zone ID to use * @return an instance of <code>Age</code> */ public static Age fromDateOfBirth(Date date, ZoneId zone) { Objects.requireNonNull(date, "date"); Objects.requireNonNull(zone, "zone"); Instant instant = Instant.ofEpochMilli(date.getTime()); LocalDateTime dateTime = LocalDateTime.ofInstant(instant, zone); return fromDateOfBirth(dateTime, zone); } /** * Calculates and returns an instance of <code>Age</code> for the given * {@link LocalDateTime}. The <code>Age</code> will be calculated with * respect to the system default time-zone. * * @author TapasB * @param date * - the birth date * @return an instance of <code>Age</code> */ public static Age fromDateOfBirth(LocalDateTime date) { Objects.requireNonNull(date, "date"); return fromDateOfBirth(date, ZoneId.systemDefault()); } /** * Calculates and returns an instance of <code>Age</code> for the given * {@link LocalDateTime} with respect to the given {@link ZoneId}. * * @author TapasB * @param date * - the birth date * @param zone * - the zone ID to use * @return an instance of <code>Age</code> */ public static Age fromDateOfBirth(LocalDateTime date, ZoneId zone) { Objects.requireNonNull(date, "date"); Objects.requireNonNull(zone, "zone"); ZonedDateTime zonedDate = date.atZone(zone); return fromDateOfBirth(zonedDate); } /** * Calculates and returns an instance of <code>Age</code> for the given * {@link ZonedDateTime}. * * @author TapasB * @param date * - the birth date * @return an instance of <code>Age</code> */ public static Age fromDateOfBirth(ZonedDateTime date) { Objects.requireNonNull(date, "date"); ZonedDateTime now = ZonedDateTime.now(date.getZone()); ZonedDateTime tempDate = ZonedDateTime.from(date); long years = tempDate.until(now, ChronoUnit.YEARS); tempDate = tempDate.plusYears(years); long months = tempDate.until(now, ChronoUnit.MONTHS); tempDate = tempDate.plusMonths(months); long days = tempDate.until(now, ChronoUnit.DAYS); tempDate = tempDate.plusDays(days); long hours = tempDate.until(now, ChronoUnit.HOURS); tempDate = tempDate.plusHours(hours); long minutes = tempDate.until(now, ChronoUnit.MINUTES); tempDate = tempDate.plusMinutes(minutes); long seconds = tempDate.until(now, ChronoUnit.SECONDS); Age age = getDefault(); if (years > 0) { age.years = years; } if (months > 0) { age.months = months; } if (days > 0) { age.days = days; } if (hours > 0) { age.hours = hours; } if (minutes > 0) { age.minutes = minutes; } if (seconds > 0) { age.seconds = seconds; } return age; } /** * Sets the given years to this object. * * @author TapasB * @param years * - years to set * @return this instance for chain call */ public Age setYears(long years) { checkValidValue("years", ValueRange.of(0, ChronoField.YEAR.range().getMaximum()), years); this.years = years; return this; } /** * Sets the given months to this object. * * @author TapasB * @param months * - months to set * @return this instance for chain call */ public Age setMonths(long months) { checkValidValue("months", ValueRange.of(0, ChronoField.MONTH_OF_YEAR.range().getMaximum()), months); this.months = months; return this; } /** * Sets the given days to this object. * * @author TapasB * @param days * - days to set * @return this instance for chain call */ public Age setDays(long days) { checkValidValue("days", ValueRange.of(0, ChronoField.DAY_OF_MONTH.range().getMaximum()), days); this.days = days; return this; } /** * Sets the given hours to this object. * * @author TapasB * @param hours * - hours to set * @return this instance for chain call */ public Age setHours(long hours) { checkValidValue("hours", ValueRange.of(0, ChronoField.HOUR_OF_DAY.range().getMaximum()), hours); this.hours = hours; return this; } /** * Sets the given minutes to this object. * * @author TapasB * @param minutes * - minutes to set * @return this instance for chain call */ public Age setMinutes(long minutes) { checkValidValue("minutes", ValueRange.of(0, ChronoField.MINUTE_OF_HOUR.range().getMaximum()), minutes); this.minutes = minutes; return this; } /** * Sets the given seconds to this object. * * @author TapasB * @param seconds * - seconds to set * @return this instance for chain call */ public Age setSeconds(long seconds) { checkValidValue("seconds", ValueRange.of(0, ChronoField.SECOND_OF_MINUTE.range().getMaximum()), seconds); this.seconds = seconds; return this; } /** * Sets the given value to this object to the given field defined by * {@link ChronoUnit}. * * <p> * The accepted list of values are: * <ul> * <li>ChronoUnit.YEARS</li> * <li>ChronoUnit.MONTHS</li> * <li>ChronoUnit.DAYS</li> * <li>ChronoUnit.HOURS</li> * <li>ChronoUnit.MINUTES</li> * <li>ChronoUnit.SECONDS</li> * </ul> * </p> * * @author TapasB * @param value * - value to set * @param unit * - field to which the value needs to be set * @return this instance for chain call * @throws UnsupportedOperationException * if the unit passed to it is not belongs to the allowed list * of values */ public Age set(long value, ChronoUnit unit) throws UnsupportedOperationException { Objects.requireNonNull(unit, "unit"); switch (unit) { case YEARS: return setYears(value); case MONTHS: return setMonths(value); case DAYS: return setDays(value); case HOURS: return setHours(value); case MINUTES: return setMinutes(value); case SECONDS: return setSeconds(value); default: throw new UnsupportedOperationException("Unit " + unit + " not supported"); } } /** * Calculates the <code>Age</code> and returns the birth date as * {@link LocalDateTime}. * * @author TapasB * @return date of birth as <code>LocalDateTime</code> */ public LocalDateTime getDateOfBirth() { LocalDateTime now = LocalDateTime.now(); LocalDateTime dob = now.minus(this.years, ChronoUnit.YEARS). minus(this.months, ChronoUnit.MONTHS). minus(this.days, ChronoUnit.DAYS). minus(this.hours, ChronoUnit.HOURS). minus(this.minutes, ChronoUnit.MINUTES). minus(this.seconds, ChronoUnit.SECONDS); return dob; } /** * Calculates the <code>Age</code> and returns the birth date as * {@link ZonedDateTime} with respect to the given {@link ZoneId}. * * @author TapasB * @param zone * - the zone ID to use * @return date of birth as <code>ZonedDateTime</code> */ public ZonedDateTime getDateOfBirth(ZoneId zone) { ZonedDateTime now = ZonedDateTime.now(zone); ZonedDateTime dob = now.minus(this.years, ChronoUnit.YEARS). minus(this.months, ChronoUnit.MONTHS). minus(this.days, ChronoUnit.DAYS). minus(this.hours, ChronoUnit.HOURS). minus(this.minutes, ChronoUnit.MINUTES). minus(this.seconds, ChronoUnit.SECONDS); return dob; } /** * Returns elapsed seconds from date of birth for this <code>Age</code>. * * @author TapasB * @return seconds */ public long asSeconds() { return asSeconds(ZoneId.systemDefault()); } /** * Returns elapsed seconds from date of birth for this <code>Age</code> for * the given. zone * * @author TapasB * @param zone * - the zone ID to use * @return seconds */ public long asSeconds(ZoneId zone) { return calculateDifference(ChronoUnit.SECONDS, zone); } /** * Returns elapsed minutes from date of birth for this <code>Age</code>. * * @author TapasB * @return minutes */ public long asMinutes() { return asMinutes(ZoneId.systemDefault()); } /** * Returns elapsed minutes from date of birth for this <code>Age</code> for * the given zone. * * @author TapasB * @param zone * - the zone ID to use * @return minutes */ public long asMinutes(ZoneId zone) { return calculateDifference(ChronoUnit.MINUTES, zone); } /** * Returns elapsed hours from date of birth for this <code>Age</code>. * * @author TapasB * @return hours */ public long asHours() { return asHours(ZoneId.systemDefault()); } /** * Returns elapsed hours from date of birth for this <code>Age</code> for * the given zone. * * @author TapasB * @param zone * - the zone ID to use * @return hours */ public long asHours(ZoneId zone) { return calculateDifference(ChronoUnit.HOURS, zone); } /** * Returns elapsed days from date of birth for this <code>Age</code>. * * @author TapasB * @return days */ public long asDays() { return asDays(ZoneId.systemDefault()); } /** * Returns elapsed days from date of birth for this <code>Age</code> for the * given zone. * * @author TapasB * @param zone * - the zone ID to use * @return days */ public long asDays(ZoneId zone) { return calculateDifference(ChronoUnit.DAYS, zone); } /** * Returns elapsed weeks from date of birth for this <code>Age</code>. * * @author TapasB * @return weeks */ public long asWeeks() { return asWeeks(ZoneId.systemDefault()); } /** * Returns elapsed weeks from date of birth for this <code>Age</code> for * the given zone. * * @author TapasB * @param zone * - the zone ID to use * @return weeks */ public long asWeeks(ZoneId zone) { return calculateDifference(ChronoUnit.WEEKS, zone); } /** * Returns elapsed months from date of birth for this <code>Age</code>. * * @author TapasB * @return months */ public long asMonths() { return asMonths(ZoneId.systemDefault()); } /** * Returns elapsed months from date of birth for this <code>Age</code> for * the given zone. * * @author TapasB * @param zone * - the zone ID to use * @return months */ public long asMonths(ZoneId zone) { return calculateDifference(ChronoUnit.MONTHS, zone); } /** * Returns string representation of the <code>Age</code> in the following * format: <br/> * <code>A Years B Months C Days D Hours E Minutes F Seconds</code> <br/> * Any field except Seconds will be omitted if the value is 0. * * @author TapasB * @return the string representation */ public String getDisplayString() { StringBuilder builder = new StringBuilder(); setString(years, builder, "Years"); setString(months, builder, "Months"); setString(days, builder, "Days"); setString(hours, builder, "Hours"); setString(minutes, builder, "Minutes"); builder.append(seconds); builder.append(" Seconds."); return builder.toString(); } /** * Returns string representation of the <code>Age</code> in the following * format: <br/> * <code>A Years B Months C Days D Hours E Minutes F Seconds</code> <br/> * Any field except Seconds will be omitted if the value is 0. * * @author TapasB * @return the string representation */ @Override public String toString() { return getDisplayString(); } /** * Compares two <code>Age</code> objects. * * @author TapasB * @param age * - the other <code>Age</code> to be compared * @return the value 0 if the argument <code>Age</code> is equal to this * <code>Age</code>; a value less than 0 if this <code>Age</code> is * before the <code>Age</code> argument; and a value greater than 0 * if this <code>Age</code> is after the <code>Age</code> argument. */ @Override public int compareTo(Age age) { Objects.requireNonNull(age, "age"); long thisSeconds = asSeconds(); long otherSeconds = age.asSeconds(); return Long.compare(thisSeconds, otherSeconds); } /** * Creates and returns a copy of this object with the same years, months, * days, hours, minutes and seconds. * * @author TapasB * @return a clone of this instance of <code>Age</code> */ @Override protected Age clone() { return of(years, months, days, hours, minutes, seconds); } /** * Returns a hash code value for the <code>Age</code> object. * * @author TapasB * @return a hash code value for this object */ @Override public int hashCode() { final int prime = 31; int result = 1; Long thisSeconds = asSeconds(); int intValue = thisSeconds.intValue(); result = prime * result + (int) (intValue ^ (intValue >>> 32)); return result; } /** * Indicates whether some other <code>Age</code> object is same to this one. * * @author TapasB * @param obj * - the reference object with which to compare * @return true if this object is the same as the obj argument; false * otherwise. * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Age age = (Age) obj; return compareTo(age) == 0; } // Checks the validity of the field value private void checkValidValue(String field, ValueRange range, long value) { if (!range.isValidValue(value)) { throw new DateTimeException(getInvalidFieldMessage(field, range, value)); } } // Returns message in case of invalid value which is used by // DateTimeException private String getInvalidFieldMessage(String field, ValueRange range, long value) { return "Invalid value " + value + " for " + field + ". Valid values: (" + range.getMinimum() + ", " + range.getMaximum() + ")."; } // Appends the field value of Age to the StringBuilder with the token private void setString(long value, StringBuilder builder, String token) { if (value > 0) { builder.append(value); builder.append(" ").append(token).append(" "); } } // Calculates the amount of time with respect to the given ChronoUnit // and ZoneId between the birth date and now private long calculateDifference(ChronoUnit chronoUnit, ZoneId zone) { Objects.requireNonNull(chronoUnit); Objects.requireNonNull(zone); ZonedDateTime dob = getDateOfBirth(zone); ZonedDateTime now = ZonedDateTime.now(zone); return chronoUnit.between(dob, now); } }Few example code to use:
public static void getDateOfBirthTest() { Age age = Age.of(29, 8, 28, 12, 49, 0); System.out.println(age.getDateOfBirth()); } public static void getAgeFromDateTest() { Age age = Age.fromDateOfBirth(LocalDateTime.of(1984, Month.DECEMBER, 16, 7, 45, 0)); System.out.println(age); } public static void ageAsUnitsTest() { Age age = Age.fromDateOfBirth(LocalDateTime.of(1984, Month.DECEMBER, 16, 7, 45, 0)); System.out.println(age.asWeeks()); } @SuppressWarnings("resource") public static void serializationTest() throws FileNotFoundException, IOException, ClassNotFoundException { Age age = Age.fromDateOfBirth(LocalDateTime.of(1984, Month.DECEMBER, 16, 7, 45)); System.out.println(age); ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(new File("C:/tmp"))); outputStream.writeObject(age); outputStream.flush(); ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(new File("C:/tmp"))); age = Age.class.cast(inputStream.readObject()); System.out.println(age); } public static void equalsTest() { Age age = Age.of(29, 8, 28, 12, 49, 0); Age clone = age.clone(); System.out.println(age.equals(clone)); }While developing this class I was stuck to calculate the difference between to date-time and I got help from one SO user and I really appreciate that.
You comments and suggestion is appreciable to improve it.
Happy coding.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.