What is the Best Practice for manipulating and storing dates in Java?

后端 未结 5 1528
夕颜
夕颜 2021-02-03 22:37

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

5条回答
  •  醉梦人生
    2021-02-03 23:03

    UTC

    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.

    java.time

    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?

    • Java SE 8 and SE 9 and later
      • Built-in.
      • Part of the standard Java API with a bundled implementation.
      • Java 9 adds some minor features and fixes.
    • Java SE 6 and SE 7
      • Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
    • Android
      • The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) for Android specifically.
      • See How to use….

    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.

    ISO 8601

    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.

    Database

    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.

    Example code

    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();
    

    Formatting

    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:

    • FormatStyle to determine how long or abbreviated should the string be.
    • Locale to determine (a) the human language for translation of name of day, name of month, and such, and (b) the cultural norms deciding issues of abbreviation, capitalization, punctuation, and such.

    Example:

    Locale l = Locale.CANADA_FRENCH ; 
    DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( l );
    String output = zdt.format( f );
    

    Conversion

    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

    Use objects rather than mere coded primitives and simple strings. For example:

    • Do not use 1-7 to represent a day-of-week, use the DayOfWeek enum such as DayOfWeek.TUESDAY.
    • Rather than passing around a string as a date, pass around LocalDate objects.
    • Rather than pass around a pair of integers for a year-and-month, pass around YearMonth objects.
    • Instead of 1-12 for a month, use the much more readable Month enum such as Month.JANUARY.

    Using such objects makes your code more self-documenting, ensures valid values, and provides type-safety.

提交回复
热议问题