tl;dr
- Nanos give only 3 centuries.
- Never roll-your-own time-keeping. ☛ Use java.time ☚
- When exchanging date-time values, never use a count-from-epoch, use text in standard ISO 8601 format.
Doing the math
You must pick an epoch reference date. While dozens of various epochs are used in the industry, one of the more common is often referred to as Unix Time: The first moment of 1970 in UTC, 1970-01-01T00:00:00Z. Moments after that point are tracked using a positive number count, and prior moments use a negative number count.
Of course the length of a year varies by Leap Year, but we can easily approximate. Get the number of nanoseconds in a year using the TimeUnit
class with its handy conversion methods.
Crunch some numbers.
long nanosPerDay = TimeUnit.DAYS.toNanos( 1L );
long nanosPerYear = ( long ) ( nanosPerDay * 365.25 );
long maximumYears = ( Long.MAX_VALUE / nanosPerYear );
Dump to console.
System.out.println( "nanosPerDay: " + nanosPerDay );
System.out.println( "nanosPerYear: " + nanosPerYear );
System.out.println( "Long.MAX_VALUE: " + Long.MAX_VALUE + " provides for maximumYears: " + maximumYears );
See this code run live at IdeOne.com.
nanosPerDay: 86400000000000
nanosPerYear: 31557600000000000
Long.MAX_VALUE: 9223372036854775807 provides for maximumYears: 292
So we get less than ±3 centuries before and after 1970. So this approach does work, if a few centuries is good enough for your app. At least you won’t be around to fix it when the calendar runs out. ;-)
Instant
Fortunately we need not do this math ourselves.
Modern Java happens to come with the most advanced date-time handling framework available for business-oriented apps: The java.time classes defined in JSR 310.
The java.time.Instant
class uses a different strategy to tracking time. That class keeps a count of whole seconds before/after the epoch of 1970 UTC, plus a separate count of nanoseconds for the fractional-second of the moment.
Think of it as:
moment = ( whole-seconds + fractional-second ) ; // Since 1970-01-01T00:00:00Z.
This enables a minimum/maximum of:
Notice that java.time classes do carry a resolution of nanoseconds, while the legacy classes they supplanted (Date
, Calendar
, etc.) resolved to milliseconds.
For the gnarly details, see the class JavaDoc and the OpenJDK source code (in Mercurial).
Tip: The java.time classes are most appropriate to business-oriented purposes. If you are doing scientific, engineering, archeology, or history work, use other notations appropriate to the field, likely as String
type.
Granularity
Be aware that different computer systems use different granularities in their time-keeping.
As noted above, the java.time classes resolve to nanoseconds, for nine digits of a decimal fractional second. The legacy classes used milliseconds, for three digits of a decimal fractional second. Other systems, such as the Postgres database, use microseconds for six digits of a decimal fractional second. When exchanging or comparing data across sytems, you may want to use the truncatedTo
methods on various java.time classes.
Current conventional computer hardware clocks are accurate only to microseconds or grosser. They may report on nanoseconds but are not accurate to that degree. So, you will find that the Instant.now()
method in Java 9, 10, & 11 capture the current moment to the microsecond (while the data type can handle nanos, it detects the current time in micros).
Instant.now().toString(): 2019-01-23T12:34:56.123456Z
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
.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.*
classes. Hibernate 5 & JPA 2.2 support java.time.
Where to obtain the java.time classes?