4

I have a design issue I'm facing, I bring it up here so maybe I can get more ideas.

Currently in my project, I have a scenario where I need to represent the age of children.

The age can be from 0 to 15. from age 1 to 15 it's easy, I can store it as an integer, but the problem is if the age is between 0 and 1. Then I want to store it in months.

Storing the age as a java.util.Date won't work because I don't want to do comparison with the current date (I'm dealing with a really big database). regarding floating point, well you know that there are 12 months in one year, so then I need to to translate 0.5 to six months and 0.7 to I don't know...

I'm looking for a good way to store the age in one type. It can be Enum, etc. It should be small as possible. and it should be possible to persist the type in a database without any issues...

mprabhat
  • 20,107
  • 7
  • 46
  • 63
stdcall
  • 27,613
  • 18
  • 81
  • 125

6 Answers6

7

You could always store the age in an appropriate small unit (e.g. months), and convert it to years when you want.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
4

I would suggest two things:

  1. In the database, store the birthdate, as a date in the database's appropriate date type. If you store age in the database, then it will constantly be out of date, and will need to be updated. You said that your database is large, however that should not be a reason to store a transient value like 'age'. Put a good index on birthdate, and let the database calculate the age for you when needed.

  2. Use one of the above suggestions to represent the (calculated) age appropriately in the java class if you need to (though even this is questionable). One way would be to have separate 'months' and 'years' members, with the months only being significant when the years value is zero.

GreyBeardedGeek
  • 29,460
  • 2
  • 47
  • 67
2

I would do it in either one of the two following ways:

  • Store the age for everyone in months, and convert that to years when necessary.
  • Have an int for age, and have a boolean flag which represents whether the value of age is years or months.
AusCBloke
  • 18,014
  • 6
  • 40
  • 44
2

When you store the values relative to the current date, you need to update the values daily. You could still store the birthdate in a numeric representation say days since 1.1.1900.

stacker
  • 68,052
  • 28
  • 140
  • 210
1

tl;dr

SQL…

SELECT * FROM person WHERE birthdate <= ? ;

For years…

LocalDate.now( ZoneId.of( "Africa/Tunis" ) )
         .minus( Period.ofYears( 15 )  )

And for months…

LocalDate.now( ZoneId.of( "Africa/Tunis" ) )
         .minus( Period.ofMonths( 6 )  )

Details

The accepted Answer by GreyBeardedGeek is correct. You should be storing a date value in a date type of column. Frame your queries as finding rows whose birthdate is before/after a certain date for a certain age.

java.time

Here is some Java code as example. The modern approach uses the java.time classes built into Java 8 and later.

Avoid the troublesome old date-time classes such as Date and Calendar. They are poorly-designed and confusing. They are now legacy as of JSR 310.

LocalDate

The LocalDate class represents a date-only value without time-of-day and without time zone.

A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.

Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).

ZoneId z = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( z );

Period

Represent a span of time unattached to the timeline as a Period or Duration.

Period fifteenYears = Period.ofYears( 15 ) ;

Calculate the target date for the SQL query.

LocalDate ld = today.minus( fifteenYears ) ;  // Get the date fifteen years ago.

Your SQL will run something like this example. The ? is a placeholder for the LocalDate value determined above. Whether to compare using <= or < depends on your business rules’ definition of age.

SELECT * 
FROM person 
WHERE birthdate <= ? 
;

Your issue about months versus years is no problem at all. Use a Period of months rather than years.

Period sixMonths = Period.ofMonths( 6 ) ;

You may want to make that range of likely Period objects to be constants or perhaps an Enum.

JDBC 4.2

As of JDBC 4.2, we can directly exchange java.time types with a database. The date-time related java.sql classes are now legacy, such as java.sql.Timestamp and java.sql.Date. Good riddance to those kludges.

myPreparedStatement.setObject( 1 , ld ) ;

…and…

LocalDate birthdate = myResultSet.getObject( … , LocalDate.class ) ;

Two criteria

If you want only 15-year-olds, for example, and not 16-year-olds, use two criteria, a pair of earlier and later birthdates.

LocalDate earlierBirthDate = today.minus( Period.ofYears( 16 ) ) ;
LocalDate laterBirthDate = today.minus( Period.ofYears( 15 ) ) ;

The SQL would be something like this:

SELECT * 
FROM person 
WHERE birthdate > ? 
AND birthdate <= ? 
;

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
0

I would do

enum Age
{
     ZERO_MONTHS ,
     ONE_MONTH ,
     TWO_MONTHS ,
     THREE_MONTHS ,
     ... ,
     ELEVEN_MONTHS ,
     ONE_YEAR ,
     TWO_YEARS ,
     THREE_YEARS ,
     ... ,
     FIFTEEN_YEARS
}
emory
  • 10,725
  • 2
  • 30
  • 58
  • @BasileStarynkevitch I don't understand what is special about 18 such that this solution would not work. Anyway by the original problem statement no one in the population of interest is over 15 years old. – emory Oct 11 '12 at 21:44
  • You did not mention `EIGHTEEN_YEARS` – Basile Starynkevitch Oct 12 '12 at 05:19
  • @BasileStarynkevitch exactly, the age of children can be 0 to 11 months or 1 to 15 years. An 18 year child in the context of the original question would be erroneous data. On the other hand, if the data needs change, the enum can change too. – emory Oct 15 '12 at 17:04