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

后端 未结 5 1538
夕颜
夕颜 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 22:50

    To get the discussion started, here's been my experience:

    When creating standards for a typical 3-tier Java Enterprise project, I would generally recommend that the project use GregorianCalendar for manipulating dates. Reason is GregorianCalendar is the de facto standard over any other Calendar instance e.g. Julian calendar etc. It's the recognized calendar in most countries and properly handles leap years, etc. On top of that, I would recommend that the application store its dates as UTC so that you can easily perform date calculations such as finding the difference between two dates (if it were stored as EST for example, you'd have to take day light savings time into account). The date can be then be localized to whatever timezone you need it to be displayed to the user as -- such as localizing it to EST if you are an east-coast US company and you want your time information shown in EST.

    0 讨论(0)
  • 2021-02-03 22:58

    The best practice is usually precisely NOT to think in term of heavy date objects but to store a point in time. This is typically done by storing a value that doesn't suffer from corner cases nor from potential parsing problems. To do this, people usually store the number of milliseconds (or seconds) elapsed since a fixed point that we call the epoch (1970-01-01). This is very common and any Java API will always allow you to convert any kind of date to/from the time expressed in ms since the epoch.

    That's for storage. You can also store, for example, the user's preferred timezone, if there's such a need.

    Now such a date in milliseconds, like:

    System.out.println( System.currentTimeMillis() );
    1264875453
    

    ain't very useful when it's displayed to the end user, that's for granted.

    Which is why you use, for example, the example Joda time to convert it to some user-friendly format before displaying it to the end-user.

    You asked for best practice, here's my take on it: storing "date" objects in a DB instead of the time in milliseconds is right there with using floating point numbers to represent monetary amounts.

    It's usually a huge code smell.

    So Joda time in Java is the way to manipulate date, yes. But is Joda the way to go to store dates? CERTAINLY NOT.

    0 讨论(0)
  • 2021-02-03 22:59

    Joda time (100% interoperable with the JDK)

    Joda-Time provides a quality replacement for the Java date and time classes. The design allows for multiple calendar systems, while still providing a simple API

    0 讨论(0)
  • 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.

    0 讨论(0)
  • 2021-02-03 23:15

    Joda is the way to go. Why ?

    1. it has a much more powerful and intuitive interface than the standard Date/Time API
    2. there are no threading issues with date/time formatting. java.text.SimpleDateFormat is not thread-safe (not a lot of people know this!)

    At some stage the Java Date/Time API is going to be superseded (by JSR-310). I believe this is going to be based upon the work done by those behind Joda, and as such you'll be learning an API that will influence a new standard Java API.

    0 讨论(0)
提交回复
热议问题