diff --git a/packages/nodes-base/nodes/DateTime.node.ts b/packages/nodes-base/nodes/DateTime.node.ts new file mode 100644 index 0000000000..50f9e07ede --- /dev/null +++ b/packages/nodes-base/nodes/DateTime.node.ts @@ -0,0 +1,259 @@ +import { IExecuteFunctions } from 'n8n-core'; +import { + INodeExecutionData, + INodeType, + INodeTypeDescription, + INodePropertyOptions, + ILoadOptionsFunctions, + IDataObject, +} from 'n8n-workflow'; + +import * as moment from 'moment-timezone'; + +export class DateTime implements INodeType { + description: INodeTypeDescription = { + displayName: 'Date & Time', + name: 'dateTime', + icon: 'fa:calendar', + group: ['transform'], + version: 1, + description: 'Allows you to manipulate date and time values', + defaults: { + name: 'Date & Time', + color: '#408000', + }, + inputs: ['main'], + outputs: ['main'], + properties: [ + { + displayName: 'Action', + name: 'action', + type: 'options', + options: [ + { + name: 'Format a Date', + description: 'Apply to a date a diferent format', + value: 'format' + }, + ], + default: 'format', + }, + { + displayName: 'Field Name', + name: 'fieldName', + displayOptions: { + show: { + action:[ + 'format', + ], + }, + }, + type: 'string', + default: '', + required: true, + }, + { + displayName: 'Custom Format', + name: 'custom', + displayOptions: { + show: { + action:[ + 'format', + ], + }, + }, + type: 'boolean', + default: false, + }, + { + displayName: 'To Format', + name: 'toFormat', + displayOptions: { + show: { + action:[ + 'format', + ], + custom: [ + true, + ], + }, + }, + type: 'string', + default: '', + }, + { + displayName: 'To Format', + name: 'toFormat', + type: 'options', + displayOptions: { + show: { + action:[ + 'format', + ], + custom:[ + false, + ], + }, + }, + options: [ + { + name: 'MM/DD/YYYY', + value: 'MM/DD/YYYY', + description: 'Example: 09/04/1986', + }, + { + name: 'YYYY/MM/DD', + value: 'YYYY/MM/DD', + description: 'Example: 1986/04/09', + }, + { + name: 'MMMM DD YYYY', + value: 'MMMM DD YYYY', + description: 'Example: April 09 1986', + }, + { + name: 'MM-DD-YYYY', + value: 'MM-DD-YYYY', + description: 'Example: 09-04-1986', + }, + { + name: 'YYYY-MM-DD', + value: 'YYYY-MM-DD', + description: 'Example: 1986-04-09', + }, + { + name: 'Unix Timestamp', + value: 'X', + description: 'Example: 513388800.879', + }, + { + name: 'Unix Ms Timestamp', + value: 'x', + description: 'Example: 513388800', + }, + ], + default: 'MM/DD/YYYY', + }, + { + displayName: 'To Timezone', + name: 'toTimezone', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTimezones', + }, + displayOptions: { + show: { + action:[ + 'format' + ], + }, + }, + default: 'UTC', + }, + { + displayName: 'Options', + name: 'options', + displayOptions: { + show: { + action:[ + 'format' + ], + }, + }, + type: 'collection', + placeholder: 'Add Option', + default: {}, + options: [ + { + displayName: 'From Timezone', + name: 'fromTimezone', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTimezones', + }, + default: 'UTC', + }, + { + displayName: 'From Format', + name: 'fromFormat', + type: 'string', + default: '', + description: 'In case the input format is not recognized you can provide the format ', + }, + { + displayName: 'Keep Old Date', + name: 'keepOldDate', + type: 'boolean', + default: false, + }, + ], + }, + ], + }; + + methods = { + loadOptions: { + // Get all the timezones to display them to user so that he can + // select them easily + async getTimezones(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + for (const timezone of moment.tz.names()) { + const timezoneName = timezone; + const timezoneId = timezone; + returnData.push({ + name: timezoneName, + value: timezoneId, + }); + } + return returnData; + }, + } + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + const length = items.length as unknown as number; + let responseData; + for (let i = 0; i < length; i++) { + const action = this.getNodeParameter('action', 0) as string; + if (action === 'format') { + const fieldName = this.getNodeParameter('fieldName', i) as string; + const toTimezone = this.getNodeParameter('toTimezone', i) as string; + const toFormat = this.getNodeParameter('toFormat', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + let newDate; + let clone = { ...items[i].json }; + if (clone[fieldName] === undefined) { + throw new Error(`The field ${fieldName} does not exist on the input data`); + } + if (!moment(clone[fieldName] as string | number).isValid()) { + throw new Error('The date input format is not recognized, please set the "From Format" field'); + } + if (Number.isInteger(clone[fieldName] as number)) { + newDate = moment.unix(clone[fieldName] as number).tz(toTimezone).format(toFormat); + } else { + newDate = moment(clone[fieldName] as string).tz(toTimezone).format(toFormat); + if (options.fromTimezone) { + newDate = moment.tz(clone[fieldName] as string, options.fromTimezone as string).tz(toTimezone).format(toFormat); + } + if (options.fromFormat) { + newDate = moment(clone[fieldName] as string, options.fromFormat as string).tz(toTimezone).format(toFormat); + } + } + if (!options.keepOldDate) { + clone[fieldName] = newDate; + } else { + clone['newDate'] = newDate; + } + responseData = clone; + } + if (Array.isArray(responseData)) { + returnData.push.apply(returnData, responseData as IDataObject[]); + } else { + returnData.push(responseData as IDataObject); + } + } + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 9c1beb2006..eac3cca07b 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -118,6 +118,7 @@ "dist/nodes/Discord/Discord.node.js", "dist/nodes/Disqus/Disqus.node.js", "dist/nodes/Dropbox/Dropbox.node.js", + "dist/nodes/DateTime.node.js", "dist/nodes/EditImage.node.js", "dist/nodes/EmailReadImap.node.js", "dist/nodes/EmailSend.node.js", @@ -220,6 +221,7 @@ "@types/imap-simple": "^4.2.0", "@types/jest": "^24.0.18", "@types/lodash.set": "^4.3.6", + "@types/moment-timezone": "^0.5.12", "@types/mongodb": "^3.3.6", "@types/node": "^10.10.1", "@types/nodemailer": "^4.6.5",