Skip to content

Commit c275076

Browse files
Kostas Pagratisjenkins
Kostas Pagratis
authored and
jenkins
committed
util-core: add convenience functions for java.time Instant,
ZonedDateTime, and OffsetDateTime Problem: When working with both com.twitter.util.Time and java.time, switching to the latter is verbose. Solution: Add convenience functions to convert to java.time objects. Differential Revision: https://phabricator.twitter.biz/D757636
1 parent bf493f9 commit c275076

File tree

3 files changed

+79
-8
lines changed

3 files changed

+79
-8
lines changed

CHANGELOG.rst

+8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ Note that ``PHAB_ID=#`` and ``RB_ID=#`` correspond to associated messages in com
77
Unreleased
88
----------
99

10+
New Features
11+
~~~~~~~~~~~~
12+
13+
* util-core: Add convenience methods to convert to java.time.ZonedDateTime and
14+
java.time.OffsetDateTime. `toInstant`, `toZonedDateTime`, and `toOffsetDateTime` also preserve
15+
nanosecond resolution. ``PHAB_ID=D757636``
16+
17+
1018
21.9.0
1119
------
1220

util-core/src/main/scala/com/twitter/util/Time.scala

+27-2
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,15 @@
1717
package com.twitter.util
1818

1919
import java.io.Serializable
20-
import java.time.{Clock, Instant}
20+
import java.time.ZonedDateTime
21+
import java.time.Clock
22+
import java.time.Instant
23+
import java.time.OffsetDateTime
24+
import java.time.ZoneOffset
2125
import java.util.concurrent.TimeUnit
22-
import java.util.{Date, Locale, TimeZone}
26+
import java.util.Date
27+
import java.util.Locale
28+
import java.util.TimeZone
2329

2430
/**
2531
* @define now
@@ -688,6 +694,25 @@ sealed class Time private[util] (protected val nanos: Long)
688694
*/
689695
def toDate: Date = new Date(inMillis)
690696

697+
/**
698+
* Converts this Time object to a java.time.Instant
699+
*/
700+
def toInstant: Instant = {
701+
val millis = inMilliseconds
702+
val nanos = inNanoseconds - (millis * Duration.NanosPerMillisecond)
703+
Instant.ofEpochMilli(millis).plusNanos(nanos)
704+
}
705+
706+
/**
707+
* Converts this Time object to a java.time.ZonedDateTime with ZoneId of UTC
708+
*/
709+
def toZonedDateTime: ZonedDateTime = ZonedDateTime.ofInstant(toInstant, ZoneOffset.UTC)
710+
711+
/**
712+
* Converts this Time object to a java.time.OffsetDateTime with ZoneId of UTC
713+
*/
714+
def toOffsetDateTime: OffsetDateTime = OffsetDateTime.ofInstant(toInstant, ZoneOffset.UTC)
715+
691716
private def writeReplace(): Object = TimeBox.Finite(inNanoseconds)
692717

693718
/**

util-core/src/test/scala/com/twitter/util/TimeTest.scala

+44-6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
package com.twitter.util
22

3-
import java.io.{ByteArrayInputStream, ByteArrayOutputStream, ObjectInputStream, ObjectOutputStream}
4-
import java.util.{Locale, TimeZone}
3+
import java.io.ByteArrayInputStream
4+
import java.io.ByteArrayOutputStream
5+
import java.io.ObjectInputStream
6+
import java.io.ObjectOutputStream
7+
import java.util.Locale
8+
import java.util.TimeZone
59
import java.util.concurrent.TimeUnit
6-
7-
import org.scalatest.concurrent.{Eventually, IntegrationPatience}
10+
import org.scalatest.concurrent.Eventually
11+
import org.scalatest.concurrent.IntegrationPatience
812
import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks
9-
1013
import com.twitter.conversions.DurationOps._
14+
import java.time.Instant
15+
import java.time.OffsetDateTime
16+
import java.time.ZoneOffset
17+
import java.time.ZonedDateTime
1118
import org.scalatest.wordspec.AnyWordSpec
1219

1320
trait TimeLikeSpec[T <: TimeLike[T]] extends AnyWordSpec with ScalaCheckDrivenPropertyChecks {
@@ -509,7 +516,9 @@ class TimeTest
509516
val t = new Thread(r)
510517
t.start()
511518
assert(t.isAlive == true)
512-
eventually { assert(t.getState == Thread.State.TIMED_WAITING) }
519+
eventually {
520+
assert(t.getState == Thread.State.TIMED_WAITING)
521+
}
513522
ctl.advance(5.seconds)
514523
t.join()
515524
assert(t.isAlive == false)
@@ -712,5 +721,34 @@ class TimeTest
712721
assert(t0.untilNow == 100.hours)
713722
}
714723
}
724+
725+
"toInstant" in {
726+
Time.withCurrentTimeFrozen { _ =>
727+
val instant = Instant.ofEpochMilli(Time.now.inMilliseconds)
728+
assert(instant.toEpochMilli == Time.now.inMillis)
729+
// java.time.Instant:getNano returns the nanoseconds of the second
730+
assert(instant.getNano == Time.now.inNanoseconds % Duration.NanosPerSecond)
731+
}
732+
}
733+
734+
"toZonedDateTime" in {
735+
Time.withCurrentTimeFrozen { _ =>
736+
val zonedDateTime =
737+
ZonedDateTime.ofInstant(Instant.ofEpochMilli(Time.now.inMilliseconds), ZoneOffset.UTC)
738+
assert(Time.now.toZonedDateTime == zonedDateTime)
739+
// java.time.Instant:getNano returns the nanoseconds of the second
740+
assert(zonedDateTime.getNano == Time.now.inNanoseconds % Duration.NanosPerSecond)
741+
}
742+
}
743+
744+
"toOffsetDateTime" in {
745+
Time.withCurrentTimeFrozen { _ =>
746+
val offsetDateTime =
747+
OffsetDateTime.ofInstant(Instant.ofEpochMilli(Time.now.inMilliseconds), ZoneOffset.UTC)
748+
assert(Time.now.toOffsetDateTime == offsetDateTime)
749+
// java.time.Instant:getNano returns the nanoseconds of the second
750+
assert(offsetDateTime.getNano == Time.now.inNanoseconds % Duration.NanosPerSecond)
751+
}
752+
}
715753
}
716754
}

0 commit comments

Comments
 (0)