@odoo/o-spreadsheet 16.2.26 → 16.2.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -286,15 +286,76 @@ const FONT_SIZES = [6, 7, 8, 9, 10, 11, 12, 14, 18, 24, 36];
286
286
  // -----------------------------------------------------------------------------
287
287
  // Date Type
288
288
  // -----------------------------------------------------------------------------
289
+ /**
290
+ * A DateTime object that can be used to manipulate spreadsheet dates.
291
+ * Conceptually, a spreadsheet date is simply a number with a date format,
292
+ * and it is timezone-agnostic.
293
+ * This DateTime object consistently uses UTC time to represent a naive date and time.
294
+ */
295
+ class DateTime {
296
+ constructor(year, month, day, hours = 0, minutes = 0, seconds = 0) {
297
+ this.jsDate = new Date(Date.UTC(year, month, day, hours, minutes, seconds, 0));
298
+ }
299
+ static fromTimestamp(timestamp) {
300
+ const date = new Date(timestamp);
301
+ return new DateTime(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
302
+ }
303
+ static now() {
304
+ const now = new Date();
305
+ return new DateTime(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds());
306
+ }
307
+ toString() {
308
+ return this.jsDate.toString();
309
+ }
310
+ getTime() {
311
+ return this.jsDate.getTime();
312
+ }
313
+ getFullYear() {
314
+ return this.jsDate.getUTCFullYear();
315
+ }
316
+ getMonth() {
317
+ return this.jsDate.getUTCMonth();
318
+ }
319
+ getDate() {
320
+ return this.jsDate.getUTCDate();
321
+ }
322
+ getDay() {
323
+ return this.jsDate.getUTCDay();
324
+ }
325
+ getHours() {
326
+ return this.jsDate.getUTCHours();
327
+ }
328
+ getMinutes() {
329
+ return this.jsDate.getUTCMinutes();
330
+ }
331
+ getSeconds() {
332
+ return this.jsDate.getUTCSeconds();
333
+ }
334
+ setFullYear(year) {
335
+ this.jsDate.setFullYear(year);
336
+ }
337
+ setDate(date) {
338
+ this.jsDate.setUTCDate(date);
339
+ }
340
+ setHours(hours) {
341
+ this.jsDate.setUTCHours(hours);
342
+ }
343
+ setMinutes(minutes) {
344
+ this.jsDate.setUTCMinutes(minutes);
345
+ }
346
+ setSeconds(seconds) {
347
+ this.jsDate.setUTCSeconds(seconds);
348
+ }
349
+ }
289
350
  // -----------------------------------------------------------------------------
290
351
  // Parsing
291
352
  // -----------------------------------------------------------------------------
292
- const INITIAL_1900_DAY = new Date(1899, 11, 30);
353
+ const INITIAL_1900_DAY = new DateTime(1899, 11, 30);
293
354
  const MS_PER_DAY = 24 * 60 * 60 * 1000;
294
355
  const CURRENT_MILLENIAL = 2000; // note: don't forget to update this in 2999
295
- const CURRENT_YEAR = new Date().getFullYear();
296
- const INITIAL_JS_DAY = new Date(0);
297
- const DATE_JS_1900_OFFSET = INITIAL_JS_DAY - INITIAL_1900_DAY;
356
+ const CURRENT_YEAR = DateTime.now().getFullYear();
357
+ const INITIAL_JS_DAY = DateTime.fromTimestamp(0);
358
+ const DATE_JS_1900_OFFSET = INITIAL_JS_DAY.getTime() - INITIAL_1900_DAY.getTime();
298
359
  const mdyDateRegexp = /^\d{1,2}(\/|-|\s)\d{1,2}((\/|-|\s)\d{1,4})?$/;
299
360
  const ymdDateRegexp = /^\d{3,4}(\/|-|\s)\d{1,2}(\/|-|\s)\d{1,2}$/;
300
361
  const timeRegexp = /((\d+(:\d+)?(:\d+)?\s*(AM|PM))|(\d+:\d+(:\d+)?))$/;
@@ -343,7 +404,7 @@ function _parseDateTime(str) {
343
404
  return {
344
405
  value: date.value + time.value,
345
406
  format: date.format + " " + (time.format === "hhhh:mm:ss" ? "hh:mm:ss" : time.format),
346
- jsDate: new Date(date.jsDate.getFullYear() + time.jsDate.getFullYear() - 1899, date.jsDate.getMonth() + time.jsDate.getMonth() - 11, date.jsDate.getDate() + time.jsDate.getDate() - 30, date.jsDate.getHours() + time.jsDate.getHours(), date.jsDate.getMinutes() + time.jsDate.getMinutes(), date.jsDate.getSeconds() + time.jsDate.getSeconds()),
407
+ jsDate: new DateTime(date.jsDate.getFullYear() + time.jsDate.getFullYear() - 1899, date.jsDate.getMonth() + time.jsDate.getMonth() - 11, date.jsDate.getDate() + time.jsDate.getDate() - 30, date.jsDate.getHours() + time.jsDate.getHours(), date.jsDate.getMinutes() + time.jsDate.getMinutes(), date.jsDate.getSeconds() + time.jsDate.getSeconds()),
347
408
  };
348
409
  }
349
410
  return date || time;
@@ -360,13 +421,13 @@ function parseDate(str, dateFormat) {
360
421
  const day = Number(parts[dayIndex]);
361
422
  const leadingZero = (parts[monthIndex].length === 2 && month < 10) || (parts[dayIndex].length === 2 && day < 10);
362
423
  const year = parts[yearIndex] ? inferYear(parts[yearIndex]) : CURRENT_YEAR;
363
- const jsDate = new Date(year, month - 1, day);
424
+ const jsDate = new DateTime(year, month - 1, day);
364
425
  const sep = str.match(/\/|-|\s/)[0];
365
426
  if (jsDate.getMonth() !== month - 1 || jsDate.getDate() !== day) {
366
427
  // invalid date
367
428
  return null;
368
429
  }
369
- const delta = jsDate - INITIAL_1900_DAY;
430
+ const delta = jsDate.getTime() - INITIAL_1900_DAY.getTime();
370
431
  let format = leadingZero ? `mm${sep}dd` : `m${sep}d`;
371
432
  if (parts[yearIndex]) {
372
433
  format = isMDY ? format + sep + "yyyy" : "yyyy" + sep + format;
@@ -426,7 +487,7 @@ function parseTime(str) {
426
487
  if (hours >= 24) {
427
488
  format = "hhhh:mm:ss";
428
489
  }
429
- const jsDate = new Date(1899, 11, 30, hours, minutes, seconds);
490
+ const jsDate = new DateTime(1899, 11, 30, hours, minutes, seconds);
430
491
  return {
431
492
  value: hours / 24 + minutes / 1440 + seconds / 86400,
432
493
  format: format,
@@ -440,7 +501,7 @@ function parseTime(str) {
440
501
  // -----------------------------------------------------------------------------
441
502
  function numberToJsDate(value) {
442
503
  const truncValue = Math.trunc(value);
443
- let date = new Date(truncValue * MS_PER_DAY - DATE_JS_1900_OFFSET);
504
+ let date = DateTime.fromTimestamp(truncValue * MS_PER_DAY - DATE_JS_1900_OFFSET);
444
505
  let time = value - truncValue;
445
506
  time = time < 0 ? 1 + time : time;
446
507
  const hours = Math.round(time * 24);
@@ -457,7 +518,7 @@ function jsDateToRoundNumber(date) {
457
518
  }
458
519
  /** Return the number of days in the current month of the given date */
459
520
  function getDaysInMonth(date) {
460
- return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
521
+ return new DateTime(date.getFullYear(), date.getMonth() + 1, 0).getDate();
461
522
  }
462
523
  function isLastDayOfMonth(date) {
463
524
  return getDaysInMonth(date) === date.getDate();
@@ -475,7 +536,7 @@ function addMonthsToDate(date, months, keepEndOfMonth) {
475
536
  const yStart = date.getFullYear();
476
537
  const mStart = date.getMonth();
477
538
  const dStart = date.getDate();
478
- const jsDate = new Date(yStart, mStart + months);
539
+ const jsDate = new DateTime(yStart, mStart + months, 1);
479
540
  if (keepEndOfMonth && dStart === getDaysInMonth(date)) {
480
541
  jsDate.setDate(getDaysInMonth(jsDate));
481
542
  }
@@ -1896,7 +1957,7 @@ function formatJSTime(jsDate, format) {
1896
1957
  .map((p) => {
1897
1958
  switch (p) {
1898
1959
  case "hhhh":
1899
- const helapsedHours = Math.floor((jsDate.getTime() - INITIAL_1900_DAY) / (60 * 60 * 1000));
1960
+ const helapsedHours = Math.floor((jsDate.getTime() - INITIAL_1900_DAY.getTime()) / (60 * 60 * 1000));
1900
1961
  return helapsedHours.toString();
1901
1962
  case "hh":
1902
1963
  return hours.toString().padStart(2, "0");
@@ -9492,6 +9553,9 @@ function arg(definition, description = "") {
9492
9553
  function makeArg(str) {
9493
9554
  let parts = str.match(ARG_REGEXP);
9494
9555
  let name = parts[1].trim();
9556
+ if (!name) {
9557
+ throw new Error(`Function argument definition is missing a name: '${str}'.`);
9558
+ }
9495
9559
  let types = [];
9496
9560
  let isOptional = false;
9497
9561
  let isRepeating = false;
@@ -11955,7 +12019,7 @@ const DATE = {
11955
12019
  if (_year < 1900) {
11956
12020
  _year += 1900;
11957
12021
  }
11958
- const jsDate = new Date(_year, _month - 1, _day);
12022
+ const jsDate = new DateTime(_year, _month - 1, _day);
11959
12023
  const result = jsDateToRoundNumber(jsDate);
11960
12024
  assert(() => result >= 0, _lt(`The function [[FUNCTION_NAME]] result must be greater than or equal 01/01/1900.`));
11961
12025
  return result;
@@ -12063,7 +12127,7 @@ const EOMONTH = {
12063
12127
  const _months = Math.trunc(toNumber(months));
12064
12128
  const yStart = _startDate.getFullYear();
12065
12129
  const mStart = _startDate.getMonth();
12066
- const jsDate = new Date(yStart, mStart + _months + 1, 0);
12130
+ const jsDate = new DateTime(yStart, mStart + _months + 1, 0);
12067
12131
  return jsDateToRoundNumber(jsDate);
12068
12132
  },
12069
12133
  isExported: true,
@@ -12100,17 +12164,17 @@ const ISOWEEKNUM = {
12100
12164
  // The first week of the year is the week that contains the first
12101
12165
  // Thursday of the year.
12102
12166
  let firstThursday = 1;
12103
- while (new Date(y, 0, firstThursday).getDay() !== 4) {
12167
+ while (new DateTime(y, 0, firstThursday).getDay() !== 4) {
12104
12168
  firstThursday += 1;
12105
12169
  }
12106
- const firstDayOfFirstWeek = new Date(y, 0, firstThursday - 3);
12170
+ const firstDayOfFirstWeek = new DateTime(y, 0, firstThursday - 3);
12107
12171
  // The last week of the year is the week that contains the last Thursday of
12108
12172
  // the year.
12109
12173
  let lastThursday = 31;
12110
- while (new Date(y, 11, lastThursday).getDay() !== 4) {
12174
+ while (new DateTime(y, 11, lastThursday).getDay() !== 4) {
12111
12175
  lastThursday -= 1;
12112
12176
  }
12113
- const lastDayOfLastWeek = new Date(y, 11, lastThursday + 3);
12177
+ const lastDayOfLastWeek = new DateTime(y, 11, lastThursday + 3);
12114
12178
  // B - If our date > lastDayOfLastWeek then it's in the weeks of the year after
12115
12179
  // If our date < firstDayOfFirstWeek then it's in the weeks of the year before
12116
12180
  let offsetYear;
@@ -12136,17 +12200,17 @@ const ISOWEEKNUM = {
12136
12200
  case 1:
12137
12201
  // firstDay is the 1st day of the 1st week of the year after
12138
12202
  // firstDay = lastDayOfLastWeek + 1 Day
12139
- firstDay = new Date(y, 11, lastThursday + 3 + 1);
12203
+ firstDay = new DateTime(y, 11, lastThursday + 3 + 1);
12140
12204
  break;
12141
12205
  case -1:
12142
12206
  // firstDay is the 1st day of the 1st week of the previous year.
12143
12207
  // The first week of the previous year is the week that contains the
12144
12208
  // first Thursday of the previous year.
12145
12209
  let firstThursdayPreviousYear = 1;
12146
- while (new Date(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
12210
+ while (new DateTime(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
12147
12211
  firstThursdayPreviousYear += 1;
12148
12212
  }
12149
- firstDay = new Date(y - 1, 0, firstThursdayPreviousYear - 3);
12213
+ firstDay = new DateTime(y - 1, 0, firstThursdayPreviousYear - 3);
12150
12214
  break;
12151
12215
  }
12152
12216
  const diff = (_date.getTime() - firstDay.getTime()) / MS_PER_DAY;
@@ -12281,8 +12345,8 @@ const NETWORKDAYS_INTL = {
12281
12345
  });
12282
12346
  }
12283
12347
  const invertDate = _startDate.getTime() > _endDate.getTime();
12284
- const stopDate = new Date((invertDate ? _startDate : _endDate).getTime());
12285
- let stepDate = new Date((invertDate ? _endDate : _startDate).getTime());
12348
+ const stopDate = DateTime.fromTimestamp((invertDate ? _startDate : _endDate).getTime());
12349
+ let stepDate = DateTime.fromTimestamp((invertDate ? _endDate : _startDate).getTime());
12286
12350
  const timeStopDate = stopDate.getTime();
12287
12351
  let timeStepDate = stepDate.getTime();
12288
12352
  let netWorkingDay = 0;
@@ -12306,8 +12370,7 @@ const NOW = {
12306
12370
  returns: ["DATE"],
12307
12371
  computeFormat: () => "m/d/yyyy hh:mm:ss",
12308
12372
  compute: function () {
12309
- let today = new Date();
12310
- today.setMilliseconds(0);
12373
+ let today = DateTime.now();
12311
12374
  const delta = today.getTime() - INITIAL_1900_DAY.getTime();
12312
12375
  const time = today.getHours() / 24 + today.getMinutes() / 1440 + today.getSeconds() / 86400;
12313
12376
  return Math.floor(delta / MS_PER_DAY) + time;
@@ -12377,8 +12440,8 @@ const TODAY = {
12377
12440
  returns: ["DATE"],
12378
12441
  computeFormat: () => "m/d/yyyy",
12379
12442
  compute: function () {
12380
- const today = new Date();
12381
- const jsDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());
12443
+ const today = DateTime.now();
12444
+ const jsDate = new DateTime(today.getFullYear(), today.getMonth(), today.getDate());
12382
12445
  return jsDateToRoundNumber(jsDate);
12383
12446
  },
12384
12447
  isExported: true,
@@ -12433,10 +12496,10 @@ const WEEKNUM = {
12433
12496
  }
12434
12497
  const y = _date.getFullYear();
12435
12498
  let dayStart = 1;
12436
- let startDayOfFirstWeek = new Date(y, 0, dayStart);
12499
+ let startDayOfFirstWeek = new DateTime(y, 0, dayStart);
12437
12500
  while (startDayOfFirstWeek.getDay() !== startDayOfWeek) {
12438
12501
  dayStart += 1;
12439
- startDayOfFirstWeek = new Date(y, 0, dayStart);
12502
+ startDayOfFirstWeek = new DateTime(y, 0, dayStart);
12440
12503
  }
12441
12504
  const dif = (_date.getTime() - startDayOfFirstWeek.getTime()) / MS_PER_DAY;
12442
12505
  if (dif < 0) {
@@ -12490,7 +12553,7 @@ const WORKDAY_INTL = {
12490
12553
  timesHoliday.add(holiday.getTime());
12491
12554
  });
12492
12555
  }
12493
- let stepDate = new Date(_startDate.getTime());
12556
+ let stepDate = DateTime.fromTimestamp(_startDate.getTime());
12494
12557
  let timeStepDate = stepDate.getTime();
12495
12558
  const unitDay = Math.sign(_numDays);
12496
12559
  let stepDay = Math.abs(_numDays);
@@ -12552,7 +12615,7 @@ const MONTH_START = {
12552
12615
  const _startDate = toJsDate(date);
12553
12616
  const yStart = _startDate.getFullYear();
12554
12617
  const mStart = _startDate.getMonth();
12555
- const jsDate = new Date(yStart, mStart, 1);
12618
+ const jsDate = new DateTime(yStart, mStart, 1);
12556
12619
  return jsDateToRoundNumber(jsDate);
12557
12620
  },
12558
12621
  };
@@ -12590,7 +12653,7 @@ const QUARTER_START = {
12590
12653
  compute: function (date) {
12591
12654
  const quarter = QUARTER.compute(date);
12592
12655
  const year = YEAR.compute(date);
12593
- const jsDate = new Date(year, (quarter - 1) * 3, 1);
12656
+ const jsDate = new DateTime(year, (quarter - 1) * 3, 1);
12594
12657
  return jsDateToRoundNumber(jsDate);
12595
12658
  },
12596
12659
  };
@@ -12605,7 +12668,7 @@ const QUARTER_END = {
12605
12668
  compute: function (date) {
12606
12669
  const quarter = QUARTER.compute(date);
12607
12670
  const year = YEAR.compute(date);
12608
- const jsDate = new Date(year, quarter * 3, 0);
12671
+ const jsDate = new DateTime(year, quarter * 3, 0);
12609
12672
  return jsDateToRoundNumber(jsDate);
12610
12673
  },
12611
12674
  };
@@ -12619,7 +12682,7 @@ const YEAR_START = {
12619
12682
  computeFormat: () => "m/d/yyyy",
12620
12683
  compute: function (date) {
12621
12684
  const year = YEAR.compute(date);
12622
- const jsDate = new Date(year, 0, 1);
12685
+ const jsDate = new DateTime(year, 0, 1);
12623
12686
  return jsDateToRoundNumber(jsDate);
12624
12687
  },
12625
12688
  };
@@ -12633,7 +12696,7 @@ const YEAR_END = {
12633
12696
  computeFormat: () => "m/d/yyyy",
12634
12697
  compute: function (date) {
12635
12698
  const year = YEAR.compute(date);
12636
- const jsDate = new Date(year + 1, 0, 0);
12699
+ const jsDate = new DateTime(year + 1, 0, 0);
12637
12700
  return jsDateToRoundNumber(jsDate);
12638
12701
  },
12639
12702
  };
@@ -12680,8 +12743,8 @@ const DEFAULT_DELTA_ARG = 0;
12680
12743
  const DELTA = {
12681
12744
  description: _lt("Compare two numeric values, returning 1 if they're equal."),
12682
12745
  args: [
12683
- arg(" (number)", _lt("The first number to compare.")),
12684
- arg(` (number, default=${DEFAULT_DELTA_ARG})`, _lt("The second number to compare.")),
12746
+ arg("number1 (number)", _lt("The first number to compare.")),
12747
+ arg(`number2 (number, default=${DEFAULT_DELTA_ARG})`, _lt("The second number to compare.")),
12685
12748
  ],
12686
12749
  returns: ["NUMBER"],
12687
12750
  compute: function (number1, number2 = DEFAULT_DELTA_ARG) {
@@ -12767,7 +12830,7 @@ function assertDeprecationFactorStrictlyPositive(factor) {
12767
12830
  function assertSettlementLessThanOneYearBeforeMaturity(settlement, maturity) {
12768
12831
  const startDate = toJsDate(settlement);
12769
12832
  const endDate = toJsDate(maturity);
12770
- const startDatePlusOneYear = new Date(startDate);
12833
+ const startDatePlusOneYear = toJsDate(settlement);
12771
12834
  startDatePlusOneYear.setFullYear(startDate.getFullYear() + 1);
12772
12835
  assert(() => endDate.getTime() <= startDatePlusOneYear.getTime(), _lt("The settlement date (%s) must at most one year after the maturity date (%s).", settlement.toString(), maturity.toString()));
12773
12836
  }
@@ -12902,7 +12965,7 @@ const AMORLINC = {
12902
12965
  arg("salvage (number)", _lt("The value of the asset at the end of depreciation.")),
12903
12966
  arg("period (number)", _lt("The single period within life for which to calculate depreciation.")),
12904
12967
  arg("rate (number)", _lt("The deprecation rate.")),
12905
- arg(" (number, optional)", _lt("An indicator of what day count method to use.")),
12968
+ arg("day_count_convention (number, optional)", _lt("An indicator of what day count method to use.")),
12906
12969
  ],
12907
12970
  returns: ["NUMBER"],
12908
12971
  compute: function (cost, purchaseDate, firstPeriodEnd, salvage, period, rate, dayCountConvention = DEFAULT_DAY_COUNT_CONVENTION) {
@@ -15846,8 +15909,8 @@ const MID = {
15846
15909
  description: _lt("A segment of a string."),
15847
15910
  args: [
15848
15911
  arg("text (string)", _lt("The string to extract a segment from.")),
15849
- arg(" (number)", _lt("The index from the left of string from which to begin extracting. The first character in string has the index 1.")),
15850
- arg(" (number)", _lt("The length of the segment to extract.")),
15912
+ arg("starting_at (number)", _lt("The index from the left of string from which to begin extracting. The first character in string has the index 1.")),
15913
+ arg("extract_length (number)", _lt("The length of the segment to extract.")),
15851
15914
  ],
15852
15915
  returns: ["STRING"],
15853
15916
  compute: function (text, starting_at, extract_length) {
@@ -45569,6 +45632,6 @@ function addFunction(functionName, functionDescription) {
45569
45632
  export { AbstractChart, CommandResult, CorePlugin, DATETIME_FORMAT, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, components, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, invalidateEvaluationCommands, links, load, parse, readonlyAllowedCommands, registries, setTranslationMethod, tokenize };
45570
45633
 
45571
45634
 
45572
- __info__.version = '16.2.26';
45573
- __info__.date = '2024-01-04T16:19:52.806Z';
45574
- __info__.hash = 'ef51232';
45635
+ __info__.version = '16.2.27';
45636
+ __info__.date = '2024-01-12T13:42:07.889Z';
45637
+ __info__.hash = 'd8b1d67';