From e50ff9db6ae91e1ad34bc50935300515841bf719 Mon Sep 17 00:00:00 2001
From: Marie <github@yuugi.dev>
Date: Sun, 15 Dec 2024 22:41:16 +0100
Subject: [PATCH] upd: make schedule time work cross timezones

---
 packages/backend/package.json                 |  2 +-
 .../api/endpoints/notes/schedule/create.ts    | 10 ++--
 packages/frontend/package.json                |  1 +
 .../src/components/MkScheduleEditor.vue       |  3 +-
 pnpm-lock.yaml                                | 54 +++++++------------
 5 files changed, 28 insertions(+), 42 deletions(-)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index 702b788061..dd6c9cc792 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -141,11 +141,11 @@
 		"juice": "11.0.0",
 		"megalodon": "workspace:*",
 		"meilisearch": "0.45.0",
-		"juice": "11.0.0",
 		"microformats-parser": "2.0.2",
 		"mime-types": "2.1.35",
 		"misskey-js": "workspace:*",
 		"misskey-reversi": "workspace:*",
+		"moment": "^2.30.1",
 		"ms": "3.0.0-canary.1",
 		"nanoid": "5.0.8",
 		"nested-property": "4.0.0",
diff --git a/packages/backend/src/server/api/endpoints/notes/schedule/create.ts b/packages/backend/src/server/api/endpoints/notes/schedule/create.ts
index 7d20b6b82a..c6032fbdae 100644
--- a/packages/backend/src/server/api/endpoints/notes/schedule/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/schedule/create.ts
@@ -6,6 +6,7 @@
 import ms from 'ms';
 import { In } from 'typeorm';
 import { Inject, Injectable } from '@nestjs/common';
+import moment from 'moment';
 import { isPureRenote } from '@/misc/is-renote.js';
 import type { MiUser } from '@/models/User.js';
 import type {
@@ -307,7 +308,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			if (ps.poll) {
 				let scheduleNote_scheduledAt = Date.now();
 				if (typeof ps.scheduleNote.scheduledAt === 'number') {
-					scheduleNote_scheduledAt = ps.scheduleNote.scheduledAt;
+					scheduleNote_scheduledAt = moment.utc(ps.scheduleNote.scheduledAt).local().valueOf();
 				}
 				if (typeof ps.poll.expiresAt === 'number') {
 					if (ps.poll.expiresAt < scheduleNote_scheduledAt) {
@@ -318,7 +319,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				}
 			}
 			if (typeof ps.scheduleNote.scheduledAt === 'number') {
-				if (ps.scheduleNote.scheduledAt < Date.now()) {
+				if (moment.utc(ps.scheduleNote.scheduledAt).local().valueOf() < Date.now()) {
 					throw new ApiError(meta.errors.cannotCreateAlreadyExpiredSchedule);
 				}
 			} else {
@@ -347,14 +348,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			if (ps.scheduleNote.scheduledAt) {
 				me.token = null;
 				const noteId = this.idService.gen(new Date().getTime());
+				const schedNoteLocalTime = moment.utc(ps.scheduleNote.scheduledAt).local().valueOf();
 				await this.noteScheduleRepository.insert({
 					id: noteId,
 					note: note,
 					userId: me.id,
-					scheduledAt: new Date(ps.scheduleNote.scheduledAt),
+					scheduledAt: new Date(schedNoteLocalTime),
 				});
 
-				const delay = new Date(ps.scheduleNote.scheduledAt).getTime() - Date.now();
+				const delay = new Date(schedNoteLocalTime).getTime() - Date.now();
 				await this.queueService.ScheduleNotePostQueue.add(String(delay), {
 					scheduleNoteId: noteId,
 				}, {
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 752b6cb388..ce3fd90a86 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -57,6 +57,7 @@
 		"misskey-bubble-game": "workspace:*",
 		"misskey-js": "workspace:*",
 		"misskey-reversi": "workspace:*",
+		"moment": "^2.30.1",
 		"photoswipe": "5.4.4",
 		"punycode": "2.3.1",
 		"rollup": "4.26.0",
diff --git a/packages/frontend/src/components/MkScheduleEditor.vue b/packages/frontend/src/components/MkScheduleEditor.vue
index f40d37c962..695a474998 100644
--- a/packages/frontend/src/components/MkScheduleEditor.vue
+++ b/packages/frontend/src/components/MkScheduleEditor.vue
@@ -18,6 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted, ref, watch } from 'vue';
+import moment from 'moment';
 import MkInput from '@/components/MkInput.vue';
 import { formatDateTimeString } from '@/scripts/format-time-string.js';
 import { addTime } from '@/scripts/time.js';
@@ -46,7 +47,7 @@ if (props.modelValue.scheduledAt) {
 
 function get() {
 	const calcAt = () => {
-		return new Date(`${ atDate.value } ${ atTime.value }`).getTime();
+		return moment(`${ atDate.value } ${ atTime.value }`).utc().valueOf();
 	};
 
 	return { scheduledAt: calcAt() };
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d01454e892..39956522fc 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -139,7 +139,7 @@ importers:
         version: 10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)
       '@nestjs/testing':
         specifier: 10.4.7
-        version: 10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7))
+        version: 10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7)(@nestjs/platform-express@10.4.7)
       '@peertube/http-signature':
         specifier: 1.7.0
         version: 1.7.0
@@ -320,6 +320,9 @@ importers:
       misskey-reversi:
         specifier: workspace:*
         version: link:../misskey-reversi
+      moment:
+        specifier: ^2.30.1
+        version: 2.30.1
       ms:
         specifier: 3.0.0-canary.1
         version: 3.0.0-canary.1
@@ -832,6 +835,9 @@ importers:
       misskey-reversi:
         specifier: workspace:*
         version: link:../misskey-reversi
+      moment:
+        specifier: ^2.30.1
+        version: 2.30.1
       photoswipe:
         specifier: 5.4.4
         version: 5.4.4
@@ -1194,7 +1200,7 @@ importers:
         version: 7.17.0(eslint@9.14.0)(typescript@5.6.3)
       '@vitest/coverage-v8':
         specifier: 1.6.0
-        version: 1.6.0(vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1)(sass@1.79.4)(terser@5.36.0))
+        version: 1.6.0(vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.79.4)(terser@5.36.0))
       '@vue/runtime-core':
         specifier: 3.5.12
         version: 3.5.12
@@ -8425,6 +8431,9 @@ packages:
   module-details-from-path@1.0.3:
     resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==}
 
+  moment@2.30.1:
+    resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
+
   ms@2.0.0:
     resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
 
@@ -13269,7 +13278,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@nestjs/testing@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7))':
+  '@nestjs/testing@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7)(@nestjs/platform-express@10.4.7)':
     dependencies:
       '@nestjs/common': 10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1)
       '@nestjs/core': 10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)
@@ -15490,7 +15499,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1)(sass@1.79.4)(terser@5.36.0))':
+  '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.79.4)(terser@5.36.0))':
     dependencies:
       '@ampproject/remapping': 2.2.1
       '@bcoe/v8-coverage': 0.2.3
@@ -15505,7 +15514,7 @@ snapshots:
       std-env: 3.7.0
       strip-literal: 2.1.0
       test-exclude: 6.0.0
-      vitest: 1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1)(sass@1.79.4)(terser@5.36.0)
+      vitest: 1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.79.4)(terser@5.36.0)
     transitivePeerDependencies:
       - supports-color
 
@@ -19348,35 +19357,6 @@ snapshots:
 
   jsdoc-type-pratt-parser@4.1.0: {}
 
-  jsdom@24.1.1:
-    dependencies:
-      cssstyle: 4.0.1
-      data-urls: 5.0.0
-      decimal.js: 10.4.3
-      form-data: 4.0.1
-      html-encoding-sniffer: 4.0.0
-      http-proxy-agent: 7.0.2
-      https-proxy-agent: 7.0.5
-      is-potential-custom-element-name: 1.0.1
-      nwsapi: 2.2.12
-      parse5: 7.2.1
-      rrweb-cssom: 0.7.1
-      saxes: 6.0.0
-      symbol-tree: 3.2.4
-      tough-cookie: 4.1.4
-      w3c-xmlserializer: 5.0.0
-      webidl-conversions: 7.0.0
-      whatwg-encoding: 3.1.1
-      whatwg-mimetype: 4.0.0
-      whatwg-url: 14.0.0
-      ws: 8.18.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
-      xml-name-validator: 5.0.0
-    transitivePeerDependencies:
-      - bufferutil
-      - supports-color
-      - utf-8-validate
-    optional: true
-
   jsdom@24.1.1(bufferutil@4.0.7)(utf-8-validate@6.0.3):
     dependencies:
       cssstyle: 4.0.1
@@ -20215,6 +20195,8 @@ snapshots:
 
   module-details-from-path@1.0.3: {}
 
+  moment@2.30.1: {}
+
   ms@2.0.0: {}
 
   ms@2.1.2: {}
@@ -22848,7 +22830,7 @@ snapshots:
       - supports-color
       - terser
 
-  vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1)(sass@1.79.4)(terser@5.36.0):
+  vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.79.4)(terser@5.36.0):
     dependencies:
       '@vitest/expect': 1.6.0
       '@vitest/runner': 1.6.0
@@ -22873,7 +22855,7 @@ snapshots:
     optionalDependencies:
       '@types/node': 22.9.0
       happy-dom: 10.0.3
-      jsdom: 24.1.1
+      jsdom: 24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)
     transitivePeerDependencies:
       - less
       - lightningcss
-- 
GitLab