JSR310
JSR310
ThreeTen : a better
calendar library
GDG devFest 2017
Colorgy
Outline
1. What’s wrong with Calendar and Date?
2. Introduce ThreeTen
3. Convert between ThreeTen and Date
4. Unit test with ThreeTen
5. Integrate with kotlin
What’s wrong with
Calendar and Date?
Begin of this Month?
private Date beginOfThisMonth(){
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.DAY_OF_MONTH, 1);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTime();
}
Other use cases
Boolean isSameday(Date date1, Date date2)
Date firstDayOfWeek()
Date getBeginOfDay(int year, int month, int day)
int dayDiff(Date date1, Date date2)
Other Date use cases
Boolean isSameday(Date date1, Date date2)
Date firstDayOfWeek()
Date getBeginOfDay(int year, int month, int day)
int dayDiff(Date date1, Date date2)
int dayDiff(Calendar date1, Calendar date2) ?????
Unit test is hard
1. Date() has Singleton component and Calendar.getInstance() is
Singleton
2. Need to adjust timezone
3. DateFormat cannot be mocked
Introduce ThreeTen
What is ThreeTen?
What is ThreeTen?
● JSR310 : A Date and Time api in Java8
● ThreeTen : integrate JSR310 into OpenJDK
Why not JodaTime?
JodaTime has design flaw :
http://www.infoq.com/cn/news/2010/05/jsr-310
Why ThreeTen?
1. Immutable
2. Avoid null
3. OpenJDK
4. Easy to use, easy to test
Gradle 3.0 setting
dependencies {
implementation "com.jakewharton.threetenabp:threetenabp:1.0.5"
testImplementation "org.threeten:threetenbp:1.3.3"
}
android.unitTestVariants.all { variant ->
variant.getCompileConfiguration().exclude group:
'com.jakewharton.threetenabp', module: 'threetenabp'
variant.getRuntimeConfiguration().exclude group:
'com.jakewharton.threetenabp', module: 'threetenabp'
}
ThreeTen Api
LocalDate
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plusDays(1);
LocalDate myBirthDay = LocalDate.of(1988, 4, 23);
boolean todayIsMyBirthday = today.equals(myBirthDay);
LocalDate
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plusDays(1);
LocalDate myBirthDay = LocalDate.of(1988, 4, 23);
boolean todayIsMyBirthday = today.equals(myBirthDay);
Immutable
LocalTime
LocalTime offWork = LocalTime.of(18 , 30);
LocalTime goToWork = LocalTime.of(9, 0);
LocalTime lunchTime = LocalTime.of(12, 10, 30);
Let’s look Date again
Date now = new Date();
Date today = new Date();
Now ?? Today??
Let’s look Date again
Date now = new Date();
Date today = new Date();
The Date is not a Date!! It is a instant!!
Period
Period training = Period.ofDays(40);
Period sprints = Period.ofWeeks(2);
LocalDate retireDay = LocalDate.of(2018, 6, 6);
Period logoutArmy = Period.between(LocalDate.now(), retireDay);
More Examples
Temporal = Temporal + TemporalAmount
Tomorrow = Today + 1 day
LocalDate tomorrow = LocalDate.now().plus(Period.ofDays(1))
More Examples
TemporalAmount = TemporalAmount + TemporalAmount
4 days = 3days + 1days
Period fourDays = Period.ofDays(3).plus(Period.ofDays(1))
More Examples
Boolean = Temporal < Temporal
true = 8:00 < 12:00
boolean shouldBeTure = LocalTime.of(8, 0).compareTo(LocalTime.of(12, 0)) <
0
Convert between String and LocalDate
LocalDate today = LocalDate.now();
String format = today.format(DateTimeFormatter.ISO_OFFSET_DATE);
LocalDate christmas = LocalDate.parse("2017-12-25");
LocalDate christmas2 = LocalDate.parse("2017-12-25",
DateTimeFormatter.ISO_DATE);
Convert between String and LocalDate
LocalDate today = LocalDate.now();
String format = today.format(DateTimeFormatter.ISO_OFFSET_DATE);
LocalDate christmas = LocalDate.parse("2017-12-25");
LocalDate christmas2 = LocalDate.parse("2017-12-25",
DateTimeFormatter.ISO_DATE);
No more “yyyyMMdd - hh:mm” m?? M??
And Others...
● LocalDateTime
● Duration
● Instant
● TemporalAdjuster
● Month
● DayOfWeek
Convert Between
ThreeTen and Date
https://gist.github.com/hungyanbin/5b9fdfd
c62b1c7f1b8f4bfcd7958b69f
Unit test with ThreeTen
Test with date-related code
//should climb mountain at Wednesday
boolean shouldClimbMountain()
Test with date-related code
//** this test only passed at Wednesday
void testShouldClimbMountain(){
...
}
Test with date-related code
//** this test only passed at Wednesday
@Ignore
void testShouldClimbMountain(){
...
}
Test with date-related code
//** this test only passed at Wednesday
/*Unit test sucks
void testShouldClimbMountain(){
...
}*/
Solution: Set System Time
private var clock = Clock.systemDefaultZone()
@VisibleForTesting
@JvmStatic
fun setSystemClock(clock: Clock){
this.clock = clock
}
@JvmStatic
fun getNow(): LocalDateTime = LocalDateTime.now(clock)
Integrate with kotlin
Operator overloading and Conventions
class Point(val x: Int, val y: Int){
}
Operator overloading and Conventions
class Point(val x: Int, val y: Int){
}
fun AddPoints(){
val p1 = Point(10, 15)
val p2 = Point(5, -5)
val p3 = p1 + p2
print(p3)
//Point(5, 10)
}
Operator overloading and Conventions
class Point(val x: Int, val y: Int){
operator fun plus(other: Point): Point{
return Point(x + other.x, y + other.y)
}
}
fun AddPoints(){
val p1 = Point(10, 15)
val p2 = Point(5, -5)
val p3 = p1 + p2
print(p3)
//Point(5, 10)
}
Operator overloading and Conventions
class Point(val x: Int, val y: Int){
operator fun plus(other: Point): Point{
return Point(x + other.x, y + other.y)
}
}
fun AddPoints(){
val p1 = Point(10, 15)
val p2 = Point(5, -5)
val p3 = p1 + p2
print(p3)
//Point(5, 10)
}
Operator overloading and Conventions(java)
public class Point {
int x;
int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public Point plus(Point other){
return new Point(x + other.x, y + other.y);
}
}
Operator overloading and Conventions(java)
public class Point {
int x;
int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public Point plus(Point other){
return new Point(x + other.x, y + other.y);
}
}
ThreeTen Api conventions
1. Plus
2. Minus
3. Range
4. compareTo
5. contains
Plus, Minus
public final class LocalDate extends ChronoLocalDate implements Temporal{
...
Public LocalDate plus(TemporalAmount var1);
Public LocalDate minus(TemporalAmount var1);
...
}
Plus(+), Minus(-)
val today = LocalDate.now()
val tomorrow = today + Period.ofDays(1)
val lastWeek = today - Period.ofWeeks(1)
Kotlin Method Extensions
fun Int.Days(): Period = Period.ofDays(this)
fun Int.Weeks(): Period = Period.ofWeeks(this)
Method Extensions
fun Int.Days(): Period = Period.ofDays(this)
fun Int.Weeks(): Period = Period.ofWeeks(this)
val now = LocalDateTime.now()
val tomorrow = now + 1.Days()
val lastWeek = now - 1.Weeks()
CompareTo (<, >, =, >=, <=)
fun isFutureTime(dateTime: LocalDateTime): Boolean {
val now = LocalDateTime.now()
return dateTime > now
}
CompareTo (<, >, =, >=, <=)
fun isFutureTime(dateTime: LocalDateTime): Boolean {
val now = LocalDateTime.now()
return dateTime > now
}
range(...), contains(in)
fun isInThirtyDays(localDate: LocalDate): Boolean{
val now = LocalDate.now()
val nextThirtyDay = now + 30.Days()
val range = now..nextThirtyDay
return localDate in range
}
range(...), contains(in)
fun isInThirtyDays(localDate: LocalDate): Boolean{
val now = LocalDate.now()
val nextThirtyDay = now + 30.Days()
val range = now..nextThirtyDay
return localDate in range
}
range(...), contains(in)
fun isInThirtyDays(localDate: LocalDate): Boolean{
val now = LocalDate.now()
val nextThirtyDay = now + 30.Days()
val range = now..nextThirtyDay
return localDate in range
}
After this talk...
1. How to design a good Api ?
2. When you design a class, have you choosen it’s class name
carefully?
3. Can you list bad smell of Calendar?
4. What makes test hard ? How to write it easily ?
Q & A

ThreeTen

  • 1.
  • 2.
  • 3.
    ThreeTen : abetter calendar library GDG devFest 2017
  • 4.
  • 5.
    Outline 1. What’s wrongwith Calendar and Date? 2. Introduce ThreeTen 3. Convert between ThreeTen and Date 4. Unit test with ThreeTen 5. Integrate with kotlin
  • 6.
  • 7.
    Begin of thisMonth? private Date beginOfThisMonth(){ Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.DAY_OF_MONTH, 1); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); return calendar.getTime(); }
  • 8.
    Other use cases BooleanisSameday(Date date1, Date date2) Date firstDayOfWeek() Date getBeginOfDay(int year, int month, int day) int dayDiff(Date date1, Date date2)
  • 9.
    Other Date usecases Boolean isSameday(Date date1, Date date2) Date firstDayOfWeek() Date getBeginOfDay(int year, int month, int day) int dayDiff(Date date1, Date date2) int dayDiff(Calendar date1, Calendar date2) ?????
  • 10.
    Unit test ishard 1. Date() has Singleton component and Calendar.getInstance() is Singleton 2. Need to adjust timezone 3. DateFormat cannot be mocked
  • 11.
  • 12.
  • 13.
    What is ThreeTen? ●JSR310 : A Date and Time api in Java8 ● ThreeTen : integrate JSR310 into OpenJDK
  • 14.
    Why not JodaTime? JodaTimehas design flaw : http://www.infoq.com/cn/news/2010/05/jsr-310
  • 15.
    Why ThreeTen? 1. Immutable 2.Avoid null 3. OpenJDK 4. Easy to use, easy to test
  • 16.
    Gradle 3.0 setting dependencies{ implementation "com.jakewharton.threetenabp:threetenabp:1.0.5" testImplementation "org.threeten:threetenbp:1.3.3" } android.unitTestVariants.all { variant -> variant.getCompileConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp' variant.getRuntimeConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp' }
  • 17.
  • 18.
    LocalDate LocalDate today =LocalDate.now(); LocalDate tomorrow = today.plusDays(1); LocalDate myBirthDay = LocalDate.of(1988, 4, 23); boolean todayIsMyBirthday = today.equals(myBirthDay);
  • 19.
    LocalDate LocalDate today =LocalDate.now(); LocalDate tomorrow = today.plusDays(1); LocalDate myBirthDay = LocalDate.of(1988, 4, 23); boolean todayIsMyBirthday = today.equals(myBirthDay); Immutable
  • 20.
    LocalTime LocalTime offWork =LocalTime.of(18 , 30); LocalTime goToWork = LocalTime.of(9, 0); LocalTime lunchTime = LocalTime.of(12, 10, 30);
  • 21.
    Let’s look Dateagain Date now = new Date(); Date today = new Date(); Now ?? Today??
  • 22.
    Let’s look Dateagain Date now = new Date(); Date today = new Date(); The Date is not a Date!! It is a instant!!
  • 23.
    Period Period training =Period.ofDays(40); Period sprints = Period.ofWeeks(2); LocalDate retireDay = LocalDate.of(2018, 6, 6); Period logoutArmy = Period.between(LocalDate.now(), retireDay);
  • 25.
    More Examples Temporal =Temporal + TemporalAmount Tomorrow = Today + 1 day LocalDate tomorrow = LocalDate.now().plus(Period.ofDays(1))
  • 26.
    More Examples TemporalAmount =TemporalAmount + TemporalAmount 4 days = 3days + 1days Period fourDays = Period.ofDays(3).plus(Period.ofDays(1))
  • 27.
    More Examples Boolean =Temporal < Temporal true = 8:00 < 12:00 boolean shouldBeTure = LocalTime.of(8, 0).compareTo(LocalTime.of(12, 0)) < 0
  • 28.
    Convert between Stringand LocalDate LocalDate today = LocalDate.now(); String format = today.format(DateTimeFormatter.ISO_OFFSET_DATE); LocalDate christmas = LocalDate.parse("2017-12-25"); LocalDate christmas2 = LocalDate.parse("2017-12-25", DateTimeFormatter.ISO_DATE);
  • 29.
    Convert between Stringand LocalDate LocalDate today = LocalDate.now(); String format = today.format(DateTimeFormatter.ISO_OFFSET_DATE); LocalDate christmas = LocalDate.parse("2017-12-25"); LocalDate christmas2 = LocalDate.parse("2017-12-25", DateTimeFormatter.ISO_DATE); No more “yyyyMMdd - hh:mm” m?? M??
  • 30.
    And Others... ● LocalDateTime ●Duration ● Instant ● TemporalAdjuster ● Month ● DayOfWeek
  • 31.
  • 32.
  • 33.
  • 34.
    Test with date-relatedcode //should climb mountain at Wednesday boolean shouldClimbMountain()
  • 35.
    Test with date-relatedcode //** this test only passed at Wednesday void testShouldClimbMountain(){ ... }
  • 36.
    Test with date-relatedcode //** this test only passed at Wednesday @Ignore void testShouldClimbMountain(){ ... }
  • 37.
    Test with date-relatedcode //** this test only passed at Wednesday /*Unit test sucks void testShouldClimbMountain(){ ... }*/
  • 38.
    Solution: Set SystemTime private var clock = Clock.systemDefaultZone() @VisibleForTesting @JvmStatic fun setSystemClock(clock: Clock){ this.clock = clock } @JvmStatic fun getNow(): LocalDateTime = LocalDateTime.now(clock)
  • 39.
  • 40.
    Operator overloading andConventions class Point(val x: Int, val y: Int){ }
  • 41.
    Operator overloading andConventions class Point(val x: Int, val y: Int){ } fun AddPoints(){ val p1 = Point(10, 15) val p2 = Point(5, -5) val p3 = p1 + p2 print(p3) //Point(5, 10) }
  • 42.
    Operator overloading andConventions class Point(val x: Int, val y: Int){ operator fun plus(other: Point): Point{ return Point(x + other.x, y + other.y) } } fun AddPoints(){ val p1 = Point(10, 15) val p2 = Point(5, -5) val p3 = p1 + p2 print(p3) //Point(5, 10) }
  • 43.
    Operator overloading andConventions class Point(val x: Int, val y: Int){ operator fun plus(other: Point): Point{ return Point(x + other.x, y + other.y) } } fun AddPoints(){ val p1 = Point(10, 15) val p2 = Point(5, -5) val p3 = p1 + p2 print(p3) //Point(5, 10) }
  • 44.
    Operator overloading andConventions(java) public class Point { int x; int y; public Point(int x, int y) { this.x = x; this.y = y; } public Point plus(Point other){ return new Point(x + other.x, y + other.y); } }
  • 45.
    Operator overloading andConventions(java) public class Point { int x; int y; public Point(int x, int y) { this.x = x; this.y = y; } public Point plus(Point other){ return new Point(x + other.x, y + other.y); } }
  • 46.
    ThreeTen Api conventions 1.Plus 2. Minus 3. Range 4. compareTo 5. contains
  • 47.
    Plus, Minus public finalclass LocalDate extends ChronoLocalDate implements Temporal{ ... Public LocalDate plus(TemporalAmount var1); Public LocalDate minus(TemporalAmount var1); ... }
  • 48.
    Plus(+), Minus(-) val today= LocalDate.now() val tomorrow = today + Period.ofDays(1) val lastWeek = today - Period.ofWeeks(1)
  • 49.
    Kotlin Method Extensions funInt.Days(): Period = Period.ofDays(this) fun Int.Weeks(): Period = Period.ofWeeks(this)
  • 50.
    Method Extensions fun Int.Days():Period = Period.ofDays(this) fun Int.Weeks(): Period = Period.ofWeeks(this) val now = LocalDateTime.now() val tomorrow = now + 1.Days() val lastWeek = now - 1.Weeks()
  • 51.
    CompareTo (<, >,=, >=, <=) fun isFutureTime(dateTime: LocalDateTime): Boolean { val now = LocalDateTime.now() return dateTime > now }
  • 52.
    CompareTo (<, >,=, >=, <=) fun isFutureTime(dateTime: LocalDateTime): Boolean { val now = LocalDateTime.now() return dateTime > now }
  • 53.
    range(...), contains(in) fun isInThirtyDays(localDate:LocalDate): Boolean{ val now = LocalDate.now() val nextThirtyDay = now + 30.Days() val range = now..nextThirtyDay return localDate in range }
  • 54.
    range(...), contains(in) fun isInThirtyDays(localDate:LocalDate): Boolean{ val now = LocalDate.now() val nextThirtyDay = now + 30.Days() val range = now..nextThirtyDay return localDate in range }
  • 55.
    range(...), contains(in) fun isInThirtyDays(localDate:LocalDate): Boolean{ val now = LocalDate.now() val nextThirtyDay = now + 30.Days() val range = now..nextThirtyDay return localDate in range }
  • 56.
    After this talk... 1.How to design a good Api ? 2. When you design a class, have you choosen it’s class name carefully? 3. Can you list bad smell of Calendar? 4. What makes test hard ? How to write it easily ?
  • 57.