jupyter-ijavascript-utils 1.55.0 → 1.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DOCS.md +1 -0
- package/README.md +1 -0
- package/package.json +1 -1
- package/src/date.js +133 -41
- package/src/object.js +27 -4
package/DOCS.md
CHANGED
|
@@ -74,6 +74,7 @@ Give it a try here:
|
|
|
74
74
|
[](https://mybinder.org/v2/gh/paulroth3d/jupyter-ijavascript-utils/main?labpath=example.ipynb)
|
|
75
75
|
|
|
76
76
|
## What's New
|
|
77
|
+
* 1.56 - #84 (object.renamePropertiesFromList), #82 (date.getWeekday)
|
|
77
78
|
* 1.55 - #76, #77, #74, #78
|
|
78
79
|
* 1.54 - additional Date logic, and formatting. #70 #71 #72
|
|
79
80
|
* 1.53 - additional docs and examples for {@link module:color|color/colour} package.
|
package/README.md
CHANGED
|
@@ -54,6 +54,7 @@ This is not intended to be the only way to accomplish many of these tasks, and a
|
|
|
54
54
|

|
|
55
55
|
|
|
56
56
|
# What's New
|
|
57
|
+
* 1.56 - #84 (object.renamePropertiesFromList), #82 (date.getWeekday)
|
|
57
58
|
* 1.55 - #76, #77, #74, #78
|
|
58
59
|
* 1.54 - additional Date logic, and formatting. #70 #71 #72
|
|
59
60
|
* 1.53 - additional docs and examples for color/colour package.
|
package/package.json
CHANGED
package/src/date.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* * {@link module:date.parse|date.parse(String)} - parse a date and throw an exception if it is not a valid date
|
|
9
9
|
* * Timezones
|
|
10
10
|
* * {@link module:date.toLocalISO|date.toLocalISO} - prints in 8601 format with timezone offset based on a tz entry - like america/chicago
|
|
11
|
+
* * {@link module:date.localISOFormatter|date.localISOFormatter} - prints in 8601 format - slightly improved performance for large scale use
|
|
11
12
|
* * {@link module:date.getTimezoneOffset|date.getTimezoneOffset(String)} - gets the number of milliseconds offset for a given timezone
|
|
12
13
|
* * {@link module:date.correctForTimezone|date.correctForTimezone(Date, String)} - meant to correct a date already off from UTC to the correct time
|
|
13
14
|
* * {@link module:date.epochShift|date.epochShift(Date, String)} - offsets a date from UTC to a given time amount
|
|
@@ -180,12 +181,22 @@ module.exports.durationLong = function durationLong(epochDifference) {
|
|
|
180
181
|
return `${signStr}${days} days, ${hours} hours, ${minutes} minutes, ${seconds}.${milli} seconds`;
|
|
181
182
|
};
|
|
182
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Function that is passed a date for formatting
|
|
186
|
+
*
|
|
187
|
+
* @callback dateFormatter
|
|
188
|
+
* @param {Date} dateToFormat - the date to format
|
|
189
|
+
*/
|
|
190
|
+
|
|
183
191
|
/**
|
|
184
192
|
* @typedef {Object} TimezoneEntry
|
|
185
193
|
* @property {String} tz - the name of the timezone
|
|
186
194
|
* @property {Function} formatter - formats a date to that local timezone
|
|
187
195
|
* @property {Number} epoch - the difference in milliseconds from that tz to UTC
|
|
188
196
|
* @property {String} offset - ISO format for how many hours and minutes offset to UTC '+|-' HH:MMM
|
|
197
|
+
* @property {dateFormatter} toLocalISO - formatter function that formats a date to local ISO
|
|
198
|
+
* @property {dateFormatter} toLocalISOWeekday - formatter function that formats a date to local ISO + weekday
|
|
199
|
+
* @property {dateFormatter} getWeekday - formatter function that determines the day of week for a date
|
|
189
200
|
*/
|
|
190
201
|
|
|
191
202
|
/**
|
|
@@ -220,6 +231,12 @@ module.exports.getTimezoneEntry = function getTimezoneEntry(timezoneStr) {
|
|
|
220
231
|
timeZone: cleanTz
|
|
221
232
|
});
|
|
222
233
|
|
|
234
|
+
const dayOfWeekFormat = new Intl.DateTimeFormat('en-us', {
|
|
235
|
+
weekday: 'short',
|
|
236
|
+
timeZone: cleanTz
|
|
237
|
+
});
|
|
238
|
+
const getWeekday = (date) => dayOfWeekFormat.format(date);
|
|
239
|
+
|
|
223
240
|
const getOffset = (dateValue) => {
|
|
224
241
|
const dm = dtFormat.formatToParts(dateValue)
|
|
225
242
|
.filter(({ type }) => type !== 'literal')
|
|
@@ -258,7 +275,10 @@ module.exports.getTimezoneEntry = function getTimezoneEntry(timezoneStr) {
|
|
|
258
275
|
const diffMinutes = remainder.value;
|
|
259
276
|
const offset = `${diffSign}${DateUtils.padTime(diffHours)}:${DateUtils.padTime(diffMinutes)}`;
|
|
260
277
|
|
|
261
|
-
const
|
|
278
|
+
const toLocalISO = (date) => `${formatter(date)}${offset}`;
|
|
279
|
+
const toLocalISOWeekday = (date) => `${formatter(date)}${offset} - ${getWeekday(date)}`;
|
|
280
|
+
|
|
281
|
+
const result = ({ tz: cleanTz, formatter, toLocalISO, toLocalISOWeekday, getWeekday, epoch: diff, offset });
|
|
262
282
|
|
|
263
283
|
DateUtils.timezoneOffsetMap.set(cleanTz, result);
|
|
264
284
|
|
|
@@ -381,27 +401,14 @@ module.exports.correctForOtherTimezone = function correctForTimezones(date, sour
|
|
|
381
401
|
*
|
|
382
402
|
* Use this if you somehow have a date that needs to be shifted by the timezone offset.
|
|
383
403
|
*
|
|
384
|
-
*
|
|
385
|
-
* you can use this to correct for "local dates" but are in another timezone than you are in.
|
|
386
|
-
*
|
|
387
|
-
* (For example, you got a local date for 2:15 PM EST, but your current computer is in CST)
|
|
404
|
+
* For example, if you have a time that is already in GMT, and want the date shifted by a timezone.
|
|
388
405
|
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
* * Parse the date assuming local timezone
|
|
392
|
-
* * Parse the date using [ISO 8601 formats](https://www.iso.org/iso-8601-date-and-time-format.html)
|
|
393
|
-
* * This option DOES provide an option for providing a timezone offset (ex: `-0500`)
|
|
394
|
-
*
|
|
395
|
-
* Since this is the opposite of {@link module:date.correctForTimezone|date.correctForTimezone}, this can be useful.
|
|
396
|
-
*
|
|
397
|
-
* But most likely, you'd like to use either:
|
|
398
|
-
*
|
|
399
|
-
* * {@link module:date.correctForTimezone|date.correctForTimezone} or
|
|
400
|
-
* * {@link module:date.correctForTimezones|date.correctForTimezones}.
|
|
406
|
+
* This is used internally for {@link module:date.correctForOtherTimezone|date.correctForOtherTimezone}
|
|
407
|
+
* if local dates are provided - but for a different timezone you yourself are not in.
|
|
401
408
|
*
|
|
402
409
|
* ---
|
|
403
410
|
*
|
|
404
|
-
* Epoch shift a date, so the utcDate is no longer correct,
|
|
411
|
+
* Epoch shift changes the internals of a JavaScript date, so the utcDate is no longer correct,
|
|
405
412
|
* but many other functions behave closer to expected.
|
|
406
413
|
*
|
|
407
414
|
* Once you epoch shift the date, then time stored in the date is incorrect (because it always points to GMT)
|
|
@@ -410,20 +417,8 @@ module.exports.correctForOtherTimezone = function correctForTimezones(date, sour
|
|
|
410
417
|
*
|
|
411
418
|
* See {@link https://stackoverflow.com/a/15171030|here why this might not be what you want}
|
|
412
419
|
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
415
|
-
* Sometimes though, some libraries use the "getMonth()", "getDate()" of the date, and do not support using timezones.
|
|
416
|
-
*
|
|
417
|
-
* That is when this shines.
|
|
418
|
-
*
|
|
419
|
-
* ```
|
|
420
|
-
* timeStamp = 1738437341000;
|
|
421
|
-
* d = new Date(timeStamp);
|
|
422
|
-
* d.toIsoString(); // 2025-02-01T19:15:41.000Z
|
|
423
|
-
* `current time is: ${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}` // 'current time is: 2025-2-1'
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
* ```
|
|
420
|
+
* * {@link module:date.correctForTimezone|date.correctForTimezone} or
|
|
421
|
+
* * {@link module:date.correctForTimezones|date.correctForTimezones}.
|
|
427
422
|
*
|
|
428
423
|
* @param {Date} date - date to shift
|
|
429
424
|
* @param {String} timezoneStr - the tz database name of the timezone
|
|
@@ -456,13 +451,66 @@ module.exports.epochShift = function epochShift(date, timezoneStr) {
|
|
|
456
451
|
* utils.date.toLocalISO(d, 'europe/paris'); // '2024-12-27T14:30:00.000+01:00'
|
|
457
452
|
* ```
|
|
458
453
|
*
|
|
454
|
+
* Sometimes it is helpful to have the weekday to make sense of things
|
|
455
|
+
*
|
|
456
|
+
* ```
|
|
457
|
+
* utils.date.toLocalISO(d, 'america/Chicago', true); // '2024-12-27T07:30:00.000-06:00 FRI'
|
|
458
|
+
* utils.date.toLocalISO(d, 'europe/paris', true); // '2024-12-27T14:30:00.000+01:00 FRI'
|
|
459
|
+
* ```
|
|
460
|
+
*
|
|
459
461
|
* @param {Date} date - date to print
|
|
460
462
|
* @param {String} timezoneStr - the tz database name of the timezone
|
|
463
|
+
* @param {Boolean} [includeWeekday=false] - whether to include the weekday
|
|
464
|
+
* @see {@link module:date.localISOFormatter|date.localISOFormatter} - if you're converting to string frequently
|
|
461
465
|
* @returns {String} - ISO format with timezone offset
|
|
462
466
|
*/
|
|
463
|
-
module.exports.toLocalISO = function toLocalISO(date, timezoneStr) {
|
|
464
|
-
|
|
465
|
-
|
|
467
|
+
module.exports.toLocalISO = function toLocalISO(date, timezoneStr, includeWeekday = false) {
|
|
468
|
+
if (includeWeekday) {
|
|
469
|
+
return DateUtils.getTimezoneEntry(timezoneStr).toLocalISOWeekday(date);
|
|
470
|
+
}
|
|
471
|
+
return DateUtils.getTimezoneEntry(timezoneStr).toLocalISO(date);
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* If repeatedly asking for a local time, use this method instead.
|
|
476
|
+
*
|
|
477
|
+
* ```
|
|
478
|
+
* myDate = new Date('2025-01-15T06:00:00.000Z');
|
|
479
|
+
* centralFormatter = utils.date.localISOFormatter('us/central');
|
|
480
|
+
* centralFormatter(myDate); // '2025-01-15T00:00:00.000Z'
|
|
481
|
+
* ```
|
|
482
|
+
*
|
|
483
|
+
* as opposed to
|
|
484
|
+
*
|
|
485
|
+
* ```
|
|
486
|
+
* myDate = new Date('2025-01-15T06:00:00.000Z');
|
|
487
|
+
* utils.date.toLocalISO(myDate, 'us/central'); // '2025-01-15T00:00:00.000Z'
|
|
488
|
+
* ```
|
|
489
|
+
*
|
|
490
|
+
* @param {String} timezoneStr
|
|
491
|
+
* @returns {dateFormatter} - (date) => {String} 'yyyy-mm-ddThh:mm:ss.MMM[+-]TZOFFSET'
|
|
492
|
+
* @see {@link module:date.toLocalISO|date.toLocalISO}
|
|
493
|
+
*/
|
|
494
|
+
module.exports.localISOFormatter = function localISOFormatter(timezoneStr, includeWeekday = false) {
|
|
495
|
+
if (includeWeekday) {
|
|
496
|
+
return DateUtils.getTimezoneEntry(timezoneStr).toLocalISOWeekday;
|
|
497
|
+
}
|
|
498
|
+
return DateUtils.getTimezoneEntry(timezoneStr).toLocalISO;
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Determines the weekday of a date
|
|
503
|
+
* @param {Date} date - date to print
|
|
504
|
+
* @param {String} timezoneStr - the tz database name of the timezone
|
|
505
|
+
* @returns {String} - Currently returns `en-us` formatted day of week of a date
|
|
506
|
+
* @see {@link module:date.toLocalISO|date.toLocalISO}
|
|
507
|
+
* @example
|
|
508
|
+
* date = new Date('2025-01-15T06:00:00.000Z');
|
|
509
|
+
* utils.date.getWeekday(date, 'us/pacific'); // Tue
|
|
510
|
+
* utils.date.getWeekday(date, 'us/eastern'); // Wed
|
|
511
|
+
*/
|
|
512
|
+
module.exports.getWeekday = function weekdayFormatter(date, timezoneStr) {
|
|
513
|
+
return DateUtils.getTimezoneEntry(timezoneStr).getWeekday(date);
|
|
466
514
|
};
|
|
467
515
|
|
|
468
516
|
module.exports.toIsoStringNoTimezone = function toIsoStringNoTimezone(date) {
|
|
@@ -771,12 +819,19 @@ class DateRange {
|
|
|
771
819
|
*/
|
|
772
820
|
endDate;
|
|
773
821
|
|
|
822
|
+
/**
|
|
823
|
+
* Data attached to the DateTime
|
|
824
|
+
* @type {any}
|
|
825
|
+
*/
|
|
826
|
+
data;
|
|
827
|
+
|
|
774
828
|
/**
|
|
775
829
|
* @param {Date|String} startDate - the starting date
|
|
776
830
|
* @param {Date|String} endDate - the ending date
|
|
831
|
+
* @param {any} [data] - any data to store
|
|
777
832
|
*/
|
|
778
|
-
constructor(startDate, endDate) {
|
|
779
|
-
this.reinitialize(startDate, endDate);
|
|
833
|
+
constructor(startDate, endDate, data = null) {
|
|
834
|
+
this.reinitialize(startDate, endDate, data);
|
|
780
835
|
}
|
|
781
836
|
|
|
782
837
|
/**
|
|
@@ -794,6 +849,31 @@ class DateRange {
|
|
|
794
849
|
* // {start: 2025-03-01TT00:00:00, end: 2025-04-01TT00:00:00}]
|
|
795
850
|
* ```
|
|
796
851
|
*
|
|
852
|
+
* Often though, we want to remember something about the DateRange,
|
|
853
|
+
* like which dates that it collected.
|
|
854
|
+
*
|
|
855
|
+
* ```
|
|
856
|
+
* arrayGenerator = function() { return [] };
|
|
857
|
+
* rangeList = utils.DateRange.fromList(dates, arrayGenerator);
|
|
858
|
+
* // [{start: 2025-01-01T00:00:00, end: 2025-02-01TT00:00:00},
|
|
859
|
+
* // {start: 2025-02-01TT00:00:00, end: 2025-03-01TT00:00:00},
|
|
860
|
+
* // {start: 2025-03-01TT00:00:00, end: 2025-04-01TT00:00:00}]
|
|
861
|
+
*
|
|
862
|
+
* dates.forEach((date) => rangeList
|
|
863
|
+
* .find(rl => rl.contains(date))
|
|
864
|
+
* .data.push(date)
|
|
865
|
+
* );
|
|
866
|
+
*
|
|
867
|
+
* rangeList
|
|
868
|
+
* .map(rl => `${rl.toString()}: has ${rl.data.length}`)
|
|
869
|
+
* .join('\n');
|
|
870
|
+
*
|
|
871
|
+
* // 2025-01-01T00:00:00.000Z to 2025-02-01T00:00:00.000Z: has 2
|
|
872
|
+
* // 2025-02-01T00:00:00.000Z to 2025-03-01T00:00:00.000Z: has 1
|
|
873
|
+
* // 2025-03-01T00:00:00.000Z to 2025-04-01T00:00:00.000Z: has 1
|
|
874
|
+
*
|
|
875
|
+
* ```
|
|
876
|
+
*
|
|
797
877
|
* (Note: you can also use {@link module:date.arrange|date.arrange} or
|
|
798
878
|
* {@link module:date.generateDateSequence|date.generateDateSequence}
|
|
799
879
|
* to come up with the list of those dates)
|
|
@@ -802,18 +882,27 @@ class DateRange {
|
|
|
802
882
|
* the simplest is to remove the dates from the resulting list.)
|
|
803
883
|
*
|
|
804
884
|
* @param {Date[]} dateList - list of dates
|
|
885
|
+
* @param {Function} [dataCreationFn] - optional generator for data to be stored in each DataRange in the sequence
|
|
805
886
|
* @returns {DateRange[]} - list of dateList.length-1 dateRanges,
|
|
806
887
|
* where the end of the firstRange is the start of the next.
|
|
807
888
|
* @see {@link module:date.arrange|date.arrange} - to create dates by adding a value multiple times
|
|
808
889
|
* @see {@link module:date.generateDateSequence|date.generateDateSequence} - to create dates between a start and an end date
|
|
809
890
|
*/
|
|
810
|
-
static fromList(dateSequence) {
|
|
891
|
+
static fromList(dateSequence, dataCreationFn) {
|
|
811
892
|
if (dateSequence.length < 2) return [];
|
|
812
893
|
|
|
813
894
|
const results = new Array(dateSequence.length - 2);
|
|
814
|
-
|
|
815
|
-
|
|
895
|
+
|
|
896
|
+
if (dataCreationFn) {
|
|
897
|
+
for (let i = 0; i < dateSequence.length - 1; i += 1) {
|
|
898
|
+
results[i] = new DateRange(dateSequence[i], dateSequence[i + 1], dataCreationFn());
|
|
899
|
+
}
|
|
900
|
+
} else {
|
|
901
|
+
for (let i = 0; i < dateSequence.length - 1; i += 1) {
|
|
902
|
+
results[i] = new DateRange(dateSequence[i], dateSequence[i + 1]);
|
|
903
|
+
}
|
|
816
904
|
}
|
|
905
|
+
|
|
817
906
|
return results;
|
|
818
907
|
}
|
|
819
908
|
|
|
@@ -824,8 +913,9 @@ class DateRange {
|
|
|
824
913
|
*
|
|
825
914
|
* @param {Date|String} startDate - the starting date
|
|
826
915
|
* @param {Date|String} endDate - the ending date
|
|
916
|
+
* @param {any} [data] - any data to store
|
|
827
917
|
*/
|
|
828
|
-
reinitialize(startDate, endDate) {
|
|
918
|
+
reinitialize(startDate, endDate, data = null) {
|
|
829
919
|
const cleanStart = startDate instanceof Date
|
|
830
920
|
? startDate
|
|
831
921
|
: new Date(Date.parse(startDate));
|
|
@@ -840,6 +930,8 @@ class DateRange {
|
|
|
840
930
|
this.startDate = cleanStart;
|
|
841
931
|
this.endDate = cleanEnd;
|
|
842
932
|
}
|
|
933
|
+
|
|
934
|
+
this.data = data;
|
|
843
935
|
}
|
|
844
936
|
|
|
845
937
|
/**
|
package/src/object.js
CHANGED
|
@@ -413,9 +413,32 @@ module.exports.cleanPropertyName = function cleanPropertyName(property) {
|
|
|
413
413
|
return cleanProperty;
|
|
414
414
|
};
|
|
415
415
|
|
|
416
|
-
|
|
416
|
+
/**
|
|
417
|
+
* Renames properties on an object with a list of original keys and new keys.
|
|
418
|
+
*
|
|
419
|
+
* For example:
|
|
420
|
+
*
|
|
421
|
+
* ```
|
|
422
|
+
* myData = [{ _time: '...', 'series001': 1, 'series002': 2 }];
|
|
423
|
+
*
|
|
424
|
+
* originalKeys = utils.object.keys(myData);
|
|
425
|
+
* // ['series001', 'series002'];
|
|
426
|
+
*
|
|
427
|
+
* myMap = new Map([['series001': 'Alpha'], ['series002', 'Bravo']]);
|
|
428
|
+
* newKeys = utils.format.replaceStrings(originalKeys, myMap);
|
|
429
|
+
* // ['Alpha', 'Bravo'];
|
|
430
|
+
*
|
|
431
|
+
* utils.object.renamePropertiesFromList(myData, originalKeys, newKeys);
|
|
432
|
+
* // [{ _time: '...', 'Alpha': 1, 'Bravo': 2 }];
|
|
433
|
+
*
|
|
434
|
+
* @param {Object[]} objects - objects to reassign - likely from a CSV
|
|
435
|
+
* @param {String[]} originalKeys - list of keys to change FROM
|
|
436
|
+
* @param {String[]} updatedKeys - list of keys to change TO
|
|
437
|
+
* @returns {Object[]}
|
|
438
|
+
*/
|
|
439
|
+
module.exports.renamePropertiesFromList = function renamePropertiesFromList(object, originalKeys, targetKeys) {
|
|
417
440
|
const result = { ...object };
|
|
418
|
-
originalKeys.forEach((originalKey, index) => {
|
|
441
|
+
Array.from(originalKeys).forEach((originalKey, index) => {
|
|
419
442
|
const targetKey = targetKeys[index];
|
|
420
443
|
if (targetKey !== originalKey) {
|
|
421
444
|
result[targetKey] = result[originalKey];
|
|
@@ -440,10 +463,10 @@ module.exports.renameProperties = function renameProperties(objects, propertyTra
|
|
|
440
463
|
|
|
441
464
|
if (Array.isArray(objects)) {
|
|
442
465
|
return objects.map(
|
|
443
|
-
(object) =>
|
|
466
|
+
(object) => ObjectUtils.renamePropertiesFromList(object, originalKeys, targetKeys)
|
|
444
467
|
);
|
|
445
468
|
}
|
|
446
|
-
return
|
|
469
|
+
return ObjectUtils.renamePropertiesFromList(objects, originalKeys, targetKeys);
|
|
447
470
|
};
|
|
448
471
|
|
|
449
472
|
const collapseSpecificObject = function collapseSpecificObject(sourceObj, targetObj, depth) {
|