@cratis/fundamentals 7.22.4 → 7.22.5
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/DateOnly.ts +28 -1
- package/TimeOnly.ts +51 -11
- package/TimeSpan.ts +26 -1
- package/dist/DateOnly.d.ts +1 -0
- package/dist/DateOnly.d.ts.map +1 -1
- package/dist/DateOnly.js +21 -1
- package/dist/DateOnly.js.map +1 -1
- package/dist/TimeOnly.d.ts +2 -1
- package/dist/TimeOnly.d.ts.map +1 -1
- package/dist/TimeOnly.js +30 -4
- package/dist/TimeOnly.js.map +1 -1
- package/dist/TimeSpan.d.ts.map +1 -1
- package/dist/TimeSpan.js +17 -0
- package/dist/TimeSpan.js.map +1 -1
- package/dist/cjs/DateOnly.d.ts +1 -0
- package/dist/cjs/DateOnly.d.ts.map +1 -1
- package/dist/cjs/DateOnly.js +21 -1
- package/dist/cjs/DateOnly.js.map +1 -1
- package/dist/cjs/TimeOnly.d.ts +2 -1
- package/dist/cjs/TimeOnly.d.ts.map +1 -1
- package/dist/cjs/TimeOnly.js +30 -4
- package/dist/cjs/TimeOnly.js.map +1 -1
- package/dist/cjs/TimeSpan.d.ts.map +1 -1
- package/dist/cjs/TimeSpan.js +17 -0
- package/dist/cjs/TimeSpan.js.map +1 -1
- package/dist/esm/DateOnly.d.ts +1 -0
- package/dist/esm/DateOnly.d.ts.map +1 -1
- package/dist/esm/DateOnly.js +21 -1
- package/dist/esm/DateOnly.js.map +1 -1
- package/dist/esm/TimeOnly.d.ts +2 -1
- package/dist/esm/TimeOnly.d.ts.map +1 -1
- package/dist/esm/TimeOnly.js +30 -4
- package/dist/esm/TimeOnly.js.map +1 -1
- package/dist/esm/TimeSpan.d.ts.map +1 -1
- package/dist/esm/TimeSpan.js +17 -0
- package/dist/esm/TimeSpan.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/for_DateOnly/when_parsing_an_out_of_range_calendar_date.ts +13 -0
- package/for_DateOnly/when_parsing_february_in_leap_and_common_years.ts +11 -0
- package/for_JsonSerializer/when_deserializing_a_value_map_with_out_of_range_temporal_keys.ts +19 -0
- package/for_JsonSerializer/when_deserializing_out_of_range_temporal_values.ts +20 -0
- package/for_JsonSerializer/when_round_tripping_a_value_map_with_time_only_keys_differing_below_a_millisecond.ts +20 -0
- package/for_TimeOnly/when_creating_with_out_of_range_sub_millisecond_ticks.ts +11 -0
- package/for_TimeOnly/when_parsing_an_out_of_range_time_of_day.ts +11 -0
- package/for_TimeOnly/when_parsing_the_last_moment_of_the_day.ts +10 -0
- package/for_TimeOnly/when_parsing_the_shapes_the_server_sends.ts +6 -6
- package/for_TimeOnly/when_parsing_times_that_differ_below_a_millisecond.ts +14 -0
- package/for_TimeOnly/when_rendering_a_fraction.ts +11 -0
- package/for_TimeSpan/when_parsing_out_of_range_time.ts +11 -0
- package/for_TimeSpan/when_parsing_the_largest_in_range_time_span.ts +10 -0
- package/for_TimeSpan/when_parsing_time_beyond_what_a_time_span_can_hold.ts +11 -0
- package/package.json +1 -1
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { DateOnly } from '../DateOnly';
|
|
5
|
+
|
|
6
|
+
describe('when parsing february in leap and common years', () => {
|
|
7
|
+
it('should accept the 29th in a leap year', () => DateOnly.parse('2024-02-29').day.should.equal(29));
|
|
8
|
+
it('should accept the 29th in a year divisible by 400', () => DateOnly.parse('2000-02-29').day.should.equal(29));
|
|
9
|
+
it('should reject the 29th in a common year', () => (() => DateOnly.parse('2023-02-29')).should.throw('day 29 is out of range'));
|
|
10
|
+
it('should reject the 29th in a century year not divisible by 400', () => (() => DateOnly.parse('1900-02-29')).should.throw('day 29 is out of range'));
|
|
11
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { DateOnly } from '../DateOnly';
|
|
5
|
+
import { TimeOnly } from '../TimeOnly';
|
|
6
|
+
import { TimeSpan } from '../TimeSpan';
|
|
7
|
+
import { JsonSerializer } from '../JsonSerializer';
|
|
8
|
+
import { ValueMap } from '../ValueMap';
|
|
9
|
+
import { field } from '../fieldDecorator';
|
|
10
|
+
|
|
11
|
+
class Dates { @field(ValueMap, { genericArguments: [DateOnly, Number] }) byKey!: ValueMap<DateOnly, number>; }
|
|
12
|
+
class Times { @field(ValueMap, { genericArguments: [TimeOnly, Number] }) byKey!: ValueMap<TimeOnly, number>; }
|
|
13
|
+
class Spans { @field(ValueMap, { genericArguments: [TimeSpan, Number] }) byKey!: ValueMap<TimeSpan, number>; }
|
|
14
|
+
|
|
15
|
+
describe('when deserializing a value map with out of range temporal keys', () => {
|
|
16
|
+
it('should reject a DateOnly key', () => (() => JsonSerializer.deserialize(Dates, '{"byKey":{"2024-13-45":1}}')).should.throw('not a valid DateOnly'));
|
|
17
|
+
it('should reject a TimeOnly key', () => (() => JsonSerializer.deserialize(Times, '{"byKey":{"25:99:00":1}}')).should.throw('not a valid TimeOnly'));
|
|
18
|
+
it('should reject a TimeSpan key', () => (() => JsonSerializer.deserialize(Spans, '{"byKey":{"00:60:00":1}}')).should.throw('not a valid TimeSpan'));
|
|
19
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { DateOnly } from '../DateOnly';
|
|
5
|
+
import { TimeOnly } from '../TimeOnly';
|
|
6
|
+
import { TimeSpan } from '../TimeSpan';
|
|
7
|
+
import { JsonSerializer } from '../JsonSerializer';
|
|
8
|
+
import { field } from '../fieldDecorator';
|
|
9
|
+
|
|
10
|
+
class Holder {
|
|
11
|
+
@field(DateOnly) date!: DateOnly;
|
|
12
|
+
@field(TimeOnly) time!: TimeOnly;
|
|
13
|
+
@field(TimeSpan) span!: TimeSpan;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe('when deserializing out of range temporal values', () => {
|
|
17
|
+
it('should reject a DateOnly value', () => (() => JsonSerializer.deserialize(Holder, '{"date":"2024-13-45"}')).should.throw('Invalid DateOnly'));
|
|
18
|
+
it('should reject a TimeOnly value', () => (() => JsonSerializer.deserialize(Holder, '{"time":"25:99:00"}')).should.throw('Invalid TimeOnly'));
|
|
19
|
+
it('should reject a TimeSpan value', () => (() => JsonSerializer.deserialize(Holder, '{"span":"00:60:00"}')).should.throw('Invalid TimeSpan'));
|
|
20
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { TimeOnly } from '../TimeOnly';
|
|
5
|
+
import { JsonSerializer } from '../JsonSerializer';
|
|
6
|
+
import { ValueMap } from '../ValueMap';
|
|
7
|
+
import { field } from '../fieldDecorator';
|
|
8
|
+
|
|
9
|
+
class Totals {
|
|
10
|
+
@field(ValueMap, { genericArguments: [TimeOnly, Number] })
|
|
11
|
+
byKey!: ValueMap<TimeOnly, number>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe('when round tripping a value map with time only keys differing below a millisecond', () => {
|
|
15
|
+
const result = JsonSerializer.deserialize(Totals, '{"byKey":{"10:11:12.1234567":1,"10:11:12.1239999":2}}');
|
|
16
|
+
|
|
17
|
+
it('should keep both entries', () => [...result.byKey.entries()].length.should.equal(2));
|
|
18
|
+
it('should find the first value', () => result.byKey.get(TimeOnly.parse('10:11:12.1234567'))!.should.equal(1));
|
|
19
|
+
it('should find the second value', () => result.byKey.get(TimeOnly.parse('10:11:12.1239999'))!.should.equal(2));
|
|
20
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { TimeOnly } from '../TimeOnly';
|
|
5
|
+
|
|
6
|
+
describe('when creating with out of range sub millisecond ticks', () => {
|
|
7
|
+
it('should reject ticks above 9999', () => (() => TimeOnly.from(1, 2, 3, 4, 10000)).should.throw('subMillisecondTicks 10000 is out of range'));
|
|
8
|
+
it('should reject negative ticks', () => (() => TimeOnly.from(1, 2, 3, 4, -1)).should.throw('subMillisecondTicks -1 is out of range'));
|
|
9
|
+
it('should reject fractional ticks', () => (() => TimeOnly.from(1, 2, 3, 4, 1.5)).should.throw('out of range'));
|
|
10
|
+
it('should accept the largest value', () => TimeOnly.from(1, 2, 3, 4, 9999).subMillisecondTicks.should.equal(9999));
|
|
11
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { TimeOnly } from '../TimeOnly';
|
|
5
|
+
|
|
6
|
+
describe('when parsing an out of range time of day', () => {
|
|
7
|
+
it('should reject an hour above 23', () => (() => TimeOnly.parse('24:00:00')).should.throw('hour 24 is out of range'));
|
|
8
|
+
it('should reject a minute above 59', () => (() => TimeOnly.parse('10:60:00')).should.throw('minute 60 is out of range'));
|
|
9
|
+
it('should reject a second above 59', () => (() => TimeOnly.parse('10:00:60')).should.throw('second 60 is out of range'));
|
|
10
|
+
it('should reject every component out of range', () => (() => TimeOnly.parse('25:99:00')).should.throw('Invalid TimeOnly'));
|
|
11
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { TimeOnly } from '../TimeOnly';
|
|
5
|
+
|
|
6
|
+
describe('when parsing the last moment of the day', () => {
|
|
7
|
+
const parsed = TimeOnly.parse('23:59:59.9999999');
|
|
8
|
+
|
|
9
|
+
it('should keep the full fraction', () => parsed.toString().should.equal('23:59:59.9999999'));
|
|
10
|
+
});
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
import { TimeOnly } from '../TimeOnly';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* The server omits the seconds and the fraction when they are zero, and writes up to seven fractional digits
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* The server omits the seconds and the fraction when they are zero, and writes up to seven fractional digits.
|
|
8
|
+
* Every one of those shapes has to parse, and a value has to survive a parse followed by a render without
|
|
9
|
+
* moving.
|
|
10
10
|
*/
|
|
11
11
|
describe('when parsing the shapes the server sends', () => {
|
|
12
12
|
const withoutSeconds = TimeOnly.parse('14:30');
|
|
@@ -16,9 +16,9 @@ describe('when parsing the shapes the server sends', () => {
|
|
|
16
16
|
it('should default the seconds when they are omitted', () => withoutSeconds.second.should.equal(0));
|
|
17
17
|
it('should render omitted seconds explicitly', () => withoutSeconds.toString().should.equal('14:30:00'));
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
it('should
|
|
21
|
-
it('should render the fraction
|
|
19
|
+
it('should keep the milliseconds of the fraction', () => withFraction.millisecond.should.equal(123));
|
|
20
|
+
it('should keep the ticks below the millisecond', () => withFraction.subMillisecondTicks.should.equal(4567));
|
|
21
|
+
it('should render the full fraction', () => withFraction.toString().should.equal('14:30:45.1234567'));
|
|
22
22
|
|
|
23
23
|
it('should parse midnight', () => midnight.toString().should.equal('00:00:00'));
|
|
24
24
|
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { TimeOnly } from '../TimeOnly';
|
|
5
|
+
|
|
6
|
+
describe('when parsing times that differ below a millisecond', () => {
|
|
7
|
+
const first = TimeOnly.parse('10:11:12.1234567');
|
|
8
|
+
const second = TimeOnly.parse('10:11:12.1239999');
|
|
9
|
+
|
|
10
|
+
it('should not be equal', () => first.equals(second).should.be.false);
|
|
11
|
+
it('should render the full seven digit fraction', () => first.toString().should.equal('10:11:12.1234567'));
|
|
12
|
+
it('should render differently', () => first.toJSON().should.not.equal(second.toJSON()));
|
|
13
|
+
it('should be equal to the same time written with trailing zeros', () => TimeOnly.parse('10:11:12.1230000').equals(TimeOnly.parse('10:11:12.123')).should.be.true);
|
|
14
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { TimeOnly } from '../TimeOnly';
|
|
5
|
+
|
|
6
|
+
describe('when rendering a fraction', () => {
|
|
7
|
+
it('should keep the three digit form when there is no part below the millisecond', () => TimeOnly.parse('10:11:12.1').toString().should.equal('10:11:12.100'));
|
|
8
|
+
it('should pad to seven digits when there is a part below the millisecond', () => TimeOnly.parse('10:11:12.1230100').toString().should.equal('10:11:12.1230100'));
|
|
9
|
+
it('should pad a short part below the millisecond', () => TimeOnly.parse('10:11:12.0000001').toString().should.equal('10:11:12.0000001'));
|
|
10
|
+
it('should render the time alone when there is no fraction', () => TimeOnly.parse('10:11:12').toString().should.equal('10:11:12'));
|
|
11
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { TimeSpan } from '../TimeSpan';
|
|
5
|
+
|
|
6
|
+
describe('when parsing out of range time', () => {
|
|
7
|
+
it('should reject minutes above 59', () => (() => TimeSpan.parse('00:60:00')).should.throw('minutes 60 is out of range'));
|
|
8
|
+
it('should reject seconds above 59', () => (() => TimeSpan.parse('00:00:60')).should.throw('seconds 60 is out of range'));
|
|
9
|
+
it('should reject hours above 23', () => (() => TimeSpan.parse('1.24:00:00')).should.throw('hours 24 is out of range'));
|
|
10
|
+
it('should reject hours above 23 without days', () => (() => TimeSpan.parse('25:00:00')).should.throw('hours 25 is out of range'));
|
|
11
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { TimeSpan } from '../TimeSpan';
|
|
5
|
+
|
|
6
|
+
describe('when parsing the largest in range time span', () => {
|
|
7
|
+
const parsed = TimeSpan.parse('1.23:59:59');
|
|
8
|
+
|
|
9
|
+
it('should render back to what it was parsed from', () => parsed.toString().should.equal('1.23:59:59'));
|
|
10
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { TimeSpan } from '../TimeSpan';
|
|
5
|
+
|
|
6
|
+
describe('when parsing time beyond what a time span can hold', () => {
|
|
7
|
+
it('should reject more days than the maximum', () => (() => TimeSpan.parse('10675200.00:00:00')).should.throw('days 10675200 is out of range'));
|
|
8
|
+
it('should reject the maximum days with a remainder above the maximum', () => (() => TimeSpan.parse('10675199.02:48:05.4775808')).should.throw('outside the range'));
|
|
9
|
+
it('should accept the maximum value', () => TimeSpan.parse('10675199.02:48:05.4775807').days.should.equal(10675199));
|
|
10
|
+
it('should accept the minimum value', () => TimeSpan.parse('-10675199.02:48:05.4775808').days.should.equal(-10675199));
|
|
11
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cratis/fundamentals",
|
|
3
|
-
"version": "7.22.
|
|
3
|
+
"version": "7.22.5",
|
|
4
4
|
"description": "The shared building blocks beneath the Cratis stack — GUIDs, concepts, derived-type serialization, property-path proxies, and decorators for TypeScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cratis",
|