Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: time with timezone #14213

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/manual/other-topics/other-data-types.md
Expand Up @@ -189,4 +189,6 @@ DataTypes.MACADDR // MACADDR PostgreSQL only
DataTypes.GEOMETRY // Spatial column. PostgreSQL (with PostGIS) or MySQL only.
DataTypes.GEOMETRY('POINT') // Spatial column with geometry type. PostgreSQL (with PostGIS) or MySQL only.
DataTypes.GEOMETRY('POINT', 4326) // Spatial column with geometry type and SRID. PostgreSQL (with PostGIS) or MySQL only.

DateTypes.TIME.ZONED // TIME WITH TIMEZONE PostgreSQL only
```
16 changes: 15 additions & 1 deletion src/data-types.d.ts
Expand Up @@ -324,7 +324,21 @@ export const BOOLEAN: AbstractDataTypeConstructor;
/**
* A time column
*/
export const TIME: AbstractDataTypeConstructor;
export const TIME: TimeDataTypeConstructor;

interface TimeDataTypeConstructor extends AbstractDataTypeConstructor {
ZONED: this;
new (options?: TimeDataTypeOptions): TimeDataType;
(options?: TimeDataTypeOptions): TimeDataType;
}

export interface TimeDataType extends AbstractDataType {
options: TimeDataTypeOptions;
}

export interface TimeDataTypeOptions {
zoned?: boolean;
}

/**
* A datetime column
Expand Down
28 changes: 27 additions & 1 deletion src/data-types.js
Expand Up @@ -462,8 +462,34 @@ BOOLEAN.parse = BOOLEAN.prototype._sanitize;
*
*/
class TIME extends ABSTRACT {
/**
* @param {object} options type options
* @param {object} [options.zoned] Is zoned?
*/
constructor(options = {}) {
super();

this._zoned = options?.zoned ?? false;
}

toSql() {
return 'TIME';
let result = this.key;

if (this._zoned) {
result += ' WITH TIME ZONE';
}

return result;
}

get ZONED() {
this._zoned = true;

return this;
}

static get ZONED() {
return new this().ZONED;
}
}

Expand Down
15 changes: 15 additions & 0 deletions src/dialects/db2/data-types.js
Expand Up @@ -265,6 +265,20 @@ module.exports = BaseTypes => {
}
}

class TIME extends BaseTypes.TIME {
constructor() {
super();

if (this._zoned) {
warn('DB2 does not support TIME WITH TIMEZONE. Plain `TIME` will be used instead.');
this.options.zoned = undefined;
this._zoned = undefined;
}
}
}

delete TIME.parse;
Sasha-hk marked this conversation as resolved.
Show resolved Hide resolved

class INTEGER extends BaseTypes.INTEGER {
constructor(length) {
super(length);
Expand Down Expand Up @@ -368,6 +382,7 @@ module.exports = BaseTypes => {
UUID,
DATE,
DATEONLY,
TIME,
NOW,
TINYINT,
SMALLINT,
Expand Down
15 changes: 15 additions & 0 deletions src/dialects/ibmi/data-types.js
Expand Up @@ -90,6 +90,20 @@ module.exports = BaseTypes => {
}
}

class TIME extends BaseTypes.TIME {
constructor() {
super();

if (this._zoned) {
warn('ibmi does not support TIME WITH TIMEZONE. Plain `TIME` will be used instead.');
this.options.zoned = undefined;
this._zoned = undefined;
}
}
}

delete TIME.parse;

class BOOLEAN extends BaseTypes.BOOLEAN {
toSql() {
return 'SMALLINT';
Expand Down Expand Up @@ -309,6 +323,7 @@ module.exports = BaseTypes => {
BOOLEAN,
DATE,
DATEONLY,
TIME,
STRING,
CHAR,
NUMBER,
Expand Down
16 changes: 16 additions & 0 deletions src/dialects/mariadb/data-types.js
Expand Up @@ -6,6 +6,7 @@ const momentTz = require('moment-timezone');
const moment = require('moment');

module.exports = BaseTypes => {
const warn = BaseTypes.ABSTRACT.warn.bind(undefined, 'https://mariadb.com/kb/en/data-types/');
BaseTypes.ABSTRACT.prototype.dialectTypes = 'https://mariadb.com/kb/en/library/resultset/#field-types';

/**
Expand Down Expand Up @@ -87,6 +88,20 @@ module.exports = BaseTypes => {
}
}

class TIME extends BaseTypes.TIME {
constructor() {
super();

if (this._zoned) {
warn('MariaDB does not support TIME WITH TIMEZONE. Plain `TIME` will be used instead.');
this.options.zoned = undefined;
this._zoned = undefined;
}
}
}

delete TIME.parse;

class UUID extends BaseTypes.UUID {
toSql() {
return 'CHAR(36) BINARY';
Expand Down Expand Up @@ -139,6 +154,7 @@ module.exports = BaseTypes => {
ENUM,
DATE,
DATEONLY,
TIME,
UUID,
GEOMETRY,
DECIMAL,
Expand Down
15 changes: 15 additions & 0 deletions src/dialects/mssql/data-types.js
Expand Up @@ -142,6 +142,20 @@ module.exports = BaseTypes => {
}
}

class TIME extends BaseTypes.TIME {
constructor() {
super();

if (this._zoned) {
warn('MSSQL does not support TIME WITH TIMEZONE. Plain `TIME` will be used instead.');
this.options.zoned = undefined;
ephys marked this conversation as resolved.
Show resolved Hide resolved
this._zoned = undefined;
}
}
}

delete TIME.parse;

class INTEGER extends BaseTypes.INTEGER {
constructor(length) {
super(length);
Expand Down Expand Up @@ -223,6 +237,7 @@ module.exports = BaseTypes => {
UUID,
DATE,
DATEONLY,
TIME,
NOW,
TINYINT,
SMALLINT,
Expand Down
16 changes: 16 additions & 0 deletions src/dialects/mysql/data-types.js
Expand Up @@ -6,6 +6,7 @@ const momentTz = require('moment-timezone');
const moment = require('moment');

module.exports = BaseTypes => {
const warn = BaseTypes.ABSTRACT.warn.bind(undefined, 'https://dev.mysql.com/doc/refman/8.0/en/data-types.html');
BaseTypes.ABSTRACT.prototype.dialectTypes = 'https://dev.mysql.com/doc/refman/5.7/en/data-types.html';

/**
Expand Down Expand Up @@ -92,6 +93,20 @@ module.exports = BaseTypes => {
}
}

class TIME extends BaseTypes.TIME {
constructor() {
super();

if (this._zoned) {
warn('MySQL does not support TIME WITH TIMEZONE. Plain `TIME` will be used instead.');
this.options.zoned = undefined;
this._zoned = undefined;
}
}
}

delete TIME.parse;

class UUID extends BaseTypes.UUID {
toSql() {
return 'CHAR(36) BINARY';
Expand Down Expand Up @@ -153,6 +168,7 @@ module.exports = BaseTypes => {
ENUM,
DATE,
DATEONLY,
TIME,
UUID,
GEOMETRY,
DECIMAL,
Expand Down
16 changes: 16 additions & 0 deletions src/dialects/snowflake/data-types.js
Expand Up @@ -4,6 +4,7 @@ const momentTz = require('moment-timezone');
const moment = require('moment');

module.exports = BaseTypes => {
const warn = BaseTypes.ABSTRACT.warn.bind(undefined, 'https://docs.snowflake.com/en/sql-reference/data-types.html');
BaseTypes.ABSTRACT.prototype.dialectTypes = 'https://dev.snowflake.com/doc/refman/5.7/en/data-types.html';

/**
Expand Down Expand Up @@ -76,6 +77,20 @@ module.exports = BaseTypes => {
}
}

class TIME extends BaseTypes.TIME {
constructor() {
super();

if (this._zoned) {
warn('Snowflake does not support TIME WITH TIMEZONE. Plain `TIME` will be used instead.');
this.options.zoned = undefined;
this._zoned = undefined;
}
}
}

delete TIME.parse;

class UUID extends BaseTypes.UUID {
toSql() {
// https://community.snowflake.com/s/question/0D50Z00009LH2fl/what-is-the-best-way-to-store-uuids
Expand Down Expand Up @@ -106,6 +121,7 @@ module.exports = BaseTypes => {
DATE,
BOOLEAN,
DATEONLY,
TIME,
UUID,
JSON: JSONTYPE,
};
Expand Down
15 changes: 15 additions & 0 deletions src/dialects/sqlite/data-types.js
Expand Up @@ -66,6 +66,20 @@ module.exports = BaseTypes => {
}
}

class TIME extends BaseTypes.TIME {
constructor() {
super();

if (this._zoned) {
warn('SQLite does not support TIME WITH TIMEZONE. Plain `TIME` will be used instead.');
this.options.zoned = undefined;
this._zoned = undefined;
}
}
}

delete TIME.parse;

class STRING extends BaseTypes.STRING {
toSql() {
if (this._binary) {
Expand Down Expand Up @@ -203,6 +217,7 @@ module.exports = BaseTypes => {
return {
DATE,
DATEONLY,
TIME,
STRING,
CHAR,
NUMBER,
Expand Down
8 changes: 7 additions & 1 deletion test/types/data-types.ts
@@ -1,7 +1,13 @@
import { expectTypeOf } from 'expect-type';
import { DataTypes } from '@sequelize/core';

const { TINYINT, SMALLINT, MEDIUMINT, BIGINT, INTEGER, JSON, JSONB, CITEXT, MACADDR, TSVECTOR, CIDR, INET } = DataTypes;
const { TIME, TINYINT, SMALLINT, MEDIUMINT, BIGINT, INTEGER, JSON, JSONB, CITEXT, MACADDR, TSVECTOR, CIDR, INET } = DataTypes;

// TIME
expectTypeOf(TIME()).toEqualTypeOf<DataTypes.TimeDataType>();
expectTypeOf(new TIME()).toEqualTypeOf<DataTypes.TimeDataType>();
expectTypeOf(TIME.ZONED()).toEqualTypeOf<DataTypes.TimeDataType>();
expectTypeOf(new TIME.ZONED()).toEqualTypeOf<DataTypes.TimeDataType>();

// TINYINT
expectTypeOf(TINYINT()).toEqualTypeOf<DataTypes.TinyIntegerDataType>();
Expand Down
35 changes: 35 additions & 0 deletions test/unit/sql/data-types.test.js
Expand Up @@ -233,6 +233,41 @@ describe(Support.getTestDialectTeaser('SQL'), () => {
});
});

describe('TIME', () => {
testsql('TIME', DataTypes.TIME, {
ibmi: 'TIME',
postgres: 'TIME',
mssql: 'TIME',
mariadb: 'TIME',
mysql: 'TIME',
db2: 'TIME',
sqlite: 'TIME',
snowflake: 'TIME',
});

testsql('TIME.ZONED', DataTypes.TIME({ zoned: true }), {
ephys marked this conversation as resolved.
Show resolved Hide resolved
ibmi: 'TIME',
postgres: 'TIME WITH TIME ZONE',
mssql: 'TIME',
mariadb: 'TIME',
mysql: 'TIME',
db2: 'TIME',
sqlite: 'TIME',
snowflake: 'TIME',
});

testsql('TIME.ZONED', DataTypes.TIME.ZONED, {
Sasha-hk marked this conversation as resolved.
Show resolved Hide resolved
ibmi: 'TIME',
postgres: 'TIME WITH TIME ZONE',
mssql: 'TIME',
mariadb: 'TIME',
mysql: 'TIME',
db2: 'TIME',
sqlite: 'TIME',
snowflake: 'TIME',
});
});

if (current.dialect.supports.HSTORE) {
describe('HSTORE', () => {
describe('validate', () => {
Expand Down