|
|
@ -1652,15 +1652,17 @@ class BaseModelSqlv2 { |
|
|
|
} |
|
|
|
} |
|
|
|
} else if (this.isMssql) { |
|
|
|
} else if (this.isMssql) { |
|
|
|
// if there is no timezone info, convert to database timezone, then convert to UTC
|
|
|
|
// if there is no timezone info, convert to database timezone, then convert to UTC
|
|
|
|
if (column.dt !== 'datetime2') { |
|
|
|
if (column.dt !== 'datetimeoffset') { |
|
|
|
const col = `${sanitize(alias || this.model.table_name)}.${ |
|
|
|
|
|
|
|
column.column_name |
|
|
|
|
|
|
|
}`;
|
|
|
|
|
|
|
|
res[sanitize(column.title || column.column_name)] = |
|
|
|
res[sanitize(column.title || column.column_name)] = |
|
|
|
this.dbDriver.raw( |
|
|
|
this.dbDriver.raw( |
|
|
|
`SWITCHOFFSET(??, DATEPART(TZOFFSET, ??)) AT TIME ZONE 'UTC'`, |
|
|
|
`CONVERT(DATETIMEOFFSET, ?? AT TIME ZONE 'UTC')`, |
|
|
|
[col, col], |
|
|
|
[ |
|
|
|
|
|
|
|
`${sanitize(alias || this.model.table_name)}.${ |
|
|
|
|
|
|
|
column.column_name |
|
|
|
|
|
|
|
}`,
|
|
|
|
|
|
|
|
], |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
res[sanitize(column.title || column.column_name)] = sanitize( |
|
|
|
res[sanitize(column.title || column.column_name)] = sanitize( |
|
|
@ -3296,13 +3298,14 @@ class BaseModelSqlv2 { |
|
|
|
this._convertAttachmentType(attachmentColumns, d), |
|
|
|
this._convertAttachmentType(attachmentColumns, d), |
|
|
|
); |
|
|
|
); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
this._convertAttachmentType(attachmentColumns, data); |
|
|
|
data = this._convertAttachmentType(attachmentColumns, data); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return data; |
|
|
|
return data; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO(timezone): retrieve the format from the corresponding column meta
|
|
|
|
private _convertDateFormat( |
|
|
|
private _convertDateFormat( |
|
|
|
dateTimeColumns: Record<string, any>[], |
|
|
|
dateTimeColumns: Record<string, any>[], |
|
|
|
d: Record<string, any>, |
|
|
|
d: Record<string, any>, |
|
|
@ -3311,6 +3314,60 @@ class BaseModelSqlv2 { |
|
|
|
for (const col of dateTimeColumns) { |
|
|
|
for (const col of dateTimeColumns) { |
|
|
|
if (!d[col.title]) continue; |
|
|
|
if (!d[col.title]) continue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (col.uidt === UITypes.Formula) { |
|
|
|
|
|
|
|
if (!d[col.title] || typeof d[col.title] !== 'string') { |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// cater MYSQL
|
|
|
|
|
|
|
|
d[col.title] = d[col.title].replace(/\.000000/g, ''); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/g.test(d[col.title])) { |
|
|
|
|
|
|
|
// convert ISO string (e.g. in MSSQL) to YYYY-MM-DD hh:mm:ssZ
|
|
|
|
|
|
|
|
// e.g. 2023-05-18T05:30:00.000Z -> 2023-05-18 11:00:00+05:30
|
|
|
|
|
|
|
|
d[col.title] = d[col.title].replace( |
|
|
|
|
|
|
|
/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/g, |
|
|
|
|
|
|
|
(d: string) => { |
|
|
|
|
|
|
|
return dayjs(d).isValid() |
|
|
|
|
|
|
|
? dayjs(d).utc(true).format('YYYY-MM-DD HH:mm:ssZ') |
|
|
|
|
|
|
|
: d; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// convert all date time values to utc
|
|
|
|
|
|
|
|
// the datetime is either YYYY-MM-DD hh:mm:ss (xcdb)
|
|
|
|
|
|
|
|
// or YYYY-MM-DD hh:mm:ss+/-xx:yy (ext)
|
|
|
|
|
|
|
|
d[col.title] = d[col.title].replace( |
|
|
|
|
|
|
|
/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:[+-]\d{2}:\d{2})?/g, |
|
|
|
|
|
|
|
(d: string) => { |
|
|
|
|
|
|
|
if (!dayjs(d).isValid()) { |
|
|
|
|
|
|
|
return d; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (this.isSqlite) { |
|
|
|
|
|
|
|
// if there is no timezone info,
|
|
|
|
|
|
|
|
// we assume the input is on NocoDB server timezone
|
|
|
|
|
|
|
|
// then we convert to UTC from server timezone
|
|
|
|
|
|
|
|
// e.g. 2023-04-27 10:00:00 (IST) -> 2023-04-27 04:30:00+00:00
|
|
|
|
|
|
|
|
return dayjs(d) |
|
|
|
|
|
|
|
.tz(Intl.DateTimeFormat().resolvedOptions().timeZone) |
|
|
|
|
|
|
|
.utc() |
|
|
|
|
|
|
|
.format('YYYY-MM-DD HH:mm:ssZ'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// set keepLocalTime to true if timezone info is not found
|
|
|
|
|
|
|
|
const keepLocalTime = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/g.test( |
|
|
|
|
|
|
|
d, |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return dayjs(d).utc(keepLocalTime).format('YYYY-MM-DD HH:mm:ssZ'); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let keepLocalTime = true; |
|
|
|
let keepLocalTime = true; |
|
|
|
|
|
|
|
|
|
|
|
if (this.isSqlite) { |
|
|
|
if (this.isSqlite) { |
|
|
@ -3367,12 +3424,14 @@ class BaseModelSqlv2 { |
|
|
|
if (data) { |
|
|
|
if (data) { |
|
|
|
const dateTimeColumns = ( |
|
|
|
const dateTimeColumns = ( |
|
|
|
childTable ? childTable.columns : this.model.columns |
|
|
|
childTable ? childTable.columns : this.model.columns |
|
|
|
).filter((c) => c.uidt === UITypes.DateTime); |
|
|
|
).filter( |
|
|
|
|
|
|
|
(c) => c.uidt === UITypes.DateTime || c.uidt === UITypes.Formula, |
|
|
|
|
|
|
|
); |
|
|
|
if (dateTimeColumns.length) { |
|
|
|
if (dateTimeColumns.length) { |
|
|
|
if (Array.isArray(data)) { |
|
|
|
if (Array.isArray(data)) { |
|
|
|
data = data.map((d) => this._convertDateFormat(dateTimeColumns, d)); |
|
|
|
data = data.map((d) => this._convertDateFormat(dateTimeColumns, d)); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
this._convertDateFormat(dateTimeColumns, data); |
|
|
|
data = this._convertDateFormat(dateTimeColumns, data); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|