@canvas-components/utils 0.2.1 → 0.2.2
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 +130 -103
- package/dist/date/index.cjs +162 -0
- package/dist/date/index.cjs.map +1 -0
- package/dist/date/index.d.cts +13 -0
- package/dist/date/index.d.ts +13 -0
- package/dist/date/index.js +158 -0
- package/dist/date/index.js.map +1 -0
- package/dist/index.cjs +553 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +537 -1
- package/dist/index.js.map +1 -1
- package/dist/number/index.cjs +399 -0
- package/dist/number/index.cjs.map +1 -0
- package/dist/number/index.d.cts +44 -0
- package/dist/number/index.d.ts +44 -0
- package/dist/number/index.js +384 -0
- package/dist/number/index.js.map +1 -0
- package/package.json +11 -1
package/README.md
CHANGED
|
@@ -1,114 +1,141 @@
|
|
|
1
|
-
# @canvas-components/utils
|
|
2
|
-
|
|
3
|
-
`@canvas-components/utils`
|
|
4
|
-
|
|
5
|
-
## 功能作用
|
|
6
|
-
|
|
1
|
+
# @canvas-components/utils
|
|
2
|
+
|
|
3
|
+
`@canvas-components/utils` 提供表格包之间可复用的纯原生工具能力,覆盖日期时间、数据读取、剪贴板写入和导出文件。
|
|
4
|
+
|
|
5
|
+
## 功能作用
|
|
6
|
+
|
|
7
7
|
- 写入文本、二维表格文本、JSON 到系统剪贴板
|
|
8
8
|
- 通过 `dataIndex` 安全读取嵌套数据
|
|
9
|
+
- 使用 Day.js 风格的常用 token 格式化日期时间,不依赖 Day.js 运行时
|
|
10
|
+
- 复用数字、金额、百分比、科学计数法和自定义数字格式化
|
|
9
11
|
- 复用数据路径比较、数值约束和矩形命中等纯函数
|
|
10
|
-
- 把行列数据导出为 `csv`、`txt`、`xlsx`
|
|
11
|
-
- 生成导出前的中间文本 / 记录结构
|
|
12
|
-
- 对大批量导出做分片让步,减少主线程长时间阻塞
|
|
13
|
-
|
|
14
|
-
## 安装方法
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
pnpm add @canvas-components/utils
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
最低环境要求:
|
|
21
|
-
|
|
22
|
-
- `Node.js >= 18.12.0`
|
|
23
|
-
- `pnpm >= 9.0.0`
|
|
24
|
-
|
|
25
|
-
## 使用方法
|
|
26
|
-
|
|
27
|
-
```ts
|
|
28
|
-
import {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
{ name: "
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
|
75
|
-
|
|
|
76
|
-
| `
|
|
77
|
-
|
|
78
|
-
| `copyJson` | `(value: unknown, space = 2) => Promise<void>` | 写入 JSON 文本 |
|
|
79
|
-
| `formatRowsForClipboard` | `(rows) => string` | 把二维数据转成制表符文本 |
|
|
80
|
-
|
|
12
|
+
- 把行列数据导出为 `csv`、`txt`、`xlsx`
|
|
13
|
+
- 生成导出前的中间文本 / 记录结构
|
|
14
|
+
- 对大批量导出做分片让步,减少主线程长时间阻塞
|
|
15
|
+
|
|
16
|
+
## 安装方法
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add @canvas-components/utils
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
最低环境要求:
|
|
23
|
+
|
|
24
|
+
- `Node.js >= 18.12.0`
|
|
25
|
+
- `pnpm >= 9.0.0`
|
|
26
|
+
|
|
27
|
+
## 使用方法
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { copyRows, exportTableToXlsx } from "@canvas-components/utils";
|
|
31
|
+
|
|
32
|
+
await copyRows([
|
|
33
|
+
["姓名", "年龄"],
|
|
34
|
+
["张三", 28],
|
|
35
|
+
["李四", 31],
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
await exportTableToXlsx({
|
|
39
|
+
fileName: "users.xlsx",
|
|
40
|
+
rows: [
|
|
41
|
+
{ name: "张三", age: 28 },
|
|
42
|
+
{ name: "李四", age: 31 },
|
|
43
|
+
],
|
|
44
|
+
columns: [
|
|
45
|
+
{ key: "name", title: "姓名", value: (row) => row.name },
|
|
46
|
+
{ key: "age", title: "年龄", value: (row) => row.age },
|
|
47
|
+
],
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 属性说明
|
|
52
|
+
|
|
53
|
+
### `ExportTableColumn<Row>`
|
|
54
|
+
|
|
55
|
+
| 属性 | 类型 | 说明 |
|
|
56
|
+
| ------- | -------------------------------------- | ------------------ |
|
|
57
|
+
| `key` | `string` | 列唯一标识 |
|
|
58
|
+
| `title` | `string` | 导出列标题 |
|
|
59
|
+
| `value` | `(row: Row, index: number) => unknown` | 从行数据中取导出值 |
|
|
60
|
+
|
|
61
|
+
### `ClipboardCellValue`
|
|
62
|
+
|
|
63
|
+
支持的剪贴板单元格值类型:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
type ClipboardCellValue = string | number | boolean | null | undefined;
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 方法说明
|
|
70
|
+
|
|
71
|
+
### 剪贴板
|
|
72
|
+
|
|
73
|
+
| 方法 | 签名 | 说明 |
|
|
74
|
+
| ------------------------ | ------------------------------------------------- | ------------------------ |
|
|
75
|
+
| `copyText` | `(text: string) => Promise<void>` | 写入纯文本 |
|
|
76
|
+
| `copyRows` | `(rows: ClipboardCellValue[][]) => Promise<void>` | 把二维数据写入剪贴板 |
|
|
77
|
+
| `copyJson` | `(value: unknown, space = 2) => Promise<void>` | 写入 JSON 文本 |
|
|
78
|
+
| `formatRowsForClipboard` | `(rows) => string` | 把二维数据转成制表符文本 |
|
|
79
|
+
|
|
81
80
|
### 导出
|
|
82
|
-
|
|
83
|
-
| 方法
|
|
84
|
-
|
|
|
85
|
-
| `mapRowsToExportRecords` | `({ rows, columns }) => Record<string, unknown>[]`
|
|
86
|
-
| `buildCsvContent`
|
|
87
|
-
| `serializeCsvRows`
|
|
88
|
-
| `buildTxtContent`
|
|
89
|
-
| `downloadJsonFile`
|
|
90
|
-
| `exportTableToCsv`
|
|
91
|
-
| `exportTableToTxt`
|
|
92
|
-
| `exportTableToXlsx`
|
|
81
|
+
|
|
82
|
+
| 方法 | 签名 | 说明 |
|
|
83
|
+
| ------------------------ | ------------------------------------------------------------ | --------------------------- |
|
|
84
|
+
| `mapRowsToExportRecords` | `({ rows, columns }) => Record<string, unknown>[]` | 生成导出记录数组 |
|
|
85
|
+
| `buildCsvContent` | `({ rows, columns }) => string` | 生成 CSV 文本 |
|
|
86
|
+
| `serializeCsvRows` | `(rows, lineBreak?) => string` | 将二维数据序列化为 CSV 文本 |
|
|
87
|
+
| `buildTxtContent` | `({ rows, columns }) => string` | 生成 TXT 文本 |
|
|
88
|
+
| `downloadJsonFile` | `({ fileName, data, space? }) => Promise<void>` | 下载 JSON |
|
|
89
|
+
| `exportTableToCsv` | `({ fileName, rows, columns }) => Promise<void>` | 导出 CSV |
|
|
90
|
+
| `exportTableToTxt` | `({ fileName, rows, columns }) => Promise<void>` | 导出 TXT |
|
|
91
|
+
| `exportTableToXlsx` | `({ fileName, rows, columns, sheetName? }) => Promise<void>` | 导出 XLSX |
|
|
93
92
|
|
|
94
93
|
### 数据读取
|
|
95
94
|
|
|
96
|
-
| 方法
|
|
97
|
-
|
|
|
95
|
+
| 方法 | 签名 | 说明 |
|
|
96
|
+
| --------------------- | --------------------------------- | -------------------------------------------- |
|
|
98
97
|
| `getValueByDataIndex` | `(source, dataIndex?) => unknown` | 通过字符串、数字或属性路径数组安全读取嵌套值 |
|
|
99
|
-
| `stringifyDataIndex`
|
|
100
|
-
| `isSameDataIndex`
|
|
98
|
+
| `stringifyDataIndex` | `(dataIndex) => string` | 将属性路径转换为稳定字符串 |
|
|
99
|
+
| `isSameDataIndex` | `(left, right) => boolean` | 判断两个属性路径是否一致 |
|
|
100
|
+
|
|
101
|
+
### 日期时间
|
|
102
|
+
|
|
103
|
+
可从主入口或 `@canvas-components/utils/date` 子路径引入。
|
|
104
|
+
|
|
105
|
+
| 方法 | 签名 | 说明 |
|
|
106
|
+
| ------------------------- | --------------------------------------------- | ---------------------------------------------- |
|
|
107
|
+
| `resolveDateValue` | `(value: unknown) => Date \| null` | 将日期、时间戳或日期字符串解析为有效日期 |
|
|
108
|
+
| `isDateTimeFormatPattern` | `(pattern: string) => boolean` | 判断是否包含日期时间格式 token |
|
|
109
|
+
| `formatDateTimeValue` | `(value, pattern, locale?) => string \| null` | 按格式字符串输出日期时间,默认使用中文区域设置 |
|
|
110
|
+
|
|
111
|
+
常用 token 包括 `YYYY`、`YY`、`M`、`MM`、`D`、`DD`、`ddd`、`dddd`、`H`、`HH`、`h`、`hh`、`m`、`mm`、`s`、`ss`、`SSS`、`A`。方括号中的内容按普通文本输出,例如 `YYYY年M月D日 [更新]`。
|
|
112
|
+
|
|
113
|
+
### 数字格式
|
|
114
|
+
|
|
115
|
+
可从主入口或 `@canvas-components/utils/number` 子路径引入。
|
|
116
|
+
|
|
117
|
+
| 方法 | 签名 | 说明 |
|
|
118
|
+
| --------------------------- | ------------------------------------ | -------------------------------------------- |
|
|
119
|
+
| `parseNumberValue` | `(value: unknown) => number \| null` | 解析数字和带千分位的数字字符串 |
|
|
120
|
+
| `formatNumberValue` | `(value, options?) => string` | 格式化数字、金额和千分位数字 |
|
|
121
|
+
| `formatPercentValue` | `(value, decimalPlaces?) => string` | 将比例数值格式化为百分比 |
|
|
122
|
+
| `formatScientificValue` | `(value, decimalPlaces?) => string` | 格式化科学计数法 |
|
|
123
|
+
| `formatCustomNumberPattern` | `(value, pattern) => string` | 按常见表格数字 pattern 格式化 |
|
|
124
|
+
| `formatChineseNumber` | `(value, style?) => string \| null` | 将数字转换为普通或金融大写中文数字 |
|
|
125
|
+
| `parseChineseNumber` | `(value) => number \| null` | 将规范的普通或金融大写中文数字转为阿拉伯数字 |
|
|
126
|
+
|
|
127
|
+
金融大写使用规范数字字符,例如 `918000` 输出 `玖拾壹万捌仟`;不兼容形近字或错别字。
|
|
101
128
|
|
|
102
129
|
### 基础几何
|
|
103
130
|
|
|
104
|
-
| 方法
|
|
105
|
-
|
|
|
106
|
-
| `clamp`
|
|
107
|
-
| `isPointInRect` | `(x, y, rect) => boolean`
|
|
108
|
-
|
|
109
|
-
## 适用场景
|
|
110
|
-
|
|
111
|
-
- 表格导出
|
|
112
|
-
- 系统复制功能
|
|
113
|
-
- 大批量数据下载
|
|
114
|
-
- 非 React 项目里的浏览器工具函数复用
|
|
131
|
+
| 方法 | 签名 | 说明 |
|
|
132
|
+
| --------------- | ------------------------------------- | ---------------------- |
|
|
133
|
+
| `clamp` | `(value, minimum, maximum) => number` | 将数值限制在闭区间内 |
|
|
134
|
+
| `isPointInRect` | `(x, y, rect) => boolean` | 判断坐标是否位于矩形内 |
|
|
135
|
+
|
|
136
|
+
## 适用场景
|
|
137
|
+
|
|
138
|
+
- 表格导出
|
|
139
|
+
- 系统复制功能
|
|
140
|
+
- 大批量数据下载
|
|
141
|
+
- 非 React 项目里的浏览器工具函数复用
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/date/index.ts
|
|
4
|
+
var DATE_TIME_FORMAT_TOKEN = /Y{2,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|m{1,2}|s{1,2}|S{1,3}|A|a/;
|
|
5
|
+
var FORMAT_TOKEN = /\[[^\]]*\]|YYYY|MMMM|MMM|dddd|ddd|SSS|YY|MM|DD|dd|HH|hh|mm|ss|SS|M|D|d|H|h|m|s|S|A|a/g;
|
|
6
|
+
var ZH_WEEKDAYS = [
|
|
7
|
+
"\u661F\u671F\u65E5",
|
|
8
|
+
"\u661F\u671F\u4E00",
|
|
9
|
+
"\u661F\u671F\u4E8C",
|
|
10
|
+
"\u661F\u671F\u4E09",
|
|
11
|
+
"\u661F\u671F\u56DB",
|
|
12
|
+
"\u661F\u671F\u4E94",
|
|
13
|
+
"\u661F\u671F\u516D"
|
|
14
|
+
];
|
|
15
|
+
var ZH_WEEKDAYS_SHORT = [
|
|
16
|
+
"\u5468\u65E5",
|
|
17
|
+
"\u5468\u4E00",
|
|
18
|
+
"\u5468\u4E8C",
|
|
19
|
+
"\u5468\u4E09",
|
|
20
|
+
"\u5468\u56DB",
|
|
21
|
+
"\u5468\u4E94",
|
|
22
|
+
"\u5468\u516D"
|
|
23
|
+
];
|
|
24
|
+
var EN_WEEKDAYS = [
|
|
25
|
+
"Sunday",
|
|
26
|
+
"Monday",
|
|
27
|
+
"Tuesday",
|
|
28
|
+
"Wednesday",
|
|
29
|
+
"Thursday",
|
|
30
|
+
"Friday",
|
|
31
|
+
"Saturday"
|
|
32
|
+
];
|
|
33
|
+
var EN_MONTHS = [
|
|
34
|
+
"January",
|
|
35
|
+
"February",
|
|
36
|
+
"March",
|
|
37
|
+
"April",
|
|
38
|
+
"May",
|
|
39
|
+
"June",
|
|
40
|
+
"July",
|
|
41
|
+
"August",
|
|
42
|
+
"September",
|
|
43
|
+
"October",
|
|
44
|
+
"November",
|
|
45
|
+
"December"
|
|
46
|
+
];
|
|
47
|
+
function resolveDateValue(value) {
|
|
48
|
+
if (value instanceof Date) {
|
|
49
|
+
return Number.isNaN(value.getTime()) ? null : value;
|
|
50
|
+
}
|
|
51
|
+
if (typeof value !== "string" && typeof value !== "number") {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
if (typeof value === "string") {
|
|
55
|
+
const localDate = resolveLocalDateString(value);
|
|
56
|
+
if (localDate) {
|
|
57
|
+
return localDate;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const resolved = new Date(value);
|
|
61
|
+
return Number.isNaN(resolved.getTime()) ? null : resolved;
|
|
62
|
+
}
|
|
63
|
+
function isDateTimeFormatPattern(pattern) {
|
|
64
|
+
return DATE_TIME_FORMAT_TOKEN.test(pattern.replace(/\[[^\]]*\]/g, ""));
|
|
65
|
+
}
|
|
66
|
+
function formatDateTimeValue(value, pattern, locale = "zh-cn") {
|
|
67
|
+
const date = resolveDateValue(value);
|
|
68
|
+
const normalizedPattern = pattern.trim();
|
|
69
|
+
if (!date || !normalizedPattern) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const year = date.getFullYear();
|
|
73
|
+
const monthIndex = date.getMonth();
|
|
74
|
+
const month = monthIndex + 1;
|
|
75
|
+
const day = date.getDate();
|
|
76
|
+
const weekdayIndex = date.getDay();
|
|
77
|
+
const hour = date.getHours();
|
|
78
|
+
const hour12 = hour % 12 || 12;
|
|
79
|
+
const minute = date.getMinutes();
|
|
80
|
+
const second = date.getSeconds();
|
|
81
|
+
const millisecond = date.getMilliseconds();
|
|
82
|
+
const chinese = locale.toLowerCase().startsWith("zh");
|
|
83
|
+
const marker = hour >= 12 ? chinese ? "\u4E0B\u5348" : "PM" : chinese ? "\u4E0A\u5348" : "AM";
|
|
84
|
+
const weekday = chinese ? ZH_WEEKDAYS[weekdayIndex] ?? "" : EN_WEEKDAYS[weekdayIndex] ?? "";
|
|
85
|
+
const weekdayShort = chinese ? ZH_WEEKDAYS_SHORT[weekdayIndex] ?? "" : weekday.slice(0, 3);
|
|
86
|
+
const monthName = chinese ? `${month}\u6708` : EN_MONTHS[monthIndex] ?? "";
|
|
87
|
+
const values = {
|
|
88
|
+
YYYY: String(year).padStart(4, "0"),
|
|
89
|
+
YY: String(year).slice(-2),
|
|
90
|
+
M: String(month),
|
|
91
|
+
MM: padNumber(month),
|
|
92
|
+
MMM: chinese ? monthName : monthName.slice(0, 3),
|
|
93
|
+
MMMM: monthName,
|
|
94
|
+
D: String(day),
|
|
95
|
+
DD: padNumber(day),
|
|
96
|
+
d: String(weekdayIndex),
|
|
97
|
+
dd: chinese ? weekdayShort.slice(-1) : weekday.slice(0, 2),
|
|
98
|
+
ddd: weekdayShort,
|
|
99
|
+
dddd: weekday,
|
|
100
|
+
H: String(hour),
|
|
101
|
+
HH: padNumber(hour),
|
|
102
|
+
h: String(hour12),
|
|
103
|
+
hh: padNumber(hour12),
|
|
104
|
+
m: String(minute),
|
|
105
|
+
mm: padNumber(minute),
|
|
106
|
+
s: String(second),
|
|
107
|
+
ss: padNumber(second),
|
|
108
|
+
S: String(Math.floor(millisecond / 100)),
|
|
109
|
+
SS: String(Math.floor(millisecond / 10)).padStart(2, "0"),
|
|
110
|
+
SSS: String(millisecond).padStart(3, "0"),
|
|
111
|
+
A: marker,
|
|
112
|
+
a: marker.toLowerCase()
|
|
113
|
+
};
|
|
114
|
+
return normalizedPattern.replace(
|
|
115
|
+
FORMAT_TOKEN,
|
|
116
|
+
(token) => token.startsWith("[") && token.endsWith("]") ? token.slice(1, -1) : values[token] ?? token
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
function padNumber(value) {
|
|
120
|
+
return String(value).padStart(2, "0");
|
|
121
|
+
}
|
|
122
|
+
function resolveLocalDateString(value) {
|
|
123
|
+
const match = value.trim().match(
|
|
124
|
+
/^(\d{4})[-/](\d{1,2})[-/](\d{1,2})(?:[ T](\d{1,2}):?(\d{2})?(?::?(\d{2}))?(?:\.(\d{1,3}))?)?$/
|
|
125
|
+
);
|
|
126
|
+
if (!match) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
const [
|
|
130
|
+
,
|
|
131
|
+
yearText,
|
|
132
|
+
monthText,
|
|
133
|
+
dayText,
|
|
134
|
+
hourText,
|
|
135
|
+
minuteText,
|
|
136
|
+
secondText,
|
|
137
|
+
millisecondText
|
|
138
|
+
] = match;
|
|
139
|
+
const year = Number(yearText);
|
|
140
|
+
const monthIndex = Number(monthText) - 1;
|
|
141
|
+
const day = Number(dayText);
|
|
142
|
+
const hour = Number(hourText ?? 0);
|
|
143
|
+
const minute = Number(minuteText ?? 0);
|
|
144
|
+
const second = Number(secondText ?? 0);
|
|
145
|
+
const millisecond = Number((millisecondText ?? "0").padEnd(3, "0"));
|
|
146
|
+
const resolved = new Date(
|
|
147
|
+
year,
|
|
148
|
+
monthIndex,
|
|
149
|
+
day,
|
|
150
|
+
hour,
|
|
151
|
+
minute,
|
|
152
|
+
second,
|
|
153
|
+
millisecond
|
|
154
|
+
);
|
|
155
|
+
return resolved.getFullYear() === year && resolved.getMonth() === monthIndex && resolved.getDate() === day && resolved.getHours() === hour && resolved.getMinutes() === minute && resolved.getSeconds() === second ? resolved : null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
exports.formatDateTimeValue = formatDateTimeValue;
|
|
159
|
+
exports.isDateTimeFormatPattern = isDateTimeFormatPattern;
|
|
160
|
+
exports.resolveDateValue = resolveDateValue;
|
|
161
|
+
//# sourceMappingURL=index.cjs.map
|
|
162
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/date/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/** Day.js 风格日期时间格式中支持识别的 token。 */\nconst DATE_TIME_FORMAT_TOKEN =\n /Y{2,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|m{1,2}|s{1,2}|S{1,3}|A|a/;\n\nconst FORMAT_TOKEN =\n /\\[[^\\]]*\\]|YYYY|MMMM|MMM|dddd|ddd|SSS|YY|MM|DD|dd|HH|hh|mm|ss|SS|M|D|d|H|h|m|s|S|A|a/g;\n\nconst ZH_WEEKDAYS = [\n \"星期日\",\n \"星期一\",\n \"星期二\",\n \"星期三\",\n \"星期四\",\n \"星期五\",\n \"星期六\",\n];\nconst ZH_WEEKDAYS_SHORT = [\n \"周日\",\n \"周一\",\n \"周二\",\n \"周三\",\n \"周四\",\n \"周五\",\n \"周六\",\n];\nconst EN_WEEKDAYS = [\n \"Sunday\",\n \"Monday\",\n \"Tuesday\",\n \"Wednesday\",\n \"Thursday\",\n \"Friday\",\n \"Saturday\",\n];\nconst EN_MONTHS = [\n \"January\",\n \"February\",\n \"March\",\n \"April\",\n \"May\",\n \"June\",\n \"July\",\n \"August\",\n \"September\",\n \"October\",\n \"November\",\n \"December\",\n];\n\n/** 将未知值解析为有效日期。 */\nexport function resolveDateValue(value: unknown): Date | null {\n if (value instanceof Date) {\n return Number.isNaN(value.getTime()) ? null : value;\n }\n if (typeof value !== \"string\" && typeof value !== \"number\") {\n return null;\n }\n if (typeof value === \"string\") {\n const localDate = resolveLocalDateString(value);\n if (localDate) {\n return localDate;\n }\n }\n const resolved = new Date(value);\n return Number.isNaN(resolved.getTime()) ? null : resolved;\n}\n\n/** 判断字符串中是否包含 Day.js 风格的日期时间格式 token。 */\nexport function isDateTimeFormatPattern(pattern: string) {\n return DATE_TIME_FORMAT_TOKEN.test(pattern.replace(/\\[[^\\]]*\\]/g, \"\"));\n}\n\n/**\n * 使用 Day.js 风格的常用 token 格式化日期时间,不依赖 Day.js 运行时。\n *\n * 支持年、月、日、星期、24/12 小时、分、秒、毫秒和上下午标记;\n * 默认使用中文区域设置,值或 pattern 无效时返回 `null`。\n */\nexport function formatDateTimeValue(\n value: unknown,\n pattern: string,\n locale = \"zh-cn\",\n): string | null {\n const date = resolveDateValue(value);\n const normalizedPattern = pattern.trim();\n if (!date || !normalizedPattern) {\n return null;\n }\n const year = date.getFullYear();\n const monthIndex = date.getMonth();\n const month = monthIndex + 1;\n const day = date.getDate();\n const weekdayIndex = date.getDay();\n const hour = date.getHours();\n const hour12 = hour % 12 || 12;\n const minute = date.getMinutes();\n const second = date.getSeconds();\n const millisecond = date.getMilliseconds();\n const chinese = locale.toLowerCase().startsWith(\"zh\");\n const marker =\n hour >= 12 ? (chinese ? \"下午\" : \"PM\") : chinese ? \"上午\" : \"AM\";\n const weekday = chinese\n ? (ZH_WEEKDAYS[weekdayIndex] ?? \"\")\n : (EN_WEEKDAYS[weekdayIndex] ?? \"\");\n const weekdayShort = chinese\n ? (ZH_WEEKDAYS_SHORT[weekdayIndex] ?? \"\")\n : weekday.slice(0, 3);\n const monthName = chinese ? `${month}月` : (EN_MONTHS[monthIndex] ?? \"\");\n const values: Record<string, string> = {\n YYYY: String(year).padStart(4, \"0\"),\n YY: String(year).slice(-2),\n M: String(month),\n MM: padNumber(month),\n MMM: chinese ? monthName : monthName.slice(0, 3),\n MMMM: monthName,\n D: String(day),\n DD: padNumber(day),\n d: String(weekdayIndex),\n dd: chinese ? weekdayShort.slice(-1) : weekday.slice(0, 2),\n ddd: weekdayShort,\n dddd: weekday,\n H: String(hour),\n HH: padNumber(hour),\n h: String(hour12),\n hh: padNumber(hour12),\n m: String(minute),\n mm: padNumber(minute),\n s: String(second),\n ss: padNumber(second),\n S: String(Math.floor(millisecond / 100)),\n SS: String(Math.floor(millisecond / 10)).padStart(2, \"0\"),\n SSS: String(millisecond).padStart(3, \"0\"),\n A: marker,\n a: marker.toLowerCase(),\n };\n\n return normalizedPattern.replace(FORMAT_TOKEN, (token) =>\n token.startsWith(\"[\") && token.endsWith(\"]\")\n ? token.slice(1, -1)\n : (values[token] ?? token),\n );\n}\n\nfunction padNumber(value: number) {\n return String(value).padStart(2, \"0\");\n}\n\nfunction resolveLocalDateString(value: string) {\n const match = value\n .trim()\n .match(\n /^(\\d{4})[-/](\\d{1,2})[-/](\\d{1,2})(?:[ T](\\d{1,2}):?(\\d{2})?(?::?(\\d{2}))?(?:\\.(\\d{1,3}))?)?$/,\n );\n if (!match) {\n return null;\n }\n const [\n ,\n yearText,\n monthText,\n dayText,\n hourText,\n minuteText,\n secondText,\n millisecondText,\n ] = match;\n const year = Number(yearText);\n const monthIndex = Number(monthText) - 1;\n const day = Number(dayText);\n const hour = Number(hourText ?? 0);\n const minute = Number(minuteText ?? 0);\n const second = Number(secondText ?? 0);\n const millisecond = Number((millisecondText ?? \"0\").padEnd(3, \"0\"));\n const resolved = new Date(\n year,\n monthIndex,\n day,\n hour,\n minute,\n second,\n millisecond,\n );\n return resolved.getFullYear() === year &&\n resolved.getMonth() === monthIndex &&\n resolved.getDate() === day &&\n resolved.getHours() === hour &&\n resolved.getMinutes() === minute &&\n resolved.getSeconds() === second\n ? resolved\n : null;\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** 将未知值解析为有效日期。 */
|
|
2
|
+
declare function resolveDateValue(value: unknown): Date | null;
|
|
3
|
+
/** 判断字符串中是否包含 Day.js 风格的日期时间格式 token。 */
|
|
4
|
+
declare function isDateTimeFormatPattern(pattern: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* 使用 Day.js 风格的常用 token 格式化日期时间,不依赖 Day.js 运行时。
|
|
7
|
+
*
|
|
8
|
+
* 支持年、月、日、星期、24/12 小时、分、秒、毫秒和上下午标记;
|
|
9
|
+
* 默认使用中文区域设置,值或 pattern 无效时返回 `null`。
|
|
10
|
+
*/
|
|
11
|
+
declare function formatDateTimeValue(value: unknown, pattern: string, locale?: string): string | null;
|
|
12
|
+
|
|
13
|
+
export { formatDateTimeValue, isDateTimeFormatPattern, resolveDateValue };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** 将未知值解析为有效日期。 */
|
|
2
|
+
declare function resolveDateValue(value: unknown): Date | null;
|
|
3
|
+
/** 判断字符串中是否包含 Day.js 风格的日期时间格式 token。 */
|
|
4
|
+
declare function isDateTimeFormatPattern(pattern: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* 使用 Day.js 风格的常用 token 格式化日期时间,不依赖 Day.js 运行时。
|
|
7
|
+
*
|
|
8
|
+
* 支持年、月、日、星期、24/12 小时、分、秒、毫秒和上下午标记;
|
|
9
|
+
* 默认使用中文区域设置,值或 pattern 无效时返回 `null`。
|
|
10
|
+
*/
|
|
11
|
+
declare function formatDateTimeValue(value: unknown, pattern: string, locale?: string): string | null;
|
|
12
|
+
|
|
13
|
+
export { formatDateTimeValue, isDateTimeFormatPattern, resolveDateValue };
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// src/date/index.ts
|
|
2
|
+
var DATE_TIME_FORMAT_TOKEN = /Y{2,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|m{1,2}|s{1,2}|S{1,3}|A|a/;
|
|
3
|
+
var FORMAT_TOKEN = /\[[^\]]*\]|YYYY|MMMM|MMM|dddd|ddd|SSS|YY|MM|DD|dd|HH|hh|mm|ss|SS|M|D|d|H|h|m|s|S|A|a/g;
|
|
4
|
+
var ZH_WEEKDAYS = [
|
|
5
|
+
"\u661F\u671F\u65E5",
|
|
6
|
+
"\u661F\u671F\u4E00",
|
|
7
|
+
"\u661F\u671F\u4E8C",
|
|
8
|
+
"\u661F\u671F\u4E09",
|
|
9
|
+
"\u661F\u671F\u56DB",
|
|
10
|
+
"\u661F\u671F\u4E94",
|
|
11
|
+
"\u661F\u671F\u516D"
|
|
12
|
+
];
|
|
13
|
+
var ZH_WEEKDAYS_SHORT = [
|
|
14
|
+
"\u5468\u65E5",
|
|
15
|
+
"\u5468\u4E00",
|
|
16
|
+
"\u5468\u4E8C",
|
|
17
|
+
"\u5468\u4E09",
|
|
18
|
+
"\u5468\u56DB",
|
|
19
|
+
"\u5468\u4E94",
|
|
20
|
+
"\u5468\u516D"
|
|
21
|
+
];
|
|
22
|
+
var EN_WEEKDAYS = [
|
|
23
|
+
"Sunday",
|
|
24
|
+
"Monday",
|
|
25
|
+
"Tuesday",
|
|
26
|
+
"Wednesday",
|
|
27
|
+
"Thursday",
|
|
28
|
+
"Friday",
|
|
29
|
+
"Saturday"
|
|
30
|
+
];
|
|
31
|
+
var EN_MONTHS = [
|
|
32
|
+
"January",
|
|
33
|
+
"February",
|
|
34
|
+
"March",
|
|
35
|
+
"April",
|
|
36
|
+
"May",
|
|
37
|
+
"June",
|
|
38
|
+
"July",
|
|
39
|
+
"August",
|
|
40
|
+
"September",
|
|
41
|
+
"October",
|
|
42
|
+
"November",
|
|
43
|
+
"December"
|
|
44
|
+
];
|
|
45
|
+
function resolveDateValue(value) {
|
|
46
|
+
if (value instanceof Date) {
|
|
47
|
+
return Number.isNaN(value.getTime()) ? null : value;
|
|
48
|
+
}
|
|
49
|
+
if (typeof value !== "string" && typeof value !== "number") {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
if (typeof value === "string") {
|
|
53
|
+
const localDate = resolveLocalDateString(value);
|
|
54
|
+
if (localDate) {
|
|
55
|
+
return localDate;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const resolved = new Date(value);
|
|
59
|
+
return Number.isNaN(resolved.getTime()) ? null : resolved;
|
|
60
|
+
}
|
|
61
|
+
function isDateTimeFormatPattern(pattern) {
|
|
62
|
+
return DATE_TIME_FORMAT_TOKEN.test(pattern.replace(/\[[^\]]*\]/g, ""));
|
|
63
|
+
}
|
|
64
|
+
function formatDateTimeValue(value, pattern, locale = "zh-cn") {
|
|
65
|
+
const date = resolveDateValue(value);
|
|
66
|
+
const normalizedPattern = pattern.trim();
|
|
67
|
+
if (!date || !normalizedPattern) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const year = date.getFullYear();
|
|
71
|
+
const monthIndex = date.getMonth();
|
|
72
|
+
const month = monthIndex + 1;
|
|
73
|
+
const day = date.getDate();
|
|
74
|
+
const weekdayIndex = date.getDay();
|
|
75
|
+
const hour = date.getHours();
|
|
76
|
+
const hour12 = hour % 12 || 12;
|
|
77
|
+
const minute = date.getMinutes();
|
|
78
|
+
const second = date.getSeconds();
|
|
79
|
+
const millisecond = date.getMilliseconds();
|
|
80
|
+
const chinese = locale.toLowerCase().startsWith("zh");
|
|
81
|
+
const marker = hour >= 12 ? chinese ? "\u4E0B\u5348" : "PM" : chinese ? "\u4E0A\u5348" : "AM";
|
|
82
|
+
const weekday = chinese ? ZH_WEEKDAYS[weekdayIndex] ?? "" : EN_WEEKDAYS[weekdayIndex] ?? "";
|
|
83
|
+
const weekdayShort = chinese ? ZH_WEEKDAYS_SHORT[weekdayIndex] ?? "" : weekday.slice(0, 3);
|
|
84
|
+
const monthName = chinese ? `${month}\u6708` : EN_MONTHS[monthIndex] ?? "";
|
|
85
|
+
const values = {
|
|
86
|
+
YYYY: String(year).padStart(4, "0"),
|
|
87
|
+
YY: String(year).slice(-2),
|
|
88
|
+
M: String(month),
|
|
89
|
+
MM: padNumber(month),
|
|
90
|
+
MMM: chinese ? monthName : monthName.slice(0, 3),
|
|
91
|
+
MMMM: monthName,
|
|
92
|
+
D: String(day),
|
|
93
|
+
DD: padNumber(day),
|
|
94
|
+
d: String(weekdayIndex),
|
|
95
|
+
dd: chinese ? weekdayShort.slice(-1) : weekday.slice(0, 2),
|
|
96
|
+
ddd: weekdayShort,
|
|
97
|
+
dddd: weekday,
|
|
98
|
+
H: String(hour),
|
|
99
|
+
HH: padNumber(hour),
|
|
100
|
+
h: String(hour12),
|
|
101
|
+
hh: padNumber(hour12),
|
|
102
|
+
m: String(minute),
|
|
103
|
+
mm: padNumber(minute),
|
|
104
|
+
s: String(second),
|
|
105
|
+
ss: padNumber(second),
|
|
106
|
+
S: String(Math.floor(millisecond / 100)),
|
|
107
|
+
SS: String(Math.floor(millisecond / 10)).padStart(2, "0"),
|
|
108
|
+
SSS: String(millisecond).padStart(3, "0"),
|
|
109
|
+
A: marker,
|
|
110
|
+
a: marker.toLowerCase()
|
|
111
|
+
};
|
|
112
|
+
return normalizedPattern.replace(
|
|
113
|
+
FORMAT_TOKEN,
|
|
114
|
+
(token) => token.startsWith("[") && token.endsWith("]") ? token.slice(1, -1) : values[token] ?? token
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
function padNumber(value) {
|
|
118
|
+
return String(value).padStart(2, "0");
|
|
119
|
+
}
|
|
120
|
+
function resolveLocalDateString(value) {
|
|
121
|
+
const match = value.trim().match(
|
|
122
|
+
/^(\d{4})[-/](\d{1,2})[-/](\d{1,2})(?:[ T](\d{1,2}):?(\d{2})?(?::?(\d{2}))?(?:\.(\d{1,3}))?)?$/
|
|
123
|
+
);
|
|
124
|
+
if (!match) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
const [
|
|
128
|
+
,
|
|
129
|
+
yearText,
|
|
130
|
+
monthText,
|
|
131
|
+
dayText,
|
|
132
|
+
hourText,
|
|
133
|
+
minuteText,
|
|
134
|
+
secondText,
|
|
135
|
+
millisecondText
|
|
136
|
+
] = match;
|
|
137
|
+
const year = Number(yearText);
|
|
138
|
+
const monthIndex = Number(monthText) - 1;
|
|
139
|
+
const day = Number(dayText);
|
|
140
|
+
const hour = Number(hourText ?? 0);
|
|
141
|
+
const minute = Number(minuteText ?? 0);
|
|
142
|
+
const second = Number(secondText ?? 0);
|
|
143
|
+
const millisecond = Number((millisecondText ?? "0").padEnd(3, "0"));
|
|
144
|
+
const resolved = new Date(
|
|
145
|
+
year,
|
|
146
|
+
monthIndex,
|
|
147
|
+
day,
|
|
148
|
+
hour,
|
|
149
|
+
minute,
|
|
150
|
+
second,
|
|
151
|
+
millisecond
|
|
152
|
+
);
|
|
153
|
+
return resolved.getFullYear() === year && resolved.getMonth() === monthIndex && resolved.getDate() === day && resolved.getHours() === hour && resolved.getMinutes() === minute && resolved.getSeconds() === second ? resolved : null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export { formatDateTimeValue, isDateTimeFormatPattern, resolveDateValue };
|
|
157
|
+
//# sourceMappingURL=index.js.map
|
|
158
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/date/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/** Day.js 风格日期时间格式中支持识别的 token。 */\nconst DATE_TIME_FORMAT_TOKEN =\n /Y{2,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|m{1,2}|s{1,2}|S{1,3}|A|a/;\n\nconst FORMAT_TOKEN =\n /\\[[^\\]]*\\]|YYYY|MMMM|MMM|dddd|ddd|SSS|YY|MM|DD|dd|HH|hh|mm|ss|SS|M|D|d|H|h|m|s|S|A|a/g;\n\nconst ZH_WEEKDAYS = [\n \"星期日\",\n \"星期一\",\n \"星期二\",\n \"星期三\",\n \"星期四\",\n \"星期五\",\n \"星期六\",\n];\nconst ZH_WEEKDAYS_SHORT = [\n \"周日\",\n \"周一\",\n \"周二\",\n \"周三\",\n \"周四\",\n \"周五\",\n \"周六\",\n];\nconst EN_WEEKDAYS = [\n \"Sunday\",\n \"Monday\",\n \"Tuesday\",\n \"Wednesday\",\n \"Thursday\",\n \"Friday\",\n \"Saturday\",\n];\nconst EN_MONTHS = [\n \"January\",\n \"February\",\n \"March\",\n \"April\",\n \"May\",\n \"June\",\n \"July\",\n \"August\",\n \"September\",\n \"October\",\n \"November\",\n \"December\",\n];\n\n/** 将未知值解析为有效日期。 */\nexport function resolveDateValue(value: unknown): Date | null {\n if (value instanceof Date) {\n return Number.isNaN(value.getTime()) ? null : value;\n }\n if (typeof value !== \"string\" && typeof value !== \"number\") {\n return null;\n }\n if (typeof value === \"string\") {\n const localDate = resolveLocalDateString(value);\n if (localDate) {\n return localDate;\n }\n }\n const resolved = new Date(value);\n return Number.isNaN(resolved.getTime()) ? null : resolved;\n}\n\n/** 判断字符串中是否包含 Day.js 风格的日期时间格式 token。 */\nexport function isDateTimeFormatPattern(pattern: string) {\n return DATE_TIME_FORMAT_TOKEN.test(pattern.replace(/\\[[^\\]]*\\]/g, \"\"));\n}\n\n/**\n * 使用 Day.js 风格的常用 token 格式化日期时间,不依赖 Day.js 运行时。\n *\n * 支持年、月、日、星期、24/12 小时、分、秒、毫秒和上下午标记;\n * 默认使用中文区域设置,值或 pattern 无效时返回 `null`。\n */\nexport function formatDateTimeValue(\n value: unknown,\n pattern: string,\n locale = \"zh-cn\",\n): string | null {\n const date = resolveDateValue(value);\n const normalizedPattern = pattern.trim();\n if (!date || !normalizedPattern) {\n return null;\n }\n const year = date.getFullYear();\n const monthIndex = date.getMonth();\n const month = monthIndex + 1;\n const day = date.getDate();\n const weekdayIndex = date.getDay();\n const hour = date.getHours();\n const hour12 = hour % 12 || 12;\n const minute = date.getMinutes();\n const second = date.getSeconds();\n const millisecond = date.getMilliseconds();\n const chinese = locale.toLowerCase().startsWith(\"zh\");\n const marker =\n hour >= 12 ? (chinese ? \"下午\" : \"PM\") : chinese ? \"上午\" : \"AM\";\n const weekday = chinese\n ? (ZH_WEEKDAYS[weekdayIndex] ?? \"\")\n : (EN_WEEKDAYS[weekdayIndex] ?? \"\");\n const weekdayShort = chinese\n ? (ZH_WEEKDAYS_SHORT[weekdayIndex] ?? \"\")\n : weekday.slice(0, 3);\n const monthName = chinese ? `${month}月` : (EN_MONTHS[monthIndex] ?? \"\");\n const values: Record<string, string> = {\n YYYY: String(year).padStart(4, \"0\"),\n YY: String(year).slice(-2),\n M: String(month),\n MM: padNumber(month),\n MMM: chinese ? monthName : monthName.slice(0, 3),\n MMMM: monthName,\n D: String(day),\n DD: padNumber(day),\n d: String(weekdayIndex),\n dd: chinese ? weekdayShort.slice(-1) : weekday.slice(0, 2),\n ddd: weekdayShort,\n dddd: weekday,\n H: String(hour),\n HH: padNumber(hour),\n h: String(hour12),\n hh: padNumber(hour12),\n m: String(minute),\n mm: padNumber(minute),\n s: String(second),\n ss: padNumber(second),\n S: String(Math.floor(millisecond / 100)),\n SS: String(Math.floor(millisecond / 10)).padStart(2, \"0\"),\n SSS: String(millisecond).padStart(3, \"0\"),\n A: marker,\n a: marker.toLowerCase(),\n };\n\n return normalizedPattern.replace(FORMAT_TOKEN, (token) =>\n token.startsWith(\"[\") && token.endsWith(\"]\")\n ? token.slice(1, -1)\n : (values[token] ?? token),\n );\n}\n\nfunction padNumber(value: number) {\n return String(value).padStart(2, \"0\");\n}\n\nfunction resolveLocalDateString(value: string) {\n const match = value\n .trim()\n .match(\n /^(\\d{4})[-/](\\d{1,2})[-/](\\d{1,2})(?:[ T](\\d{1,2}):?(\\d{2})?(?::?(\\d{2}))?(?:\\.(\\d{1,3}))?)?$/,\n );\n if (!match) {\n return null;\n }\n const [\n ,\n yearText,\n monthText,\n dayText,\n hourText,\n minuteText,\n secondText,\n millisecondText,\n ] = match;\n const year = Number(yearText);\n const monthIndex = Number(monthText) - 1;\n const day = Number(dayText);\n const hour = Number(hourText ?? 0);\n const minute = Number(minuteText ?? 0);\n const second = Number(secondText ?? 0);\n const millisecond = Number((millisecondText ?? \"0\").padEnd(3, \"0\"));\n const resolved = new Date(\n year,\n monthIndex,\n day,\n hour,\n minute,\n second,\n millisecond,\n );\n return resolved.getFullYear() === year &&\n resolved.getMonth() === monthIndex &&\n resolved.getDate() === day &&\n resolved.getHours() === hour &&\n resolved.getMinutes() === minute &&\n resolved.getSeconds() === second\n ? resolved\n : null;\n}\n"]}
|