Skip to content

Commit

Permalink
feat(formula): supplement EPOCHTODATE/TO_DATE/ISDATE formula (#3979)
Browse files Browse the repository at this point in the history
Co-authored-by: wpxp123456 <Wpxp1223456>
  • Loading branch information
wpxp123456 authored Nov 21, 2024
1 parent b4cfe8d commit 0656167
Show file tree
Hide file tree
Showing 22 changed files with 882 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { describe, expect, it } from 'vitest';

import { ErrorType } from '../../../../basics/error-type';
import { ArrayValueObject, transformToValueObject } from '../../../../engine/value-object/array-value-object';
import { ErrorValueObject } from '../../../../engine/value-object/base-value-object';
import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import { getObjectValue } from '../../../__tests__/create-function-test-bed';
import { FUNCTION_NAMES_DATE } from '../../function-names';
import { Epochtodate } from '../index';

describe('Test epochtodate function', () => {
const testFunction = new Epochtodate(FUNCTION_NAMES_DATE.EPOCHTODATE);

describe('Epochtodate', () => {
it('value is normal', () => {
const timestamp = NumberValueObject.create(1655906710);
const result = testFunction.calculate(timestamp);
expect(getObjectValue(result)).toBe(44734.58692129629);
});

it('Timestamp value test', () => {
const timestamp = NullValueObject.create();
const result = testFunction.calculate(timestamp);
expect(getObjectValue(result)).toBe(ErrorType.VALUE);

const timestamp2 = StringValueObject.create('test');
const result2 = testFunction.calculate(timestamp2);
expect(getObjectValue(result2)).toBe(ErrorType.VALUE);

const timestamp3 = BooleanValueObject.create(true);
const result3 = testFunction.calculate(timestamp3);
expect(getObjectValue(result3)).toBe(ErrorType.VALUE);

const timestamp4 = ErrorValueObject.create(ErrorType.NAME);
const result4 = testFunction.calculate(timestamp4);
expect(getObjectValue(result4)).toBe(ErrorType.NAME);

const timestamp5 = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[1, 2],
]),
rowCount: 1,
columnCount: 2,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const result5 = testFunction.calculate(timestamp5);
expect(getObjectValue(result5)).toBe(ErrorType.VALUE);

const timestamp6 = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[-1],
]),
rowCount: 1,
columnCount: 1,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const result6 = testFunction.calculate(timestamp6);
expect(getObjectValue(result6)).toBe(ErrorType.NUM);

const timestamp7 = StringValueObject.create('50%');
const result7 = testFunction.calculate(timestamp7);
expect(getObjectValue(result7)).toBe(ErrorType.VALUE);
});

it('Unit value test', () => {
const timestamp = NumberValueObject.create(1655906710);
const unit = NumberValueObject.create(2);
const result = testFunction.calculate(timestamp, unit);
expect(getObjectValue(result)).toBe(25588.165586921295);

const unit2 = NumberValueObject.create(3);
const result2 = testFunction.calculate(timestamp, unit2);
expect(getObjectValue(result2)).toBe(25569.019165578702);

const unit3 = NullValueObject.create();
const result3 = testFunction.calculate(timestamp, unit3);
expect(getObjectValue(result3)).toBe(ErrorType.NUM);

const unit4 = StringValueObject.create('test');
const result4 = testFunction.calculate(timestamp, unit4);
expect(getObjectValue(result4)).toBe(ErrorType.VALUE);

const unit5 = BooleanValueObject.create(true);
const result5 = testFunction.calculate(timestamp, unit5);
expect(getObjectValue(result5)).toBe(44734.58692129629);

const unit6 = ErrorValueObject.create(ErrorType.NAME);
const result6 = testFunction.calculate(timestamp, unit6);
expect(getObjectValue(result6)).toBe(ErrorType.NAME);

const unit7 = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[1, 2],
]),
rowCount: 1,
columnCount: 2,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const result7 = testFunction.calculate(timestamp, unit7);
expect(getObjectValue(result7)).toBe(ErrorType.VALUE);

const unit8 = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[-1],
]),
rowCount: 1,
columnCount: 1,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const result8 = testFunction.calculate(timestamp, unit8);
expect(getObjectValue(result8)).toBe(ErrorType.NUM);
});
});
});
147 changes: 147 additions & 0 deletions packages/engine-formula/src/functions/date/epochtodate/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import type { BaseReferenceObject, FunctionVariantType } from '../../../engine/reference-object/base-reference-object';
import type { ArrayValueObject } from '../../../engine/value-object/array-value-object';
import { excelDateTimeSerial } from '../../../basics/date';
import { ErrorType } from '../../../basics/error-type';
import { checkVariantsErrorIsStringToNumber } from '../../../engine/utils/check-variant-error';
import { type BaseValueObject, ErrorValueObject } from '../../../engine/value-object/base-value-object';
import { NumberValueObject } from '../../../engine/value-object/primitive-object';
import { BaseFunction } from '../../base-function';

export class Epochtodate extends BaseFunction {
override minParams = 1;

override maxParams = 2;

override needsReferenceObject = true;

override calculate(timestamp: FunctionVariantType, unit?: FunctionVariantType): BaseValueObject {
const _unit = unit ?? NumberValueObject.create(1);

const { isError, errorObject, timestampIsReferenceObject, timestampObject, unitObject } = this._checkVariants(timestamp, _unit);

if (isError) {
return errorObject as ErrorValueObject;
}

if ((timestampObject as BaseValueObject).isNull() || (timestampObject as BaseValueObject).isBoolean() || (timestampObject as BaseValueObject).isString()) {
return ErrorValueObject.create(ErrorType.VALUE);
}

if (!timestampIsReferenceObject && (timestampObject as BaseValueObject).isNumber() && (timestampObject as BaseValueObject).getPattern() !== '') {
return ErrorValueObject.create(ErrorType.VALUE);
}

let timestampValue = +(timestampObject as BaseValueObject).getValue();

const { isError: _isError, errorObject: _errorObject, variants } = checkVariantsErrorIsStringToNumber(unitObject as BaseValueObject);

if (_isError) {
return _errorObject as ErrorValueObject;
}

const [_unitObject] = variants as BaseValueObject[];

const unitValue = Math.floor(+_unitObject.getValue());

if (timestampValue < 0 || unitValue < 1 || unitValue > 3) {
return ErrorValueObject.create(ErrorType.NUM);
}

if (unitValue === 1) {
timestampValue = timestampValue * 1000;
}

if (unitValue === 3) {
timestampValue = timestampValue / 1000;
}

const date = new Date(Date.UTC(1970, 0, 1, 0, 0, 0, 0) + timestampValue);
const dateSerialNumber = excelDateTimeSerial(date);

return NumberValueObject.create(dateSerialNumber, 'yyyy-MM-dd hh:mm:ss AM/PM');
}

private _checkVariants(timestamp: FunctionVariantType, unit: FunctionVariantType) {
const timestampIsReferenceObject = timestamp.isReferenceObject();
const unitIsReferenceObject = unit.isReferenceObject();

let _timestamp = timestamp;

if (timestampIsReferenceObject) {
_timestamp = (timestamp as BaseReferenceObject).toArrayValueObject();
}

if (_timestamp.isArray()) {
const rowCount = (_timestamp as ArrayValueObject).getRowCount();
const columnCount = (_timestamp as ArrayValueObject).getColumnCount();

if (rowCount > 1 || columnCount > 1) {
return {
isError: true,
errorObject: ErrorValueObject.create(ErrorType.VALUE),
};
}

_timestamp = (_timestamp as ArrayValueObject).get(0, 0) as BaseValueObject;
}

if (_timestamp.isError()) {
return {
isError: true,
errorObject: _timestamp as ErrorValueObject,
};
}

let _unit = unit;

if (unitIsReferenceObject) {
_unit = (unit as BaseReferenceObject).toArrayValueObject();
}

if (_unit.isArray()) {
const rowCount = (_unit as ArrayValueObject).getRowCount();
const columnCount = (_unit as ArrayValueObject).getColumnCount();

if (rowCount > 1 || columnCount > 1) {
return {
isError: true,
errorObject: ErrorValueObject.create(ErrorType.VALUE),
};
}

_unit = (_unit as ArrayValueObject).get(0, 0) as BaseValueObject;
}

if (_unit.isError()) {
return {
isError: true,
errorObject: _unit as ErrorValueObject,
};
}

return {
isError: false,
errorObject: null,
timestampIsReferenceObject,
timestampObject: _timestamp as BaseValueObject,
unitIsReferenceObject,
unitObject: _unit as BaseValueObject,
};
}
}
6 changes: 5 additions & 1 deletion packages/engine-formula/src/functions/date/function-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
* limitations under the License.
*/

import { FUNCTION_NAMES_DATE } from './function-names';
import { DateFunction } from './date';
import { Datedif } from './datedif';
import { Datevalue } from './datevalue';
Expand All @@ -23,6 +22,8 @@ import { Days } from './days';
import { Days360 } from './days360';
import { Edate } from './edate';
import { Eomonth } from './eomonth';
import { Epochtodate } from './epochtodate';
import { FUNCTION_NAMES_DATE } from './function-names';
import { Hour } from './hour';
import { Isoweeknum } from './isoweeknum';
import { Minute } from './minute';
Expand All @@ -33,6 +34,7 @@ import { Now } from './now';
import { Second } from './second';
import { Time } from './time';
import { Timevalue } from './timevalue';
import { ToDate } from './to-date';
import { Today } from './today';
import { Weekday } from './weekday';
import { Weeknum } from './weeknum';
Expand All @@ -50,6 +52,7 @@ export const functionDate = [
[Days360, FUNCTION_NAMES_DATE.DAYS360],
[Edate, FUNCTION_NAMES_DATE.EDATE],
[Eomonth, FUNCTION_NAMES_DATE.EOMONTH],
[Epochtodate, FUNCTION_NAMES_DATE.EPOCHTODATE],
[Hour, FUNCTION_NAMES_DATE.HOUR],
[Isoweeknum, FUNCTION_NAMES_DATE.ISOWEEKNUM],
[Minute, FUNCTION_NAMES_DATE.MINUTE],
Expand All @@ -60,6 +63,7 @@ export const functionDate = [
[Second, FUNCTION_NAMES_DATE.SECOND],
[Time, FUNCTION_NAMES_DATE.TIME],
[Timevalue, FUNCTION_NAMES_DATE.TIMEVALUE],
[ToDate, FUNCTION_NAMES_DATE.TO_DATE],
[Today, FUNCTION_NAMES_DATE.TODAY],
[Weekday, FUNCTION_NAMES_DATE.WEEKDAY],
[Weeknum, FUNCTION_NAMES_DATE.WEEKNUM],
Expand Down
2 changes: 2 additions & 0 deletions packages/engine-formula/src/functions/date/function-names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export enum FUNCTION_NAMES_DATE {
DAYS360 = 'DAYS360',
EDATE = 'EDATE',
EOMONTH = 'EOMONTH',
EPOCHTODATE = 'EPOCHTODATE',
HOUR = 'HOUR',
ISOWEEKNUM = 'ISOWEEKNUM',
MINUTE = 'MINUTE',
Expand All @@ -33,6 +34,7 @@ export enum FUNCTION_NAMES_DATE {
SECOND = 'SECOND',
TIME = 'TIME',
TIMEVALUE = 'TIMEVALUE',
TO_DATE = 'TO_DATE',
TODAY = 'TODAY',
WEEKDAY = 'WEEKDAY',
WEEKNUM = 'WEEKNUM',
Expand Down
Loading

0 comments on commit 0656167

Please sign in to comment.