@bbn/bbn 2.0.151 → 2.0.153

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.
@@ -15,8 +15,7 @@ export default class bbnDtDateTime extends bbnDt {
15
15
  value = new Temporal.PlainDateTime(y.getFullYear(), y.getMonth() + 1, y.getDate(), y.getHours(), y.getMinutes(), y.getSeconds(), y.getMilliseconds());
16
16
  }
17
17
  else if (typeof y === 'number') {
18
- const d = new Date(y);
19
- value = new Temporal.PlainDateTime(d.getFullYear(), d.getMonth() + 1, d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
18
+ value = Temporal.Instant.fromEpochMilliseconds(y).toZonedDateTimeISO(bbnDt.systemTimeZoneId).toPlainDateTime();
20
19
  }
21
20
  else {
22
21
  throw new Error('Invalid value for bbnDtDateTime');
@@ -30,7 +29,7 @@ export default class bbnDtDateTime extends bbnDt {
30
29
  }
31
30
  format(format) {
32
31
  // long
33
- if (format === true) {
32
+ if ([true, 'long', 'full'].includes(format)) {
34
33
  format = getRow(bbn.dt.locales.date, { year: 'numeric', month: 'long', day: 'long', weekday: 'long', hour: '2-digit', minute: '2-digit', second: undefined }).pattern;
35
34
  }
36
35
  // short
@@ -44,7 +43,7 @@ export default class bbnDtDateTime extends bbnDt {
44
43
  return '';
45
44
  }
46
45
  const date = new Date(this.year(), this.month() - 1, this.day(), this.hour(), this.minute(), this.second());
47
- const opt = Object.assign(Object.assign({ year: 'numeric', month: long ? 'long' : 'numeric', day: 'numeric' }, (weekday ? { weekday: (long ? 'long' : 'short') } : {})), (withTime ? { hour: (long ? '2-digit' : 'numeric'), minute: '2-digit' } : {}));
46
+ const opt = Object.assign(Object.assign({ year: 'numeric', month: long ? 'long' : '2-digit', day: '2-digit' }, (weekday ? { weekday: (long ? 'long' : 'short') } : {})), (withTime ? { hour: (long ? '2-digit' : 'numeric'), minute: '2-digit' } : {}));
48
47
  const d = new Intl.DateTimeFormat([bbn.env.lang, ...navigator.languages], opt);
49
48
  return d.format(date);
50
49
  }
@@ -7,12 +7,13 @@ export declare abstract class bbnDt<TValue extends bbnDtTemporal> {
7
7
  readonly __bbnNoData = true;
8
8
  readonly __isBbnDt = true;
9
9
  isValid: boolean;
10
+ defaultValue: TimeProperties | undefined;
10
11
  constructor(value?: TValue);
11
12
  setValid(isValid: boolean): void;
12
13
  get value(): TValue | undefined;
13
14
  get hasFullDate(): boolean;
14
15
  /** System time zone ID (e.g. "Europe/Rome") */
15
- private static readonly systemTimeZoneId;
16
+ static readonly systemTimeZoneId: string;
16
17
  /**
17
18
  * Convert this.value (PlainDate, PlainTime, PlainDateTime, YearMonth,
18
19
  * MonthDay, ZonedDateTime) into epoch milliseconds, using the system
@@ -51,10 +52,8 @@ export declare abstract class bbnDt<TValue extends bbnDtTemporal> {
51
52
  isAfterOrSame(other: any, unit?: string): boolean;
52
53
  isSame(other: any, unit?: string): boolean;
53
54
  equals(other: any): boolean;
54
- toJSON(): {
55
- kind: bbnDtKind;
56
- value: string;
57
- };
55
+ toJSON(): string;
56
+ timezone(d: any): string | bbnDt<any>;
58
57
  toString(): string;
59
58
  year(v?: any): number | bbnDt<any>;
60
59
  month(v?: any): number | bbnDt<any>;
@@ -62,6 +61,7 @@ export declare abstract class bbnDt<TValue extends bbnDtTemporal> {
62
61
  hour(v?: any): number | bbnDt<any>;
63
62
  minute(v?: any): number | bbnDt<any>;
64
63
  second(v?: any): number | bbnDt<any>;
64
+ millisecond(v?: any): number | bbnDt<any>;
65
65
  weekday(v?: any, past?: any): number | bbnDt<any>;
66
66
  date(v?: any): string | bbnDt<any>;
67
67
  datetime(v?: any): string | bbnDt<any>;
@@ -87,6 +87,7 @@ export declare abstract class bbnDt<TValue extends bbnDtTemporal> {
87
87
  get mm(): string;
88
88
  get I(): string;
89
89
  get SS(): string;
90
+ get SSS(): string;
90
91
  get S(): string;
91
92
  get WW(): string;
92
93
  get W(): string;
@@ -110,8 +111,8 @@ export declare abstract class bbnDt<TValue extends bbnDtTemporal> {
110
111
  setWeekday(weekday: number | string, past?: boolean, locale?: string): bbnDt<any>;
111
112
  diff(date: any, unit?: string, abs?: boolean): number;
112
113
  guessUnit(valueInMs: number): string | null;
113
- fromNow(unit?: string): any;
114
- fromDate(date: any, unit?: string): string;
114
+ fromNow(unit?: string): string;
115
+ fromDate(date: bbnDt<any>, unit?: string): string;
115
116
  startOf(unit?: string): bbnDt<any>;
116
117
  endOf(unit?: string): bbnDt<any>;
117
118
  clone(): bbnDt<any>;
@@ -51,6 +51,20 @@ export class bbnDt {
51
51
  configurable: true
52
52
  });
53
53
  __classPrivateFieldSet(this, _bbnDt_value, value, "f");
54
+ const d = new Date();
55
+ Object.defineProperty(this, 'defaultValue', {
56
+ value: {
57
+ year: d.getFullYear(),
58
+ month: d.getMonth() + 1,
59
+ day: d.getDate(),
60
+ hour: d.getHours(),
61
+ minute: d.getMinutes(),
62
+ second: d.getSeconds(),
63
+ millisecond: d.getMilliseconds()
64
+ },
65
+ writable: false,
66
+ configurable: false
67
+ });
54
68
  }
55
69
  setValid(isValid) {
56
70
  Object.defineProperty(this, 'isValid', {
@@ -77,52 +91,57 @@ export class bbnDt {
77
91
  * - month-day → that month/day in *this year* at 00:00 in system tz
78
92
  */
79
93
  toEpochMs() {
94
+ if (this.kind === 'zoned') {
95
+ const v = this.value;
96
+ return v.toInstant().epochMilliseconds;
97
+ }
80
98
  const tz = bbnDt.systemTimeZoneId;
99
+ let iso;
81
100
  switch (this.kind) {
82
- case 'zoned': {
83
- const v = this.value;
84
- return v.toInstant().epochMilliseconds;
85
- }
86
101
  case 'dateTime': {
87
102
  const v = this.value;
88
103
  // RFC 9557 string: "YYYY-MM-DDTHH:mm:ss[Europe/Rome]"
89
- const iso = `${v.toString()}[${tz}]`;
90
- const zdt = Temporal.ZonedDateTime.from(iso);
91
- return zdt.toInstant().epochMilliseconds;
104
+ return v.toZonedDateTime(tz).toInstant().epochMilliseconds;
105
+ iso = `${v.toString()}[${tz}]`;
92
106
  }
93
107
  case 'date': {
94
108
  const d = this.value;
109
+ return d.toZonedDateTime({
110
+ timeZone: tz,
111
+ plainTime: {
112
+ hour: this.defaultValue.hour,
113
+ minute: this.defaultValue.minute,
114
+ second: this.defaultValue.second,
115
+ millisecond: this.defaultValue.millisecond
116
+ }
117
+ }).toInstant().epochMilliseconds;
118
+ const today = new Date();
95
119
  // "YYYY-MM-DDT00:00[Europe/Rome]"
96
- const iso = `${d.toString()}T00:00[${tz}]`;
97
- const zdt = Temporal.ZonedDateTime.from(iso);
98
- return zdt.toInstant().epochMilliseconds;
120
+ iso = `${d.toString()}T00:00[${tz}]`;
99
121
  }
100
122
  case 'time': {
101
123
  const t = this.value;
102
124
  const today = Temporal.Now.plainDateISO();
103
125
  // "YYYY-MM-DDTHH:mm[:ss][Europe/Rome]"
104
- const iso = `${today.toString()}T${t.toString()}[${tz}]`;
105
- const zdt = Temporal.ZonedDateTime.from(iso);
106
- return zdt.toInstant().epochMilliseconds;
126
+ iso = `${today.toString()}T${t.toString()}[${tz}]`;
107
127
  }
108
128
  case 'yearMonth': {
109
129
  const ym = this.value;
110
130
  const d = ym.toPlainDate({ day: 1 }); // first day of month
111
- const iso = `${d.toString()}T00:00[${tz}]`;
112
- const zdt = Temporal.ZonedDateTime.from(iso);
113
- return zdt.toInstant().epochMilliseconds;
131
+ iso = `${d.toString()}T00:00[${tz}]`;
114
132
  }
115
133
  case 'monthDay': {
116
134
  const md = this.value;
117
135
  const today = Temporal.Now.plainDateISO();
118
136
  const d = md.toPlainDate({ year: today.year }); // current year
119
- const iso = `${d.toString()}T00:00[${tz}]`;
120
- const zdt = Temporal.ZonedDateTime.from(iso);
121
- return zdt.toInstant().epochMilliseconds;
137
+ iso = `${d.toString()}T00:00[${tz}]`;
122
138
  }
123
- default:
124
- throw new Error(`Unsupported kind '${this.kind}' in toEpochMs`);
125
139
  }
140
+ if (iso) {
141
+ const zdt = Temporal.ZonedDateTime.from(iso);
142
+ return zdt.toInstant().epochMilliseconds; // - Math.round(zdt.offsetNanoseconds / 1000000);
143
+ }
144
+ throw new Error(`Unsupported kind '${this.kind}' in toEpochMs`);
126
145
  }
127
146
  /**
128
147
  * "Now" value in the same *kind* as this instance.
@@ -326,13 +345,48 @@ export class bbnDt {
326
345
  }
327
346
  // ---- Serialization ----
328
347
  toJSON() {
329
- return {
330
- kind: this.kind,
331
- value: String(this.value)
332
- };
348
+ if ('toZonedDateTime' in __classPrivateFieldGet(this, _bbnDt_value, "f")) {
349
+ const zdt = __classPrivateFieldGet(this, _bbnDt_value, "f").toZonedDateTime(bbnDt.systemTimeZoneId);
350
+ const zdt2 = zdt.withTimeZone('UTC');
351
+ return bbn.dt(zdt2).format('YYYY-MM-DDTHH:II:SS.SSS') + 'Z';
352
+ }
353
+ if (this.kind === 'zoned') {
354
+ return this.timezone('America/New_York').format('YYYY-MM-DDTHH:II:SS.SSS') + 'Z';
355
+ }
356
+ }
357
+ timezone(d) {
358
+ switch (this.kind) {
359
+ case 'zoned':
360
+ if (d === undefined) {
361
+ const v = this.value;
362
+ return v.timeZoneId;
363
+ }
364
+ else {
365
+ const v = this.value;
366
+ const newZdt = v.withTimeZone(d);
367
+ return bbn.dt(newZdt);
368
+ }
369
+ break;
370
+ case 'dateTime':
371
+ if (d === undefined) {
372
+ const v = this.value;
373
+ const iso = `${v.toString()}[${bbnDt.systemTimeZoneId}]`;
374
+ const zdt = Temporal.ZonedDateTime.from(iso);
375
+ return zdt.timeZoneId;
376
+ }
377
+ else {
378
+ const v = this.value;
379
+ const iso = `${v.toString()}[${d}]`;
380
+ const zdt = Temporal.ZonedDateTime.from(iso);
381
+ return bbn.dt(zdt);
382
+ }
383
+ break;
384
+ default:
385
+ throw new Error(`timezone() is not supported for kind '${this.kind}'`);
386
+ }
333
387
  }
334
388
  toString() {
335
- return String(this.value);
389
+ return new Date(this.valueOf()).toString();
336
390
  }
337
391
  year(v) {
338
392
  if (!this.value) {
@@ -421,6 +475,22 @@ export class bbnDt {
421
475
  }
422
476
  return this.value.second;
423
477
  }
478
+ millisecond(v) {
479
+ if (!this.value) {
480
+ return undefined;
481
+ }
482
+ if (!('millisecond' in this.value)) {
483
+ if (this.hasFullDate) {
484
+ return 0;
485
+ }
486
+ throw new Error('millisecond() is not supported for this type');
487
+ }
488
+ if ((v !== undefined) && !(v instanceof Event)) {
489
+ const d = this.value.with({ millisecond: v });
490
+ return new this.constructor(d);
491
+ }
492
+ return this.value.millisecond;
493
+ }
424
494
  weekday(v, past) {
425
495
  if (!this.value) {
426
496
  return undefined;
@@ -470,7 +540,7 @@ export class bbnDt {
470
540
  if (!this.value) {
471
541
  return '';
472
542
  }
473
- return bbn.dt.locales.formatters.short.format(new Date(this.toEpochMs()));
543
+ return bbn.dt.locales.formatters[long ? 'long' : 'numeric'].format(new Date(this.toEpochMs()));
474
544
  }
475
545
  ftime(withSeconds = true) {
476
546
  if (!this.value) {
@@ -482,10 +552,25 @@ export class bbnDt {
482
552
  if (!this.value) {
483
553
  return undefined;
484
554
  }
485
- if (!('weekOfYear' in this.value)) {
486
- throw new Error('week() is not supported for this type');
487
- }
488
- return this.value.weekOfYear;
555
+ const year = this.year();
556
+ // Normalize to UTC midnight to avoid DST issues
557
+ const d = new Date(Date.UTC(year, this.month() - 1, this.day()));
558
+ const jan1 = new Date(Date.UTC(year, 0, 1));
559
+ const MS_PER_DAY = 24 * 60 * 60 * 1000;
560
+ const diffDays = Math.floor((d.getTime() - jan1.getTime()) / MS_PER_DAY); // 0 for Jan 1
561
+ const jan1Dow = jan1.getUTCDay(); // 0–6, Sunday=0
562
+ // How many days long is week 1?
563
+ // Week 1 starts on Jan 1 and ends the day before the first `weekStart` after Jan 1.
564
+ let offset = (bbn.dt.locales.weekStart - jan1Dow + 7) % 7;
565
+ const firstWeekLength = offset === 0 ? 7 : offset;
566
+ // Still in the first (possibly partial) week
567
+ if (diffDays < firstWeekLength) {
568
+ return 1;
569
+ }
570
+ // Remaining days after week 1
571
+ const remainingDays = diffDays - firstWeekLength;
572
+ // Each full block of 7 days is one more week
573
+ return 2 + Math.floor(remainingDays / 7);
489
574
  }
490
575
  get YYYY() {
491
576
  if ('year' in this.value) {
@@ -567,7 +652,7 @@ export class bbnDt {
567
652
  return h < 10 ? '0' + h.toString() : h.toString();
568
653
  }
569
654
  else if (this.hasFullDate) {
570
- return '00';
655
+ return this.defaultValue.hour.toString().padStart(2, '0');
571
656
  }
572
657
  return undefined;
573
658
  }
@@ -586,7 +671,7 @@ export class bbnDt {
586
671
  return i < 10 ? '0' + i.toString() : i.toString();
587
672
  }
588
673
  else if (this.hasFullDate) {
589
- return '00';
674
+ return this.defaultValue.minute.toString().padStart(2, '0');
590
675
  }
591
676
  return undefined;
592
677
  }
@@ -596,7 +681,7 @@ export class bbnDt {
596
681
  return i < 10 ? '0' + i.toString() : i.toString();
597
682
  }
598
683
  else if (this.hasFullDate) {
599
- return '00';
684
+ return this.defaultValue.minute.toString().padStart(2, '0');
600
685
  }
601
686
  return undefined;
602
687
  }
@@ -615,7 +700,17 @@ export class bbnDt {
615
700
  return s < 10 ? '0' + s.toString() : s.toString();
616
701
  }
617
702
  else if (this.hasFullDate) {
618
- return '00';
703
+ return this.defaultValue.second.toString().padStart(2, '0');
704
+ }
705
+ return undefined;
706
+ }
707
+ get SSS() {
708
+ if ('millisecond' in this.value) {
709
+ const s = parseInt(this.millisecond().toString());
710
+ return s < 10 ? '00' + s.toString() : (s < 100 ? '0' + s.toString() : s.toString());
711
+ }
712
+ else if (this.hasFullDate) {
713
+ return this.defaultValue.millisecond.toString().padStart(3, '0');
619
714
  }
620
715
  return undefined;
621
716
  }
@@ -704,13 +799,16 @@ export class bbnDt {
704
799
  const diffDays = startThis.diff(startNow, "day");
705
800
  const rtf = new Intl.RelativeTimeFormat(bbn.env.lang, { numeric: "auto" });
706
801
  let phrase;
707
- if (diffDays >= -6 && diffDays <= 6) {
802
+ if (Math.abs(diffDays) <= 6) {
708
803
  phrase = rtf.format(diffDays, "day");
709
804
  }
710
- else {
805
+ else if (Math.abs(diffDays) <= 30) {
711
806
  const diffWeeks = Math.floor(diffDays / 7);
712
807
  phrase = rtf.format(diffWeeks, "week");
713
808
  }
809
+ else {
810
+ return this.fdate();
811
+ }
714
812
  return `${phrase} ${this.ftime()}`;
715
813
  }
716
814
  matchFormat(value, format) {
@@ -845,7 +943,7 @@ export class bbnDt {
845
943
  guessUnit(valueInMs) {
846
944
  const absDiff = Math.abs(valueInMs);
847
945
  for (const [shortUnit, rtfUnit, ms] of units) {
848
- if ((absDiff >= ms) || (rtfUnit === 'second')) {
946
+ if ((absDiff >= ms) || (rtfUnit === 'millisecond')) {
849
947
  return shortUnit;
850
948
  }
851
949
  }
@@ -853,7 +951,7 @@ export class bbnDt {
853
951
  }
854
952
  fromNow(unit = '') {
855
953
  const nowValue = bbnDt.nowForKind(this.kind);
856
- const temp = bbn.dt(nowValue, null, this.kind);
954
+ const temp = bbn.dt(undefined, null, this.kind);
857
955
  const rawDiffMs = this.diff(temp);
858
956
  const chosenUnit = unitsCorrespondence[unit] || this.guessUnit(rawDiffMs);
859
957
  if (!chosenUnit) {
@@ -12,15 +12,7 @@ export default class bbnDtDuration {
12
12
  hours(remaining?: boolean): number;
13
13
  minutes(remaining?: boolean): number;
14
14
  seconds(remaining?: boolean): number;
15
- toJSON(): {
16
- years: number;
17
- months: number;
18
- days: number;
19
- hours: number;
20
- minutes: number;
21
- seconds: number;
22
- milliseconds: number;
23
- };
15
+ toJSON(): string;
24
16
  /**
25
17
  * Returns the full duration expressed as X (float), like Day.js.
26
18
  */
@@ -83,15 +83,7 @@ class bbnDtDuration {
83
83
  // "asX" conversions
84
84
  // -----------------------
85
85
  toJSON() {
86
- return {
87
- years: this.years(true),
88
- months: this.months(true),
89
- days: this.days(true),
90
- hours: this.hours(true),
91
- minutes: this.minutes(true),
92
- seconds: this.seconds(true),
93
- milliseconds: this.toMilliseconds()
94
- };
86
+ return this.value.toJSON();
95
87
  }
96
88
  /**
97
89
  * Returns the full duration expressed as X (float), like Day.js.
@@ -6,7 +6,7 @@ export default class bbnDtZoned extends bbnDt {
6
6
  let value;
7
7
  if (!z) {
8
8
  const date = new Date();
9
- value = new Temporal.ZonedDateTime(BigInt(date.getTime() * 1000000), Intl.DateTimeFormat().resolvedOptions().timeZone);
9
+ value = Temporal.Now.zonedDateTimeISO(Intl.DateTimeFormat().resolvedOptions().timeZone);
10
10
  }
11
11
  else if (y === undefined) {
12
12
  if (z instanceof Temporal.ZonedDateTime) {
@@ -16,8 +16,7 @@ export default class bbnDtZoned extends bbnDt {
16
16
  value = fromJsDate(z, true);
17
17
  }
18
18
  else if (typeof z === 'number') {
19
- const d = new Date(z);
20
- value = fromJsDate(d, true);
19
+ value = Temporal.Instant.fromEpochMilliseconds(z).toZonedDateTimeISO(Intl.DateTimeFormat().resolvedOptions().timeZone);
21
20
  }
22
21
  else {
23
22
  throw new Error('Invalid value for bbnDtZoned');
@@ -235,7 +235,9 @@ const buildLocaleFromIntl = () => {
235
235
  const fmtWeekShort = new Intl.DateTimeFormat(langs, { weekday: 'short' });
236
236
  const fmtShort = new Intl.DateTimeFormat(langs, { dateStyle: 'short', timeStyle: 'short' });
237
237
  const fmtLong = new Intl.DateTimeFormat(langs, { dateStyle: 'long', timeStyle: 'long' });
238
+ const fmtNum = new Intl.DateTimeFormat(langs, { year: 'numeric', month: 'numeric', day: 'numeric' });
238
239
  const rtf = new Intl.RelativeTimeFormat(langs, { numeric: 'auto' });
240
+ const locale = new Intl.Locale(langs[0]);
239
241
  // Create 12 dates for months (2020 chosen arbitrarily)
240
242
  const monthsLong = [];
241
243
  const monthsShort = [];
@@ -259,12 +261,15 @@ const buildLocaleFromIntl = () => {
259
261
  formatters: {
260
262
  short: fmtShort,
261
263
  long: fmtLong,
264
+ numeric: fmtNum,
262
265
  relative: rtf
263
266
  },
267
+ firstDayOfWeek: locale.weekInfo.firstDay,
264
268
  monthsLong,
265
269
  monthsShort,
266
270
  weekdaysLong,
267
271
  weekdaysShort,
272
+ locale,
268
273
  date,
269
274
  time,
270
275
  datetime
@@ -1,6 +1,6 @@
1
- declare const units: [string, Intl.RelativeTimeFormatUnit, number][];
1
+ declare const units: [TimeFormatSymbol, TimeFormatUnit, number][];
2
2
  declare const unitsCorrespondence: {
3
- [key: string]: string;
3
+ [key: string]: TimeFormatSymbol;
4
4
  };
5
5
  declare const unitsMap: {
6
6
  [key: string]: string;
@@ -5,7 +5,8 @@ const units = [
5
5
  ['d', "day", 24 * 60 * 60 * 1000],
6
6
  ['h', "hour", 60 * 60 * 1000],
7
7
  ['i', "minute", 60 * 1000],
8
- ['s', "second", 1000]
8
+ ['s', "second", 1000],
9
+ ['l', "millisecond", 1]
9
10
  ];
10
11
  const unitsCorrespondence = {
11
12
  'years': 'y',
@@ -60,6 +61,9 @@ const unitsCorrespondence = {
60
61
  'mn': 'i',
61
62
  'mm': 'i',
62
63
  'min': 'i',
64
+ 'ms': 'l',
65
+ 'SSS': 'l',
66
+ 'sss': 'l',
63
67
  'SS': 's',
64
68
  'ss': 's',
65
69
  'seconds': 's',
@@ -97,6 +101,7 @@ const unitsMap = {
97
101
  'h': 'Hours',
98
102
  'i': 'Minutes',
99
103
  's': 'Seconds',
104
+ 'l': 'Milliseconds',
100
105
  'z': 'Offset'
101
106
  };
102
107
  const formatsMap = {
package/dist/dt.js CHANGED
@@ -190,7 +190,7 @@ const dt = (value, inputFormat = null, cls = 'auto') => {
190
190
  }
191
191
  }
192
192
  if (typeof value === 'number') {
193
- return new bbnDtDateTime(value < 99999999999 ? value * 1000 : value);
193
+ return new bbnDtDateTime(Math.abs(value) < 99999999999 ? value * 1000 : value);
194
194
  }
195
195
  else if (value.__isBbnDt) {
196
196
  return value;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbn/bbn",
3
- "version": "2.0.151",
3
+ "version": "2.0.153",
4
4
  "description": "Javascript toolkit",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",