From 9cedf085a3c7af9faecc821da20e597712eed10a Mon Sep 17 00:00:00 2001 From: DungTT Date: Fri, 29 May 2026 14:15:06 +0700 Subject: [PATCH] date time --- web-server/.env.example | 1 + web-server/server.js | 11 +++ web-server/src/db.js | 3 +- web-server/src/repository.js | 72 +++++++++++++++++-- web-server/views/partials/package-modal.ejs | 2 +- .../views/partials/update-package-modal.ejs | 2 +- 6 files changed, 82 insertions(+), 9 deletions(-) diff --git a/web-server/.env.example b/web-server/.env.example index f13df78..1157366 100644 --- a/web-server/.env.example +++ b/web-server/.env.example @@ -12,6 +12,7 @@ SQLSERVER_USER=sa SQLSERVER_PASSWORD=change_me SQLSERVER_ENCRYPT=false SQLSERVER_TRUST_SERVER_CERTIFICATE=true +SQLSERVER_USE_UTC=true AUTH_SECRET=change_this_to_a_long_random_value SESSION_MAX_AGE_MS=28800000 SESSION_COOKIE_SECURE=true diff --git a/web-server/server.js b/web-server/server.js index 0fae90c..5edfbd1 100644 --- a/web-server/server.js +++ b/web-server/server.js @@ -293,6 +293,16 @@ function getCurrentPath(req) { return `${url.pathname}${url.search}` || '/'; } +function toDateInputValue(value = new Date()) { + const date = value instanceof Date ? value : new Date(value); + const safeDate = Number.isNaN(date.getTime()) ? new Date() : date; + const year = safeDate.getFullYear(); + const month = String(safeDate.getMonth() + 1).padStart(2, '0'); + const day = String(safeDate.getDate()).padStart(2, '0'); + + return `${year}-${month}-${day}`; +} + function viewModel(req, active, title, pageData, extra = {}) { const currentUser = pageData.currentUser || req.currentUser || { name: 'Guest', @@ -314,6 +324,7 @@ function viewModel(req, active, title, pageData, extra = {}) { notice: getNotice(req), currentPath: getCurrentPath(req), databaseLabel: getSqlServerDisplayLabel(), + todayDate: toDateInputValue(), helpers: helpers(), ...extra }; diff --git a/web-server/src/db.js b/web-server/src/db.js index 6846a86..e61e74c 100644 --- a/web-server/src/db.js +++ b/web-server/src/db.js @@ -16,7 +16,8 @@ function getConfig() { password: process.env.SQLSERVER_PASSWORD, options: { encrypt: boolFromEnv(process.env.SQLSERVER_ENCRYPT, false), - trustServerCertificate: boolFromEnv(process.env.SQLSERVER_TRUST_SERVER_CERTIFICATE, true) + trustServerCertificate: boolFromEnv(process.env.SQLSERVER_TRUST_SERVER_CERTIFICATE, true), + useUTC: boolFromEnv(process.env.SQLSERVER_USE_UTC, true) }, pool: { max: 10, diff --git a/web-server/src/repository.js b/web-server/src/repository.js index dc4ee6c..9526e6f 100644 --- a/web-server/src/repository.js +++ b/web-server/src/repository.js @@ -11,10 +11,70 @@ const EMAIL_CONFIRMATION_EXPIRES_MS = Number(process.env.EMAIL_CONFIRMATION_EXPI let emailConfirmationSchemaPromise; let applicationOpenUrlSchemaPromise; +function padDatePart(value) { + return String(value).padStart(2, '0'); +} + +function getLocalDateInputValue(value = new Date()) { + const date = value instanceof Date ? value : new Date(value); + const safeDate = Number.isNaN(date.getTime()) ? new Date() : date; + + return [ + safeDate.getFullYear(), + padDatePart(safeDate.getMonth() + 1), + padDatePart(safeDate.getDate()) + ].join('-'); +} + +function getUtcDateInputValue(value) { + return [ + value.getUTCFullYear(), + padDatePart(value.getUTCMonth() + 1), + padDatePart(value.getUTCDate()) + ].join('-'); +} + +function parseDateInputValue(value) { + if (typeof value !== 'string') return ''; + + const match = /^(\d{4})-(\d{2})-(\d{2})(?:T.*)?$/.exec(value.trim()); + if (!match) return ''; + + const year = Number(match[1]); + const month = Number(match[2]); + const day = Number(match[3]); + const date = new Date(Date.UTC(year, month - 1, day)); + + if ( + date.getUTCFullYear() !== year + || date.getUTCMonth() !== month - 1 + || date.getUTCDate() !== day + ) { + return ''; + } + + return `${match[1]}-${match[2]}-${match[3]}`; +} + function toDateInputValue(value) { - const date = value ? new Date(value) : new Date(); - if (Number.isNaN(date.getTime())) return new Date().toISOString().slice(0, 10); - return date.toISOString().slice(0, 10); + if (!value) return ''; + + const parsedDateInput = parseDateInputValue(value); + if (parsedDateInput) return parsedDateInput; + + const date = value instanceof Date ? value : new Date(value); + if (Number.isNaN(date.getTime())) return ''; + + return getUtcDateInputValue(date); +} + +function toReleaseDateValue(value) { + return toDateInputValue(value) || getLocalDateInputValue(); +} + +function toSqlDateTime2Date(value) { + const [year, month, day] = toReleaseDateValue(value).split('-').map(Number); + return new Date(Date.UTC(year, month - 1, day)); } function formatDate(value) { @@ -1133,7 +1193,7 @@ async function createPackageWithVersion(input) { .input('FileChecksumSha256', sql.Char(64), input.checksum || null) .input('FileSizeBytes', sql.BigInt, input.fileSizeBytes || null) .input('ChangeLog', sql.NVarChar(sql.MAX), input.changeLog || null) - .input('ReleaseDate', sql.DateTime2, input.releaseDate ? new Date(input.releaseDate) : new Date()) + .input('ReleaseDate', sql.DateTime2, toSqlDateTime2Date(input.releaseDate)) .query(` INSERT dbo.PackageVersions ( PackageId, Version, FilePath, DockerImage, FileChecksumSha256, @@ -1175,7 +1235,7 @@ async function addPackageVersion(input) { .input('FileChecksumSha256', sql.Char(64), input.checksum || null) .input('FileSizeBytes', sql.BigInt, input.fileSizeBytes || null) .input('ChangeLog', sql.NVarChar(sql.MAX), input.changeLog || null) - .input('ReleaseDate', sql.DateTime2, input.releaseDate ? new Date(input.releaseDate) : new Date()) + .input('ReleaseDate', sql.DateTime2, toSqlDateTime2Date(input.releaseDate)) .query(` INSERT dbo.PackageVersions ( PackageId, Version, FilePath, DockerImage, FileChecksumSha256, @@ -1213,7 +1273,7 @@ async function replacePackageVersionArtifact(input) { .input('FileChecksumSha256', sql.Char(64), input.checksum || null) .input('FileSizeBytes', sql.BigInt, input.fileSizeBytes || null) .input('ChangeLog', sql.NVarChar(sql.MAX), input.changeLog || null) - .input('ReleaseDate', sql.DateTime2, input.releaseDate ? new Date(input.releaseDate) : new Date()) + .input('ReleaseDate', sql.DateTime2, toSqlDateTime2Date(input.releaseDate)) .query(` UPDATE dbo.PackageVersions SET FilePath = @FilePath, diff --git a/web-server/views/partials/package-modal.ejs b/web-server/views/partials/package-modal.ejs index 5fa48c7..8642e6b 100644 --- a/web-server/views/partials/package-modal.ejs +++ b/web-server/views/partials/package-modal.ejs @@ -33,7 +33,7 @@
Package file diff --git a/web-server/views/partials/update-package-modal.ejs b/web-server/views/partials/update-package-modal.ejs index 06f303d..f2bca2d 100644 --- a/web-server/views/partials/update-package-modal.ejs +++ b/web-server/views/partials/update-package-modal.ejs @@ -23,7 +23,7 @@
Package file