java.time
The industry-leading date-time framework is java.time, built into Java 8 and later, defined by JSR 310.
The man leading this project is Stephen Colebourne. He also led its predecessor, the very successful Joda-Time project. The lessons learned with Joda-Time were applied in designing the all-new java.time classes. By the way, Joda-Time was ported to .Net in the NodaTime project.
find the number of days between two given dates,
Use the Period
class to represent a span-of-time in granularity of years-months-days.
LocalDate start = LocalDate.of( 2019 , Month.JANUARY , 23 ) ;
LocalDate stop = LocalDate.of( 2019 , Month.MARCH , 3 ) ;
Period p = Period.between( start , stop ) ;
Or if you want just want a total count of days, use ChronoUnit
.
long days = ChronoUnit.DAYS.between( start , stop ) ;
number of minutes between two given minute periods, etc.
Use Duration
class to represent a span-of-time in granularity of days (24-hour chunks of time unrelated to calendar), hours, minutes, seconds, fractional second.
Instant start = Instant.now() ; // Capture the current moment as seen in UTC.
…
Instant stop = Instant.now() ;
Duration d = Duration.between( start , stop ) ;
If you want total number of minutes elapsed of the entire span-of-time, call toMinutes
.
long elapsedMinutes = d.toMinutes() ;
add and subtract intervals from timestamps
You can do date-time math using the Period
and Duration
classes mentioned above, passing to plus
& minus
methods on various classes.
Instant now = Instant.now() ;
Duration d = Duration.ofMinutes( 7 ) ;
Instant later = now.plus( d ) ;
allow simple conversion between timezones, with Daylight Saving Time changes by region automatically accounted for
The ZoneId
class stores a history of past, present, and future changes to the offset used by people of a specific region, that is, a time zone.
Specify a proper time zone name in the format of Continent/Region
, such as America/Montreal
, Africa/Casablanca
, or Pacific/Auckland
. Never use the 2-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( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ; // Get the current date as seen by the people of a certain region.
If you want to use the JVM’s current default time zone, ask for it and pass as an argument. If omitted, the code becomes ambiguous to read in that we do not know for certain if you intended to use the default or if you, like so many programmers, were unaware of the issue.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
We can use the ZoneId
to adjust between zones. First, let's get the current moment as seen in UTC.
Instant instant = Instant.now() ;
Apply a time zone for the time zone in Tunisia. Apply a ZoneId
to the Instant
to yield a ZonedDateTime
object. Same moment, same point on the timeline, but a different wall-clock time.
ZoneId zTunis = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdtTunis = instant.atZone( zTunis ) ;
Let us see the same moment as it would appear to someone in Japan who is looking up at the clock on their wall.
ZoneId zTokyo = ZoneId.of( "Asia/Tokyo" ) ;
ZonedDateTime zdtTokyo = zdtTunis.withZoneSameInstant( zTokyo ) ; // Same moment, different wall-clock time.
All three objects, instant
, zdtTunis
, and zdtTokyo
all represent the same moment. Imagine a three-way conference call between someone in Iceland (where they use UTC), someone in Tunisia, and someone in Japan. If each person at the same moment looks up at the clock and calendar on their respective wall, they will each see a different time-of-day on their clock and possibly a different date on their calendar.
Notice that java.time uses immutable objects. Rather than change (“mutate”) an object, return a fresh new object based on the original’s values.
(given that there's an accurate supporting database of regional settings available)
Java includes a copy of tzdata, the standard time zone database. Be sure to keep your JVM up-to-date to carry current time-zone definitions. Unfortunately, politicians around the globe have shown a penchant for redefining the time zone(s) of their jurisdiction with little or no advance warning. So you may need update the tzddata manually if a time zone you care about changes suddenly.
By the way, your operating system likely carries its own copy of tzdata as well. Keep that fresh for your non-Java needs. Ditto for any other systems you may have installed such as a database server like Postgres with its own copy of tzdata.
get the period that a given timestamp falls into, given period granularity ("what calendar day is this date in?")
By “calendar day”, do you mean day-of-week? In java.time, we have DayOfWeek
enum that predefines seven objects, one for each day of the week.
DayOfWeek dow = LocalDate.now( z ).getDayOfWeek() ;
By “calendar day”, do you mean the day of the year (1-366)?
int dayOfYear = LocalDate.now( z ).getDayOfYear() ;
By “calendar day”, do you mean a representation of the year-month?
YearMonth ym = YearMonth.from( today ) ; // `today` being `LocalDate.now( ZoneId.of( "Pacific/Auckland" ) )`.
Perhaps month-day?
MonthDay md = MonthDay.from( today ) ;
support very general string-to-date conversions (given a pattern)
You can specify a custom formatting pattern to use in parsing/generating string that represent the value of a date-time object. See the DateTimeFormatter.ofPattern
method. Search Stack Overflow for more info, as this has been handled many many times.
If your string is properly formatted by the localization rules for a particular culture, you can let java.time do the work of parsing without bothering to define a formatting pattern.
Locale locale = Locale.CANADA_FRENCH ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDate( FormatStyle.MEDIUM ).withLocale( locale ) ;
String output = LocalDate.now( z ).format( f ) ;
if there's a Java-style Calendar/GregorianCalendar setup
The Calendar
and GregorianCalendar
classes bundled with the earliest versions of Java are terrible. Never use them. They are supplanted entirely by the java.time classes, specifically the ZonedDateTime
class.
accommodating toward subclasses if I need to roll my own Hebrew, Babylonian, Tolkien, or MartianCalendar. (Java Calendars make this pointlessly hard, for example.)
Many calendaring systems have already been implemented for java.time. Each is known as a chronology. The calendaring system commonly used in the West and in much business around the globe, is the ISO 8601 chronology. This is used by default in java.time, java.time.chrono.IsoChronology
.
Bundled with Java you will also find additional chronologies including the Hijrah version of the Islamic calendar, the Japanese Imperial calendar system, Minguo calendar system (Taiwan, etc.), and the Thai Buddhist calendar.
You will find more chronologies defined in the ThreeTen-Extra project. See the org.threeten.extra.chrono
package for a list including: IRS/IFRS standard accounting calendar, British Julian-Gregorian cutover calendar system, Coptic Christian calendar, the Discordian calendar system, Ethiopic calendar, the International Fixed calendar (Eastman Kodak calendar), Julian calendar, and more.
But if you need some other calendar, java.time provides the AbstractChronology
to get you started. But do some serious web-searching before embarking on your own, as it may already be built. And all the above listed chronologies are open-source, so you can study them for guidance.
"how many minutes are there between 2002 and next Valentine's Day?"
LocalDate date2002 = Year.of( 2002 ).atDay( 1 );
MonthDay valentinesHoliday = MonthDay.of( Month.FEBRUARY , 14 );
ZoneId z = ZoneId.of( "America/Edmonton" );
LocalDate today = LocalDate.now( z );
LocalDate valDayThisYear = today.with( valentinesHoliday );
LocalDate nextValDay = valDayThisYear;
if ( valDayThisYear.isBefore( today ) )
{ // If Valentine's day already happened this year, move to next year’s Valentine's Day.
nextValDay = valDayThisYear.plusYears( 1 );
}
ZonedDateTime start = date2002.atStartOfDay( z );
ZonedDateTime stop = nextValDay.atStartOfDay( z );
Duration d = Duration.between( start , stop );
long minutes = d.toMinutes();
System.out.println( "From start: " + start + " to stop: " + stop + " is duration: " + d + " or a total in minutes: " + minutes + "." );
LocalDate date2002 = Year.of( 2002 ).atDay( 1 );
MonthDay valentinesHoliday = MonthDay.of( Month.FEBRUARY , 14 );
ZoneId z = ZoneId.of( "America/Edmonton" );
LocalDate today = LocalDate.now( z );
LocalDate valDayThisYear = today.with( valentinesHoliday );
LocalDate nextValDay = valDayThisYear;
if ( valDayThisYear.isBefore( today ) )
{ // If Valentine's day already happened this year, move to next year’s Valentine's Day.
nextValDay = valDayThisYear.plusYears( 1 );
}
ZonedDateTime start = date2002.atStartOfDay( z );
ZonedDateTime stop = nextValDay.atStartOfDay( z );
Duration d = Duration.between( start , stop );
long minutes = d.toMinutes();
System.out.println( "From start: " + start + " to stop: " + stop + " is duration: " + d + " or a total in minutes: " + minutes + "." );
When run.
From start: 2002-01-01T00:00-07:00[America/Edmonton] to stop: 2020-02-14T00:00-07:00[America/Edmonton] is duration: PT158832H or a total in minutes: 9529920.
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.
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.