mdtg 3.3.0 → 4.1.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/README.md +65 -15
- package/package.json +10 -6
- package/src/constants.js +4 -4
- package/src/format.js +12 -12
- package/src/index.d.ts +28 -0
- package/src/index.js +4 -4
- package/src/parse.js +28 -19
- package/src/validation.js +12 -9
package/README.md
CHANGED
|
@@ -11,18 +11,20 @@ npm install mdtg
|
|
|
11
11
|
### Usage
|
|
12
12
|
|
|
13
13
|
```js
|
|
14
|
-
import {
|
|
14
|
+
import { formatMDTG, parseMDTG } from "mdtg";
|
|
15
15
|
|
|
16
|
-
const date = new Date();
|
|
16
|
+
const date = new Date(Date.UTC(2024, 7, 12, 11, 55, 30));
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
formatMDTG(date, { form: "short" }); // "121155Z"
|
|
19
|
+
formatMDTG(date, { form: "standard" }); // "121155Zaug24"
|
|
20
|
+
formatMDTG(date); // "12115530Zaug24" (extended by default)
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
// A is UTC+1, so local time is one hour ahead of UTC.
|
|
23
|
+
const value = formatMDTG(date, { timezone: "A" }); // "12125530Aaug24"
|
|
24
|
+
parseMDTG(value).toISOString(); // "2024-08-12T11:55:30.000Z"
|
|
25
|
+
|
|
26
|
+
parseMDTG("121155Zaug24").toISOString(); // "2024-08-12T11:55:00.000Z"
|
|
27
|
+
parseMDTG("121155Z"); // month and year are current UTC month/year
|
|
26
28
|
```
|
|
27
29
|
|
|
28
30
|
### API
|
|
@@ -30,23 +32,71 @@ console.log(parse("121355M"));
|
|
|
30
32
|
#### `formatMDTG(date, options)`
|
|
31
33
|
|
|
32
34
|
Converts a valid `Date` to a date-time group. `date` defaults to the current
|
|
33
|
-
date. `options.form` is `"short"`, `"
|
|
35
|
+
date. `options.form` is `"short"`, `"standard"`, or `"extended"` (the default).
|
|
34
36
|
`options.timezone` accepts the NATO time-zone letters `A` through `Z`;
|
|
35
|
-
lowercase letters are accepted and unknown values default to `Z`.
|
|
37
|
+
lowercase letters are accepted and unknown values default to `Z`. The timezone
|
|
38
|
+
letter determines the displayed local time; parsing converts it back to a UTC
|
|
39
|
+
`Date`.
|
|
36
40
|
|
|
37
41
|
```js
|
|
38
42
|
formatMDTG(new Date(Date.UTC(2024, 7, 12, 11, 55, 30)), {
|
|
39
|
-
form: "
|
|
43
|
+
form: "extended",
|
|
40
44
|
timezone: "A",
|
|
41
45
|
});
|
|
42
46
|
// "12125530Aaug24"
|
|
43
47
|
```
|
|
44
48
|
|
|
45
|
-
#### `
|
|
49
|
+
#### `parseMDTG(value, options)`
|
|
46
50
|
|
|
47
|
-
Parses short (`121155Z`),
|
|
51
|
+
Parses short (`121155Z`), standard (`121155Zaug24`), and extended
|
|
48
52
|
(`12115530Zaug24`) formats into a `Date`. Short values use the current UTC
|
|
49
|
-
month and year. Years are interpreted as `2000` through
|
|
53
|
+
month and year and have no seconds. Years are interpreted as `2000` through
|
|
54
|
+
`2099`.
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
import { parseMDTG } from "mdtg";
|
|
58
|
+
|
|
59
|
+
parseMDTG("121155Zaug24").toISOString(); // "2024-08-12T11:55:00.000Z"
|
|
60
|
+
parseMDTG("12115530Zaug24").toISOString(); // "2024-08-12T11:55:30.000Z"
|
|
61
|
+
parseMDTG("12125530Aaug24").toISOString(); // "2024-08-12T11:55:30.000Z"
|
|
62
|
+
|
|
63
|
+
parseMDTG("121155Z"); // uses the current UTC month and year
|
|
64
|
+
|
|
65
|
+
parseMDTG("121155Z", {
|
|
66
|
+
referenceDate: new Date(Date.UTC(2024, 7, 1)),
|
|
67
|
+
}); // uses August 2024, independent of the system date
|
|
68
|
+
```
|
|
50
69
|
|
|
51
70
|
Invalid calendar dates and times, such as `310224Zfeb24` or `126155Z`, throw a
|
|
52
71
|
`RangeError`. Other invalid values throw an `Error`.
|
|
72
|
+
|
|
73
|
+
`referenceDate` is an optional valid `Date` used only for short values that omit
|
|
74
|
+
the month and year. If omitted, the current UTC month and year are used.
|
|
75
|
+
|
|
76
|
+
#### Format checks
|
|
77
|
+
|
|
78
|
+
`isShortFormat`, `isStandardFormat`, and `isExtendedFormat` check whether a string
|
|
79
|
+
matches the structure of the corresponding format. `isMDTG` checks whether a
|
|
80
|
+
string matches any of the three structures. These checks do not validate
|
|
81
|
+
calendar dates or times; use `isValidMDTG` when semantic validity is needed.
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
import {
|
|
85
|
+
isShortFormat,
|
|
86
|
+
isStandardFormat,
|
|
87
|
+
isExtendedFormat,
|
|
88
|
+
isMDTG,
|
|
89
|
+
isValidMDTG,
|
|
90
|
+
} from "mdtg";
|
|
91
|
+
|
|
92
|
+
isShortFormat("121155Z"); // true
|
|
93
|
+
isStandardFormat("121155Zaug24"); // true
|
|
94
|
+
isExtendedFormat("12115530Zaug24"); // true
|
|
95
|
+
isMDTG("121155Zaug24"); // true
|
|
96
|
+
isMDTG("invalid"); // false
|
|
97
|
+
isMDTG("310224Zfeb24"); // true: structurally valid
|
|
98
|
+
isValidMDTG("310224Zfeb24"); // false: invalid calendar date
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
For short values, `isValidMDTG` also accepts the same optional `referenceDate`
|
|
102
|
+
as `parseMDTG`.
|
package/package.json
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mdtg",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"main": "src/index.js",
|
|
5
|
+
"types": "src/index.d.ts",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"exports": {
|
|
7
|
-
".":
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./src/index.d.ts",
|
|
10
|
+
"import": "./src/index.js"
|
|
11
|
+
}
|
|
8
12
|
},
|
|
9
13
|
"engines": {
|
|
10
14
|
"node": ">=24.0.0",
|
|
@@ -29,10 +33,10 @@
|
|
|
29
33
|
"license": "MIT",
|
|
30
34
|
"description": "This package offers you methods to convert Date Objects to military date time and vice versa",
|
|
31
35
|
"devDependencies": {
|
|
32
|
-
"@biomejs/biome": "2.5.
|
|
33
|
-
"@vitest/coverage-v8": "5.0.
|
|
34
|
-
"knip": "6.
|
|
36
|
+
"@biomejs/biome": "2.5.15",
|
|
37
|
+
"@vitest/coverage-v8": "5.0.3",
|
|
38
|
+
"knip": "6.39.0",
|
|
35
39
|
"prettier": "3.9.9",
|
|
36
|
-
"vitest": "5.0.
|
|
40
|
+
"vitest": "5.0.3"
|
|
37
41
|
}
|
|
38
42
|
}
|
package/src/constants.js
CHANGED
|
@@ -43,13 +43,13 @@ export const OFFSETS = Object.freeze({
|
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
export const SHORT_FORMAT_REGEX = /^[0-9]{6}[A-Z]$/;
|
|
46
|
-
export const
|
|
46
|
+
export const STANDARD_FORMAT_REGEX =
|
|
47
47
|
/^[0-9]{6}[A-Z](jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[0-9]{2}$/i;
|
|
48
|
-
export const
|
|
48
|
+
export const EXTENDED_FORMAT_REGEX =
|
|
49
49
|
/^[0-9]{8}[A-Z](jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[0-9]{2}$/i;
|
|
50
50
|
|
|
51
51
|
export const FORM = Object.freeze({
|
|
52
52
|
SHORT: "short",
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
STANDARD: "standard",
|
|
54
|
+
EXTENDED: "extended",
|
|
55
55
|
});
|
package/src/format.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { FORM, MONTHS, OFFSETS } from "./constants.js";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
isShortenedFormat,
|
|
3
|
+
isExtendedFormat,
|
|
5
4
|
isShortFormat,
|
|
5
|
+
isStandardFormat,
|
|
6
6
|
validateDate,
|
|
7
7
|
validateTimezone,
|
|
8
8
|
} from "./validation.js";
|
|
@@ -25,7 +25,7 @@ const buildShort = (date, timezone) => {
|
|
|
25
25
|
return result;
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
const
|
|
28
|
+
const buildExtended = (date, timezone) => {
|
|
29
29
|
const result = [
|
|
30
30
|
date.getUTCDate(),
|
|
31
31
|
date.getUTCHours(),
|
|
@@ -40,22 +40,22 @@ const buildLong = (date, timezone) => {
|
|
|
40
40
|
)
|
|
41
41
|
.join("");
|
|
42
42
|
|
|
43
|
-
if (!
|
|
44
|
-
throw new Error("Failed to build
|
|
43
|
+
if (!isExtendedFormat(result)) {
|
|
44
|
+
throw new Error("Failed to build extended MDTG");
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
return result;
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
const
|
|
50
|
+
const buildStandard = (date, timezone) => {
|
|
51
51
|
const result = [
|
|
52
52
|
buildShort(date, timezone),
|
|
53
53
|
MONTHS[date.getUTCMonth()],
|
|
54
54
|
normalizeNumber(date.getUTCFullYear() % 100),
|
|
55
55
|
].join("");
|
|
56
56
|
|
|
57
|
-
if (!
|
|
58
|
-
throw new Error("Failed to build
|
|
57
|
+
if (!isStandardFormat(result)) {
|
|
58
|
+
throw new Error("Failed to build standard MDTG");
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
return result;
|
|
@@ -63,7 +63,7 @@ const buildShortened = (date, timezone) => {
|
|
|
63
63
|
|
|
64
64
|
export const formatMDTG = (
|
|
65
65
|
date = new Date(),
|
|
66
|
-
{ form = FORM.
|
|
66
|
+
{ form = FORM.EXTENDED, timezone: timezoneInput = "Z" } = {},
|
|
67
67
|
) => {
|
|
68
68
|
validateDate(date);
|
|
69
69
|
|
|
@@ -73,9 +73,9 @@ export const formatMDTG = (
|
|
|
73
73
|
switch (form) {
|
|
74
74
|
case FORM.SHORT:
|
|
75
75
|
return buildShort(adjustedDate, timezone);
|
|
76
|
-
case FORM.
|
|
77
|
-
return
|
|
76
|
+
case FORM.STANDARD:
|
|
77
|
+
return buildStandard(adjustedDate, timezone);
|
|
78
78
|
default:
|
|
79
|
-
return
|
|
79
|
+
return buildExtended(adjustedDate, timezone);
|
|
80
80
|
}
|
|
81
81
|
};
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
declare function formatMDTG(
|
|
2
|
+
date?: Date,
|
|
3
|
+
options?: { form?: "short" | "extended" | "standard"; timezone?: string },
|
|
4
|
+
): string;
|
|
5
|
+
|
|
6
|
+
declare function isValidMDTG(
|
|
7
|
+
mdtg: string,
|
|
8
|
+
options?: { referenceDate?: Date },
|
|
9
|
+
): boolean;
|
|
10
|
+
declare function parseMDTG(
|
|
11
|
+
mdtg: string,
|
|
12
|
+
options?: { referenceDate?: Date },
|
|
13
|
+
): Date;
|
|
14
|
+
|
|
15
|
+
declare function isExtendedFormat(mdtg: string): boolean;
|
|
16
|
+
declare function isMDTG(mdtg: string): boolean;
|
|
17
|
+
declare function isShortFormat(mdtg: string): boolean;
|
|
18
|
+
declare function isStandardFormat(mdtg: string): boolean;
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
formatMDTG,
|
|
22
|
+
isExtendedFormat,
|
|
23
|
+
isMDTG,
|
|
24
|
+
isShortFormat,
|
|
25
|
+
isStandardFormat,
|
|
26
|
+
isValidMDTG,
|
|
27
|
+
parseMDTG,
|
|
28
|
+
};
|
package/src/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { FORM } from "./constants.js";
|
|
2
1
|
export { formatMDTG } from "./format.js";
|
|
3
|
-
export {
|
|
2
|
+
export { isValidMDTG, parseMDTG } from "./parse.js";
|
|
4
3
|
export {
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
isExtendedFormat,
|
|
5
|
+
isMDTG,
|
|
7
6
|
isShortFormat,
|
|
7
|
+
isStandardFormat,
|
|
8
8
|
} from "./validation.js";
|
package/src/parse.js
CHANGED
|
@@ -1,37 +1,34 @@
|
|
|
1
1
|
import { MONTHS, OFFSETS } from "./constants.js";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
isExtendedFormat,
|
|
4
|
+
isMDTG,
|
|
5
5
|
isShortFormat,
|
|
6
|
+
isStandardFormat,
|
|
7
|
+
validateDate,
|
|
6
8
|
validateDateTime,
|
|
7
9
|
} from "./validation.js";
|
|
8
10
|
|
|
9
11
|
const parseInteger = (input) => Number.parseInt(input, 10);
|
|
10
12
|
|
|
11
|
-
const parseShort = (value) => {
|
|
13
|
+
const parseShort = (value, referenceDate) => {
|
|
12
14
|
const day = parseInteger(value.slice(0, 2));
|
|
13
15
|
const hours = parseInteger(value.slice(2, 4));
|
|
14
16
|
const minutes = parseInteger(value.slice(4, 6));
|
|
15
17
|
const timezone = value.slice(6, 7).toUpperCase();
|
|
16
|
-
|
|
18
|
+
validateDate(referenceDate);
|
|
17
19
|
|
|
18
20
|
validateDateTime({
|
|
19
|
-
year:
|
|
20
|
-
month:
|
|
21
|
+
year: referenceDate.getUTCFullYear(),
|
|
22
|
+
month: referenceDate.getUTCMonth(),
|
|
21
23
|
day,
|
|
22
24
|
hours,
|
|
23
25
|
minutes,
|
|
24
26
|
});
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
day,
|
|
31
|
-
hours + OFFSETS[timezone],
|
|
32
|
-
minutes,
|
|
33
|
-
),
|
|
34
|
-
);
|
|
28
|
+
const result = new Date(referenceDate);
|
|
29
|
+
result.setUTCDate(day);
|
|
30
|
+
result.setUTCHours(hours + OFFSETS[timezone], minutes, 0, 0);
|
|
31
|
+
return result;
|
|
35
32
|
};
|
|
36
33
|
|
|
37
34
|
const parseWithMonth = (value, { hasSeconds, timezoneIndex, monthStart }) => {
|
|
@@ -60,16 +57,16 @@ const parseWithMonth = (value, { hasSeconds, timezoneIndex, monthStart }) => {
|
|
|
60
57
|
return new Date(Date.UTC(...dateParts));
|
|
61
58
|
};
|
|
62
59
|
|
|
63
|
-
export const
|
|
64
|
-
if (isShortFormat(value)) return parseShort(value);
|
|
65
|
-
if (
|
|
60
|
+
export const parseMDTG = (value, { referenceDate = new Date() } = {}) => {
|
|
61
|
+
if (isShortFormat(value)) return parseShort(value, referenceDate);
|
|
62
|
+
if (isStandardFormat(value)) {
|
|
66
63
|
return parseWithMonth(value, {
|
|
67
64
|
hasSeconds: false,
|
|
68
65
|
timezoneIndex: 6,
|
|
69
66
|
monthStart: 7,
|
|
70
67
|
});
|
|
71
68
|
}
|
|
72
|
-
if (
|
|
69
|
+
if (isExtendedFormat(value)) {
|
|
73
70
|
return parseWithMonth(value, {
|
|
74
71
|
hasSeconds: true,
|
|
75
72
|
timezoneIndex: 8,
|
|
@@ -79,3 +76,15 @@ export const parse = (value) => {
|
|
|
79
76
|
|
|
80
77
|
throw new Error(`Invalid MDTG string "${value}"`);
|
|
81
78
|
};
|
|
79
|
+
|
|
80
|
+
export const isValidMDTG = (value, options) => {
|
|
81
|
+
if (!isMDTG(value)) return false;
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
parseMDTG(value, options);
|
|
85
|
+
return true;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (error instanceof RangeError) return false;
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
};
|
package/src/validation.js
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
EXTENDED_FORMAT_REGEX,
|
|
3
3
|
OFFSETS,
|
|
4
4
|
SHORT_FORMAT_REGEX,
|
|
5
|
-
|
|
5
|
+
STANDARD_FORMAT_REGEX,
|
|
6
6
|
} from "./constants.js";
|
|
7
7
|
|
|
8
8
|
export const isShortFormat = (value) =>
|
|
9
9
|
typeof value === "string" && SHORT_FORMAT_REGEX.test(value);
|
|
10
10
|
|
|
11
|
-
export const
|
|
12
|
-
typeof value === "string" &&
|
|
11
|
+
export const isStandardFormat = (value) =>
|
|
12
|
+
typeof value === "string" && STANDARD_FORMAT_REGEX.test(value);
|
|
13
13
|
|
|
14
|
-
export const
|
|
15
|
-
typeof value === "string" &&
|
|
14
|
+
export const isExtendedFormat = (value) =>
|
|
15
|
+
typeof value === "string" && EXTENDED_FORMAT_REGEX.test(value);
|
|
16
|
+
|
|
17
|
+
export const isMDTG = (value) =>
|
|
18
|
+
isShortFormat(value) || isStandardFormat(value) || isExtendedFormat(value);
|
|
16
19
|
|
|
17
20
|
export const validateDate = (date) => {
|
|
18
21
|
if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
|
|
@@ -33,9 +36,9 @@ export const validateDateTime = ({
|
|
|
33
36
|
minutes,
|
|
34
37
|
seconds,
|
|
35
38
|
}) => {
|
|
36
|
-
const candidate = new Date(
|
|
37
|
-
|
|
38
|
-
);
|
|
39
|
+
const candidate = new Date(0);
|
|
40
|
+
candidate.setUTCFullYear(year, month, day);
|
|
41
|
+
candidate.setUTCHours(hours, minutes, seconds ?? 0, 0);
|
|
39
42
|
|
|
40
43
|
if (
|
|
41
44
|
candidate.getUTCFullYear() !== year ||
|