Skip to content

Commit 85e49df

Browse files
ummelsnicolasstucki
authored andcommitted
Add first batch of java.time classes
1 parent 9e7936c commit 85e49df

16 files changed

+1170
-0
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package java.time
2+
3+
private[time] object Constants {
4+
final val NANOS_IN_MICRO = 1000
5+
6+
final val MICROS_IN_MILLI = 1000
7+
8+
final val NANOS_IN_MILLI = MICROS_IN_MILLI * NANOS_IN_MICRO
9+
10+
final val MILLIS_IN_SECOND = 1000
11+
12+
final val MICROS_IN_SECOND = MILLIS_IN_SECOND * MICROS_IN_MILLI
13+
14+
final val NANOS_IN_SECOND = MILLIS_IN_SECOND * NANOS_IN_MILLI
15+
16+
final val SECONDS_IN_MINUTE = 60
17+
18+
final val MILLIS_IN_MINUTE = SECONDS_IN_MINUTE * MILLIS_IN_SECOND
19+
20+
final val MICROS_IN_MINUTE = SECONDS_IN_MINUTE * MICROS_IN_SECOND
21+
22+
final val NANOS_IN_MINUTE = SECONDS_IN_MINUTE * NANOS_IN_SECOND.toLong
23+
24+
final val MINUTES_IN_HOUR = 60
25+
26+
final val SECONDS_IN_HOUR = MINUTES_IN_HOUR * SECONDS_IN_MINUTE
27+
28+
final val MILLIS_IN_HOUR = MINUTES_IN_HOUR * MILLIS_IN_MINUTE
29+
30+
final val MICROS_IN_HOUR = MINUTES_IN_HOUR * MICROS_IN_MINUTE.toLong
31+
32+
final val NANOS_IN_HOUR = MINUTES_IN_HOUR * NANOS_IN_MINUTE
33+
34+
final val HOURS_IN_DAY = 24
35+
36+
final val MINUTES_IN_DAY = HOURS_IN_DAY * MINUTES_IN_HOUR
37+
38+
final val SECONDS_IN_DAY = HOURS_IN_DAY * SECONDS_IN_HOUR
39+
40+
final val MILLIS_IN_DAY = HOURS_IN_DAY * MILLIS_IN_HOUR
41+
42+
final val MICROS_IN_DAY = HOURS_IN_DAY * MICROS_IN_HOUR
43+
44+
final val NANOS_IN_DAY = HOURS_IN_DAY * NANOS_IN_HOUR
45+
46+
final val SECONDS_IN_WEEK = SECONDS_IN_DAY * 7
47+
48+
final val SECONDS_IN_MONTH = 2629746
49+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package java.time
2+
3+
class DateTimeException(message: String, cause: Throwable)
4+
extends RuntimeException(message, cause) {
5+
def this(message: String) = this(message, null)
6+
}
Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
package java.time
2+
3+
import scala.collection.JavaConverters._
4+
5+
import java.time.temporal._
6+
7+
final class Duration private (seconds: Long, nanos: Int)
8+
extends TemporalAmount with Comparable[Duration]
9+
with java.io.Serializable {
10+
11+
import Preconditions.requireDateTime
12+
import Constants._
13+
import ChronoUnit._
14+
15+
requireDateTime(nanos >= 0 && nanos <= 999999999,
16+
"nanos must be >= 0 and <= 999999999")
17+
18+
private val (normalizedSeconds, normalizedNanos) =
19+
if (seconds < 0 && nanos > 0) (seconds + 1, nanos - NANOS_IN_SECOND)
20+
else (seconds, nanos)
21+
22+
def get(unit: TemporalUnit): Long = unit match {
23+
case SECONDS => seconds
24+
25+
case NANOS => nanos
26+
27+
case _ =>
28+
throw new UnsupportedTemporalTypeException(s"Unit not supported: $unit")
29+
}
30+
31+
def getUnits(): java.util.List[TemporalUnit] =
32+
Seq[TemporalUnit](SECONDS, NANOS).asJava
33+
34+
def isZero(): Boolean = seconds == 0 && nanos == 0
35+
36+
def isNegative(): Boolean = seconds < 0
37+
38+
def getSeconds(): Long = seconds
39+
40+
def getNano(): Int = nanos
41+
42+
def withSeconds(seconds: Long): Duration =
43+
new Duration(seconds, nanos)
44+
45+
def withNanos(nanosOfSecond: Int): Duration =
46+
new Duration(seconds, nanosOfSecond)
47+
48+
def plus(duration: Duration): Duration = {
49+
val seconds1 = duration.getSeconds
50+
val sumNanos = nanos + duration.getNano
51+
if (seconds1 >= 0) {
52+
val sumSeconds = Math.addExact(seconds, seconds1)
53+
if (sumNanos >= NANOS_IN_SECOND)
54+
new Duration(Math.incrementExact(sumSeconds),
55+
sumNanos - NANOS_IN_SECOND)
56+
else
57+
new Duration(sumSeconds, sumNanos)
58+
} else {
59+
val sumSeconds = Math.addExact(seconds, seconds1 + 1)
60+
if (sumNanos >= NANOS_IN_SECOND)
61+
new Duration(sumSeconds, sumNanos - NANOS_IN_SECOND)
62+
else
63+
new Duration(Math.decrementExact(sumSeconds), sumNanos)
64+
}
65+
}
66+
67+
def plus(amount: Long, unit: TemporalUnit): Duration = {
68+
if (!unit.isDurationEstimated || unit == DAYS)
69+
plus(unit.getDuration.multipliedBy(amount))
70+
else
71+
throw new UnsupportedTemporalTypeException(s"Unit not supported: $unit")
72+
}
73+
74+
def plusDays(days: Long): Duration = plus(days, DAYS)
75+
76+
def plusHours(hours: Long): Duration = plus(hours, HOURS)
77+
78+
def plusMinutes(minutes: Long): Duration = plus(minutes, MINUTES)
79+
80+
def plusSeconds(seconds: Long): Duration = plus(seconds, SECONDS)
81+
82+
def plusMillis(millis: Long): Duration = plus(millis, MILLIS)
83+
84+
def plusNanos(nanos: Long): Duration = plus(nanos, NANOS)
85+
86+
def minus(duration: Duration): Duration = {
87+
if (duration == Duration.Min)
88+
plus(Duration.Max).plusNanos(1)
89+
else
90+
plus(duration.negated())
91+
}
92+
93+
def minus(amount: Long, unit: TemporalUnit): Duration = {
94+
if (!unit.isDurationEstimated || unit == DAYS)
95+
minus(unit.getDuration.multipliedBy(amount))
96+
else
97+
throw new UnsupportedTemporalTypeException(s"Unit not supported: $unit")
98+
}
99+
100+
def minusDays(days: Long): Duration = minus(days, DAYS)
101+
102+
def minusHours(hours: Long): Duration = minus(hours, HOURS)
103+
104+
def minusMinutes(minutes: Long): Duration = minus(minutes, MINUTES)
105+
106+
def minusSeconds(seconds: Long): Duration = minus(seconds, SECONDS)
107+
108+
def minusMillis(millis: Long): Duration = minus(millis, MILLIS)
109+
110+
def minusNanos(nanos: Long): Duration = minus(nanos, NANOS)
111+
112+
def multipliedBy(multiplicand: Long): Duration = {
113+
val (prodNanosQuot, prodNanosRem) = {
114+
try {
115+
val prodNanos = Math.multiplyExact(normalizedNanos, multiplicand)
116+
(prodNanos / NANOS_IN_SECOND, (prodNanos % NANOS_IN_SECOND).toInt)
117+
} catch {
118+
case _: ArithmeticException =>
119+
val prodNanos = BigInt(normalizedNanos) * multiplicand
120+
((prodNanos / NANOS_IN_SECOND).toLong, (prodNanos % NANOS_IN_SECOND).toInt)
121+
}
122+
}
123+
val prodSeconds = Math.multiplyExact(normalizedSeconds, multiplicand)
124+
val newSeconds =
125+
if (prodNanosRem >= 0) Math.addExact(prodSeconds, prodNanosQuot)
126+
else Math.addExact(prodSeconds, prodNanosQuot - 1)
127+
val newNanos =
128+
if (prodNanosRem >= 0) prodNanosRem
129+
else prodNanosRem + NANOS_IN_SECOND
130+
new Duration(newSeconds, newNanos)
131+
}
132+
133+
def dividedBy(divisor: Long): Duration = {
134+
val secondsQuot = normalizedSeconds / divisor
135+
val secondsRem = normalizedSeconds % divisor
136+
val nanos = {
137+
try {
138+
val total = Math.addExact(
139+
Math.multiplyExact(secondsRem, NANOS_IN_SECOND),
140+
normalizedNanos)
141+
total / divisor
142+
} catch {
143+
case _: ArithmeticException =>
144+
val total = BigInt(secondsRem) * NANOS_IN_SECOND + normalizedNanos
145+
(total / divisor).toLong
146+
}
147+
}
148+
Duration.ofSeconds(secondsQuot).plusNanos(nanos)
149+
}
150+
151+
def negated(): Duration = multipliedBy(-1)
152+
153+
def abs(): Duration = if (isNegative()) negated() else this
154+
155+
def addTo(temporal: Temporal): Temporal =
156+
temporal.plus(normalizedSeconds, SECONDS).plus(normalizedNanos, NANOS)
157+
158+
def subtractFrom(temporal: Temporal): Temporal =
159+
temporal.minus(normalizedSeconds, SECONDS).minus(normalizedNanos, NANOS)
160+
161+
def toDays(): Long = seconds / SECONDS_IN_DAY
162+
163+
def toHours(): Long = seconds / SECONDS_IN_HOUR
164+
165+
def toMinutes(): Long = seconds / SECONDS_IN_MINUTE
166+
167+
def toMillis(): Long = {
168+
val millis1 = Math.multiplyExact(seconds, MILLIS_IN_SECOND)
169+
val millis2 = nanos / NANOS_IN_MILLI
170+
Math.addExact(millis1, millis2)
171+
}
172+
173+
def toNanos(): Long =
174+
Math.addExact(
175+
Math.multiplyExact(seconds, NANOS_IN_SECOND), nanos)
176+
177+
def compareTo(that: Duration): Int = {
178+
val secCmp = seconds.compareTo(that.getSeconds)
179+
if (secCmp == 0) nanos.compareTo(that.getNano)
180+
else secCmp
181+
}
182+
183+
override def equals(that: Any): Boolean = that match {
184+
case that: Duration =>
185+
seconds == that.getSeconds && nanos == that.getNano
186+
187+
case _ => false
188+
}
189+
190+
override def hashCode(): Int = 31 * seconds.hashCode + nanos
191+
192+
override def toString(): String = {
193+
val mins = normalizedSeconds / 60
194+
val secsOfMin = normalizedSeconds % 60
195+
val hours = mins / 60
196+
val minsOfHour = mins % 60
197+
val hourPart = if (hours == 0) "" else hours.toString + "H"
198+
val minPart = if (minsOfHour == 0) "" else minsOfHour.toString + "M"
199+
val nanos1 = math.abs(normalizedNanos)
200+
val decimals = f"$nanos1%09d".reverse.dropWhile(_ == '0').reverse
201+
val decimalPart = if (decimals.isEmpty) "" else "." + decimals
202+
val secsPart = secsOfMin match {
203+
case 0 if seconds != 0 && nanos == 0 => ""
204+
case 0 if seconds < 0 => "-0" + decimalPart + "S"
205+
case n => n.toString + decimalPart + "S"
206+
}
207+
"PT" + hourPart + minPart + secsPart
208+
}
209+
}
210+
211+
object Duration {
212+
import Constants._
213+
214+
final val ZERO = new Duration(0, 0)
215+
216+
private[time] final val Min = new Duration(Long.MinValue, 0)
217+
218+
private[time] final val Max = new Duration(Long.MaxValue, 999999999)
219+
220+
private[time] final val OneNano = new Duration(0, 1)
221+
222+
private[time] final val OneMicro = new Duration(0, NANOS_IN_MICRO)
223+
224+
private[time] final val OneMilli = new Duration(0, NANOS_IN_MILLI)
225+
226+
private[time] final val OneSecond = new Duration(1, 0)
227+
228+
private[time] final val OneMinute = new Duration(SECONDS_IN_MINUTE, 0)
229+
230+
private[time] final val OneHour = new Duration(SECONDS_IN_HOUR, 0)
231+
232+
private[time] final val OneDay = new Duration(SECONDS_IN_DAY, 0)
233+
234+
private[time] final val OneWeek = new Duration(SECONDS_IN_WEEK, 0)
235+
236+
private[time] final val OneMonth = new Duration(SECONDS_IN_MONTH, 0)
237+
238+
private[time] final val OneYear = OneMonth.multipliedBy(12)
239+
240+
def ofDays(days: Long): Duration = OneDay.multipliedBy(days)
241+
242+
def ofHours(hours: Long): Duration = OneHour.multipliedBy(hours)
243+
244+
def ofMinutes(minutes: Long): Duration = OneMinute.multipliedBy(minutes)
245+
246+
def ofSeconds(seconds: Long): Duration = new Duration(seconds, 0)
247+
248+
def ofSeconds(seconds: Long, nanoAdjustment: Long): Duration =
249+
ofSeconds(seconds).plusNanos(nanoAdjustment)
250+
251+
def ofMillis(millis: Long): Duration = OneMilli.multipliedBy(millis)
252+
253+
def ofNanos(nanos: Long): Duration = OneNano.multipliedBy(nanos)
254+
255+
def of(amount: Long, unit: TemporalUnit): Duration = {
256+
if (!unit.isDurationEstimated || unit == ChronoUnit.DAYS)
257+
unit.getDuration.multipliedBy(amount)
258+
else
259+
throw new UnsupportedTemporalTypeException(s"Unit not supported: $unit")
260+
}
261+
262+
def from(amount: TemporalAmount): Duration = {
263+
amount.getUnits.asScala.foldLeft(ZERO) { (d, u) =>
264+
d.plus(amount.get(u), u)
265+
}
266+
}
267+
268+
// Not implemented
269+
// def parse(text: CharSequence): Duration
270+
271+
def between(start: Temporal, end: Temporal): Duration = {
272+
try {
273+
val nanos = start.until(end, ChronoUnit.NANOS)
274+
Duration.ofNanos(nanos)
275+
} catch {
276+
case _:DateTimeException | _:ArithmeticException =>
277+
val seconds = start.until(end, ChronoUnit.SECONDS)
278+
val nanos = {
279+
try {
280+
end.get(ChronoField.NANO_OF_SECOND) -
281+
start.get(ChronoField.NANO_OF_SECOND)
282+
} catch {
283+
case _: DateTimeException => 0
284+
}
285+
}
286+
Duration.ofSeconds(seconds, nanos)
287+
}
288+
}
289+
}

0 commit comments

Comments
 (0)