diff --git a/src/botPage/bot/Interface/ToolsInterface.js b/src/botPage/bot/Interface/ToolsInterface.js index 90fb23f451..2067f0889b 100644 --- a/src/botPage/bot/Interface/ToolsInterface.js +++ b/src/botPage/bot/Interface/ToolsInterface.js @@ -1,13 +1,78 @@ import CandleInterface from './CandleInterface'; import MiscInterface from './MiscInterface'; import IndicatorsInterface from './IndicatorsInterface'; +import { translate } from '../../../common/i18n'; // prettier-ignore export default Interface => class extends IndicatorsInterface( MiscInterface(CandleInterface(Interface))) { getToolsInterface() { return { - getTime: () => parseInt(new Date().getTime() / 1000), + getTime : () => parseInt(new Date().getTime() / 1000), + toDateTime: (timestamp) => { + const getTwoDigitValue = input => { + if (input < 10) { + return `0${input}`; + } + return `${input}`; + } + const invalidTimestamp = () => `${translate('Invalid timestamp')}: ${timestamp}`; + if (typeof timestamp === 'number') { + const dateTime = new Date(timestamp * 1000); + if (dateTime.getTime()) { + const year = dateTime.getFullYear(); + const month = getTwoDigitValue(dateTime.getMonth() + 1); + const day = getTwoDigitValue(dateTime.getDate()); + const hours = getTwoDigitValue(dateTime.getHours()); + const minutes = getTwoDigitValue(dateTime.getMinutes()); + const seconds = getTwoDigitValue(dateTime.getSeconds()); + const formatGTMoffset = () => { + const GMToffsetRaw = dateTime.getTimezoneOffset(); + const sign = GMToffsetRaw > 0 ? '-' : '+'; + const GMToffset = Math.abs(GMToffsetRaw); + const h = Math.floor(GMToffset / 60); + const m = GMToffset - h * 60; + return `GMT${sign}${getTwoDigitValue(h)}${getTwoDigitValue(m)}`; + } + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds} ${formatGTMoffset()}`; + } + return invalidTimestamp(); + } + return invalidTimestamp(); + }, + toTimestamp: (dateTimeString) => { + const invalidDatetime = () => `${translate('Invalid date/time')}: ${dateTimeString}`; + if (typeof dateTimeString === 'string') { + const dateTime = dateTimeString + .replace(/[^0-9.:-\s]/g, '') + .replace(/\s+/g,' ') + .trim() + .split(' '); + + const d = /^[12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/; + const t = /^(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])(:([0-5][0-9])?)?$/; + + let validatedDateTime; + + if(dateTime.length >= 2) { + validatedDateTime = d.test(dateTime[0]) && t.test(dateTime[1]) ? `${dateTime[0]}T${dateTime[1]}` : null; + } else if(dateTime.length === 1) { + validatedDateTime = d.test(dateTime[0]) ? dateTime[0] : null; + } else { + validatedDateTime = null; + } + + if(validatedDateTime) { + const dateObj = new Date(validatedDateTime); + // eslint-disable-next-line no-restricted-globals + if(dateObj instanceof Date && !isNaN(dateObj)) { + return dateObj.getTime() / 1000; + } + } + return invalidDatetime(); + } + return invalidDatetime(); + }, ...this.getCandleInterface(), ...this.getMiscInterface(), ...this.getIndicatorsInterface(), diff --git a/src/botPage/bot/__tests__/block-tests/tools-test/Time.js b/src/botPage/bot/__tests__/block-tests/tools-test/Time.js index 344f02c738..0c38235b8c 100644 --- a/src/botPage/bot/__tests__/block-tests/tools-test/Time.js +++ b/src/botPage/bot/__tests__/block-tests/tools-test/Time.js @@ -27,3 +27,17 @@ describe('Time in tools', () => { expect(time2 - time1).most(3); }); }); + +describe('Convert timestamp to date/time and back', () => { + const timestamp = Math.ceil(new Date().getTime() / 1000); + let result; + beforeAll(done => { + run(`(function() {return Bot.toTimestamp(Bot.toDateTime(${timestamp}));})()`).then(v => { + result = v; + done(); + }); + }); + it('converts timestamp to date/time string', () => { + expect(result).satisfy(dt => dt === timestamp); + }); +}); diff --git a/src/botPage/view/blockly/blocks/tools/time/index.js b/src/botPage/view/blockly/blocks/tools/time/index.js index 2c41b91b5b..6dc16d8fa4 100644 --- a/src/botPage/view/blockly/blocks/tools/time/index.js +++ b/src/botPage/view/blockly/blocks/tools/time/index.js @@ -1,3 +1,5 @@ import './epoch'; import './timeout'; import './interval'; +import './todatetime'; +import './totimestamp'; diff --git a/src/botPage/view/blockly/blocks/tools/time/todatetime.js b/src/botPage/view/blockly/blocks/tools/time/todatetime.js new file mode 100644 index 0000000000..df1e981408 --- /dev/null +++ b/src/botPage/view/blockly/blocks/tools/time/todatetime.js @@ -0,0 +1,30 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.todatetime = { + init: function init() { + this.appendDummyInput(); + this.appendValueInput('TIMESTAMP').appendField(translate('To Date/Time')); + this.setInputsInline(true); + this.setOutput(true, 'String'); + this.setColour('#dedede'); + this.setTooltip( + translate( + 'Converts a number of seconds since Epoch into a string representing date and time. Example: 1546347825 will be converted to 2019-01-01 21:03:45.' + ) + ); + }, +}; + +Blockly.JavaScript.todatetime = block => { + const timestamp = Blockly.JavaScript.valueToCode(block, 'TIMESTAMP', Blockly.JavaScript.ORDER_ATOMIC); + // eslint-disable-next-line no-underscore-dangle + const functionName = Blockly.JavaScript.provideFunction_('timestampToDateString', [ + // eslint-disable-next-line no-underscore-dangle + `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(timestamp) { + return Bot.toDateTime(timestamp); + }`, + ]); + + const code = `${functionName}(${timestamp})`; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; +}; diff --git a/src/botPage/view/blockly/blocks/tools/time/totimestamp.js b/src/botPage/view/blockly/blocks/tools/time/totimestamp.js new file mode 100644 index 0000000000..8fdc7330c6 --- /dev/null +++ b/src/botPage/view/blockly/blocks/tools/time/totimestamp.js @@ -0,0 +1,30 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.totimestamp = { + init: function init() { + this.appendDummyInput(); + this.appendValueInput('DATETIME').appendField(translate('To Timestamp')); + this.setInputsInline(true); + this.setOutput(true, 'Number'); + this.setColour('#dedede'); + this.setTooltip( + translate( + 'Converts a string representing a date/time string into seconds since Epoch. Example: 2019-01-01 21:03:45 GMT+0800 will be converted to 1546347825. Time and time zone offset are optional.' + ) + ); + }, +}; + +Blockly.JavaScript.totimestamp = block => { + const dateString = Blockly.JavaScript.valueToCode(block, 'DATETIME', Blockly.JavaScript.ORDER_ATOMIC); + // eslint-disable-next-line no-underscore-dangle + const functionName = Blockly.JavaScript.provideFunction_('dateTimeStringToTimestamp', [ + // eslint-disable-next-line no-underscore-dangle + `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(dateTimeString) { + return Bot.toTimestamp(dateTimeString); + }`, + ]); + + const code = `${functionName}(${dateString})`; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; +}; diff --git a/static/xml/toolbox.xml b/static/xml/toolbox.xml index a5aff41962..16c6fe0a51 100644 --- a/static/xml/toolbox.xml +++ b/static/xml/toolbox.xml @@ -408,6 +408,20 @@ + + + + yyyy-mm-dd hh:mm:ss + + + + + + + 0 + + +