Murasame52 commited on
Commit
977bbba
·
verified ·
1 Parent(s): c24df68

Upload 2 files

Browse files
Files changed (2) hide show
  1. utils/DateRange.js +88 -0
  2. utils/timezone.js +142 -0
utils/DateRange.js ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // utils/DateRange.js
2
+ const dayjs = require('dayjs');
3
+ const utc = require('dayjs/plugin/utc');
4
+ const timezone = require('dayjs/plugin/timezone');
5
+
6
+ dayjs.extend(utc);
7
+ dayjs.extend(timezone);
8
+
9
+ class DateRangeHelper {
10
+ constructor(timezoneNameOrOffset = 'Asia/Shanghai') {
11
+ // 判断传入的是时区名称还是偏移量
12
+ if (typeof timezoneNameOrOffset === 'number') {
13
+ this.offsetHours = timezoneNameOrOffset;
14
+ this.offsetMs = timezoneNameOrOffset * 60 * 60 * 1000;
15
+ this.useOffset = true;
16
+ this.timezoneName = null;
17
+ } else {
18
+ this.timezoneName = timezoneNameOrOffset;
19
+ this.useOffset = false;
20
+ this.offsetMs = 0;
21
+ }
22
+ }
23
+
24
+ // 获取目标时区的当前日期
25
+ _getTodayInLocal() {
26
+ if (this.useOffset) {
27
+ // 使用偏移量计算
28
+ const now = new Date();
29
+ const localNow = new Date(now.getTime() + this.offsetMs);
30
+ const localDate = new Date(Date.UTC(
31
+ localNow.getUTCFullYear(),
32
+ localNow.getUTCMonth(),
33
+ localNow.getUTCDate()
34
+ ));
35
+ return dayjs.utc(localDate);
36
+ } else {
37
+ // 使用时区名称
38
+ return dayjs().tz(this.timezoneName).startOf('day');
39
+ }
40
+ }
41
+
42
+ // 计算月的起始和结束日期
43
+ getMonthRange(monthOffset = 0) {
44
+ const today = this._getTodayInLocal();
45
+
46
+ const monthStart = today.add(monthOffset, 'month').startOf('month');
47
+ let monthEnd = monthStart.endOf('month').startOf('day');
48
+
49
+ if (monthOffset === 0 && monthEnd.isAfter(today)) {
50
+ monthEnd = today;
51
+ }
52
+
53
+ return {
54
+ startDate: monthStart.toDate(),
55
+ endDate: monthEnd.toDate()
56
+ };
57
+ }
58
+
59
+ // 计算周的起始和结束日期
60
+ getWeekRange(weekOffset = 0) {
61
+ const today = this._getTodayInLocal();
62
+
63
+ // 获取今天是星期几(周一=1,周日=0)
64
+ const dayOfWeek = today.day();
65
+
66
+ // 计算本周一(注意周日要单独处理)
67
+ const thisMonday = today.subtract(dayOfWeek === 0 ? 6 : dayOfWeek - 1, 'day').startOf('day');
68
+
69
+ // 目标周的周一
70
+ const targetMonday = thisMonday.add(weekOffset, 'week');
71
+
72
+ // 目标周的周日
73
+ let targetSunday = targetMonday.add(6, 'day').endOf('day');
74
+
75
+ // 如果是本周,结束日期不能超过今天
76
+ if (weekOffset === 0 && targetSunday.isAfter(today)) {
77
+ targetSunday = today.endOf('day');
78
+ }
79
+
80
+ return {
81
+ startDate: targetMonday.toDate(),
82
+ endDate: targetSunday.toDate()
83
+ };
84
+ }
85
+
86
+ }
87
+
88
+ module.exports = DateRangeHelper;
utils/timezone.js ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // utils/timezone.js - 完整版, 支持偏移量与 IANA 时区名称
2
+ const dayjs = require('dayjs');
3
+ const utc = require('dayjs/plugin/utc');
4
+ const timezone = require('dayjs/plugin/timezone');
5
+
6
+ dayjs.extend(utc);
7
+ dayjs.extend(timezone);
8
+
9
+ class TimezoneUtils {
10
+ constructor(timezoneNameOrOffset) {
11
+ // 默认使用上海时区 (UTC+8)
12
+ if (timezoneNameOrOffset === null || timezoneNameOrOffset === undefined) {
13
+ timezoneNameOrOffset = 'Asia/Shanghai';
14
+ }
15
+
16
+ if (typeof timezoneNameOrOffset === 'number') {
17
+ // 数字模式 => 偏移量(小时)
18
+ this.offsetHours = timezoneNameOrOffset;
19
+ this.offsetMs = timezoneNameOrOffset * 60 * 60 * 1000;
20
+ this.useOffset = true;
21
+ this.timezoneName = null;
22
+ } else {
23
+ // 字符串模式 => IANA 时区
24
+ this.timezoneName = timezoneNameOrOffset;
25
+ this.useOffset = false;
26
+ this.offsetMs = 0;
27
+ this.offsetHours = 0;
28
+ }
29
+ }
30
+
31
+ /** ✅ UTC -> 本地时区时间 */
32
+ utcToLocal(utcDate) {
33
+ if (!(utcDate instanceof Date)) {
34
+ utcDate = new Date(utcDate);
35
+ }
36
+ if (this.useOffset) {
37
+ return new Date(utcDate.getTime() + this.offsetMs);
38
+ } else {
39
+ return dayjs.utc(utcDate).tz(this.timezoneName).toDate();
40
+ }
41
+ }
42
+
43
+ /** ✅ 本地时区时间 -> UTC */
44
+ localToUtc(localDate) {
45
+ if (!(localDate instanceof Date)) {
46
+ localDate = new Date(localDate);
47
+ }
48
+ if (this.useOffset) {
49
+ return new Date(localDate.getTime() - this.offsetMs);
50
+ } else {
51
+ return dayjs.tz(localDate, this.timezoneName).utc().toDate();
52
+ }
53
+ }
54
+
55
+ /**
56
+ * ✅ 获取某个 UTC 时间对应的“本地日期零点的 UTC 时间”
57
+ * 例如北京时间 2025-11-02T10:00Z -> 返回该天本地零点对应的 UTC 时间 2025-11-01T16:00Z
58
+ */
59
+ getLocalDayStart(timestamp) {
60
+ const utcDate = new Date(timestamp);
61
+ const localTime = this.utcToLocal(utcDate);
62
+
63
+ const year = localTime.getFullYear();
64
+ const month = localTime.getMonth();
65
+ const day = localTime.getDate();
66
+
67
+ // 构造该时区日期的本地零点
68
+ const localMidnight = new Date(Date.UTC(year, month, day, 0, 0, 0, 0));
69
+
70
+ // 转换回 UTC
71
+ return this.localToUtc(localMidnight);
72
+ }
73
+
74
+ /**
75
+ * ✅ 获取目标时区当前日期的“零点 UTC 时间”
76
+ */
77
+ getTodayStartUtc() {
78
+ const now = new Date();
79
+ return this.getLocalDayStart(now);
80
+ }
81
+
82
+ /**
83
+ * ✅ 获取目标时区的当前本地日期对象(不带时间)
84
+ */
85
+ getTodayInLocal() {
86
+ const now = new Date();
87
+ const localNow = this.utcToLocal(now);
88
+ return new Date(Date.UTC(
89
+ localNow.getUTCFullYear(),
90
+ localNow.getUTCMonth(),
91
+ localNow.getUTCDate()
92
+ ));
93
+ }
94
+
95
+ /**
96
+ * ✅ 提取日期部分(丢弃时间)
97
+ */
98
+ getLocalDateOnly(timestamp) {
99
+ const localTime = this.utcToLocal(new Date(timestamp));
100
+ return new Date(Date.UTC(
101
+ localTime.getUTCFullYear(),
102
+ localTime.getUTCMonth(),
103
+ localTime.getUTCDate()
104
+ ));
105
+ }
106
+
107
+ /**
108
+ * ✅ 解析输入日期(字符串 / Date / 时间戳)
109
+ */
110
+ parseDate(dateInput) {
111
+ if (dateInput === null || dateInput === undefined) {
112
+ throw new Error('Date input cannot be null or undefined');
113
+ }
114
+
115
+ let date;
116
+ if (dateInput instanceof Date) {
117
+ date = dateInput;
118
+ } else if (typeof dateInput === 'string') {
119
+ if (/^\d{4}-\d{2}-\d{2}$/.test(dateInput)) {
120
+ const [year, month, day] = dateInput.split('-').map(Number);
121
+ date = new Date(Date.UTC(year, month - 1, day));
122
+ } else {
123
+ date = new Date(dateInput);
124
+ if (isNaN(date.getTime())) {
125
+ throw new Error(`Invalid date string: ${dateInput}`);
126
+ }
127
+ }
128
+ } else if (typeof dateInput === 'number') {
129
+ date = new Date(dateInput);
130
+ } else {
131
+ throw new Error(`Invalid date input type: ${typeof dateInput}`);
132
+ }
133
+
134
+ return new Date(Date.UTC(
135
+ date.getUTCFullYear(),
136
+ date.getUTCMonth(),
137
+ date.getUTCDate()
138
+ ));
139
+ }
140
+ }
141
+
142
+ module.exports = TimezoneUtils;