Java Developers Toolbox - Java Date / Time
Java Developer's Toolbox
This training course walks you through many of the commonly used Java programming techniques such as autoboxing and unboxing. We’ll also cover several of the SDK base libraries and utility classes for working with Java primitives, Strings and DateTimes, etc.
- Understand what wrapper classes are and when to use them
- Be able to explain autoboxing and unboxing
- Be able to work with and define enumerations
- Make use of static imports
- Explain the core DateTime classes
- Modify and format DateTime values
- Work with time zone information
- Format Strings using the formatter syntax
- And be able to confidently use System.out and System.format to print out strings
- A basic understanding of software development
- A basic understanding of the software development life cycle
- Software Engineers interested in learning Java to develop applications
- Software Architects interested in learning Java to design applications
- Anyone interested in basic Java application development and associated tooling
- Anyone interested in understanding the basics of the Java SDK
Okay, welcome back. In this lecture, we'll introduce you to the Java Date/Time classes used to format and modify date/times. We'll cover items such as the core Date/Time classes, formatting date and time values for presentation, modifying dates using the API, and implementing applications that use Time-Zone information. The Date class. This class represents a date in time in an easy-to-use format. By constructing a date with no values, it becomes initialized to the current date and time. The Date class allows creation of a date that is accurate to a millisecond. Most of the methods on the Date class are deprecated, and have been as of JDK 1.1. The Calendar class came along and promised some relief. Note, when importing the Date class, ensure you use the right one, java.util.Date, and not mix it with java.sql.Date. Java.time. Java 8 introduced a new Date/Time API. It is modeled after the Joda Time API, and the author of Joda Time, Stephen Colebourne, worked closely with Oracle during the development of the new API. The Date/Time API was designed to address known problems within the Date class implementation. All classes of the new Date/Time API are designed to be thread-safe. Classes in the API are immutable, meaning their operations on an object result in a new object instance to be created. Most methods do not accept null pointers as import parameters and do not return null pointers, making them perfect candidates for implementations that follow the fluent interface design. Another big change when working with Date/Time values in Java is the domain-driven design of the API. The API provides several implementations, each one focused on a particular use case. Birthdays can now be defined using a type that only resembles a date, whereas, in the past, java.util.Date would also contain a time portion. Developers can now choose between implementation classes depending on whether they need time zone information in the application. The overview shown here shows the most use classes of the Date/Time API. As you can see, you can choose the appropriate class depending on the Date/Time information you need for the use case you are implementing. All classes follow the ISO-8601 calendar system. The diagram shown here shows some of the most commonly used classes of the Date/Time API. As you will see throughout this lecture, many of these methods accept any of the defined interface types. Key interfaces are the TemporalAccessor, defines the read-only access of a temporal object. Temporal defines read/write access of a temporal object, and the TemporalAdjuster defines the methods for modifying temporal objects. In addition to the classes, the Date/Time API also defines numerations for both day of week and month. The DayofWeek enum contains, besides the seven days of the week, methods to display a localized string representation of the day and methods to perform simple operations. According to the ISO-8601 standard, Monday is considered the first day of the week. The day of the week enum contains an intValue for each day of the week, and again, according to the ISO-8601 standard, to obtain this intValue, the getValue method should be used. The ordinal value of the constant DayOfweek.MONDAY, for example, will return zero. Just like the DayofWeek enum, the Month enum contains methods to obtain a localized textual representation of the month of the year, and performs simple month-related operations. Again, just like the DayofWeek enum, the Month enum also contains an intValue for each month of the year. In this case, January begins with the value one. To obtain the numeric value of a month, the getValue method should be used, and not the ordinal method, which would return zero. During this lecture, we will not cover each and every class of this API. Once you become familiar with one or two of these classes, the rest will be pretty straightforward. After all, most of them implement the same interfaces. One of the classes you will commonly use is the LocalDate class. This class represents just a date, and therefore, would be a prime candidate to hold values like birthdays or creation dates. This class, and many other temporal classes of the API, contains an of method to construct an instance using a specific moment in time, and a now method to obtain the current value. The temporal classes also contain parse methods that can be used to construct an instance using a string representation of the value. Once an instance of LocalDate has been created, several methods are available to obtain information about the date, like which day of the week the instance defines, or if the date is within a leap year. Furthermore, the date can be manipulated using any of the with methods, or can be converted into one of the other Date/Time classes. For example, adding time to the local date results in a LocalDateTime instance. Keep in mind that the instances are immutable. As a result, all operations performed on the instances will result in a new object instance to be created. The TemporalField interface defines a single Date/Time field within one of the classes. For example, the ChronoField enum provides an implementation of this interface. Custom implementations can be created when alternative calendar implementations are required. The individual Date/Time classes contain methods for obtaining fields of the instance. LocalDate contains methods like getYear, getMonth, getDayofWeek, et cetera. These methods are just methods provided on the class.
All instances of TemporalAccessor contain a get method, which takes a TemporalField type, that can be used to obtain the value of a field within the instance. To check an instance to see if the instance contains the requested field, a isSupportedBy method is available of the ChronoField enum. When your requested field is not implemented by the instance, an unsupported temporal type exception is thrown. The Date/Time API defines a clear distinction between classes that are used for human presentation. LocalDate, LocalDateTime, Month, et cetera, and classes that are used by machines. The Instance class is considered to be one of the classes that is meant to be used by machines. Similar to java.util.Date, it represents a single point on a timeline which starts at midnight of January the first, 1970. An Instant contains a long value that represents the amount of seconds since January first, 1970, but also has an intValue that contains the nanosecond of the current second. In addition to classes that represent a moment in time, the Date/Time API also contains classes that can be used to calculate the duration between temporal instances. The Duration class defines a time-based amount of time, measured in nanoseconds, and can be used to calculate the difference between temporals, e.g. the difference in time. However, the two temporal instances that are to be compared should be both of the same type. Where Duration is used to define or calculate duration of time, the Period class is used to calculate or define differences in dates. For example, Java 1 was introduced on January the 23rd, 1996, and Java 8 was introduced on March 18th, 2014. Using the between factory method of the Period class, a period instance is created which can be used to show that it took them 18 years, one month, and 23 days to get from Java 1 to the Java 8 date API shown here. As mentioned earlier, classes of the Date/Time API contain various methods allowing for the manipulation of the value. To be exact, it allows for new instances to be created containing the result of performing the required manipulation of the given date or time. Performing these manipulations not only result in new instances, again, instances are immutable, but that the result will also be a valid date. So adding one year to February 29, 2012 will result in February 28, 2013, noting the leap year component. Recalling the fluent API design goal of the Date/Time API, the manipulation methods are a prime example of this, allowing multiple methods to be chained, resulting in code that is more readable. So far, the operations that can be performed on the temporal instances have been pretty straightforward. When more complex date operations are required, instances of the TemporalAdjuster interface can be used. The TemporalAdjusters class contains a variety of factory methods that obtain some of the most commonly used adjusters, but custom implementations can also be created by implementing this interface. Since the TemporalAdjuster only contains a single abstract method, this interface is a functional interface. As a result, the adjuster logic can also be defined using a lambda expression. The code snippet shown here shows methods of the TemporalAdjusters class. Notice the use of a static input on these methods. As you can see, the TemporalAdjusters class contains several methods to obtain date information in relation to an existing temporal instance. When the TemporalAdjusters class does not provide an implementation that suits your needs, a custom implementation can be created by creating an implementation of the TemporalAdjuster interface. Naturally, when working with human-readable Date/Time information, there will come a time that the value needs to be presented. The DateTimeFormatter class is used to format instances of TemporalAccessor into a string representation. Again, factory methods have been provided for the commonly-used formatting patterns. When custom formats are required, these formats can be defined using the ofPattern method. When the format to be used cannot be defined using a simple pattern, the DateTimeFormatter builder class can be used to construct all kinds of complex formatters. Writing applications that have to take time zones into consideration is never easy. Luckily, the Date/Time API provides some relief by defining temporal implementations that deal specifically with time zones. Defining time zone information can be done using the new ZoneId class, which replaces the existing TimeZone class. The new class promises better support for Daylight Savings Time, but more importantly, works closely with the ZoneDateTime and OffsetDateTime classes of the Date/Time API. Instances that take time zone information into account can easily be constructed from temporal classes that do not contain time zone information, as shown here in the code snippet. Once a time zone aware instance has been created, methods are available to create instances of the same time, but for a different time zone. Often, time zones are defined as an offset from the UTC/Greenwich time. The ZoneOffset class is a subclass of ZoneId, and can be constructed by defining the offset. Consider the following questions to test yourself on the content that we've just reviewed. The answers to the above questions are, 2016-02-29, considering that LocalDate is immutable. Two, the interface that can be used to provide implementations to allow for the modification of dates. Three, Instant is intended for machine operation. LocalDateTime is intended for human presentation. Okay, that completes this lecture. Go ahead and close it, and we'll see you shortly in the next one.
Jeremy is a Content Lead Architect and DevOps SME here at Cloud Academy where he specializes in developing DevOps technical training documentation.
He has a strong background in software engineering, and has been coding with various languages, frameworks, and systems for the past 25+ years. In recent times, Jeremy has been focused on DevOps, Cloud (AWS, GCP, Azure), Security, Kubernetes, and Machine Learning.
Jeremy holds professional certifications for AWS, GCP, Terraform, Kubernetes (CKA, CKAD, CKS).