What is the Best Practice for manipulating and storing Dates e.g. using GregorianCalendar in an enterprise java application?
Looking for feedback and I will consolidate
Think, work, and store data in UTC rather than any time zone. Think of UTC as the One True Time, and all other time zones are mere variations. So while coding, forget all about your own time zone. Do your business logic, logging, data storage, and data exchange in UTC. I suggest every programmer keep a second clock on their desk set to UTC.
The modern way is the java.time classes.
The mentioned Joda-Time project provided the inspiration for the java.time classes, and the project is now in maintenance mode with the team advising migration to java.time classes.
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, & java.text.SimpleDateFormat.
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.
When serializing a date-time value to text, use the ISO 8601 standard.
For example, a date-time in UTC is 2016-10-17T01:24:35Z
where the Z
is short for Zulu
and means UTC. For other offset-from-UTC the offset of hours and minutes appears at the end such as 2016-01-23T12:34:56+05:30
. The java.time classes extend this standard format to append the name of the time zone (if known) in square brackets, such as 2016-01-23T12:34:56+05:30[Asia/Kolkata]
.
The standard has many other handy formats as well including for durations, intervals, ordinals, and year-week.
For database storage, use date-time types for date-time values, such as the SQL standard data types which are primarily DATE
, TIME
, and TIMESTAMP WITH TIME ZONE
.
Let your JDBC driver do the heavy lifting. The driver handles the nitty-gritty details about mediating and adapting between the internals of how Java handles the data and how your database handles the data on its side. But be sure to practice with example data to learn the behaviors of your driver and your database. The SQL standard defines very little about date-time handling and so behaviors vary widely, surprisingly so.
If using a JDBC driver compliant with JDBC 4.2 and later, you can fetch and store java.time types directly via the ResultSet::getObject
and PreparedStatement::setObject
methods.
Instant instant = myResultSet.getObject( … );
myPreparedStatement.setObject( … , instant );
For older drivers, you will need to fall back to converting through the java.sql types. Look for new conversion methods added to the old classes. For example, java.sql.Timestamp.toInstant().
Instant instant = myResultSet.getTimestamp( … ).toInstant();
myPreparedStatement.setObject( … , java.sql.Timestamp.from( instant ) );
Use the java.sql types as briefly as possible. They are a badly designed hack, such as java.sql.Date
masquerading as a date-only value but actually as a subclass of java.util.Date
it does indeed have a time-of-day set to the 00:00:00
in UTC. And, oh, you are supposed to ignore the fact of that inheritance says the class doc. An ugly mess.
Get the current moment in UTC.
Instant instant = Instant.now();
Storing and fetching that Instant
object to/from a database is shown above.
To generate an ISO 8601 string, merely call toString
. The java.time classes all use ISO 8601 formats by default for parsing and generating strings of their various date-time values.
String output = instant.toString();
Adjust into any offset-from-UTC by applying a ZoneOffset
to get an OffsetDateTime
. Call toString
to generate a String in ISO 8601 format.
ZoneOffset offset = ZoneOffset.ofHoursMinutes( 5 , 30 );
OffsetDateTime odt = instant.atOffset( offset );
A time zone is an offset plus a set of rules for handling anomalies such as Daylight Saving Time (DST). When you need to see that same moment through the lens of some region’s own wall-clock time, apply a time zone (ZoneId) to get a ZonedDateTime object.
Specify a proper time zone name in the format of continent/region
. 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( "Asia/Kolkata" );
ZonedDateTime zdt = instant.atZone( z );
Going the other direction, you can extract an Instant
from an OffsetDateTime
or ZonedDateTime
by calling toInstant
.
Instant instant = zdt.toInstant();
For presentation to the user as strings in formats other than ISO 8601, search Stack Overflow for use of the DateTimeFormatter
class.
While you can specify an custom format, usually best to let java.time automatically localize. To localize, specify:
Example:
Locale l = Locale.CANADA_FRENCH ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( l );
String output = zdt.format( f );
Best to avoid the legacy date-time types whenever possible. But if working with old code not yet updated for the java.time types, you can convert to/from the java.time types. For details, see the Question, Convert java.util.Date to what “java.time” type?.
Use objects rather than mere coded primitives and simple strings. For example:
DayOfWeek.TUESDAY
. Using such objects makes your code more self-documenting, ensures valid values, and provides type-safety.