Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
Java 8. DateTime
Serhii Kartashov
October 2015
Softjourn
Agenda
Previous implementation and problems in real world
Date Time Java 8 (JSR 310)
Legacy Date Time Integration
JSR 310 vs JodaTime
Agenda
Previous implementation and problems in real world
Date Time Java 8 (JSR 310)
Legacy Date Time Integration
JSR 310 vs JodaTime
Previous implementation and
problems in real world
• Date it’s an instance of time (not a date)
• Calendar is date and time
• Date instance are mutable
• Months is 0 based
• Years is 1900 based (the year 2015 is
represented as 115)
Example 1
public static void main(String[] args) {
Date start = new Date(2012, Calendar.FEBRUARY, 1);
Calendar startEmployment = Calendar.getInstance();
// calendar.set(2011, 2, 1);
startEmployment.set(2011, Calendar.FEBRUARY, 1);
Calendar now = Calendar.getInstance();
long numberOfDays1 = countDays(startEmployment, now);
long numberOfDays2 = countDays(startEmployment, now);
System.out.println(String.format("First try=%d , second try=%d", numberOfDays1, numberOfDays2));
}
private static long countDays(Calendar start, Calendar end) {
long totalNumberOfDays = 0;
while(start.before(end)) {
start.add(Calendar.DAY_OF_MONTH, 1);
totalNumberOfDays++;
}
return totalNumberOfDays;
}
Months starts from 0
Calendar is mutable
Deprecated
Deprecated
Example 2
private static final int SECOND = 1000;
private static final int MINUTE = 60 * SECOND;
private static final int HOUR = 60 * MINUTE;
private static final int DAY = 24 * HOUR;
public static void main(String[] args) {
Calendar now = Calendar.getInstance();
Date nowDate = now.getTime();
long twoHoursByMillis = 2 * HOUR;
long thirtyMinutesByMillis = 30 * MINUTE;
Date twoHoursAndThirtyMinutesFromNow = new Date(twoHoursByMillis
+ thirtyMinutesByMillis);
System.out.println(String.format("now %s and later %s", nowDate,
twoHoursAndThirtyMinutesFromNow));
now Wed Oct 07 17:36:13 EEST 2015 and later Thu Jan 01 04:30:00 EET 1970
Example 3
private static final int SECOND = 1000;
private static final int MINUTE = 60 * SECOND;
private static final int HOUR = 60 * MINUTE;
private static final int DAY = 24 * HOUR;
public static void main(String[] args) {
long ms = 10304004543l;
StringBuilder text = new StringBuilder("");
if (ms > DAY) {
text.append(ms / DAY).append(" days ");
ms %= DAY;
}
if (ms > HOUR) {
text.append(ms / HOUR).append(" hours ");
ms %= HOUR;
}
if (ms > MINUTE) {
text.append(ms / MINUTE).append(" minutes ");
ms %= MINUTE;
}
if (ms > SECOND) {
text.append(ms / SECOND).append(" seconds ");
ms %= SECOND;
}
text.append(ms + " ms");
System.out.println(text.toString()); 119 days 6 hours 13 minutes 24 seconds 543 ms
Difference between java.util.Date and
java.sql.Date
• java.sql.Date stores years, months and days.
Additionally sql.Date isn't tied to timezones.
• java.sql.Time only contains information about
hour, minutes, seconds and milliseconds.
• java.sql.Timestamp corresponds to SQL
TIMESTAMP which is exact date to the
nanosecond (note that util.Date only supports
milliseconds!).
Agenda
Previous implementation and problems in real world
Date Time Java 8 (JSR 310)
Legacy Date Time Integration
JSR 310 vs JodaTime
Date Time Java 8 (JSR 310)
• Immutable and Thread Safe
• Domain-Driven Design
– Well-defined and clear purpose
– Extensible, by use of strategy pattern
– Amount of time – different representation for
different cases
• Human (year, month, day, time zones, calendar systems)
• Machine Time
• Separation of chronologies (calendars)
New packages
• java.time – instants, duration, dates, times,
timezone, periods
• java.time.format – formatting and parsing
• java.time.temporal – field, unit, adjustment
access to temporals
• java.time.zone – support for time zone
• java.time.chrono – calendar systems other
than ISO-8601
DayOfWeek and Month Enums
// DayOfWeek
DayOfWeek day = DayOfWeek.MONDAY; // the numeric value of {@code 1}.
System.out.println(day); // MONDAY
System.out.println(day.getValue()); // 1
System.out.println(Arrays.asList(DayOfWeek.values()));
// format
Locale locale = Locale.getDefault();
System.out.println(day.getDisplayName(TextStyle.FULL, locale)); // Monday
System.out.println(day.getDisplayName(TextStyle.NARROW, locale)); // M
System.out.println(day.getDisplayName(TextStyle.SHORT, locale)); // Mon
// Month
Month month = Month.JANUARY; // the numeric value of {@code 1}.
System.out.println(month);
System.out.println(Arrays.asList(Month.values()));
// useful constants
LocalTime lt1 = LocalTime.MIDNIGHT; // 00:00
LocalTime lt2 = LocalTime.NOON; // 12:00
LocalTime lt3 = LocalTime.MIN; // 00:00
LocalTime lt4 = LocalTime.MAX; // 23:59:59.999999999
LocalDate lt5 = LocalDate.MAX; // +999999999-12-31
LocalDate lt6 = LocalDate.MIN; // -999999999-01-01
LocalDateTime lt7 = LocalDateTime.MAX; // +999999999-12-31T23:59:59.999999999
LocalDateTime lt8 = LocalDateTime.MAX; // +999999999-12-31T23:59:59.999999999
ChronoField millisecondsOfDay = ChronoField.MILLI_OF_DAY;
System.out.println(millisecondsOfDay); // MilliOfDay
ChronoField secondsOfMinute = ChronoField.SECOND_OF_MINUTE;
ChronoField daysOfYear = ChronoField.DAY_OF_YEAR;
Temporal Based classes
Time
Duration
(Temporal Amount)
Period
(Temporal Amount)
Instant
(Temporal)
Instant
(Temporal)
LocalDate, etc.
(Temporal)
LocalDate, etc.
(Temporal)
Machine time
Human readable
Date and Time
• LocalDate
– Contains just a date—no time and no time zone
– Corresponds to SQL DATE type
• LocalTime
– Contains just a time—no date and no time zone
– Corresponds to SQL TIME type
• LocalDateTime
– Contains both a date and time but no time zone
– Corresponds to SQL TIMESTAMP type
Examples
// LocalDate Contains just a date—no time and no time zone.
LocalDate date1 = LocalDate.of(2015, Month.JANUARY, 20);
LocalDate date2 = LocalDate.of(2015, 1, 20);
System.out.println(date1); // 2015-01-20
System.out.println(date2); // 2015-01-20
// LocalDate d = new LocalDate(); // does not compile - private constructor
// LocalDate.of(2015, Month.JANUARY, 32); // throws DateTimeException
// LocalTime Contains just a time—no date and no time zone.
LocalTime time1 = LocalTime.of(6, 15); // hour and minute
LocalTime time2 = LocalTime.of(6, 15, 30); // + seconds
LocalTime time3 = LocalTime.of(6, 15, 30, 200); // + nanoseconds
System.out.println(time1); // 06:15
System.out.println(time2); // 06:15:30
System.out.println(time3); // 06:15:30.000000200
// LocalDateTime Contains both a date and time but no time zone.
LocalDateTime dateTime1 = LocalDateTime.of(2015, Month.JANUARY, 20, 6, 15, 30);
LocalDateTime dateTime2 = LocalDateTime.of(date1, time1);
System.out.println(dateTime1); // 2015-01-20T06:15:30
System.out.println(dateTime2); // 2015-01-20T06:15
Examples
// YearMonth
YearMonth yearMonth1 = YearMonth.now();
System.out.printf("%s: %d%n", yearMonth1, yearMonth1.lengthOfMonth()); // 2015-10: 31
YearMonth yearMonth2 = YearMonth.of(2010, Month.FEBRUARY);
System.out.printf("%s: %d%n", yearMonth2, yearMonth2.lengthOfMonth()); // 2010-02: 28
YearMonth yearMonth3 = YearMonth.of(2012, Month.FEBRUARY);
System.out.printf("%s: %d%n", yearMonth3, yearMonth3.lengthOfMonth()); // 2012-02: 29
// MonthDay
MonthDay date = MonthDay.of(Month.FEBRUARY, 29);
boolean validLeapYear = date.isValidYear(2010);
System.out.println(validLeapYear); // false
// Year
boolean validLeapYear1 = Year.of(2012).isLeap();
System.out.println(validLeapYear1); // true
Instant
// Instant is useful for generating a time stamp to represent machine time.
Instant timestamp = Instant.now();
// Gets the number of seconds from the Java epoch of 1970-01-01T00:00:00Z.
System.out.println("timestamp: " + timestamp.getEpochSecond()); // timestamp: 1444654450
// How many seconds have occurred since the beginning of the Java epoch.
long secondsFromEpoch = Instant.ofEpochSecond(0L).until(Instant.now(), ChronoUnit.SECONDS);
System.out.println("Amount of seconds from java epoch: " + secondsFromEpoch); // 1444654450
long minutesFromEpoch = Instant.ofEpochSecond(0L).until(Instant.now(), ChronoUnit.MINUTES);
System.out.println("Amount of minutes from java epoch: " + minutesFromEpoch); // 24077574
// Convert "machine time" to human units
LocalDateTime humanTime = LocalDateTime.ofInstant(timestamp, ZoneId.systemDefault());
// different methods to operate time!
// toString the same
System.out.println("timestamp: " + timestamp); // 2015-10-12T12:54:10.787Z
System.out.println("humanTime: " + humanTime); // 2015-10-12T13:54:10.787Z
Parsing and Formatting
Instant instant = Instant.parse("2014-07-16T10:15:30.00Z");
LocalDate localDate = LocalDate.parse("2014-07-16", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
LocalDate localDate2 = LocalDate.parse("2014-07-16", DateTimeFormatter.ISO_LOCAL_DATE);
DateTimeFormatter strangeFormat = new DateTimeFormatterBuilder()
.appendValue(MONTH_OF_YEAR, 2)
.appendLiteral("**")
.appendValue(YEAR, 4)
.appendLiteral("--")
.appendValue(DAY_OF_MONTH, 2)
.toFormatter();
LocalDate localDate3 = LocalDate.parse("07**2014--16", strangeFormat);
System.out.println(instant);
System.out.println(localDate);
System.out.println(localDate2);
System.out.println(localDate3);
LocalDate date = Year.of(2014).atMonth(7).atDay(16);
String strangeDateFormat = date.format(strangeFormat);
System.out.println(strangeDateFormat);
Time Zone and Offset Classes
A time zone is a region of the earth where the same
standard time is used.
• ZoneId specifies a time zone identifier and
provides rules for converting between an Instant
and a LocalDateTime (format region/city
(Asia/Tokyo))
• ZoneOffset specifies a time zone offset from
Greenwich/UTC time (offset for Tokyo is +09:00).
Example
private static void printAllTimeZones(){
Set<String> allZones = ZoneId.getAvailableZoneIds();
LocalDateTime dt = LocalDateTime.now();
// Create a List using the set of zones and sort it.
List<String> zoneList = new ArrayList<String>(allZones);
//Collections.sort(zoneList);
for (String s : zoneList) {
ZoneId zone = ZoneId.of(s);
ZonedDateTime zdt = dt.atZone(zone);
ZoneOffset offset = zdt.getOffset();
int secondsOfHour = offset.getTotalSeconds() % (60 * 60); // just seconds!!!
String out = String.format("%35s %10s%n", zone, offset);
System.out.printf(out);
}
}
ZoneId Offset
Asia/Jerusalem +03:00
Europe/Andorra +02:00
US/Samoa -11:00
Asia/Vientiane +07:00
Date-Time Classes
• ZonedDateTime handles a date and time with a
corresponding time zone with a time zone offset
from Greenwich/UTC.
• OffsetDateTime handles a date and time with a
corresponding time zone offset from
Greenwich/UTC, without a time zone ID.
• OffsetTime handles time with a corresponding
time zone offset from Greenwich/UTC, without a
time zone ID.
ZonedDateTime
private static void zonedDateTime() {
DateTimeFormatter format = DateTimeFormatter.ofPattern("MMM d yyyy hh:mm a");
// Leaving from San Francisco on July 20, 2013, at 7:30 p.m.
LocalDateTime leaving = LocalDateTime.of(2015, Month.JULY, 20, 19, 30);
ZoneId leavingZone = ZoneId.of("America/Los_Angeles");
ZonedDateTime departure = ZonedDateTime.of(leaving, leavingZone);
String out1 = departure.format(format);
System.out.printf("LEAVING: %s (%s)%n", out1, leavingZone);
// Flight is 10 hours and 50 minutes, or 650 minutes
ZoneId arrivingZone = ZoneId.of("Asia/Tokyo");
ZonedDateTime arrival = departure.withZoneSameInstant(arrivingZone)
.plusHours(10).plusMinutes(50);
String out2 = arrival.format(format);
System.out.printf("ARRIVING: %s (%s)%n", out2, arrivingZone);
// літній час
if (arrivingZone.getRules().isDaylightSavings(arrival.toInstant())) {
System.out.printf(" (%s daylight saving time will be in effect.)%n", arrivingZone);
} else {
System.out.printf(" (%s standard time will be in effect.)%n", arrivingZone);
}
}
LEAVING: лип. 20 2015 07:30 PM (America/Los_Angeles)
ARRIVING: лип. 21 2015 10:20 PM (Asia/Tokyo)
(Asia/Tokyo standard time will be in effect.)
OffsetDateTime
// Find the last Thursday in October 2015.
LocalDateTime localDate = LocalDateTime.of(2015, Month.OCTOBER, 20, 19, 30);
ZoneOffset offset = ZoneOffset.of("-08:00");
OffsetDateTime offsetDate = OffsetDateTime.of(localDate, offset);
OffsetDateTime lastThursday =
offsetDate.with(TemporalAdjusters.lastInMonth(DayOfWeek.THURSDAY));
System.out.printf("The last Thursday in October 2015 is the %sth.%n",
lastThursday.getDayOfMonth());
OffsetDateTime odt = OffsetDateTime.of(LocalDateTime.now(),ZoneOffset.of("-4"));
System.out.println(odt); // 2015-10-13T11:14:03.073-04:00
This class may be used when modeling date-time
concepts in more detail, or when communicating
to a database or in a network protocol.
OffsetTime
OffsetTime ot = OffsetTime.ofInstant(Instant.now(),ZoneId.of("America/Los_Angeles"));
System.out.println(ot); // 01:14:03.073-07:00
The OffsetTime class is used in the same situations
as the OffsetDateTime class, but when tracking
the date is not needed.
Period
@Test
public void test_period_between_dates(){
LocalDate twins = LocalDate.parse("2003-11-18");
LocalDate mayhem = LocalDate.parse("2009-06-01");
Period timeBetween = Period.between(twins,mayhem);
assertThat(timeBetween.getYears(),is(5));
assertThat(timeBetween.getMonths(),is(6));
assertThat(timeBetween.getDays(),is(14));
}
public static Period period(LocalDate hiringDate) {
LocalDate today = LocalDate.now();
return Period.between(hiringDate, today);
}
private static void period() {
System.out.println("period");
Period employmentPeriod = PeriodLauncher.period(LocalDate.of(2011, Month.FEBRUARY, 1));
System.out.println(employmentPeriod.getYears()); // 4
System.out.println(employmentPeriod.getMonths()); // 8
System.out.println(employmentPeriod.getDays()); // 12
}
Define an amount of time with date-based
values (years, months, days)
JUnit
Duration
public static void main(String[] args) {
Instant t1 = Instant.now();
Instant t2 = Instant.now().plusSeconds(12);
long nanos = Duration.between(t1, t2).toNanos();
System.out.println(nanos); // 12000000000
long milis = Duration.between(t2, t1).toMillis();
System.out.println(milis); // -12000
Duration gap = Duration.ofSeconds(13);
Instant later = t1.plus(gap);
System.out.println(t1); // 2015-10-13T08:58:41.312Z
System.out.println(later); // 2015-10-13T08:58:54.312Z
}
The Duration class represents arbitrary amounts
of time in hours, minutes or seconds.
Chrono Units
public static void main(String[] args) {
// units
Instant.now().plus(1, ChronoUnit.DAYS); // Unit that represents the concept of a day.
System.out.println(ChronoUnit.DAYS); // Days
System.out.println(Arrays.asList(ChronoUnit.values()));
// [Nanos, Micros, Millis, Seconds, Minutes, Hours, HalfDays,
// Days, Weeks, Months, Years, Decades, Centuries, Millennia, Eras, Forever]
Instant previous = Instant.ofEpochSecond(54545454);
Instant current = Instant.now();
long gap = ChronoUnit.MILLIS.between(previous, current);
Duration duration = ChronoUnit.MILLIS.getDuration();
System.out.println(gap); // 1390181631925
System.out.println(duration); // PT0.001S
}
The ChronoUnit enum defines the units used to
measure time.
Class or Enum Year Month Day Hours Min Sec* Zone Offset Zone ID toString Output
Instant + 2015-08-20T15:16:26.355Z
LocalDate + + + 2015-08-20
LocalDateTime + + + + + + 2015-08-20T08:16:26.937
ZonedDateTime + + + + + + + + 2015-08-21T00:16:26.941+09:00[Asia/Tokyo]
LocalTime + + + 08:16:26.943
MonthDay + + --08-20
Year + 2015
YearMonth + + 2015-08
Mouth + AUGUST
OffsetDateTime + + + + + + + 2015-08-20T08:16:26.954-07:00
OffsetTime + + + + 08:16:26.957-07:00
Duration + PT20H (20 hours)
Period + + + P10D (10 days)
*Seconds are captured to nanosecond precision
Temporal Adjuster
private static void adjusters() {
LocalDate date = LocalDate.of(2015, Month.OCTOBER, 14);
DayOfWeek dotw = date.getDayOfWeek();
System.out.printf("%s is on a %s%n", date, dotw);
System.out.printf("first day of Month: %s%n",
date.with(TemporalAdjusters.firstDayOfMonth()));
System.out.printf("first Monday of Month: %s%n",
date.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)));
System.out.printf("last day of Month: %s%n",
date.with(TemporalAdjusters.lastDayOfMonth()));
System.out.printf("first day of next Month: %s%n",
date.with(TemporalAdjusters.firstDayOfNextMonth()));
System.out.printf("first day of next Year: %s%n",
date.with(TemporalAdjusters.firstDayOfNextYear()));
System.out.printf("first day of Year: %s%n",
date.with(TemporalAdjusters.firstDayOfYear()));
}
2015-10-14 is on a WEDNESDAY
first day of Month: 2015-10-01
first Monday of Month: 2015-10-05
last day of Month: 2015-10-31
first day of next Month: 2015-11-01
first day of next Year: 2016-01-01
first day of Year: 2015-01-01
Thanksgiving, President’s Day and
Programmer’s Day examples
private static void thanksgiving(int year) {
LocalDate thanksGiving = Year.of(year).atMonth(Month.NOVEMBER).atDay(1)
.with(TemporalAdjusters.lastInMonth(DayOfWeek.THURSDAY));
LocalDate thanksGiving4 = Year.of(year).atMonth(Month.NOVEMBER).atDay(1)
.with(TemporalAdjusters.dayOfWeekInMonth(4, DayOfWeek.THURSDAY));
// the last Thursday in November
System.out.println("ThanksGiving: " + thanksGiving);
// the 4th Thursday in November
System.out.println("ThanksGiving: " + thanksGiving4);
}
private static void programmersDay(int year) {
// on the 256th day
LocalDate programmersDay = Year.of(year).atMonth(1).atDay(1).
with(ChronoField.DAY_OF_YEAR, 256);
// LocalDate programmersDay = LocalDate.of(year, 1, 1).plusDays(256);
System.out.println("Programmer's Day: " + programmersDay);
}
ThanksGiving: 2015-11-26
ThanksGiving: 2015-11-26
ThanksGiving: 2016-11-24
ThanksGiving: 2016-11-24
ThanksGiving: 2017-11-30
ThanksGiving: 2017-11-23
Programmer's Day: 2015-09-13
Programmer's Day: 2016-09-12
Programmer's Day: 2017-09-13
private static void presidentDay(int year) {
// the first Monday Of February
LocalDate presidentsDay =
Year.of(year).atMonth(Month.FEBRUARY).atDay(1)
.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
System.out.println("President's day (USA): " + presidentsDay);
}
President's day (USA): 2015-02-02
President's day (USA): 2016-02-01
President's day (USA): 2017-02-06
Custom Adjuster
public class PaydayAdjuster implements TemporalAdjuster{
/**
* The adjustInto method accepts a Temporal instance
* and returns an adjusted LocalDate. If the passed in
* parameter is not a LocalDate, then a DateTimeException is thrown.
*/
public Temporal adjustInto(Temporal input) {
LocalDate date = LocalDate.from(input);
int day;
if (date.getDayOfMonth() < 15) {
day = 15;
} else {
day = date.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth();
}
date = date.withDayOfMonth(day);
if (date.getDayOfWeek() == DayOfWeek.SATURDAY ||
date.getDayOfWeek() == DayOfWeek.SUNDAY) {
date = date.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
}
return input.with(date);
}
}
Custom Adjuster
private static void customAdjuster() {
LocalDate october_13 = LocalDate.of(2015, Month.OCTOBER, 13);
LocalDate nextPayday1 = october_13.with(new PaydayAdjuster());
System.out.println("Given the date: " + october_13);
System.out.println("the next payday: " + nextPayday1);
LocalDate october_18 = LocalDate.of(2015, Month.OCTOBER, 18);
LocalDate nextPayday2 = october_18.with(new PaydayAdjuster());
System.out.println("Given the date: " + october_18);
System.out.println("the next payday: " + nextPayday2);
}
Given the date: 2015-10-13
the next payday: 2015-10-15
Given the date: 2015-10-18
the next payday: 2015-10-30
Temporal Query
// точність
TemporalQuery<TemporalUnit> query = TemporalQueries.precision();
System.out.printf("LocalDate precision is %s%n", LocalDate.now().query(query)); // Days
System.out.printf("LocalDateTime precision is %s%n", LocalDateTime.now().query(query)); // Nanos
System.out.printf("Year precision is %s%n", Year.now().query(query)); // Years
System.out.printf("YearMonth precision is %s%n", YearMonth.now().query(query)); // Months
System.out.printf("Instant precision is %s%n", Instant.now().query(query)); // Nanos
System.out.println();
LocalDateTime date = LocalDateTime.of(2014, Month.DECEMBER, 02, 0, 0);
ZonedDateTime zonedDate1 = ZonedDateTime.of(date,
ZoneId.of("Pacific/Chatham"));
ZonedDateTime zonedDate2 = ZonedDateTime.of(date,
ZoneId.of("Asia/Dhaka"));
zoneQueries(zonedDate1, zonedDate2);
offsetQueries(zonedDate1, zonedDate2);
Temporal Query
private static void offsetQueries(ZonedDateTime zonedDate1, ZonedDateTime zonedDate2) {
TemporalQuery<ZoneOffset> query = TemporalQueries.offset();
ZoneOffset offset1 = zonedDate1.query(query);
ZoneOffset offset2 = zonedDate2.query(query);
System.out.println(offset1); // +13:45
System.out.println(offset2); // +06:00
}
private static void zoneQueries(ZonedDateTime zonedDate1, ZonedDateTime zonedDate2) {
TemporalQuery<ZoneId> query = TemporalQueries.zone();
ZoneId zoneId1 = zonedDate1.query(query);
ZoneId zoneId2 = zonedDate2.query(query);
System.out.println(zoneId1); // "Pacific/Chatham"
System.out.println(zoneId2); // "Asia/Dhaka"
}
Pacific/Chatham
Asia/Dhaka
+13:45
+06:00
Custom Queries
private static void customQueries() {
YearMonth yearMonth = YearMonth.of(2014, 6);
System.out.println(yearMonth.query(new SchoolHolidayQuery())); // false
System.out.println(YearMonth.of(2014, Month.JULY).query(new SchoolHolidayQuery())); // true
System.out.println(YearMonth.of(2014, 8).query(new SchoolHolidayQuery())); // true
System.out.println();
YearQuarter yearQuarter1 = YearMonth.of(2014, 6).query(YearQuarterQuery::findQuarter);
System.out.println(yearQuarter1); // Q2
YearQuarter yearQuarter2 = YearMonth.of(2011, Month.DECEMBER).query(YearQuarterQuery::findQuarter);
System.out.println(yearQuarter2); // Q4
}
false
true
true
Q2
Q4
Custom Queries
public class SchoolHolidayQuery implements TemporalQuery<Boolean> {
@Override
public Boolean queryFrom(TemporalAccessor date) {
int month = date.get(ChronoField.MONTH_OF_YEAR);
if (month == Month.JULY.getValue() || month == Month.AUGUST.getValue()) {
return true;
}
return false;
}
}
public class YearQuarterQuery {
public static YearQuarter findQuarter(TemporalAccessor date) {
int month = date.get(ChronoField.MONTH_OF_YEAR);
if (month >= 1 && month <= 3) {
return YearQuarter.Q1;
} else if (month >= 4 && month <= 6) {
return YearQuarter.Q2;
} else if (month >= 7 && month <= 9) {
return YearQuarter.Q3;
} else {
return YearQuarter.Q4;
}
}
}
public enum YearQuarter {
Q1, Q2, Q3, Q4
}
Clock
The Clock class is abstract, so you cannot create
an instance of it. The following factory methods
can be useful for testing
– Clock.offset(Clock, Duration) returns a clock that
is offset by the specified Duration.
– Clock.systemUTC() returns a clock representing
the Greenwich/UTC time zone.
– Clock.fixed(Instant, ZoneId) always returns the
same Instant. For this clock, time stands still.
Method Naming Conventions
Method Access Description
of Static Create an instance with validation
to Instance Convert to another type (truncate fields)
at Instance Combines this object with another (expands)
from Static Convert input parameters to an instance
get Instance Part of the state of the object
is Instance Queries state of an object
with Instance Returns a copy of an object with one changed element
plus/minus Instance Returns a copy of an object with amount of added /
subtracted time
parse Static Parses input string to an instance
format Instance Uses formatter to format the object’s values to produce a
string
Epoch
• Reference point to measure time
• Maybe based on religious or political
milestones
• Divided the timeline into eras
• Start of a particular era
Cumputer System Epochs
• January 0, 0 – Matlab
• January 1, 1 – Symbian, .Net, new Windows
• January 1, 1601 – COBOL, old Windows
• January 1, 1900 – LISP, SNTP
• January 1, 1904 – Old Mac OS
• January 1, 1970 – Unix Epoch (Linux, Mac OS
X), Java, C, JavaScript, Perl, PHP, Python, Ruby
Calendar System
• Organizes days for social, religious,
commercial or administrative purposes
• Names periods like days, weeks, months and
years
• Periods may follow cycles of the sun or moon
• A date is a specific day in the system
• May be based on an epoch
UTC
• GMT is Greenwich Mean Time
– Mean solar time at the Royal Observatory in
Greenwich
• UTC is Coordinated Universal Time
– Precisely defined with atomic time. Does not
change with seasons
– Replaced GMT as reference time scale on 1
January 1972
ISO 8601
• International standard for representation of
dates and times
• Uses the Gregorian calendar system
• Ordered from most to least significant: year,
month, day, hour, minute
• Each date and time value has a fixed number
of digits with leading zones
• Uses four-digit year at minimum, YYYY
Chronology Interface
• Pluggable calendar system (ability to create
custom calendar)
• Provides access to date and time fields
• Built-in
– ISO8601 (default): IsoChronology
– Chinese: MinguoChronology
– Japanese: JapaneseChronology (since 1868-01-01)
– Thai Buddhist: ThaiBuddhistChronology
– Islamic: HijrahChronology
Era interface
• An era of the time-line.
• some calendar systems, have multiple eras, such as one for
the reign of each leader
– the Thai Buddhist calendar system divides time into two eras,
before and after a single date.
– the Japanese calendar system has one era for the reign of each
Emperor.
• Bilt-in (enums)
– IsoEra (BCE; CE)
– MingouEra (BEFORE_ROC; ROC)
– JapaneseEra (MEIJI; TAISHO; SHOWA; HEISEI)
– ThaiBuddhistEra (BEFORE_BE; BE)
– HijrahEra (AH)
JSR 310. New Date API in Java 8
Agenda
Previous implementation and problems in real world
Date Time Java 8 (JSR 310)
Legacy Date Time Integration
JSR 310 vs JodaTime
Legacy Date Time Integration
public class Launcher {
public static void main(String[] args) {
// Old to new
Date date = new Date();
LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
// New to old
LocalDateTime now = LocalDateTime.now();
Instant instant = now.atZone(ZoneId.systemDefault()).toInstant();
Date dateFromOld = Date.from(instant);
}
}
Mapping java.util Date and Time
Functionality to java.time
java.util Functionality java.time Functionality
java.util.Date java.time.Instant
java.util.GregorianCalendar java.time.ZonedDateTime
java.util.TimeZone java.time.ZoneId or
java.time.ZoneOffset
GregorianCalendar with the date set
to 1970-01-01
java.time.LocalTime
GregorianCalendar with time set to 00:00. java.time.LocalDate
Agenda
Previous implementation and problems in real world
Date Time Java 8 (JSR 310)
Legacy Date Time Integration
JSR 310 vs JodaTime
JSR 310 vs JodaTime
• Both libraries use immutable types. Joda-Time
also offers additional mutable types like
MutableDateTime.
• Null. Joda-Time often use NULL as default for
system timezone, default locale, current
timestamp etc. while JSR-310 almost always
rejects NULL values.
Supported fields
An overview about supported fields in Java-8 (JSR-
310) is given by some classes in the temporal-
package (for example ChronoField and WeekFields)
while Joda-Time is rather weak on this area – see
DateTimeFieldType.
The biggest lack of Joda-Time is here the absence of
localized week-related fields.
A common feature of both field implementation
design is that both are based on values of type long
(no other types, not even enums).
Enum
JSR-310 offers enums like DayOfWeek or Month
while Joda-Time does not offer this because it
was mainly developed in years 2002-2004
before Java 5.
Zone API
JSR-310 offers more timezone features than
Joda-Time.
Latter is not able to yield a programmatically
access to the history of timezone offset
transitions while JSR-310 is capable to do this.
Adjuster vs. Property
• JSR 310 has very limited value because the burden to
write code is still with the user. Built-in solutions based
on the new TemporalAdjuster-concept are not so
many, there is currently only the helper class
TemporalAdjusters with a limited set of manipulations
(and the enums Month or other temporal types).
• Joda-Time offers a field-package but practice has
shown evidence that new field implementations are
very hard to code. On the other side Joda-Time offers
so-called properties which make some manipulations
much easier and more elegant than in JSR-310
Calendar systems
Joda-Time provide 8 calendar systems:
• Buddhist
• Coptic (Egypt – Alexandrian Calendar)
• Ethiopic (Ethiopia)
• Gregorian-Julian cutover
• Gregorian
• Islamic
• ISO
• Julian
Calendars like Hebrew or Persian or Hindu are completely missing in both
libraries.
Joda-Time updates calendars with each release version (you may download it
from the web site manually) while java may update only with next path.
Clocks
JSR-310 has no interface (a design mistake) but an
abstract class java.time.Clock which can be used for
any clock dependency injection.
Joda-Time offers the interface MillisProvider and
some helper methods in DateTimeUtils instead.
So this way Joda-Time is also capable of supporting
test-driven models with different clocks (mocking
etc.).
Intervals
JSR-310 does not support this feature while
Joda-Time has limited support.
NOTE: Time4j has more rich package (range) for
this purpose!
Questions
and
Answers
Useful links
– Java Tutorial
– API
– Examples from presentation (GitHub)
– Comparison Table: JSR 310 vs JodaTime
– JSR 310 – Java 8 Date/Time library performance

More Related Content

JSR 310. New Date API in Java 8

  • 1. Java 8. DateTime Serhii Kartashov October 2015 Softjourn
  • 2. Agenda Previous implementation and problems in real world Date Time Java 8 (JSR 310) Legacy Date Time Integration JSR 310 vs JodaTime
  • 3. Agenda Previous implementation and problems in real world Date Time Java 8 (JSR 310) Legacy Date Time Integration JSR 310 vs JodaTime
  • 4. Previous implementation and problems in real world • Date it’s an instance of time (not a date) • Calendar is date and time • Date instance are mutable • Months is 0 based • Years is 1900 based (the year 2015 is represented as 115)
  • 5. Example 1 public static void main(String[] args) { Date start = new Date(2012, Calendar.FEBRUARY, 1); Calendar startEmployment = Calendar.getInstance(); // calendar.set(2011, 2, 1); startEmployment.set(2011, Calendar.FEBRUARY, 1); Calendar now = Calendar.getInstance(); long numberOfDays1 = countDays(startEmployment, now); long numberOfDays2 = countDays(startEmployment, now); System.out.println(String.format("First try=%d , second try=%d", numberOfDays1, numberOfDays2)); } private static long countDays(Calendar start, Calendar end) { long totalNumberOfDays = 0; while(start.before(end)) { start.add(Calendar.DAY_OF_MONTH, 1); totalNumberOfDays++; } return totalNumberOfDays; } Months starts from 0 Calendar is mutable Deprecated Deprecated
  • 6. Example 2 private static final int SECOND = 1000; private static final int MINUTE = 60 * SECOND; private static final int HOUR = 60 * MINUTE; private static final int DAY = 24 * HOUR; public static void main(String[] args) { Calendar now = Calendar.getInstance(); Date nowDate = now.getTime(); long twoHoursByMillis = 2 * HOUR; long thirtyMinutesByMillis = 30 * MINUTE; Date twoHoursAndThirtyMinutesFromNow = new Date(twoHoursByMillis + thirtyMinutesByMillis); System.out.println(String.format("now %s and later %s", nowDate, twoHoursAndThirtyMinutesFromNow)); now Wed Oct 07 17:36:13 EEST 2015 and later Thu Jan 01 04:30:00 EET 1970
  • 7. Example 3 private static final int SECOND = 1000; private static final int MINUTE = 60 * SECOND; private static final int HOUR = 60 * MINUTE; private static final int DAY = 24 * HOUR; public static void main(String[] args) { long ms = 10304004543l; StringBuilder text = new StringBuilder(""); if (ms > DAY) { text.append(ms / DAY).append(" days "); ms %= DAY; } if (ms > HOUR) { text.append(ms / HOUR).append(" hours "); ms %= HOUR; } if (ms > MINUTE) { text.append(ms / MINUTE).append(" minutes "); ms %= MINUTE; } if (ms > SECOND) { text.append(ms / SECOND).append(" seconds "); ms %= SECOND; } text.append(ms + " ms"); System.out.println(text.toString()); 119 days 6 hours 13 minutes 24 seconds 543 ms
  • 8. Difference between java.util.Date and java.sql.Date • java.sql.Date stores years, months and days. Additionally sql.Date isn't tied to timezones. • java.sql.Time only contains information about hour, minutes, seconds and milliseconds. • java.sql.Timestamp corresponds to SQL TIMESTAMP which is exact date to the nanosecond (note that util.Date only supports milliseconds!).
  • 9. Agenda Previous implementation and problems in real world Date Time Java 8 (JSR 310) Legacy Date Time Integration JSR 310 vs JodaTime
  • 10. Date Time Java 8 (JSR 310) • Immutable and Thread Safe • Domain-Driven Design – Well-defined and clear purpose – Extensible, by use of strategy pattern – Amount of time – different representation for different cases • Human (year, month, day, time zones, calendar systems) • Machine Time • Separation of chronologies (calendars)
  • 11. New packages • java.time – instants, duration, dates, times, timezone, periods • java.time.format – formatting and parsing • java.time.temporal – field, unit, adjustment access to temporals • java.time.zone – support for time zone • java.time.chrono – calendar systems other than ISO-8601
  • 12. DayOfWeek and Month Enums // DayOfWeek DayOfWeek day = DayOfWeek.MONDAY; // the numeric value of {@code 1}. System.out.println(day); // MONDAY System.out.println(day.getValue()); // 1 System.out.println(Arrays.asList(DayOfWeek.values())); // format Locale locale = Locale.getDefault(); System.out.println(day.getDisplayName(TextStyle.FULL, locale)); // Monday System.out.println(day.getDisplayName(TextStyle.NARROW, locale)); // M System.out.println(day.getDisplayName(TextStyle.SHORT, locale)); // Mon // Month Month month = Month.JANUARY; // the numeric value of {@code 1}. System.out.println(month); System.out.println(Arrays.asList(Month.values())); // useful constants LocalTime lt1 = LocalTime.MIDNIGHT; // 00:00 LocalTime lt2 = LocalTime.NOON; // 12:00 LocalTime lt3 = LocalTime.MIN; // 00:00 LocalTime lt4 = LocalTime.MAX; // 23:59:59.999999999 LocalDate lt5 = LocalDate.MAX; // +999999999-12-31 LocalDate lt6 = LocalDate.MIN; // -999999999-01-01 LocalDateTime lt7 = LocalDateTime.MAX; // +999999999-12-31T23:59:59.999999999 LocalDateTime lt8 = LocalDateTime.MAX; // +999999999-12-31T23:59:59.999999999 ChronoField millisecondsOfDay = ChronoField.MILLI_OF_DAY; System.out.println(millisecondsOfDay); // MilliOfDay ChronoField secondsOfMinute = ChronoField.SECOND_OF_MINUTE; ChronoField daysOfYear = ChronoField.DAY_OF_YEAR;
  • 13. Temporal Based classes Time Duration (Temporal Amount) Period (Temporal Amount) Instant (Temporal) Instant (Temporal) LocalDate, etc. (Temporal) LocalDate, etc. (Temporal) Machine time Human readable
  • 14. Date and Time • LocalDate – Contains just a date—no time and no time zone – Corresponds to SQL DATE type • LocalTime – Contains just a time—no date and no time zone – Corresponds to SQL TIME type • LocalDateTime – Contains both a date and time but no time zone – Corresponds to SQL TIMESTAMP type
  • 15. Examples // LocalDate Contains just a date—no time and no time zone. LocalDate date1 = LocalDate.of(2015, Month.JANUARY, 20); LocalDate date2 = LocalDate.of(2015, 1, 20); System.out.println(date1); // 2015-01-20 System.out.println(date2); // 2015-01-20 // LocalDate d = new LocalDate(); // does not compile - private constructor // LocalDate.of(2015, Month.JANUARY, 32); // throws DateTimeException // LocalTime Contains just a time—no date and no time zone. LocalTime time1 = LocalTime.of(6, 15); // hour and minute LocalTime time2 = LocalTime.of(6, 15, 30); // + seconds LocalTime time3 = LocalTime.of(6, 15, 30, 200); // + nanoseconds System.out.println(time1); // 06:15 System.out.println(time2); // 06:15:30 System.out.println(time3); // 06:15:30.000000200 // LocalDateTime Contains both a date and time but no time zone. LocalDateTime dateTime1 = LocalDateTime.of(2015, Month.JANUARY, 20, 6, 15, 30); LocalDateTime dateTime2 = LocalDateTime.of(date1, time1); System.out.println(dateTime1); // 2015-01-20T06:15:30 System.out.println(dateTime2); // 2015-01-20T06:15
  • 16. Examples // YearMonth YearMonth yearMonth1 = YearMonth.now(); System.out.printf("%s: %d%n", yearMonth1, yearMonth1.lengthOfMonth()); // 2015-10: 31 YearMonth yearMonth2 = YearMonth.of(2010, Month.FEBRUARY); System.out.printf("%s: %d%n", yearMonth2, yearMonth2.lengthOfMonth()); // 2010-02: 28 YearMonth yearMonth3 = YearMonth.of(2012, Month.FEBRUARY); System.out.printf("%s: %d%n", yearMonth3, yearMonth3.lengthOfMonth()); // 2012-02: 29 // MonthDay MonthDay date = MonthDay.of(Month.FEBRUARY, 29); boolean validLeapYear = date.isValidYear(2010); System.out.println(validLeapYear); // false // Year boolean validLeapYear1 = Year.of(2012).isLeap(); System.out.println(validLeapYear1); // true
  • 17. Instant // Instant is useful for generating a time stamp to represent machine time. Instant timestamp = Instant.now(); // Gets the number of seconds from the Java epoch of 1970-01-01T00:00:00Z. System.out.println("timestamp: " + timestamp.getEpochSecond()); // timestamp: 1444654450 // How many seconds have occurred since the beginning of the Java epoch. long secondsFromEpoch = Instant.ofEpochSecond(0L).until(Instant.now(), ChronoUnit.SECONDS); System.out.println("Amount of seconds from java epoch: " + secondsFromEpoch); // 1444654450 long minutesFromEpoch = Instant.ofEpochSecond(0L).until(Instant.now(), ChronoUnit.MINUTES); System.out.println("Amount of minutes from java epoch: " + minutesFromEpoch); // 24077574 // Convert "machine time" to human units LocalDateTime humanTime = LocalDateTime.ofInstant(timestamp, ZoneId.systemDefault()); // different methods to operate time! // toString the same System.out.println("timestamp: " + timestamp); // 2015-10-12T12:54:10.787Z System.out.println("humanTime: " + humanTime); // 2015-10-12T13:54:10.787Z
  • 18. Parsing and Formatting Instant instant = Instant.parse("2014-07-16T10:15:30.00Z"); LocalDate localDate = LocalDate.parse("2014-07-16", DateTimeFormatter.ofPattern("yyyy-MM-dd")); LocalDate localDate2 = LocalDate.parse("2014-07-16", DateTimeFormatter.ISO_LOCAL_DATE); DateTimeFormatter strangeFormat = new DateTimeFormatterBuilder() .appendValue(MONTH_OF_YEAR, 2) .appendLiteral("**") .appendValue(YEAR, 4) .appendLiteral("--") .appendValue(DAY_OF_MONTH, 2) .toFormatter(); LocalDate localDate3 = LocalDate.parse("07**2014--16", strangeFormat); System.out.println(instant); System.out.println(localDate); System.out.println(localDate2); System.out.println(localDate3); LocalDate date = Year.of(2014).atMonth(7).atDay(16); String strangeDateFormat = date.format(strangeFormat); System.out.println(strangeDateFormat);
  • 19. Time Zone and Offset Classes A time zone is a region of the earth where the same standard time is used. • ZoneId specifies a time zone identifier and provides rules for converting between an Instant and a LocalDateTime (format region/city (Asia/Tokyo)) • ZoneOffset specifies a time zone offset from Greenwich/UTC time (offset for Tokyo is +09:00).
  • 20. Example private static void printAllTimeZones(){ Set<String> allZones = ZoneId.getAvailableZoneIds(); LocalDateTime dt = LocalDateTime.now(); // Create a List using the set of zones and sort it. List<String> zoneList = new ArrayList<String>(allZones); //Collections.sort(zoneList); for (String s : zoneList) { ZoneId zone = ZoneId.of(s); ZonedDateTime zdt = dt.atZone(zone); ZoneOffset offset = zdt.getOffset(); int secondsOfHour = offset.getTotalSeconds() % (60 * 60); // just seconds!!! String out = String.format("%35s %10s%n", zone, offset); System.out.printf(out); } } ZoneId Offset Asia/Jerusalem +03:00 Europe/Andorra +02:00 US/Samoa -11:00 Asia/Vientiane +07:00
  • 21. Date-Time Classes • ZonedDateTime handles a date and time with a corresponding time zone with a time zone offset from Greenwich/UTC. • OffsetDateTime handles a date and time with a corresponding time zone offset from Greenwich/UTC, without a time zone ID. • OffsetTime handles time with a corresponding time zone offset from Greenwich/UTC, without a time zone ID.
  • 22. ZonedDateTime private static void zonedDateTime() { DateTimeFormatter format = DateTimeFormatter.ofPattern("MMM d yyyy hh:mm a"); // Leaving from San Francisco on July 20, 2013, at 7:30 p.m. LocalDateTime leaving = LocalDateTime.of(2015, Month.JULY, 20, 19, 30); ZoneId leavingZone = ZoneId.of("America/Los_Angeles"); ZonedDateTime departure = ZonedDateTime.of(leaving, leavingZone); String out1 = departure.format(format); System.out.printf("LEAVING: %s (%s)%n", out1, leavingZone); // Flight is 10 hours and 50 minutes, or 650 minutes ZoneId arrivingZone = ZoneId.of("Asia/Tokyo"); ZonedDateTime arrival = departure.withZoneSameInstant(arrivingZone) .plusHours(10).plusMinutes(50); String out2 = arrival.format(format); System.out.printf("ARRIVING: %s (%s)%n", out2, arrivingZone); // літній час if (arrivingZone.getRules().isDaylightSavings(arrival.toInstant())) { System.out.printf(" (%s daylight saving time will be in effect.)%n", arrivingZone); } else { System.out.printf(" (%s standard time will be in effect.)%n", arrivingZone); } } LEAVING: лип. 20 2015 07:30 PM (America/Los_Angeles) ARRIVING: лип. 21 2015 10:20 PM (Asia/Tokyo) (Asia/Tokyo standard time will be in effect.)
  • 23. OffsetDateTime // Find the last Thursday in October 2015. LocalDateTime localDate = LocalDateTime.of(2015, Month.OCTOBER, 20, 19, 30); ZoneOffset offset = ZoneOffset.of("-08:00"); OffsetDateTime offsetDate = OffsetDateTime.of(localDate, offset); OffsetDateTime lastThursday = offsetDate.with(TemporalAdjusters.lastInMonth(DayOfWeek.THURSDAY)); System.out.printf("The last Thursday in October 2015 is the %sth.%n", lastThursday.getDayOfMonth()); OffsetDateTime odt = OffsetDateTime.of(LocalDateTime.now(),ZoneOffset.of("-4")); System.out.println(odt); // 2015-10-13T11:14:03.073-04:00 This class may be used when modeling date-time concepts in more detail, or when communicating to a database or in a network protocol.
  • 24. OffsetTime OffsetTime ot = OffsetTime.ofInstant(Instant.now(),ZoneId.of("America/Los_Angeles")); System.out.println(ot); // 01:14:03.073-07:00 The OffsetTime class is used in the same situations as the OffsetDateTime class, but when tracking the date is not needed.
  • 25. Period @Test public void test_period_between_dates(){ LocalDate twins = LocalDate.parse("2003-11-18"); LocalDate mayhem = LocalDate.parse("2009-06-01"); Period timeBetween = Period.between(twins,mayhem); assertThat(timeBetween.getYears(),is(5)); assertThat(timeBetween.getMonths(),is(6)); assertThat(timeBetween.getDays(),is(14)); } public static Period period(LocalDate hiringDate) { LocalDate today = LocalDate.now(); return Period.between(hiringDate, today); } private static void period() { System.out.println("period"); Period employmentPeriod = PeriodLauncher.period(LocalDate.of(2011, Month.FEBRUARY, 1)); System.out.println(employmentPeriod.getYears()); // 4 System.out.println(employmentPeriod.getMonths()); // 8 System.out.println(employmentPeriod.getDays()); // 12 } Define an amount of time with date-based values (years, months, days) JUnit
  • 26. Duration public static void main(String[] args) { Instant t1 = Instant.now(); Instant t2 = Instant.now().plusSeconds(12); long nanos = Duration.between(t1, t2).toNanos(); System.out.println(nanos); // 12000000000 long milis = Duration.between(t2, t1).toMillis(); System.out.println(milis); // -12000 Duration gap = Duration.ofSeconds(13); Instant later = t1.plus(gap); System.out.println(t1); // 2015-10-13T08:58:41.312Z System.out.println(later); // 2015-10-13T08:58:54.312Z } The Duration class represents arbitrary amounts of time in hours, minutes or seconds.
  • 27. Chrono Units public static void main(String[] args) { // units Instant.now().plus(1, ChronoUnit.DAYS); // Unit that represents the concept of a day. System.out.println(ChronoUnit.DAYS); // Days System.out.println(Arrays.asList(ChronoUnit.values())); // [Nanos, Micros, Millis, Seconds, Minutes, Hours, HalfDays, // Days, Weeks, Months, Years, Decades, Centuries, Millennia, Eras, Forever] Instant previous = Instant.ofEpochSecond(54545454); Instant current = Instant.now(); long gap = ChronoUnit.MILLIS.between(previous, current); Duration duration = ChronoUnit.MILLIS.getDuration(); System.out.println(gap); // 1390181631925 System.out.println(duration); // PT0.001S } The ChronoUnit enum defines the units used to measure time.
  • 28. Class or Enum Year Month Day Hours Min Sec* Zone Offset Zone ID toString Output Instant + 2015-08-20T15:16:26.355Z LocalDate + + + 2015-08-20 LocalDateTime + + + + + + 2015-08-20T08:16:26.937 ZonedDateTime + + + + + + + + 2015-08-21T00:16:26.941+09:00[Asia/Tokyo] LocalTime + + + 08:16:26.943 MonthDay + + --08-20 Year + 2015 YearMonth + + 2015-08 Mouth + AUGUST OffsetDateTime + + + + + + + 2015-08-20T08:16:26.954-07:00 OffsetTime + + + + 08:16:26.957-07:00 Duration + PT20H (20 hours) Period + + + P10D (10 days) *Seconds are captured to nanosecond precision
  • 29. Temporal Adjuster private static void adjusters() { LocalDate date = LocalDate.of(2015, Month.OCTOBER, 14); DayOfWeek dotw = date.getDayOfWeek(); System.out.printf("%s is on a %s%n", date, dotw); System.out.printf("first day of Month: %s%n", date.with(TemporalAdjusters.firstDayOfMonth())); System.out.printf("first Monday of Month: %s%n", date.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY))); System.out.printf("last day of Month: %s%n", date.with(TemporalAdjusters.lastDayOfMonth())); System.out.printf("first day of next Month: %s%n", date.with(TemporalAdjusters.firstDayOfNextMonth())); System.out.printf("first day of next Year: %s%n", date.with(TemporalAdjusters.firstDayOfNextYear())); System.out.printf("first day of Year: %s%n", date.with(TemporalAdjusters.firstDayOfYear())); } 2015-10-14 is on a WEDNESDAY first day of Month: 2015-10-01 first Monday of Month: 2015-10-05 last day of Month: 2015-10-31 first day of next Month: 2015-11-01 first day of next Year: 2016-01-01 first day of Year: 2015-01-01
  • 30. Thanksgiving, President’s Day and Programmer’s Day examples private static void thanksgiving(int year) { LocalDate thanksGiving = Year.of(year).atMonth(Month.NOVEMBER).atDay(1) .with(TemporalAdjusters.lastInMonth(DayOfWeek.THURSDAY)); LocalDate thanksGiving4 = Year.of(year).atMonth(Month.NOVEMBER).atDay(1) .with(TemporalAdjusters.dayOfWeekInMonth(4, DayOfWeek.THURSDAY)); // the last Thursday in November System.out.println("ThanksGiving: " + thanksGiving); // the 4th Thursday in November System.out.println("ThanksGiving: " + thanksGiving4); } private static void programmersDay(int year) { // on the 256th day LocalDate programmersDay = Year.of(year).atMonth(1).atDay(1). with(ChronoField.DAY_OF_YEAR, 256); // LocalDate programmersDay = LocalDate.of(year, 1, 1).plusDays(256); System.out.println("Programmer's Day: " + programmersDay); } ThanksGiving: 2015-11-26 ThanksGiving: 2015-11-26 ThanksGiving: 2016-11-24 ThanksGiving: 2016-11-24 ThanksGiving: 2017-11-30 ThanksGiving: 2017-11-23 Programmer's Day: 2015-09-13 Programmer's Day: 2016-09-12 Programmer's Day: 2017-09-13 private static void presidentDay(int year) { // the first Monday Of February LocalDate presidentsDay = Year.of(year).atMonth(Month.FEBRUARY).atDay(1) .with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)); System.out.println("President's day (USA): " + presidentsDay); } President's day (USA): 2015-02-02 President's day (USA): 2016-02-01 President's day (USA): 2017-02-06
  • 31. Custom Adjuster public class PaydayAdjuster implements TemporalAdjuster{ /** * The adjustInto method accepts a Temporal instance * and returns an adjusted LocalDate. If the passed in * parameter is not a LocalDate, then a DateTimeException is thrown. */ public Temporal adjustInto(Temporal input) { LocalDate date = LocalDate.from(input); int day; if (date.getDayOfMonth() < 15) { day = 15; } else { day = date.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth(); } date = date.withDayOfMonth(day); if (date.getDayOfWeek() == DayOfWeek.SATURDAY || date.getDayOfWeek() == DayOfWeek.SUNDAY) { date = date.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY)); } return input.with(date); } }
  • 32. Custom Adjuster private static void customAdjuster() { LocalDate october_13 = LocalDate.of(2015, Month.OCTOBER, 13); LocalDate nextPayday1 = october_13.with(new PaydayAdjuster()); System.out.println("Given the date: " + october_13); System.out.println("the next payday: " + nextPayday1); LocalDate october_18 = LocalDate.of(2015, Month.OCTOBER, 18); LocalDate nextPayday2 = october_18.with(new PaydayAdjuster()); System.out.println("Given the date: " + october_18); System.out.println("the next payday: " + nextPayday2); } Given the date: 2015-10-13 the next payday: 2015-10-15 Given the date: 2015-10-18 the next payday: 2015-10-30
  • 33. Temporal Query // точність TemporalQuery<TemporalUnit> query = TemporalQueries.precision(); System.out.printf("LocalDate precision is %s%n", LocalDate.now().query(query)); // Days System.out.printf("LocalDateTime precision is %s%n", LocalDateTime.now().query(query)); // Nanos System.out.printf("Year precision is %s%n", Year.now().query(query)); // Years System.out.printf("YearMonth precision is %s%n", YearMonth.now().query(query)); // Months System.out.printf("Instant precision is %s%n", Instant.now().query(query)); // Nanos System.out.println(); LocalDateTime date = LocalDateTime.of(2014, Month.DECEMBER, 02, 0, 0); ZonedDateTime zonedDate1 = ZonedDateTime.of(date, ZoneId.of("Pacific/Chatham")); ZonedDateTime zonedDate2 = ZonedDateTime.of(date, ZoneId.of("Asia/Dhaka")); zoneQueries(zonedDate1, zonedDate2); offsetQueries(zonedDate1, zonedDate2);
  • 34. Temporal Query private static void offsetQueries(ZonedDateTime zonedDate1, ZonedDateTime zonedDate2) { TemporalQuery<ZoneOffset> query = TemporalQueries.offset(); ZoneOffset offset1 = zonedDate1.query(query); ZoneOffset offset2 = zonedDate2.query(query); System.out.println(offset1); // +13:45 System.out.println(offset2); // +06:00 } private static void zoneQueries(ZonedDateTime zonedDate1, ZonedDateTime zonedDate2) { TemporalQuery<ZoneId> query = TemporalQueries.zone(); ZoneId zoneId1 = zonedDate1.query(query); ZoneId zoneId2 = zonedDate2.query(query); System.out.println(zoneId1); // "Pacific/Chatham" System.out.println(zoneId2); // "Asia/Dhaka" } Pacific/Chatham Asia/Dhaka +13:45 +06:00
  • 35. Custom Queries private static void customQueries() { YearMonth yearMonth = YearMonth.of(2014, 6); System.out.println(yearMonth.query(new SchoolHolidayQuery())); // false System.out.println(YearMonth.of(2014, Month.JULY).query(new SchoolHolidayQuery())); // true System.out.println(YearMonth.of(2014, 8).query(new SchoolHolidayQuery())); // true System.out.println(); YearQuarter yearQuarter1 = YearMonth.of(2014, 6).query(YearQuarterQuery::findQuarter); System.out.println(yearQuarter1); // Q2 YearQuarter yearQuarter2 = YearMonth.of(2011, Month.DECEMBER).query(YearQuarterQuery::findQuarter); System.out.println(yearQuarter2); // Q4 } false true true Q2 Q4
  • 36. Custom Queries public class SchoolHolidayQuery implements TemporalQuery<Boolean> { @Override public Boolean queryFrom(TemporalAccessor date) { int month = date.get(ChronoField.MONTH_OF_YEAR); if (month == Month.JULY.getValue() || month == Month.AUGUST.getValue()) { return true; } return false; } } public class YearQuarterQuery { public static YearQuarter findQuarter(TemporalAccessor date) { int month = date.get(ChronoField.MONTH_OF_YEAR); if (month >= 1 && month <= 3) { return YearQuarter.Q1; } else if (month >= 4 && month <= 6) { return YearQuarter.Q2; } else if (month >= 7 && month <= 9) { return YearQuarter.Q3; } else { return YearQuarter.Q4; } } } public enum YearQuarter { Q1, Q2, Q3, Q4 }
  • 37. Clock The Clock class is abstract, so you cannot create an instance of it. The following factory methods can be useful for testing – Clock.offset(Clock, Duration) returns a clock that is offset by the specified Duration. – Clock.systemUTC() returns a clock representing the Greenwich/UTC time zone. – Clock.fixed(Instant, ZoneId) always returns the same Instant. For this clock, time stands still.
  • 38. Method Naming Conventions Method Access Description of Static Create an instance with validation to Instance Convert to another type (truncate fields) at Instance Combines this object with another (expands) from Static Convert input parameters to an instance get Instance Part of the state of the object is Instance Queries state of an object with Instance Returns a copy of an object with one changed element plus/minus Instance Returns a copy of an object with amount of added / subtracted time parse Static Parses input string to an instance format Instance Uses formatter to format the object’s values to produce a string
  • 39. Epoch • Reference point to measure time • Maybe based on religious or political milestones • Divided the timeline into eras • Start of a particular era
  • 40. Cumputer System Epochs • January 0, 0 – Matlab • January 1, 1 – Symbian, .Net, new Windows • January 1, 1601 – COBOL, old Windows • January 1, 1900 – LISP, SNTP • January 1, 1904 – Old Mac OS • January 1, 1970 – Unix Epoch (Linux, Mac OS X), Java, C, JavaScript, Perl, PHP, Python, Ruby
  • 41. Calendar System • Organizes days for social, religious, commercial or administrative purposes • Names periods like days, weeks, months and years • Periods may follow cycles of the sun or moon • A date is a specific day in the system • May be based on an epoch
  • 42. UTC • GMT is Greenwich Mean Time – Mean solar time at the Royal Observatory in Greenwich • UTC is Coordinated Universal Time – Precisely defined with atomic time. Does not change with seasons – Replaced GMT as reference time scale on 1 January 1972
  • 43. ISO 8601 • International standard for representation of dates and times • Uses the Gregorian calendar system • Ordered from most to least significant: year, month, day, hour, minute • Each date and time value has a fixed number of digits with leading zones • Uses four-digit year at minimum, YYYY
  • 44. Chronology Interface • Pluggable calendar system (ability to create custom calendar) • Provides access to date and time fields • Built-in – ISO8601 (default): IsoChronology – Chinese: MinguoChronology – Japanese: JapaneseChronology (since 1868-01-01) – Thai Buddhist: ThaiBuddhistChronology – Islamic: HijrahChronology
  • 45. Era interface • An era of the time-line. • some calendar systems, have multiple eras, such as one for the reign of each leader – the Thai Buddhist calendar system divides time into two eras, before and after a single date. – the Japanese calendar system has one era for the reign of each Emperor. • Bilt-in (enums) – IsoEra (BCE; CE) – MingouEra (BEFORE_ROC; ROC) – JapaneseEra (MEIJI; TAISHO; SHOWA; HEISEI) – ThaiBuddhistEra (BEFORE_BE; BE) – HijrahEra (AH)
  • 47. Agenda Previous implementation and problems in real world Date Time Java 8 (JSR 310) Legacy Date Time Integration JSR 310 vs JodaTime
  • 48. Legacy Date Time Integration public class Launcher { public static void main(String[] args) { // Old to new Date date = new Date(); LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); // New to old LocalDateTime now = LocalDateTime.now(); Instant instant = now.atZone(ZoneId.systemDefault()).toInstant(); Date dateFromOld = Date.from(instant); } }
  • 49. Mapping java.util Date and Time Functionality to java.time java.util Functionality java.time Functionality java.util.Date java.time.Instant java.util.GregorianCalendar java.time.ZonedDateTime java.util.TimeZone java.time.ZoneId or java.time.ZoneOffset GregorianCalendar with the date set to 1970-01-01 java.time.LocalTime GregorianCalendar with time set to 00:00. java.time.LocalDate
  • 50. Agenda Previous implementation and problems in real world Date Time Java 8 (JSR 310) Legacy Date Time Integration JSR 310 vs JodaTime
  • 51. JSR 310 vs JodaTime • Both libraries use immutable types. Joda-Time also offers additional mutable types like MutableDateTime. • Null. Joda-Time often use NULL as default for system timezone, default locale, current timestamp etc. while JSR-310 almost always rejects NULL values.
  • 52. Supported fields An overview about supported fields in Java-8 (JSR- 310) is given by some classes in the temporal- package (for example ChronoField and WeekFields) while Joda-Time is rather weak on this area – see DateTimeFieldType. The biggest lack of Joda-Time is here the absence of localized week-related fields. A common feature of both field implementation design is that both are based on values of type long (no other types, not even enums).
  • 53. Enum JSR-310 offers enums like DayOfWeek or Month while Joda-Time does not offer this because it was mainly developed in years 2002-2004 before Java 5.
  • 54. Zone API JSR-310 offers more timezone features than Joda-Time. Latter is not able to yield a programmatically access to the history of timezone offset transitions while JSR-310 is capable to do this.
  • 55. Adjuster vs. Property • JSR 310 has very limited value because the burden to write code is still with the user. Built-in solutions based on the new TemporalAdjuster-concept are not so many, there is currently only the helper class TemporalAdjusters with a limited set of manipulations (and the enums Month or other temporal types). • Joda-Time offers a field-package but practice has shown evidence that new field implementations are very hard to code. On the other side Joda-Time offers so-called properties which make some manipulations much easier and more elegant than in JSR-310
  • 56. Calendar systems Joda-Time provide 8 calendar systems: • Buddhist • Coptic (Egypt – Alexandrian Calendar) • Ethiopic (Ethiopia) • Gregorian-Julian cutover • Gregorian • Islamic • ISO • Julian Calendars like Hebrew or Persian or Hindu are completely missing in both libraries. Joda-Time updates calendars with each release version (you may download it from the web site manually) while java may update only with next path.
  • 57. Clocks JSR-310 has no interface (a design mistake) but an abstract class java.time.Clock which can be used for any clock dependency injection. Joda-Time offers the interface MillisProvider and some helper methods in DateTimeUtils instead. So this way Joda-Time is also capable of supporting test-driven models with different clocks (mocking etc.).
  • 58. Intervals JSR-310 does not support this feature while Joda-Time has limited support. NOTE: Time4j has more rich package (range) for this purpose!
  • 60. Useful links – Java Tutorial – API – Examples from presentation (GitHub) – Comparison Table: JSR 310 vs JodaTime – JSR 310 – Java 8 Date/Time library performance