ChengLei Shao a7df0e3fd3
refactor: datetime field (#5084)
* refactor: date field support timezone, defaultToCurrentTime, and onUpdateToCurrentTime

* refactor: availableTypes unixTimestamp

* chore: defaultToCurrentTime

* chore: unix timestamp field

* fix: bug

* chore: field type map

* refactor: local improve

* fix: bug

* fix: bug

* chore: timezone test

* chore: test

* fix: test

* fix: test

* chore: field setter

* chore: test

* chore: date only field

* chore: test

* chore: test

* fix: bug

* fix: unixTimestamp

* fix: unixTimestamp

* chore: accuracy

* fix: bug

* fix: bug

* fix: unixTimestamp

* fix: unixTimestamp

* fix: date & datetime

* refactor:  add DateFieldInterface

* fix: bug

* chore: test

* chore: test

* chore: test

* refactor: locale improve

* refactor: local improve

* fix: bug

* refactor: unixTimestamp not support default value

* refactor: timezone

* refactor: datetimeNoTzFieldInterface

* refactor: locale improve

* refactor: locale improve

* fix: test

* fix: bug

* chore: datetime no tz field

* refactor: datetimeNoTz

* refactor: datetime

* fix: bug

* refactor: timeFormat

* style: collection fields style improve

* refactor: defaultToCurrentTime

* fix: datetime no tz

* chore: field type map

* fix: bug

* fix: bug

* refactor: createAt & updateAt

* fix: bug

* fix: no tz field with timezone

* refactor: dateonly

* fix: test

* chore: data type map

* fix: dateonly

* fix: dateonly

* fix: datetime

* refactor: locale improve

* refactor: unixtimestamp

* fix: merge bug

* fix: bug

* fix: datetime

* fix: datetime no tz

* fix: datetime no tz

* chore: mysql datetime map

* chore: test

* chore: test

* chore: test

* chore: datetimeTz field

* fix: no interface option

* refactor: update type

* refactor: update type

* fix: pg no tz field

* chore: save iso8601 format to no tz field

* fix: test

* fix: test

* refactor: gannt & calender startTime & endTime

* refactor: unixTimestamp

* chore: filter of datetime field

* chore: test

* chore: test

* fix: test

* fix: datetime no tz filter

* chore: test

* chore: test

* fix: datetime default value in mysql

* fix: sqlite test

* chore: test

* fix: test

* fix: test

* fix: $dateOn

* fix: bug

* fix: bug

* refactor: datepicker

* fix: test

* refactor: datePicker

* refactor: gantt setting

---------

Co-authored-by: katherinehhh <katherine_15995@163.com>
2024-09-10 15:25:20 +08:00

163 lines
3.8 KiB
TypeScript

/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { DataTypes } from 'sequelize';
import { BaseColumnFieldOptions, Field } from './field';
import moment from 'moment';
const datetimeRegex = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
function isValidDatetime(str) {
return datetimeRegex.test(str);
}
export class DateField extends Field {
get dataType(): any {
return DataTypes.DATE(3);
}
get timezone() {
return this.isGMT() ? '+00:00' : null;
}
getProps() {
return this.options?.uiSchema?.['x-component-props'] || {};
}
isDateOnly() {
const props = this.getProps();
return !props.showTime;
}
isGMT() {
const props = this.getProps();
return props.gmt;
}
init() {
const { name, defaultToCurrentTime, onUpdateToCurrentTime, timezone } = this.options;
this.resolveTimeZone = (context) => {
// @ts-ignore
const serverTimeZone = this.database.options.rawTimezone;
if (timezone === 'server') {
return serverTimeZone;
}
if (timezone === 'client') {
return context?.timezone || serverTimeZone;
}
if (timezone) {
return timezone;
}
return serverTimeZone;
};
this.beforeSave = async (instance, options) => {
const value = instance.get(name);
if (!value && instance.isNewRecord && defaultToCurrentTime) {
instance.set(name, new Date());
return;
}
if (onUpdateToCurrentTime) {
instance.set(name, new Date());
return;
}
};
if (this.options.defaultValue && this.database.isMySQLCompatibleDialect()) {
if (typeof this.options.defaultValue === 'string' && isIso8601(this.options.defaultValue)) {
this.options.defaultValue = moment(this.options.defaultValue)
.utcOffset(this.resolveTimeZone())
.format('YYYY-MM-DD HH:mm:ss');
}
}
}
setter(value, options) {
if (value === null) {
return value;
}
if (value instanceof Date) {
return value;
}
if (typeof value === 'string' && isValidDatetime(value)) {
const dateTimezone = this.resolveTimeZone(options?.context);
const dateString = `${value} ${dateTimezone}`;
return new Date(dateString);
}
return value;
}
additionalSequelizeOptions() {
const { name } = this.options;
// @ts-ignore
const serverTimeZone = this.database.options.rawTimezone;
return {
get() {
const value = this.getDataValue(name);
if (value === null || value === undefined) {
return value;
}
if (typeof value === 'string' && isValidDatetime(value)) {
const dateString = `${value} ${serverTimeZone}`;
return new Date(dateString);
}
return new Date(value);
},
};
}
bind() {
super.bind();
if (this.options.interface === 'createdAt') {
const { model } = this.context.collection;
// @ts-ignore
model._timestampAttributes.createdAt = this.name;
// @ts-ignore
model.refreshAttributes();
}
if (this.options.interface === 'updatedAt') {
const { model } = this.context.collection;
// @ts-ignore
model._timestampAttributes.updatedAt = this.name;
// @ts-ignore
model.refreshAttributes();
}
this.on('beforeSave', this.beforeSave);
}
unbind() {
super.unbind();
this.off('beforeSave', this.beforeSave);
}
}
export interface DateFieldOptions extends BaseColumnFieldOptions {
type: 'date';
}
function isIso8601(str) {
const iso8601StrictRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
return iso8601StrictRegex.test(str);
}