@utiliread/primitives 1.0.0-7 → 1.0.0-71

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.
Files changed (161) hide show
  1. package/dist/cjs/aggregate-id.js +2 -2
  2. package/dist/cjs/consumption-type-id.js +2 -2
  3. package/dist/cjs/index.js +21 -9
  4. package/dist/cjs/index.js.map +1 -1
  5. package/dist/cjs/measure-id.js +3 -0
  6. package/dist/cjs/measure-id.js.map +1 -0
  7. package/dist/cjs/number-formatter.js +81 -50
  8. package/dist/cjs/number-formatter.js.map +1 -1
  9. package/dist/cjs/number-formatter.spec.js +98 -0
  10. package/dist/cjs/number-formatter.spec.js.map +1 -0
  11. package/dist/cjs/optional.js +3 -0
  12. package/dist/cjs/optional.js.map +1 -0
  13. package/dist/cjs/precision-number.js +36 -0
  14. package/dist/cjs/precision-number.js.map +1 -0
  15. package/dist/cjs/precision-number.spec.js +27 -0
  16. package/dist/cjs/precision-number.spec.js.map +1 -0
  17. package/dist/cjs/register-convention.js +56 -0
  18. package/dist/cjs/register-convention.js.map +1 -0
  19. package/dist/cjs/register-convention.spec.js +39 -0
  20. package/dist/cjs/register-convention.spec.js.map +1 -0
  21. package/dist/cjs/register-definition.js +2 -2
  22. package/dist/cjs/register-id.js +2 -2
  23. package/dist/cjs/register-library.js +50 -41
  24. package/dist/cjs/register-library.js.map +1 -1
  25. package/dist/cjs/register-library.spec.js +36 -29
  26. package/dist/cjs/register-library.spec.js.map +1 -1
  27. package/dist/cjs/register-pack.js +2 -2
  28. package/dist/cjs/register-utils.js +8 -0
  29. package/dist/cjs/register-utils.js.map +1 -0
  30. package/dist/cjs/repository.js +26 -0
  31. package/dist/cjs/repository.js.map +1 -0
  32. package/dist/cjs/repository.spec.js +21 -0
  33. package/dist/cjs/repository.spec.js.map +1 -0
  34. package/dist/cjs/step.js +41 -0
  35. package/dist/cjs/step.js.map +1 -0
  36. package/dist/cjs/step.spec.js +29 -0
  37. package/dist/cjs/step.spec.js.map +1 -0
  38. package/dist/cjs/time-or-rule.js +19 -0
  39. package/dist/cjs/time-or-rule.js.map +1 -0
  40. package/dist/cjs/unit-id.js +13 -13
  41. package/dist/cjs/unit-id.js.map +1 -1
  42. package/dist/esm/aggregate-id.d.ts +1 -1
  43. package/dist/esm/aggregate-id.js +1 -1
  44. package/dist/esm/consumption-type-id.d.ts +1 -1
  45. package/dist/esm/consumption-type-id.js +1 -1
  46. package/dist/esm/index.d.ts +16 -8
  47. package/dist/esm/index.js +9 -4
  48. package/dist/esm/index.js.map +1 -1
  49. package/dist/esm/measure-id.d.ts +1 -0
  50. package/dist/esm/measure-id.js +2 -0
  51. package/dist/esm/measure-id.js.map +1 -0
  52. package/dist/esm/number-formatter.d.ts +17 -8
  53. package/dist/esm/number-formatter.js +78 -47
  54. package/dist/esm/number-formatter.js.map +1 -1
  55. package/dist/esm/{register-converter.spec.d.ts → number-formatter.spec.d.ts} +1 -1
  56. package/dist/esm/number-formatter.spec.js +96 -0
  57. package/dist/esm/number-formatter.spec.js.map +1 -0
  58. package/dist/esm/optional.d.ts +1 -0
  59. package/dist/esm/optional.js +2 -0
  60. package/dist/esm/optional.js.map +1 -0
  61. package/dist/esm/precision-number.d.ts +11 -0
  62. package/dist/esm/precision-number.js +32 -0
  63. package/dist/esm/precision-number.js.map +1 -0
  64. package/dist/esm/precision-number.spec.d.ts +1 -0
  65. package/dist/esm/precision-number.spec.js +25 -0
  66. package/dist/esm/precision-number.spec.js.map +1 -0
  67. package/dist/esm/register-convention.d.ts +14 -0
  68. package/dist/esm/register-convention.js +52 -0
  69. package/dist/esm/register-convention.js.map +1 -0
  70. package/dist/esm/register-convention.spec.d.ts +1 -0
  71. package/dist/esm/register-convention.spec.js +37 -0
  72. package/dist/esm/register-convention.spec.js.map +1 -0
  73. package/dist/esm/register-definition.d.ts +13 -9
  74. package/dist/esm/register-definition.js +1 -1
  75. package/dist/esm/register-id.d.ts +1 -1
  76. package/dist/esm/register-id.js +1 -1
  77. package/dist/esm/register-library.d.ts +15 -11
  78. package/dist/esm/register-library.js +47 -38
  79. package/dist/esm/register-library.js.map +1 -1
  80. package/dist/esm/register-library.spec.d.ts +1 -1
  81. package/dist/esm/register-library.spec.js +34 -27
  82. package/dist/esm/register-library.spec.js.map +1 -1
  83. package/dist/esm/register-pack.d.ts +4 -4
  84. package/dist/esm/register-pack.js +1 -1
  85. package/dist/esm/register-utils.d.ts +3 -0
  86. package/dist/esm/register-utils.js +4 -0
  87. package/dist/esm/register-utils.js.map +1 -0
  88. package/dist/esm/repository.d.ts +9 -0
  89. package/dist/esm/repository.js +22 -0
  90. package/dist/esm/repository.js.map +1 -0
  91. package/dist/esm/repository.spec.d.ts +1 -0
  92. package/dist/esm/repository.spec.js +19 -0
  93. package/dist/esm/repository.spec.js.map +1 -0
  94. package/dist/esm/step.d.ts +15 -0
  95. package/dist/esm/step.js +38 -0
  96. package/dist/esm/step.js.map +1 -0
  97. package/dist/esm/step.spec.d.ts +1 -0
  98. package/dist/esm/step.spec.js +27 -0
  99. package/dist/esm/step.spec.js.map +1 -0
  100. package/dist/esm/time-or-rule.d.ts +7 -0
  101. package/dist/esm/time-or-rule.js +16 -0
  102. package/dist/esm/time-or-rule.js.map +1 -0
  103. package/dist/esm/unit-id.d.ts +2 -2
  104. package/dist/esm/unit-id.js +10 -10
  105. package/dist/esm/unit-id.js.map +1 -1
  106. package/karma.config.js +14 -14
  107. package/locales/da/aggregates.json +8 -0
  108. package/locales/da/consumption-types.json +7 -0
  109. package/locales/da/measures.json +16 -0
  110. package/locales/da/registers.json +221 -0
  111. package/locales/da/units.json +93 -0
  112. package/locales/en/aggregates.json +8 -0
  113. package/locales/en/consumption-types.json +7 -0
  114. package/locales/en/measures.json +16 -0
  115. package/locales/en/registers.json +221 -0
  116. package/locales/en/units.json +93 -0
  117. package/package.json +13 -6
  118. package/src/aggregate-id.ts +9 -9
  119. package/src/consumption-type-id.ts +8 -8
  120. package/src/index.ts +29 -11
  121. package/src/measure-id.ts +15 -0
  122. package/src/number-formatter.spec.ts +105 -77
  123. package/src/number-formatter.ts +95 -50
  124. package/src/optional.ts +1 -0
  125. package/src/precision-number.spec.ts +28 -0
  126. package/src/precision-number.ts +36 -0
  127. package/src/register-convention.spec.ts +43 -0
  128. package/src/register-convention.ts +61 -0
  129. package/src/register-definition.ts +14 -1
  130. package/src/register-id.ts +119 -7
  131. package/src/register-library.spec.ts +41 -33
  132. package/src/register-library.ts +57 -37
  133. package/src/register-pack.ts +5 -5
  134. package/src/register-utils.ts +6 -0
  135. package/src/repository.spec.ts +20 -0
  136. package/src/repository.ts +24 -0
  137. package/src/step.spec.ts +30 -0
  138. package/src/step.ts +37 -0
  139. package/src/time-or-rule.ts +17 -0
  140. package/src/unit-id.ts +131 -100
  141. package/tsconfig.cjs.json +7 -8
  142. package/tsconfig.json +15 -14
  143. package/coverage/Chrome Headless 90.0.4430.85 (Windows 10)/html/base.css +0 -224
  144. package/coverage/Chrome Headless 90.0.4430.85 (Windows 10)/html/block-navigation.js +0 -79
  145. package/coverage/Chrome Headless 90.0.4430.85 (Windows 10)/html/favicon.png +0 -0
  146. package/coverage/Chrome Headless 90.0.4430.85 (Windows 10)/html/index.html +0 -96
  147. package/coverage/Chrome Headless 90.0.4430.85 (Windows 10)/html/prettify.css +0 -1
  148. package/coverage/Chrome Headless 90.0.4430.85 (Windows 10)/html/prettify.js +0 -2
  149. package/coverage/Chrome Headless 90.0.4430.85 (Windows 10)/html/sort-arrow-sprite.png +0 -0
  150. package/coverage/Chrome Headless 90.0.4430.85 (Windows 10)/html/sorter.js +0 -170
  151. package/dist/cjs/register-converter.js +0 -47
  152. package/dist/cjs/register-converter.js.map +0 -1
  153. package/dist/cjs/register-converter.spec.js +0 -29
  154. package/dist/cjs/register-converter.spec.js.map +0 -1
  155. package/dist/esm/register-converter.d.ts +0 -9
  156. package/dist/esm/register-converter.js +0 -44
  157. package/dist/esm/register-converter.js.map +0 -1
  158. package/dist/esm/register-converter.spec.js +0 -27
  159. package/dist/esm/register-converter.spec.js.map +0 -1
  160. package/src/register-converter.spec.ts +0 -31
  161. package/src/register-converter.ts +0 -48
@@ -0,0 +1,93 @@
1
+ {
2
+ "unitless": [""],
3
+
4
+ "milliWattHour": ["mWh"],
5
+ "wattHour": ["Wh"],
6
+ "kiloWattHour": ["kWh"],
7
+ "megaWattHour": ["MWh"],
8
+ "gigaWattHour": ["GWh"],
9
+ "calorie": ["cal"],
10
+ "kiloCalorie": ["kcal"],
11
+ "megaCalorie": ["Mcal"],
12
+
13
+ "joule": ["J"],
14
+ "kiloJoule": ["kJ"],
15
+ "megaJoule": ["MJ"],
16
+ "gigaJoule": ["GJ"],
17
+
18
+ "liter": ["L"],
19
+ "cubicMeter": ["m\u00B3"],
20
+
21
+ "fluidOunce": ["fl oz"],
22
+ "gallon": ["gal"],
23
+
24
+ "gram": ["g"],
25
+ "kiloGram": ["kg"],
26
+ "tonne": ["T"],
27
+
28
+ "ounce": ["oz"],
29
+ "pound": ["lbs"],
30
+ "ton": ["T"],
31
+
32
+ "second": ["s"],
33
+ "minute": ["mi"],
34
+ "hour": ["h", "Hour"],
35
+ "day": ["d", "Day"],
36
+ "week": ["w", "Week"],
37
+ "month": ["mo", "Month"],
38
+ "year": ["y", "Year"],
39
+
40
+ "milliWatt": ["mW"],
41
+ "watt": ["W"],
42
+ "kiloWatt": ["kW"],
43
+ "megaWatt": ["MW"],
44
+ "gigaWatt": ["GW"],
45
+
46
+ "joulesPerSecond": ["J/s"],
47
+ "joulesPerMinute": ["J/mi"],
48
+ "joulesPerHour": ["J/t"],
49
+
50
+ "litersPerSecond": ["L/s"],
51
+ "litersPerMinute": ["L/mi"],
52
+ "litersPerHour": ["L/t"],
53
+
54
+ "cubicMetersPerSecond": ["m\u00B3/s"],
55
+ "cubicMetersPerMinute": ["m\u00B3/mi"],
56
+ "cubicMetersPerHour": ["m\u00B3/t"],
57
+
58
+ "fluidOuncesPerSecond": ["fl oz/s"],
59
+ "fluidOuncesPerMinute": ["fl oz/mi"],
60
+ "fluidOuncesPerHour": ["fl oz/t"],
61
+
62
+ "gallonsPerSecond": ["gal/s"],
63
+ "gallonsPerMinute": ["gal/mi"],
64
+ "gallonsPerHour": ["gal/t"],
65
+
66
+ "gramsPerSecond": ["g/s"],
67
+ "gramsPerMinute": ["g/mi"],
68
+ "gramsPerHour": ["g/t"],
69
+
70
+ "kiloGramsPerSecond": ["kg/s"],
71
+ "kiloGramsPerMinute": ["kg/mi"],
72
+ "kiloGramsPerHour": ["kg/t"],
73
+
74
+ "celcius": ["\u00B0C"],
75
+ "kelvin": ["K"],
76
+ "fahrenheit": ["F"],
77
+
78
+ "bar": ["bar"],
79
+ "pascal": ["Pa"],
80
+
81
+ "var": ["var"],
82
+ "kiloVar": ["kvar"],
83
+ "megaVar": ["Mvar"],
84
+
85
+ "varHour": ["varh"],
86
+ "kiloVarHour": ["kvarh"],
87
+ "megaVarHour": ["Mvarh"],
88
+
89
+ "percentage": ["%"],
90
+
91
+ "literCelcius": ["l\u00B7\u2103"],
92
+ "cubicMeterCelcius": ["m\u00B3\u00B7\u2103"]
93
+ }
package/package.json CHANGED
@@ -1,29 +1,36 @@
1
1
  {
2
2
  "name": "@utiliread/primitives",
3
- "version": "1.0.0-7",
3
+ "version": "1.0.0-71",
4
4
  "main": "dist/cjs/index.js",
5
5
  "module": "dist/esm/index.js",
6
6
  "types": "dist/esm/index.d.ts",
7
+ "sideEffects": false,
7
8
  "scripts": {
8
9
  "build": "tsc && tsc -p tsconfig.cjs.json",
9
10
  "test": "karma start --single-run --browsers ChromeHeadless karma.config.js",
10
11
  "watch": "karma start --auto-watch --browsers ChromeDebugging karma.config.js",
12
+ "chrome": "karma start --auto-watch --browsers Chrome karma.config.js",
11
13
  "clean": "rimraf dist"
12
14
  },
13
15
  "publishConfig": {
14
16
  "access": "public"
15
17
  },
16
18
  "devDependencies": {
17
- "@types/chai": "^4.2.16",
18
- "@types/mocha": "^8.2.2",
19
+ "@types/chai": "^4.2.18",
20
+ "@types/luxon": "^3.0.1",
21
+ "@types/mocha": "^10.0.0",
19
22
  "chai": "^4.3.4",
20
23
  "karma": "^6.3.2",
21
24
  "karma-chai": "^0.1.0",
22
25
  "karma-chrome-launcher": "^3.1.0",
23
26
  "karma-mocha": "^2.0.1",
24
27
  "karma-typescript": "^5.5.1",
25
- "mocha": "^8.3.2",
26
- "rimraf": "^3.0.2",
27
- "typescript": "^4.1.5"
28
+ "luxon": "^3.0.4",
29
+ "mocha": "^10.0.0",
30
+ "rimraf": "^5.0.1",
31
+ "typescript": "^5.1.6"
32
+ },
33
+ "peerDependencies": {
34
+ "luxon": "^2.1.0 || ^3.0.0"
28
35
  }
29
36
  }
@@ -1,9 +1,9 @@
1
- export type AggregateId =
2
- "instant" |
3
- "diff" |
4
- "min" |
5
- "max" |
6
- "avg" |
7
- "sum" |
8
- "cnt" |
9
- "ssq";
1
+ export type AggregateId =
2
+ "instant" |
3
+ "diff" |
4
+ "min" |
5
+ "max" |
6
+ "avg" |
7
+ "sum" |
8
+ "cnt" |
9
+ "ssq";
@@ -1,8 +1,8 @@
1
- export type ConsumptionTypeId =
2
- "none" |
3
- "heat" |
4
- "water" |
5
- "cooling" |
6
- "electricity" |
7
- "infrastructure" |
8
- "all";
1
+ export type ConsumptionTypeId =
2
+ "none" |
3
+ "heat" |
4
+ "water" |
5
+ "cooling" |
6
+ "electricity" |
7
+ "infrastructure" |
8
+ "all";
package/src/index.ts CHANGED
@@ -1,17 +1,35 @@
1
- import { ConsumptionTypeId } from "./consumption-type-id";
2
- import { RegisterId } from "./register-id";
3
- import { UnitId } from "./unit-id";
1
+ import type { ConsumptionTypeId } from "./consumption-type-id";
2
+ import type { RegisterId } from "./register-id";
3
+ import type { UnitId } from "./unit-id";
4
4
  import { RegisterLibrary } from "./register-library";
5
- import { RegisterDefinition } from "./register-definition";
6
- import { RegisterConverter } from "./register-converter";
7
- import { NumberFormatter } from "./number-formatter";
5
+ import type { RegisterDefinition } from "./register-definition";
6
+ import { NumberFormatter, invariantDecimalSeparator, localeDecimalSeparator } from "./number-formatter";
7
+ import { RegisterConvention } from "./register-convention";
8
+ import type { AggregateId } from "./aggregate-id";
9
+ import type { MeasureId } from "./measure-id";
10
+ import { Step, DayOfWeek } from "./step";
11
+ import { TimeOrRule } from "./time-or-rule";
12
+ import { PrecisionNumber } from "./precision-number";
13
+ import "./repository"; // We need this to get the Repository namespace exported
14
+ import { Repository } from "./repository";
15
+ import type { Optional } from "./optional";
8
16
 
9
17
  export {
18
+ AggregateId,
10
19
  ConsumptionTypeId,
11
- RegisterId,
20
+ DayOfWeek,
21
+ MeasureId,
22
+ NumberFormatter,
23
+ PrecisionNumber,
24
+ RegisterConvention,
12
25
  RegisterDefinition,
13
- UnitId,
26
+ RegisterId,
14
27
  RegisterLibrary,
15
- RegisterConverter,
16
- NumberFormatter,
17
- };
28
+ Repository,
29
+ Step,
30
+ TimeOrRule,
31
+ UnitId,
32
+ invariantDecimalSeparator,
33
+ localeDecimalSeparator,
34
+ Optional,
35
+ };
@@ -0,0 +1,15 @@
1
+ export type MeasureId =
2
+ "none" |
3
+ "duration" |
4
+ "energy" |
5
+ "humidity" |
6
+ "mass" |
7
+ "massFlow" |
8
+ "pressure" |
9
+ "power" |
10
+ "reactiveEnergy" |
11
+ "reactivePower" |
12
+ "temperature" |
13
+ "time" |
14
+ "volume" |
15
+ "volumeFlow";
@@ -1,77 +1,105 @@
1
- import { expect } from 'chai';
2
- import { NumberFormatter } from './number-formatter';
3
-
4
- const localeDecimalSeparator = (0.1).toLocaleString()[1];
5
-
6
- describe("toLocalePrecision", () => {
7
- it("should format correctly", () => {
8
- const data: [number, number, string][] = [
9
- [77.1274, 1, "8e+1"],
10
- [77.1274, 2, "77"],
11
- [77.1274, 4, "77.13"],
12
- [77.1274, 5, "77.127"],
13
- [77.1274, 6, "77.1274"],
14
- [77.1274, 7, "77.12740"],
15
- [77.1274, 15, "77.1274000000000"],
16
- [77.1274, 16, "77.12739999999999"],
17
- [77.1274, 17, "77.127399999999994"],
18
- [77.1274, 18, "77.1273999999999944"],
19
- [77.1274, 19, "77.12739999999999441"],
20
- [77.1274, 20, "77.127399999999994407"],
21
- [77.1274, 21, "77.1273999999999944066"],
22
- [0.00000123, 2, "0.0000012"],
23
- [0.000000123, 2, "1.2e-7"],
24
- [0.00000123, 21, "0.00000123000000000000008198"],
25
- [123456, 3, "1.23e+5"],
26
- [Number.MAX_VALUE, 3, "1.80e+308"],
27
- [123456, 15, "123456.000000000"],
28
- [0, 5, "0.0000"],
29
- [0.000001, 1, "0.000001"],
30
- [0.000001, 10, "0.000001000000000"],
31
- [0.0000001, 10, "1.000000000e-7"],
32
- [0.00000000001, 1, "1e-11"],
33
- [0.00000000001, 2, "1.0e-11"],
34
- [55, 2, "55"],
35
- [55, 1, "6e+1"],
36
- [-55, 2, "-55"],
37
- [-55, 1, "-6e+1"],
38
- [9.59, 1, "1e+1"],
39
- [9.59, 2, "10"],
40
- [9.95, 2, "10"],
41
- [9.96, 2, "10"],
42
- [-555555555555555555555, 1, "-6e+20"],
43
- [-5555555555555555555555, 1, "-6e+21"],
44
- [18014398509482012, 21, "18014398509482012.0000"],
45
- [180143985094820121234, 21, "180143985094820134912"],
46
- [0.1, 21, "0.100000000000000005551"],
47
- [Number.EPSILON, 2, "2.2e-16"],
48
- [Number.EPSILON, 21, "2.22044604925031308085e-16"],
49
- [Number.NaN, 3, "NaN"],
50
- [Number.POSITIVE_INFINITY, 3, "Infinity"],
51
- [Number.NEGATIVE_INFINITY, 3, "-Infinity"],
52
- ];
53
-
54
- for (const [value, precision, expected] of data) {
55
- expect(NumberFormatter.toLocalePrecision(value, precision)).to.equal(expected.replace(".", localeDecimalSeparator));
56
- }
57
- })
58
- })
59
-
60
- describe("toLocaleFixed", () => {
61
- it("should format correctly", () => {
62
- const data: [number, number, string][] = [
63
- [10, 1, "10"],
64
- [10, 2, "10"],
65
- [10, 3, "10.0"],
66
- [10, 4, "10.00"],
67
- [10.2, 1, "10"],
68
- [10.2, 2, "10"],
69
- [10.2, 3, "10.2"],
70
- [10.2, 4, "10.20"],
71
- ];
72
-
73
- for (const [value, precision, expected] of data) {
74
- expect(NumberFormatter.toLocaleFixed(value, precision)).to.equal(expected.replace(".", localeDecimalSeparator));
75
- }
76
- })
77
- })
1
+ import { NumberFormatter, Parts, invariantDecimalSeparator, localeDecimalSeparator } from './number-formatter';
2
+
3
+ import { expect } from 'chai';
4
+
5
+ describe("toParts and toPrecision", () => {
6
+ it("should format correctly", () => {
7
+ const data: [number, number, Parts][] = [
8
+ [0, 0, { invariantPrecisionString: "0", invariantCoefficientString: "0", exponent: 0, noOfCoefficientDecimals: 0 }],
9
+ [0.0, 1, { invariantPrecisionString: "0.0", invariantCoefficientString: "0.0", exponent: 0, noOfCoefficientDecimals: 1 }],
10
+ [77.1274, 1, { invariantPrecisionString: "8e+1", invariantCoefficientString: "8", exponent: 1, noOfCoefficientDecimals: 0 }],
11
+ [77.1274, 2, { invariantPrecisionString: "77", invariantCoefficientString: "77", exponent: 0, noOfCoefficientDecimals: 0 }],
12
+ [77.1274, 4, { invariantPrecisionString: "77.13", invariantCoefficientString: "77.13", exponent: 0, noOfCoefficientDecimals: 2 }],
13
+ [77.1274, 5, { invariantPrecisionString: "77.127", invariantCoefficientString: "77.127", exponent: 0, noOfCoefficientDecimals: 3 }],
14
+ [77.1274, 6, { invariantPrecisionString: "77.1274", invariantCoefficientString: "77.1274", exponent: 0, noOfCoefficientDecimals: 4 }],
15
+ [77.1274, 7, { invariantPrecisionString: "77.12740", invariantCoefficientString: "77.12740", exponent: 0, noOfCoefficientDecimals: 5 }],
16
+ [77.1274, 15, { invariantPrecisionString: "77.1274000000000", invariantCoefficientString: "77.1274000000000", exponent: 0, noOfCoefficientDecimals: 13 }],
17
+ [77.1274, 16, { invariantPrecisionString: "77.12739999999999", invariantCoefficientString: "77.12739999999999", exponent: 0, noOfCoefficientDecimals: 14 }],
18
+ [77.1274, 17, { invariantPrecisionString: "77.127399999999994", invariantCoefficientString: "77.127399999999994", exponent: 0, noOfCoefficientDecimals: 15 }],
19
+ [77.1274, 18, { invariantPrecisionString: "77.1273999999999944", invariantCoefficientString: "77.1273999999999944", exponent: 0, noOfCoefficientDecimals: 16 }],
20
+ [77.1274, 19, { invariantPrecisionString: "77.12739999999999441", invariantCoefficientString: "77.12739999999999441", exponent: 0, noOfCoefficientDecimals: 17 }],
21
+ [77.1274, 20, { invariantPrecisionString: "77.127399999999994407", invariantCoefficientString: "77.127399999999994407", exponent: 0, noOfCoefficientDecimals: 18 }],
22
+ [77.1274, 21, { invariantPrecisionString: "77.1273999999999944066", invariantCoefficientString: "77.1273999999999944066", exponent: 0, noOfCoefficientDecimals: 19 }],
23
+ [0.00000123, 2, { invariantPrecisionString: "0.0000012", invariantCoefficientString: "0.0000012", exponent: 0, noOfCoefficientDecimals: 7 }],
24
+ [0.000000123, 2, { invariantPrecisionString: "1.2e-7", invariantCoefficientString: "1.2", exponent: -7, noOfCoefficientDecimals: 1 }],
25
+ [0.00000123, 21, { invariantPrecisionString: "0.00000123000000000000008198", invariantCoefficientString: "0.00000123000000000000008198", exponent: 0, noOfCoefficientDecimals: 26 }],
26
+ [123456, 3, { invariantPrecisionString: "1.23e+5", invariantCoefficientString: "1.23", exponent: 5, noOfCoefficientDecimals: 2 }],
27
+ [Number.MAX_VALUE, 3, { invariantPrecisionString: "1.80e+308", invariantCoefficientString: "1.80", exponent: 308, noOfCoefficientDecimals: 2 }],
28
+ [123456, 15, { invariantPrecisionString: "123456.000000000", invariantCoefficientString: "123456.000000000", exponent: 0, noOfCoefficientDecimals: 9 }],
29
+ [0, 5, { invariantPrecisionString: "0.00000", invariantCoefficientString: "0.00000", exponent: 0, noOfCoefficientDecimals: 5 }],
30
+ [0.000001, 1, { invariantPrecisionString: "0.000001", invariantCoefficientString: "0.000001", exponent: 0, noOfCoefficientDecimals: 6 }],
31
+ [0.000001, 10, { invariantPrecisionString: "0.000001000000000", invariantCoefficientString: "0.000001000000000", exponent: 0, noOfCoefficientDecimals: 15 }],
32
+ [0.0000001, 10, { invariantPrecisionString: "1.000000000e-7", invariantCoefficientString: "1.000000000", exponent: -7, noOfCoefficientDecimals: 9 }],
33
+ [0.00000000001, 1, { invariantPrecisionString: "1e-11", invariantCoefficientString: "1", exponent: -11, noOfCoefficientDecimals: 0 }],
34
+ [0.00000000001, 2, { invariantPrecisionString: "1.0e-11", invariantCoefficientString: "1.0", exponent: -11, noOfCoefficientDecimals: 1 }],
35
+ [55, 2, { invariantPrecisionString: "55", invariantCoefficientString: "55", exponent: 0, noOfCoefficientDecimals: 0 }],
36
+ [55, 1, { invariantPrecisionString: "6e+1", invariantCoefficientString: "6", exponent: 1, noOfCoefficientDecimals: 0 }],
37
+ [-55, 2, { invariantPrecisionString: "-55", invariantCoefficientString: "-55", exponent: 0, noOfCoefficientDecimals: 0 }],
38
+ [-55, 1, { invariantPrecisionString: "-6e+1", invariantCoefficientString: "-6", exponent: 1, noOfCoefficientDecimals: 0 }],
39
+ [9.59, 1, { invariantPrecisionString: "1e+1", invariantCoefficientString: "1", exponent: 1, noOfCoefficientDecimals: 0 }],
40
+ [9.59, 2, { invariantPrecisionString: "10", invariantCoefficientString: "10", exponent: 0, noOfCoefficientDecimals: 0 }],
41
+ [9.95, 2, { invariantPrecisionString: "10", invariantCoefficientString: "10", exponent: 0, noOfCoefficientDecimals: 0 }],
42
+ [9.96, 2, { invariantPrecisionString: "10", invariantCoefficientString: "10", exponent: 0, noOfCoefficientDecimals: 0 }],
43
+ [-555555555555555555555, 1, { invariantPrecisionString: "-6e+20", invariantCoefficientString: "-6", exponent: 20, noOfCoefficientDecimals: 0 }],
44
+ [-5555555555555555555555, 1, { invariantPrecisionString: "-6e+21", invariantCoefficientString: "-6", exponent: 21, noOfCoefficientDecimals: 0 }],
45
+ [18014398509482012, 21, { invariantPrecisionString: "18014398509482012.0000", invariantCoefficientString: "18014398509482012.0000", exponent: 0, noOfCoefficientDecimals: 4 }],
46
+ [180143985094820121234, 21, { invariantPrecisionString: "180143985094820134912", invariantCoefficientString: "180143985094820134912", exponent: 0, noOfCoefficientDecimals: 0 }],
47
+ [0.1, 21, { invariantPrecisionString: "0.100000000000000005551", invariantCoefficientString: "0.100000000000000005551", exponent: 0, noOfCoefficientDecimals: 21 }],
48
+ [Number.EPSILON, 2, { invariantPrecisionString: "2.2e-16", invariantCoefficientString: "2.2", exponent: -16, noOfCoefficientDecimals: 1 }],
49
+ [Number.EPSILON, 21, { invariantPrecisionString: "2.22044604925031308085e-16", invariantCoefficientString: "2.22044604925031308085", exponent: -16, noOfCoefficientDecimals: 20 }],
50
+ [Number.NaN, 3, { invariantPrecisionString: "NaN", invariantCoefficientString: "NaN", exponent: 0, noOfCoefficientDecimals: 0 }],
51
+ [Number.POSITIVE_INFINITY, 3, { invariantPrecisionString: "Infinity", invariantCoefficientString: "Infinity", exponent: 0, noOfCoefficientDecimals: 0 }],
52
+ [Number.NEGATIVE_INFINITY, 3, { invariantPrecisionString: "-Infinity", invariantCoefficientString: "-Infinity", exponent: 0, noOfCoefficientDecimals: 0 }],
53
+ [164735280, 7, { invariantPrecisionString: "1.647353e+8", invariantCoefficientString: "1.647353", exponent: 8, noOfCoefficientDecimals: 6 }],
54
+ ];
55
+
56
+ for (const [value, precision, expectedParts] of data) {
57
+ expect(NumberFormatter.toPrecision(value, precision)).to.equal(expectedParts.invariantPrecisionString);
58
+
59
+ const localePrecisionString = expectedParts.invariantPrecisionString.replace(invariantDecimalSeparator, localeDecimalSeparator);
60
+ expect(NumberFormatter.toLocalePrecision(value, precision)).to.equal(localePrecisionString);
61
+
62
+ const parts = NumberFormatter.getParts(value, precision);
63
+ expect(parts.invariantPrecisionString).to.equal(expectedParts.invariantPrecisionString);
64
+ expect(parts.invariantCoefficientString).to.equal(expectedParts.invariantCoefficientString);
65
+ expect(parts.exponent).to.equal(expectedParts.exponent);
66
+ expect(parts.noOfCoefficientDecimals).to.equal(expectedParts.noOfCoefficientDecimals);
67
+ }
68
+ })
69
+ })
70
+
71
+ describe("toFixed", () => {
72
+ it("should format correctly", () => {
73
+ const data: [number, number, string][] = [
74
+ [0, 0, "0"],
75
+ [0.0, 1, "0.0"],
76
+ [10, 1, "10"],
77
+ [10, 2, "10"],
78
+ [10, 3, "10.0"],
79
+ [10, 4, "10.00"],
80
+ [10.2, 1, "10"],
81
+ [10.2, 2, "10"],
82
+ [10.2, 3, "10.2"],
83
+ [10.2, 4, "10.20"],
84
+ [217887000000, 6, "217887000000"],
85
+ ];
86
+
87
+ for (const [value, precision, invariantExpected] of data) {
88
+ expect(NumberFormatter.toFixed(value, precision)).to.equal(invariantExpected);
89
+
90
+ const localeExpected = invariantExpected.replace(invariantDecimalSeparator, localeDecimalSeparator);
91
+ expect(NumberFormatter.toLocaleFixed(value, precision)).to.equal(localeExpected);
92
+ }
93
+ })
94
+ })
95
+
96
+ describe("toLocaleFixed", () => {
97
+ it("should format correctly", () => {
98
+ expect(NumberFormatter.toLocaleFixed(400, 2)).to.equal("400");
99
+ })
100
+
101
+ it("should format correctly with max decimals", () => {
102
+ expect(NumberFormatter.toLocaleFixed(35.123456, 8, 2)).to.equal("35.12");
103
+ expect(NumberFormatter.toLocaleFixed(35.129999, 8, 2)).to.equal("35.13");
104
+ })
105
+ });
@@ -1,50 +1,95 @@
1
- const invariantDecimalSeparator = (0.1).toString()[1];
2
- const localeDecimalSeparator = (0.1).toLocaleString()[1];
3
-
4
- class Formatter {
5
- public toLocalePrecision(value: number, precision: number) {
6
- const absValue = Math.abs(value);
7
- if (absValue < 1) {
8
- return toPrecision(value, precision);
9
- }
10
-
11
- const noOfIntegerDigits = Math.round(absValue).toString().length;
12
- return precision >= noOfIntegerDigits
13
- ? toFixed(value, precision - noOfIntegerDigits)
14
- : toPrecision(value, precision);
15
- }
16
-
17
- public toLocaleFixed(value: number, precision: number) {
18
- const formatted = this.toLocalePrecision(value, precision);
19
- const match = /(.*)e\+(\d)$/.exec(formatted);
20
- if (match) {
21
- let number = match[1];
22
- let exponent = parseInt(match[2]);
23
- const decimalSeparatorIndex = number.indexOf(localeDecimalSeparator);
24
- if (decimalSeparatorIndex === -1) {
25
- return `${number}${"0".repeat(exponent)}`;
26
- }
27
- else if (decimalSeparatorIndex >= 0) {
28
- const noOfDecimals = number.length - decimalSeparatorIndex - 1;
29
- if (noOfDecimals < exponent) {
30
- number = number.replace(localeDecimalSeparator, "");
31
- exponent -= noOfDecimals;
32
- return `${number}${"0".repeat(exponent)}`;
33
- }
34
- }
35
- }
36
- return formatted;
37
- }
38
- }
39
-
40
- function toFixed(value: number, fractionDigits: number) {
41
- const formatted = value.toFixed(fractionDigits);
42
- return formatted.replace(invariantDecimalSeparator, localeDecimalSeparator);
43
- }
44
-
45
- function toPrecision(value: number, precision: number) {
46
- const formatted = value.toPrecision(precision);
47
- return formatted.replace(invariantDecimalSeparator, localeDecimalSeparator);
48
- }
49
-
50
- export const NumberFormatter = new Formatter();
1
+ export const invariantDecimalSeparator = (0.1).toString()[1];
2
+ export const localeDecimalSeparator = (0.1).toLocaleString()[1];
3
+
4
+ export interface Parts {
5
+ invariantPrecisionString: string;
6
+ invariantCoefficientString: string;
7
+ exponent: number;
8
+ noOfCoefficientDecimals: number;
9
+ }
10
+
11
+ class Formatter {
12
+ public toPrecision(value: number, precision: number, maxDecimals?: number) {
13
+ const absValue = Math.abs(value);
14
+ const noOfIntegerDigits = Math.round(absValue).toString().length;
15
+
16
+ // Adjust precision to maxDecimals
17
+ if (maxDecimals !== undefined) {
18
+ precision = Math.min(precision, noOfIntegerDigits + maxDecimals);
19
+ }
20
+
21
+ if (value === 0) {
22
+ // (0).toPrecision(1) == "0"
23
+ // (0).toPrecision(2) == "0.0"
24
+ // (0).toPrecision(3) == "0.00"
25
+ // However, we want:
26
+ // Formatter.toPrecision(0, 0) == "0"
27
+ // Formatter.toPrecision(0, 1) == "0.0"
28
+ // Formatter.toPrecision(0, 2) == "0.00"
29
+ return value.toPrecision(precision + 1);
30
+ }
31
+
32
+ if (absValue < 1) {
33
+ return value.toPrecision(precision);
34
+ }
35
+
36
+ return precision >= noOfIntegerDigits
37
+ ? value.toFixed(precision - noOfIntegerDigits)
38
+ : value.toPrecision(precision);
39
+ }
40
+
41
+ public toLocalePrecision(value: number, precision: number, maxDecimals?: number) {
42
+ return this.toPrecision(value, precision, maxDecimals)
43
+ .replace(invariantDecimalSeparator, localeDecimalSeparator);
44
+ }
45
+
46
+ public toFixed(value: number, precision: number, maxDecimals?: number) {
47
+ const parts = this.getParts(value, precision, maxDecimals);
48
+ if (parts.noOfCoefficientDecimals < parts.exponent) {
49
+ const number = parts.invariantCoefficientString.replace(invariantDecimalSeparator, "");
50
+ const zeros = parts.exponent - parts.noOfCoefficientDecimals;
51
+ return `${number}${"0".repeat(zeros)}`;
52
+ }
53
+ else {
54
+ return parts.invariantPrecisionString;
55
+ }
56
+ }
57
+
58
+ public toLocaleFixed(value: number, precision: number, maxDecimals?: number) {
59
+ return this.toFixed(value, precision, maxDecimals)
60
+ .replace(invariantDecimalSeparator, localeDecimalSeparator);
61
+ }
62
+
63
+ public getParts(value: number, precision: number, maxDecimals?: number): Parts {
64
+ const precisionString = this.toPrecision(value, precision, maxDecimals);
65
+ if (precisionString.indexOf("e") >= 0) {
66
+ const match = /(.*)e([\+-]\d+)$/.exec(precisionString)!;
67
+ const coefficientString = match[1];
68
+ const exponent = parseInt(match[2]);
69
+ const decimalSeparatorIndex = coefficientString.indexOf(invariantDecimalSeparator);
70
+ const noOfCoefficientDecimals = decimalSeparatorIndex >= 0
71
+ ? coefficientString.length - decimalSeparatorIndex - 1
72
+ : 0;
73
+ return {
74
+ invariantPrecisionString: precisionString,
75
+ invariantCoefficientString: coefficientString,
76
+ exponent,
77
+ noOfCoefficientDecimals,
78
+ };
79
+ }
80
+ else {
81
+ const decimalSeparatorIndex = precisionString.indexOf(invariantDecimalSeparator);
82
+ const noOfCoefficientDecimals = decimalSeparatorIndex >= 0
83
+ ? precisionString.length - decimalSeparatorIndex - 1
84
+ : 0;
85
+ return {
86
+ invariantPrecisionString: precisionString,
87
+ invariantCoefficientString: precisionString,
88
+ exponent: 0,
89
+ noOfCoefficientDecimals,
90
+ };
91
+ }
92
+ }
93
+ }
94
+
95
+ export const NumberFormatter = new Formatter();
@@ -0,0 +1 @@
1
+ export type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
@@ -0,0 +1,28 @@
1
+ import { expect } from "chai";
2
+ import { PrecisionNumber } from "./precision-number";
3
+
4
+ describe("precision-number", () => {
5
+ it("should not parse ''", () => {
6
+ const number = PrecisionNumber.parse("");
7
+ expect(Number.isNaN(number.value)).to.equal(true);
8
+ expect(number.precision).to.equal(0);
9
+ });
10
+
11
+ it("should parse '1'", () => {
12
+ const number = PrecisionNumber.parse("1");
13
+ expect(number.value).to.equal(1);
14
+ expect(number.precision).to.equal(1);
15
+ });
16
+
17
+ it("should parse '1.'", () => {
18
+ const number = PrecisionNumber.parse("1.");
19
+ expect(number.value).to.equal(1);
20
+ expect(number.precision).to.equal(2);
21
+ });
22
+
23
+ it("should parse '1.0'", () => {
24
+ const number = PrecisionNumber.parse("1.0");
25
+ expect(number.value).to.equal(1);
26
+ expect(number.precision).to.equal(2);
27
+ });
28
+ });
@@ -0,0 +1,36 @@
1
+ import { invariantDecimalSeparator } from ".";
2
+ import { localeDecimalSeparator, NumberFormatter } from "./number-formatter";
3
+
4
+ export class PrecisionNumber {
5
+ constructor(public value: number, public precision: number) {
6
+ }
7
+
8
+ static parse(input: string) {
9
+ let normalized = input.trim().replace(",", ".").replace(localeDecimalSeparator, invariantDecimalSeparator);
10
+ if (normalized.endsWith(invariantDecimalSeparator)) {
11
+ normalized += "0";
12
+ }
13
+ const precision = normalized.replace(".", "").replace("+", "").replace("-", "").length;
14
+ return new PrecisionNumber(parseFloat(normalized), precision);
15
+ }
16
+
17
+ toString() {
18
+ return this.value.toString();
19
+ }
20
+
21
+ toFixed() {
22
+ return NumberFormatter.toFixed(this.value, this.precision);
23
+ }
24
+
25
+ toPrecision() {
26
+ return NumberFormatter.toPrecision(this.value, this.precision);
27
+ }
28
+
29
+ toLocaleFixed() {
30
+ return NumberFormatter.toLocaleFixed(this.value, this.precision);
31
+ }
32
+
33
+ toLocalePrecision() {
34
+ return NumberFormatter.toLocalePrecision(this.value, this.precision);
35
+ }
36
+ }