Skip to content

Commit c7a8ee1

Browse files
committed
Provide time.toInstant method
Resolves #412 Signed-off-by: Jacob Laursen <[email protected]>
1 parent d5c8fd8 commit c7a8ee1

File tree

4 files changed

+171
-20
lines changed

4 files changed

+171
-20
lines changed

README.md

+19-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,24 @@ 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, e.g. `DecimalType`), the state is converted | `time.toInstant(items.getItem('MyItem'));` |
1104+
| `String`, `java.lang.String`, `StringType` | parsed ISO Instant | `time.toInstant('2019-10-12T07:20:50.52Z');` |
1105+
| [ISO8601 Date/Time](https://en.wikipedia.org/wiki/ISO_8601) String | | |
1106+
| RFC String (output from a Java `Instant.toString()`) | | |
1107+
1108+
When a type or string that cannot be handled is encountered, an error is thrown.
1109+
10921110
### Quantity
10931111
10941112
The `Quantity` class greatly simplifies Quantity handling by providing unit conversion, comparisons and mathematical operations.

src/time.js

+132-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,41 @@ 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 DecimalType) { // Number type Items
209+
return _addMillis(time.Instant.now(), rawState.floatValue());
210+
} else if (rawState instanceof StringType) { // String type Items
211+
return time.Instant.parse(rawState.toString());
212+
} else if (rawState instanceof DateTimeType) { // DateTime Items
213+
return javaInstantToJsInstant(rawState.getInstant());
214+
} else if (rawState instanceof QuantityType) { // Number:Time type Items
215+
return _addQuantityType(time.Instant.now(), rawState);
216+
} else {
217+
throw Error(rawState.toString() + ' is not supported for conversion to time.Instant');
218+
}
219+
}
220+
197221
/**
198222
* Convert Java Instant to JS-Joda Instant.
199223
*
@@ -255,7 +279,7 @@ function toZDT (when) {
255279
}
256280
// Convert Java ZonedDateTime
257281
if (when instanceof javaZDT) {
258-
log.debug('toZTD: Converting Java ZonedDateTime ' + when.toString());
282+
log.debug('toZDT: Converting Java ZonedDateTime ' + when.toString());
259283
return javaZDTToJsZDT(when);
260284
}
261285

@@ -282,10 +306,10 @@ function toZDT (when) {
282306
// Add JavaScript's number or JavaScript BigInt or Java Number or Java DecimalType as milliseconds to now
283307
if (typeof when === 'number' || typeof when === 'bigint') {
284308
log.debug('toZDT: Adding milliseconds ' + when + ' to now');
285-
return _addMillisToNow(when);
309+
return _addMillis(time.ZonedDateTime.now(), when);
286310
} else if (when instanceof javaNumber || when instanceof DecimalType) {
287311
log.debug('toZDT: Adding Java number or DecimalType ' + when.floatValue() + ' to now');
288-
return _addMillisToNow(when.floatValue());
312+
return _addMillis(time.ZonedDateTime.now(), when.floatValue());
289313
}
290314

291315
// DateTimeType, extract the javaInstant and convert to time.ZDT, use the configured timezone
@@ -297,10 +321,10 @@ function toZDT (when) {
297321
// Add Quantity or QuantityType<Time> to now
298322
if (_isQuantity(when)) {
299323
log.debug('toZDT: Adding Quantity ' + when + ' to now');
300-
return _addQuantityType(when.rawQtyType);
324+
return _addQuantityType(time.ZonedDateTime.now(), when.rawQtyType);
301325
} else if (when instanceof QuantityType) {
302326
log.debug('toZDT: Adding QuantityType ' + when + ' to now');
303-
return _addQuantityType(when);
327+
return _addQuantityType(time.ZonedDateTime.now(), when);
304328
}
305329

306330
// Convert items.Item or raw Item
@@ -309,10 +333,10 @@ function toZDT (when) {
309333
if (when.isUninitialized) {
310334
throw Error('Item ' + when.name + ' is NULL or UNDEF, cannot convert to a time.ZonedDateTime');
311335
}
312-
return _convertItemRawState(when.rawState);
336+
return _convertItemRawStateToZonedDateTime(when.rawState);
313337
} else if (when instanceof ohItem) {
314338
log.debug('toZDT: Converting raw Item ' + when);
315-
return _convertItemRawState(when.getState());
339+
return _convertItemRawStateToZonedDateTime(when.getState());
316340
}
317341

318342
// Unsupported
@@ -331,6 +355,95 @@ time.ZonedDateTime.prototype.toToday = function () {
331355
.withDayOfMonth(now.dayOfMonth());
332356
};
333357

358+
/**
359+
* Converts the passed in when to a time.Instant based on the following
360+
* set of rules.
361+
*
362+
* - null, undefined: time.Instant.now()
363+
* - time.Instant: unmodified
364+
* - time.ZonedDateTime: converted to the time.Instant equivalent
365+
* - Java Instant, DateTimeType: converted to time.Instant equivalent
366+
* - JavaScript native {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date Date}: converted to a `time.Instant`
367+
* - Item: converts the state of the Item based on the *Type rules described here
368+
* - String, Java String, StringType: parsed ISO Instant
369+
* - RFC (output from a Java Instant.toString()): parsed to time.ZonedDateTime
370+
* @memberof time
371+
* @param {*} [when] any of the types discussed above
372+
* @returns {time.Instant}
373+
* @throws error if the type, format, or contents of when are not supported
374+
*/
375+
function toInstant (when) {
376+
// If when is not supplied or null, return now
377+
if (when === undefined || when === null) {
378+
log.debug('toInstant: Returning Instant.now()');
379+
return time.Instant.now();
380+
}
381+
382+
// Pass through if already a time.Instant
383+
if (_isInstant(when)) {
384+
log.debug('toInstant: Passing trough ' + when);
385+
return when;
386+
}
387+
388+
// Convert time.ZonedDateTime
389+
if (_isZonedDateTime(when)) {
390+
log.debug('toInstant: Converting time.ZonedDateTime ' + when.toString());
391+
return when.toInstant();
392+
}
393+
394+
// Convert Java Instant
395+
if (when instanceof javaInstant) {
396+
log.debug('toInstant: Converting Java Instant ' + when.toString());
397+
return javaInstantToJsInstant(when);
398+
}
399+
400+
// String or StringType
401+
if (typeof when === 'string' || when instanceof javaString || when instanceof StringType) {
402+
log.debug('toInstant: Parsing string ' + when);
403+
return time.Instant.parse(when.toString());
404+
}
405+
406+
// JavaScript Native Date
407+
if (when instanceof Date) {
408+
log.debug('toInstant: Converting JS native Date ' + when);
409+
const native = time.nativeJs(when);
410+
return time.Instant.from(native);
411+
}
412+
413+
// DateTimeType, extract the javaInstant and convert to time.Instant
414+
if (when instanceof DateTimeType) {
415+
log.debug('toInstant: Converting DateTimeType ' + when);
416+
return javaInstantToJsInstant(when.getInstant());
417+
}
418+
419+
// Convert items.Item or raw Item
420+
if (_isItem(when)) {
421+
log.debug('toInstant: Converting Item ' + when);
422+
if (when.isUninitialized) {
423+
throw Error('Item ' + when.name + ' is NULL or UNDEF, cannot convert to a time.Instant');
424+
}
425+
return _convertItemRawStateToInstant(when.rawState);
426+
} else if (when instanceof ohItem) {
427+
log.debug('toInstant: Converting raw Item ' + when);
428+
return _convertItemRawStateToInstant(when.getState());
429+
}
430+
431+
// Unsupported
432+
throw Error('"' + when + '" is an unsupported type for conversion to time.Instant');
433+
}
434+
435+
/**
436+
* Moves the date portion of the date time to today, accounting for DST
437+
*
438+
* @returns {time.ZonedDateTime} a new {@link time.ZonedDateTime} with today's date
439+
*/
440+
time.ZonedDateTime.prototype.toToday = function () {
441+
const now = time.ZonedDateTime.now();
442+
return this.withYear(now.year())
443+
.withMonth(now.month())
444+
.withDayOfMonth(now.dayOfMonth());
445+
};
446+
334447
/**
335448
* Tests whether `this` time.ZonedDateTime is before the passed in timestamp.
336449
* However, the function only compares the time portion of both, ignoring the date portion.
@@ -497,6 +610,7 @@ module.exports = {
497610
javaInstantToJsInstant,
498611
javaZDTToJsZDT,
499612
toZDT,
613+
toInstant,
500614
_parseString,
501615
_parseISO8601
502616
};

types/time.d.ts

+19
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,24 @@ 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 ISO Instant
129+
* - RFC (output from a Java Instant.toString()): parsed to time.ZonedDateTime
130+
* @memberof time
131+
* @param {*} [when] any of the types discussed above
132+
* @returns {time.Instant}
133+
* @throws error if the type, format, or contents of when are not supported
134+
*/
135+
declare function toInstant(when?: any): time.Instant;
117136
/**
118137
* Parses the passed in string based on it's format and converts it to a ZonedDateTime.
119138
* If no timezone is specified, the configured timezone is used.

types/time.d.ts.map

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

0 commit comments

Comments
 (0)