2

The Expanded-Year Date Format is detailed in the ECMA-262 Language Specifications paragraph 21.4.1.15.1 Expanded Years

Quote:

21.4.1.15.1 Expanded Years

Covering the full-time value range of approximately 273,790 years forward or backwards from 1 January 1970 (21.4.1.1) requires representing years before 0 or after 9999. ISO 8601 permits expansion of the year representation.

In the simplified ECMAScript format, such an expanded year representation shall have 6 digits and is always prefixed with a + or - sign. The year 0 is considered positive and hence prefixed with a + sign.

NOTE Examples of date-time values with expanded years:

-271821-04-20T00:00:00Z ===> 271822 B.C.

-000001-01-01T00:00:00Z ===> 2 B.C.

+000000-01-01T00:00:00Z ===> 1 B.C.

+000001-01-01T00:00:00Z ===> 1 A.D.

+001970-01-01T00:00:00Z ===> 1970 A.D.

+002009-12-15T00:00:00Z ===> 2009 A.D.

+275760-09-13T00:00:00Z ===> 275760 A.D.

I have tried the following Javascript date methods to convert a (Year, Month, Day) to the corresponding Expanded-Year Date format:

const date = new Date(30,11,20);
console.log(date.toString());           // "Sat Dec 20 1930 00:00:00 GMT+0400 (Arabian Standard Time)"
console.log(date.toISOString());        // "1930-12-19T20:00:00.000Z"
console.log(date.toDateString());       // "Sat Dec 20 1930"
console.log(date.toGMTString());        // "Fri, 19 Dec 1930 20:00:00 GMT"
console.log(date.toUTCString());        // "Fri, 19 Dec 1930 20:00:00 GMT"
console.log(date.toLocaleDateString()); // "12/20/1930"

All of the above Date() Methods cannot convert a (Year, Month, Day) into the 'Expanded-Year Date' format. They also convert any year between 0 and 99 to the years 1900 to 1999.

As a possible solution, I have generated the following one-liner code as an attempt to find a way to do it:

const expandedDate=(Y, M, D)=>(Y<0?"-":"+")+("00000"+Math.abs(Y)).slice(-6)+"-"+("0"+M).slice(-2)+"-"+("0"+D).slice(-2);

However, I believe this approach of converting dates to strings and then slicing strings into parts is not what I would call the best approach.

Is there a better/simpler method either as a Javascript Built-In Method or another way to convert a (Year, Month, Day) format to the corresponding Expanded-Year Date format?

const expandedDate=(Y, M, D)=>(Y<0?"-":"+")+("00000"+Math.abs(Y)).slice(-6)+"-"+("0"+M).slice(-2)+"-"+("0"+D).slice(-2);

console.log(expandedDate(30,11,20));
console.log(expandedDate(2022,11,20));
console.log(expandedDate(-30,11,20));
console.log(expandedDate(-3200,11,20));
console.log(expandedDate(0,11,20));
console.log(expandedDate(-271821,4,20));
Mohsen Alyafei
  • 4,765
  • 3
  • 30
  • 42

3 Answers3

1

Way more lines of code and way more verbose than yours. But might be easier to follow.

function paddingZero(num, digit) {
  const toAdd = digit - num.length;
  let padding = "";
  for (let index = 0; index < toAdd; index++) {
    padding += "0";
  }
  return padding + num; 
}

function expandedDate({ year, month, day }) {
  const yyyyyy = paddingZero(Math.abs(year).toString(), 6);
  const mm = paddingZero(month.toString(), 2);
  const dd = paddingZero(day.toString(), 2);
  const prefix = year < 0 ? "-" : "+";
  return `${prefix}${yyyyyy}-${mm}-${dd}`;
}

console.log(expandedDate({ year: 30, month: 11, day:20}));
console.log(expandedDate({ year: 2022, month: 11, day: 20}));
console.log(expandedDate({ year: -30, month: 11, day: 20}));
console.log(expandedDate({ year: -3200, month: 11, day: 20}));
console.log(expandedDate({ year: 0, month: 11, day: 20}));
console.log(expandedDate({ year: -271821, month: 4, day: 20}));
Bumhan Yu
  • 2,078
  • 1
  • 10
  • 20
1

It's a legacy feature that the constructor treats year values from 0 to 99 as 1900 + year, there's no fixing that until the Temporal object is implemented (which might be in ECMAScript 2023 as it seems imminent but it's not in the latest ECMA-262 2022 draft).

As for formatting, ECMAScript has very limited formatting support for dates, which is why there are so many date libraries. You can do a combination of an existing formatting method or create an entirely new one.

E.g. if only positive years are required, then you can leverage toLocaleDateString:

console.log(
  new Date()
      .toLocaleDateString('en-CA')
      .replace(/^(.\d+)/,y => (y<0? '-' : '+')+(''+Math.abs(y))
      .padStart(6,'0'))
);

However, toLocale methods don't seem to support negative years, they always come out with no sign. So if negative year support is required, it's back to basics:

// Format as ISO 8601 with extended years
function extendedYears(date = new Date()) {
  let z = n => (n < 10? '0' : '')+n;
  let y = date.getFullYear();
  return (y<0?'-':'+') + (''+Math.abs(y)).padStart(6,'0') + 
         '-' + z(date.getMonth() + 1) + '-' + z(date.getDate());
}

console.log(extendedYears());
console.log(extendedYears(new Date(-1,0)));
console.log(extendedYears(new Date(20203,0)));

But really, if it's called as a function then a "one liner" is not really the goal: functional, clear, maintainable and bug–free code is.

RobG
  • 142,382
  • 31
  • 172
  • 209
1

There is a built-in function in Javascript that can produce the expanded date format date.toISOString().

    const mydate = new Date(-5, 12, 31);
    console.log(mydate.toISOString());

This will output: -000004-01-30T21:49:52.000Z

I think the only solution to the 0 corresponding to 1900 is to use the UNIX timestamp configuration of the Date() constructor which uses milliseconds btw, for example, new Date(-62324928850000); is 5 BC

There also is a built-in format in Javascript using date.toLocalString() method which can produce BC and AD format but it is poorly documented IMO.

Example:

    const mydate = new Date(-5, 12, 31);
    console.log(mydate.toLocaleString(undefined, {year: "numeric", era: "short"}));

The above code will output 5 BC, and if era is set to long it will output 5 Before Christ. If the local is set it will produce the date according to that local for example:

mydate.toLocaleString("ar-SA", {year: "numeric", era: "short"});

Will output ؜-٦٤٥ هـ and "zh-CN" => 公元前5年 and so on.

This also works with the expanded date format if you pass the date as string to Date() example:

    const mydate = new Date("-000001-01-01T00:00:00Z");
    console.log(mydate.toLocaleString(undefined, {year: "numeric", era: "short"}));

This will output 2 BC as desired.

mmahgoub
  • 3,910
  • 3
  • 20
  • 19