Skip to content

Commit

Permalink
fix(formula): date serial calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
Dushusir committed Feb 3, 2024
1 parent 63fca51 commit 4784af8
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 5 deletions.
4 changes: 4 additions & 0 deletions packages/engine-formula/src/basics/__tests__/date.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ import { excelDateSerial } from '../date';

describe('Test date', () => {
it('Function excelDateSerial', () => {
expect(excelDateSerial(new Date(1900, 1, 28))).toBe(59);
expect(excelDateSerial(new Date(1900, 1, 29))).toBe(61);
expect(excelDateSerial(new Date(1900, 2, 1))).toBe(61);
expect(excelDateSerial(new Date(1901, 0, 1))).toBe(367);
expect(excelDateSerial(new Date(2024, 1, 2))).toBe(45324);
});
});
22 changes: 18 additions & 4 deletions packages/engine-formula/src/basics/date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,26 @@ export const DEFFAULT_DATE_FORMAT = 'yyyy/mm/dd';
/**
* Excel stores dates as sequential serial numbers so they can be used in calculations. By default, January 1, 1900 is serial number 1, and January 1, 2008 is serial number 39448 because it is 39,447 days after January 1, 1900.
*
* Excel has a leap year error in 1900. February 29, 1900 is considered a legal date. In fact, there is no February 29 in 1900.
* 1900.2.28 Date Serial 59
* 1900.2.29 Date Serial 61
* 1900.3.1 Date Serial 61
* 1901.1.1 Date Serial 367
* @param date
* @returns
*/
export function excelDateSerial(date: Date): number {
// TODO@Dushusir: set current time zone, reference https://stackoverflow.com/questions/38399465/how-to-get-list-of-all-timezones-in-javascript
const baseDate = new Date(1900, 0, 1); // January 1, 1900
const dayDifference = (date.getTime() - baseDate.getTime()) / (1000 * 3600 * 24);
return Math.ceil(dayDifference) + 1; // +1 for adjusting for Excel's 1900 leap year error
const baseDate = new Date(Date.UTC(1900, 0, 1)); // January 1, 1900, UTC
const leapDayDate = new Date(Date.UTC(1900, 1, 28)); // February 28, 1900, UTC
const dateInUTC = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate());

// Calculate the difference in days between the base date and the input date
let dayDifference = (dateInUTC - baseDate.getTime()) / (1000 * 3600 * 24);

// If the date is later than February 28, 1900, the day difference needs to be adjusted to account for Excel errors
if (dateInUTC > leapDayDate.getTime()) {
dayDifference += 1;
}

return Math.floor(dayDifference) + 1; // Excel serial number starts from 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,19 @@ import { ErrorType } from '../../../..';

// mock new Date() use V
const _Date = Date;
global.Date = vi.fn((...params) => params.length > 0 ? new _Date(params[0], params[1], params[2]) : new _Date(2020, 0, 1)) as any;
global.Date = vi.fn((...params) => {
if (params.length === 1) {
return new _Date(params[0]);
}

if (params.length === 3) {
return new _Date(params[0], params[1], params[2]);
}

return new _Date(2020, 0, 1);
}) as any;
// global.Date = vi.fn((...params) => params.length > 0 ? new _Date(params[0], params[1], params[2]) : new _Date(2020, 0, 1)) as any;
global.Date.UTC = _Date.UTC;

describe('Test today function', () => {
const textFunction = new Today(FUNCTION_NAMES_DATE.TODAY);
Expand Down

0 comments on commit 4784af8

Please sign in to comment.