Skip to content

Commit 7edd016

Browse files
authored
[time] Add time.toInstant method (#413)
Closes #412. This adds a convenience method for constructing a `time.toInstant` from various input types, similar to `time.toZDT`. The main purpose and motivation is to simplify this: ```javascript var startTime = time.toZDT(lastUpdateItem).toInstant(); var endTime = time.Instant.now(); ``` to the more straight-forward (and faster): ```javascript var startTime = time.toInstant(lastUpdateItem); var endTime = time.toInstant(); ``` --------- Signed-off-by: Jacob Laursen <[email protected]>
1 parent d5c8fd8 commit 7edd016

File tree

6 files changed

+175
-22
lines changed

6 files changed

+175
-22
lines changed

Diff for: README.md

+18-1
Original file line numberDiff line numberDiff line change
@@ -971,7 +971,7 @@ The following rules are used during the conversion:
971971
| `null` or `undefined` | `time.ZonedDateTime.now()` | `time.toZDT();` |
972972
| `time.ZonedDateTime` | passed through unmodified | |
973973
| `java.time.ZonedDateTime` | converted to the `time.ZonedDateTime` equivalent | |
974-
| JavaScript native `Date` | converted to the equivalent `time.ZonedDateTime` using `SYSTEM` as the timezone | |
974+
| JavaScript native `Date` | converted to the `time.ZonedDateTime` equivalent using `SYSTEM` as the timezone | |
975975
| `number`, `bingint`, `java.lang.Number`, `DecimalType` | rounded to the nearest integer and added to `now` as milliseconds | `time.toZDT(1000);` |
976976
| [`Quantity`](#quantity) or `QuantityType` | if the unit is time-compatible, added to `now` | `time.toZDT(item.getItem('MyTimeItem').rawState);`, `time.toZDT(Quantity('10 min'));` |
977977
| `items.Item` or `org.openhab.core.types.Item` | if the state is supported (see the `Type` rules in this table, e.g. `DecimalType`), the state is converted | `time.toZDT(items.getItem('MyItem'));` |
@@ -1089,6 +1089,23 @@ var timestamp = time.ZonedDateTime.now().plusMinutes(5);
10891089
console.log(timestamp.getMillisFromNow());
10901090
```
10911091
1092+
#### `time.toInstant()`
1093+
1094+
The following rules are used during the conversion:
1095+
1096+
| Argument Type | Rule | Examples |
1097+
|-----------------------------------------------|------------------------------------------------------------------------------------------|----------------------------------------------|
1098+
| `null` or `undefined` | `time.Instant.now()` | `time.toInstant();` |
1099+
| `time.Instant` | passed through unmodified | |
1100+
| `java.time.Instant` | converted to the `time.Instant` equivalent | |
1101+
| `java.time.ZonedDateTime` | converted to the `time.Instant` equivalent | |
1102+
| JavaScript native `Date` | converted to the `time.Instant` equivalent | |
1103+
| `items.Item` or `org.openhab.core.types.Item` | if the state is supported (see the `*Type` rules in this table), the state is converted | `time.toInstant(items.getItem('MyItem'));` |
1104+
| `String`, `java.lang.String`, `StringType` | parsed | `time.toInstant('2019-10-12T07:20:50.52Z');` |
1105+
| `DateTimeType` | converted to the `time.Instant` equivalent | |
1106+
1107+
When a type or string that cannot be handled is encountered, an error is thrown.
1108+
10921109
### Quantity
10931110
10941111
The `Quantity` class greatly simplifies Quantity handling by providing unit conversion, comparisons and mathematical operations.

Diff for: src/time.js

+115-18
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ const time = require('@js-joda/core');
1616

1717
const log = require('./log')('time');
1818
const osgi = require('./osgi');
19-
const { _isItem, _isZonedDateTime, _isDuration, _isQuantity } = require('./helpers');
19+
const { _isItem, _isZonedDateTime, _isInstant, _isDuration, _isQuantity } = require('./helpers');
2020

2121
const javaZDT = Java.type('java.time.ZonedDateTime');
22+
const javaInstant = Java.type('java.time.Instant');
2223
const javaDuration = Java.type('java.time.Duration');
2324
const javaString = Java.type('java.lang.String');
2425
const javaNumber = Java.type('java.lang.Number');
@@ -41,28 +42,30 @@ time.ZonedDateTime.prototype.parse = function (text, formatter = rfcFormatter) {
4142
};
4243

4344
/**
44-
* Adds millis to the passed in ZDT as milliseconds. The millis is rounded first.
45+
* Adds millis to the passed in Temporal as milliseconds. The millis are rounded first.
4546
* If millis is negative they will be subtracted.
4647
* @private
48+
* @param {time.Temporal} temporal temporal to add milleseconds to
4749
* @param {number|bigint} millis number of milliseconds to add
4850
*/
49-
function _addMillisToNow (millis) {
50-
return time.ZonedDateTime.now().plus(Math.round(millis), time.ChronoUnit.MILLIS);
51+
function _addMillis (temporal, millis) {
52+
return temporal.plus(Math.round(millis), time.ChronoUnit.MILLIS);
5153
}
5254

5355
/**
54-
* Adds the passed in QuantityType<Time> to now.
56+
* Adds the passed in QuantityType<Time> to the passed Temporal.
5557
* @private
58+
* @param {time.Temporal} temporal temporal to add seconds to
5659
* @param {QuantityType} quantityType an Item's QuantityType
5760
* @returns now plus the time length in the quantityType
5861
* @throws error when the units for the quantity type are not one of the Time units
5962
*/
60-
function _addQuantityType (quantityType) {
63+
function _addQuantityType (temporal, quantityType) {
6164
const secs = quantityType.toUnit('s');
6265
if (secs) {
63-
return time.ZonedDateTime.now().plusSeconds(secs.doubleValue());
66+
return temporal.plus(secs.doubleValue(), time.ChronoUnit.SECONDS);
6467
} else {
65-
throw Error('Only Time units are supported to convert QuantityTypes to a ZonedDateTime: ' + quantityType.toString());
68+
throw Error('Only Time units are supported to add QuantityTypes to a Temporal: ' + quantityType.toString());
6669
}
6770
}
6871

@@ -180,20 +183,37 @@ function _parseString (str) {
180183
* @returns {time.ZonedDateTime}
181184
* @throws error if the Item's state is not supported or the Item itself is not supported
182185
*/
183-
function _convertItemRawState (rawState) {
186+
function _convertItemRawStateToZonedDateTime (rawState) {
184187
if (rawState instanceof DecimalType) { // Number type Items
185-
return _addMillisToNow(rawState.floatValue());
188+
return _addMillis(time.ZonedDateTime.now(), rawState.floatValue());
186189
} else if (rawState instanceof StringType) { // String type Items
187190
return _parseString(rawState.toString());
188191
} else if (rawState instanceof DateTimeType) { // DateTime Items
189192
return javaInstantToJsInstant(rawState.getInstant()).atZone(time.ZoneId.systemDefault());
190193
} else if (rawState instanceof QuantityType) { // Number:Time type Items
191-
return _addQuantityType(rawState);
194+
return _addQuantityType(time.ZonedDateTime.now(), rawState);
192195
} else {
193196
throw Error(rawState.toString() + ' is not supported for conversion to time.ZonedDateTime');
194197
}
195198
}
196199

200+
/**
201+
* Converts the state of the passed in Item to a time.Instant
202+
* @private
203+
* @param {HostState} rawState
204+
* @returns {time.Instant}
205+
* @throws error if the Item's state is not supported or the Item itself is not supported
206+
*/
207+
function _convertItemRawStateToInstant (rawState) {
208+
if (rawState instanceof StringType) { // String type Items
209+
return time.Instant.parse(rawState.toString());
210+
} else if (rawState instanceof DateTimeType) { // DateTime Items
211+
return javaInstantToJsInstant(rawState.getInstant());
212+
} else {
213+
throw Error(rawState.toString() + ' is not supported for conversion to time.Instant');
214+
}
215+
}
216+
197217
/**
198218
* Convert Java Instant to JS-Joda Instant.
199219
*
@@ -255,7 +275,7 @@ function toZDT (when) {
255275
}
256276
// Convert Java ZonedDateTime
257277
if (when instanceof javaZDT) {
258-
log.debug('toZTD: Converting Java ZonedDateTime ' + when.toString());
278+
log.debug('toZDT: Converting Java ZonedDateTime ' + when.toString());
259279
return javaZDTToJsZDT(when);
260280
}
261281

@@ -282,10 +302,10 @@ function toZDT (when) {
282302
// Add JavaScript's number or JavaScript BigInt or Java Number or Java DecimalType as milliseconds to now
283303
if (typeof when === 'number' || typeof when === 'bigint') {
284304
log.debug('toZDT: Adding milliseconds ' + when + ' to now');
285-
return _addMillisToNow(when);
305+
return _addMillis(time.ZonedDateTime.now(), when);
286306
} else if (when instanceof javaNumber || when instanceof DecimalType) {
287307
log.debug('toZDT: Adding Java number or DecimalType ' + when.floatValue() + ' to now');
288-
return _addMillisToNow(when.floatValue());
308+
return _addMillis(time.ZonedDateTime.now(), when.floatValue());
289309
}
290310

291311
// DateTimeType, extract the javaInstant and convert to time.ZDT, use the configured timezone
@@ -297,10 +317,10 @@ function toZDT (when) {
297317
// Add Quantity or QuantityType<Time> to now
298318
if (_isQuantity(when)) {
299319
log.debug('toZDT: Adding Quantity ' + when + ' to now');
300-
return _addQuantityType(when.rawQtyType);
320+
return _addQuantityType(time.ZonedDateTime.now(), when.rawQtyType);
301321
} else if (when instanceof QuantityType) {
302322
log.debug('toZDT: Adding QuantityType ' + when + ' to now');
303-
return _addQuantityType(when);
323+
return _addQuantityType(time.ZonedDateTime.now(), when);
304324
}
305325

306326
// Convert items.Item or raw Item
@@ -309,10 +329,10 @@ function toZDT (when) {
309329
if (when.isUninitialized) {
310330
throw Error('Item ' + when.name + ' is NULL or UNDEF, cannot convert to a time.ZonedDateTime');
311331
}
312-
return _convertItemRawState(when.rawState);
332+
return _convertItemRawStateToZonedDateTime(when.rawState);
313333
} else if (when instanceof ohItem) {
314334
log.debug('toZDT: Converting raw Item ' + when);
315-
return _convertItemRawState(when.getState());
335+
return _convertItemRawStateToZonedDateTime(when.getState());
316336
}
317337

318338
// Unsupported
@@ -331,6 +351,82 @@ time.ZonedDateTime.prototype.toToday = function () {
331351
.withDayOfMonth(now.dayOfMonth());
332352
};
333353

354+
/**
355+
* Converts the passed in when to a time.Instant based on the following
356+
* set of rules.
357+
*
358+
* - null, undefined: time.Instant.now()
359+
* - time.Instant: unmodified
360+
* - time.ZonedDateTime: converted to the time.Instant equivalent
361+
* - Java Instant, DateTimeType: converted to time.Instant equivalent
362+
* - JavaScript native {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date Date}: converted to a `time.Instant`
363+
* - Item: converts the state of the Item based on the *Type rules described here
364+
* - String, Java String, StringType: parsed
365+
* @memberof time
366+
* @param {*} [when] any of the types discussed above
367+
* @returns {time.Instant}
368+
* @throws error if the type, format, or contents of when are not supported
369+
*/
370+
function toInstant (when) {
371+
// If when is not supplied or null, return now
372+
if (when === undefined || when === null) {
373+
log.debug('toInstant: Returning Instant.now()');
374+
return time.Instant.now();
375+
}
376+
377+
// Pass through if already a time.Instant
378+
if (_isInstant(when)) {
379+
log.debug('toInstant: Passing trough ' + when);
380+
return when;
381+
}
382+
383+
// Convert time.ZonedDateTime
384+
if (_isZonedDateTime(when)) {
385+
log.debug('toInstant: Converting time.ZonedDateTime ' + when.toString());
386+
return when.toInstant();
387+
}
388+
389+
// Convert Java Instant
390+
if (when instanceof javaInstant) {
391+
log.debug('toInstant: Converting Java Instant ' + when.toString());
392+
return javaInstantToJsInstant(when);
393+
}
394+
395+
// String or StringType
396+
if (typeof when === 'string' || when instanceof javaString || when instanceof StringType) {
397+
log.debug('toInstant: Parsing string ' + when);
398+
return time.Instant.parse(when.toString());
399+
}
400+
401+
// JavaScript Native Date
402+
if (when instanceof Date) {
403+
log.debug('toInstant: Converting JS native Date ' + when);
404+
const native = time.nativeJs(when);
405+
return time.Instant.from(native);
406+
}
407+
408+
// DateTimeType, extract the javaInstant and convert to time.Instant
409+
if (when instanceof DateTimeType) {
410+
log.debug('toInstant: Converting DateTimeType ' + when);
411+
return javaInstantToJsInstant(when.getInstant());
412+
}
413+
414+
// Convert items.Item or raw Item
415+
if (_isItem(when)) {
416+
log.debug('toInstant: Converting Item ' + when);
417+
if (when.isUninitialized) {
418+
throw Error('Item ' + when.name + ' is NULL or UNDEF, cannot convert to a time.Instant');
419+
}
420+
return _convertItemRawStateToInstant(when.rawState);
421+
} else if (when instanceof ohItem) {
422+
log.debug('toInstant: Converting raw Item ' + when);
423+
return _convertItemRawStateToInstant(when.getState());
424+
}
425+
426+
// Unsupported
427+
throw Error('"' + when + '" is an unsupported type for conversion to time.Instant');
428+
}
429+
334430
/**
335431
* Tests whether `this` time.ZonedDateTime is before the passed in timestamp.
336432
* However, the function only compares the time portion of both, ignoring the date portion.
@@ -497,6 +593,7 @@ module.exports = {
497593
javaInstantToJsInstant,
498594
javaZDTToJsZDT,
499595
toZDT,
596+
toInstant,
500597
_parseString,
501598
_parseISO8601
502599
};

Diff for: test/jest.setup.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
const { ModuleBuilder, Configuration, QuantityType, JavaScriptExecution, JavaTransformation, JavaNotificationAction } = require('./openhab.mock');
2-
const { Class, BigDecimal, ArrayList, HashSet, Hashtable, UUID, FrameworkUtil, LoggerFactory, ZonedDateTime } = require('./java.mock');
2+
const { Class, BigDecimal, ArrayList, HashSet, Hashtable, UUID, FrameworkUtil, LoggerFactory, Instant, ZonedDateTime } = require('./java.mock');
33

44
const TYPES = {
55
'java.lang.Class': Class,
66
'java.math.BigDecimal': BigDecimal,
7+
'java.time.Instant': Instant,
78
'java.time.ZonedDateTime': ZonedDateTime,
89
'java.util.ArrayList': ArrayList,
910
'java.util.HashSet': HashSet,

Diff for: test/time.spec.js

+21-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,27 @@ describe('time.js', () => {
2222

2323
it('delegates to parseString', () => {
2424
const when = 'when';
25-
expect(() => time.toZDT(when)).toThrowError('Failed to parse string when: DateTimeParseException: Text cannot be parsed to a Duration: when, at index: 0');
25+
expect(() => time.toZDT(when)).toThrow('Failed to parse string when: DateTimeParseException: Text cannot be parsed to a Duration: when, at index: 0');
26+
});
27+
28+
// TODO: Add remaining possible cases for when
29+
});
30+
31+
describe('toInstant', () => {
32+
it('passes through if when is a time.Instant', () => {
33+
const instant = time.Instant.now();
34+
expect(time.toInstant(instant)).toBe(instant);
35+
});
36+
37+
it('converts if when is a time.ZonedDateTime', () => {
38+
const zdt = time.ZonedDateTime.now();
39+
expect(time.toInstant(zdt)).toStrictEqual(zdt.toInstant());
40+
});
41+
42+
it('converts if when is a string', () => {
43+
const instant = time.Instant.now();
44+
const string = instant.toString();
45+
expect(time.toInstant(string)).toStrictEqual(instant);
2646
});
2747

2848
// TODO: Add remaining possible cases for when

Diff for: types/time.d.ts

+18
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ declare const _exports: {
22
javaInstantToJsInstant: typeof javaInstantToJsInstant;
33
javaZDTToJsZDT: typeof javaZDTToJsZDT;
44
toZDT: typeof toZDT;
5+
toInstant: typeof toInstant;
56
_parseString: typeof _parseString;
67
_parseISO8601: typeof _parseISO8601;
78
nativeJs(date: any, zone?: time.ZoneId): time.ZonedDateTime;
@@ -114,6 +115,23 @@ declare function javaZDTToJsZDT(zdt: JavaZonedDateTime): time.ZonedDateTime;
114115
* @throws error if the type, format, or contents of when are not supported
115116
*/
116117
declare function toZDT(when?: any): time.ZonedDateTime;
118+
/**
119+
* Converts the passed in when to a time.Instant based on the following
120+
* set of rules.
121+
*
122+
* - null, undefined: time.Instant.now()
123+
* - time.Instant: unmodified
124+
* - time.ZonedDateTime: converted to the time.Instant equivalent
125+
* - Java Instant, DateTimeType: converted to time.Instant equivalent
126+
* - JavaScript native {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date Date}: converted to a `time.Instant`
127+
* - Item: converts the state of the Item based on the *Type rules described here
128+
* - String, Java String, StringType: parsed
129+
* @memberof time
130+
* @param {*} [when] any of the types discussed above
131+
* @returns {time.Instant}
132+
* @throws error if the type, format, or contents of when are not supported
133+
*/
134+
declare function toInstant(when?: any): time.Instant;
117135
/**
118136
* Parses the passed in string based on it's format and converts it to a ZonedDateTime.
119137
* If no timezone is specified, the configured timezone is used.

Diff for: types/time.d.ts.map

+1-1
Original file line numberDiff line numberDiff line change

0 commit comments

Comments
 (0)