Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BC/BCE dates #28

Open
ewg118 opened this issue Apr 20, 2017 · 12 comments
Open

BC/BCE dates #28

ewg118 opened this issue Apr 20, 2017 · 12 comments

Comments

@ewg118
Copy link

ewg118 commented Apr 20, 2017

There is currently no way to format the era when attempting to render B.C./BCE or pre-1000 years.

For the sake of expediency, an era flag should only yield "BCE" or "CE", since these both come after the year, but A.D. is supposed to proceed it.

First, does d3 conform to ISO 8601 or the XSD variant for BCE dates? That is to say, ISO 8601 dictates that "0000" is 1 BCE, "-0010" is 11 BCE. XSD is more compatible with direct integer representations, so "-0001" is 1 BCE, and "0000" is an invalid xsd:gYear.

With that said, I have attempted to format "0010" (10 CE) as "%Y", and the output is still "0010". Furthermore, there is no flag for era ("%E" would be nice), so "-0010" cannot be designated as BCE).

Ideally, "%Y" should convert a 4-character ISO date into an integer, so that a source xsd:gYear of "-0050" can be formatted with "%Y %E" into "50 BCE"

@mbostock
Copy link
Member

First, does d3 conform to ISO 8601 or the XSD variant for BCE dates?

It shows whatever date.getFullYear returns, modulo 10,000; see formatFullYear.

Regarding the leading zeroes, as the README says, %Y and other directives are padded with zeroes by default:

If no padding modifier is specified, the default is 0 for all directives except %e, which defaults to _. (In some implementations of strftime and strptime, a directive may include an optional field width or precision; this feature is not yet implemented.)

You can drop the zero padding by using %-Y instead, but as I mentioned above the range is still limited to [-9999, 9999]. For example:

d3.timeFormat("%-m/%-d/%-Y")(new Date(117, 1, 2)) // "2/2/117"
d3.timeFormat("%-m/%-d/%-Y")(new Date(-17, 1, 2)) // "2/2/-17"

(Also note the confusing behavior of the JavaScript Date constructor in regards to the full year argument…)

The reason for this behavior is primarily for symmetry with parsing: the parser needs to know how many characters to consume for the %Y directive. For example, if you have a date format like %Y%m%d, then the parser can read 20120304 as March 4, 2012. If %Y has a variable number of digits, then parsing %Y would consume all of those digits (since they are all valid numbers; see numberRe), and return January 1, 20120304.

I agree that the documentation isn’t particularly explicit about this behavior, and that the behavior tends to assume a roughly modern era, at least in the year range 1000-9999.

It seems like you are asking for two things:

  1. A directive (name TBD) to use in place of %Y that displays the absolute value of date.getFullYear, without the % 10000 so that any year can be displayed, and likewise the implication that parsing this directive will consume all contiguous number characters.

  2. A %E directive to display the era, either “BCE” or “CE” in the default U.S. English locale (and probably other locales?).

@curran
Copy link

curran commented Apr 20, 2017

Are JavaScript Date objects able to faithfully represent B.C./BCE or pre-1000 years?

@mbostock
Copy link
Member

mbostock commented Apr 20, 2017

@curran Valid dates range from -100,000,000 days to +100,000,000 days relative to January 1, 1970 UTC. That’s approximately ±273,785 years, so yes.

Edit: although, it depends on what you mean by “faithfully” since JavaScript extrapolates the modern Gregorian calendar to previous eras, and there was greater diversity in calendar systems in the past.

@curran
Copy link

curran commented Apr 20, 2017

@mbostock Very interesting! Thank you for clarifying.

@ewg118
Copy link
Author

ewg118 commented Apr 21, 2017

@mbostock Thanks for the clarification on how the parsing works on the javascript end of things. Yes, so basically, I would like those two things: the absolute integer value of the year, and an era directive (%E, or whatever you think is appropriate). Locales would probably be necessary in the long term. I don't want to get into a philosophical debate, but it might be easiest to use BCE, CE as a default when there is no locale set.

E.g., the French locale era would be "av. J.-C." for BC and "apr. J.-C." for AD.

@mbostock
Copy link
Member

Hmm. Are you sure the absolute value of the full year is correct? I thought that 1 CE follows 1 BCE, and that there is no 0 CE (or 0 BCE). If you just had the absolute value of the full year, you would get 0 CE when date.getFullYear() returns 0, and 1 BCE when date.getFullYear() returns -1. I believe it would be more accurate to get 1 - date.getFullYear() whenever date.getFullYear() returns a value less than 1, so that you get 1 BCE and 2 BCE respectively for these cases.

@ewg118
Copy link
Author

ewg118 commented Apr 21, 2017

It's true that 1 CE follows 1 BCE. As far as I can tell, there's no real standard in Javascript for 1 BC. Is it 0 like ISO 8601 dictates or is it -1 according to XSD? Here's an old discussion from four years ago: http://scholarslab.org/research-and-development/parsing-bc-dates-with-javascript/

I tried the following:

var d = new Date(-1, 5, 24);
alert(d);

And it printed "Thu Jun 24 -0001 00:00:00 GMT-0400 (LMT)" for me (and d.getFullYear() is -1). This makes me think that -1 is "-0001" according to the way that XSD dates operate. Whether this is conscious or not, I am not sure. If I replace -1 with 0, then it outputs the year 1900. So 0 is basically invalid.

So I do think we want to return the absolute value of .getFullYear.

@wenamun
Copy link

wenamun commented Apr 21, 2017

As a side note: The situation with XSD is not that clear as there are two versions which handle years differently: in version 1.0 a year value of zero was invalid (as described above), in version 1.1 a year zero is valid, and the interpretation of year values changed to: year value zero => 1 BC, year value -1 => 2 BC etc.
unfortunately both XSD versions share the same namespace URI (see: https://www.w3.org/TR/xmlschema11-1/#xsd-nss ), so you never know how to interprete XSD dates - if done wrongly there is an offset of one year...

see: https://www.w3.org/TR/xmlschema11-2/#changes

The lexical representation '0000' for years is recognized and maps to the year 1 BCE; '-0001' maps to 2 BCE, etc. This is a change from version 1.0 of this specification, in order to align with established practice (the so-called "astronomical year numbering") and [ISO 8601].

I'm not sure if these Gregorian/Julian Calendar dates are the best dating system/values to store in any database when dealing with (ancient) historical dates... thinking of using Julian Day Numbers for storing & doing calculations and converting these on the fly into (proleptic) Julian for displaying on web pages.

@ewg118
Copy link
Author

ewg118 commented Apr 21, 2017

Hmm, this is highly problematic for software implementations, as our triplestore, Apache Fuseki, will error on "0000"^^xsd:date, and Java/Saxon date-based math functions in XSLT ignore "0000", e.g., it would say that the day after "-0001-12-31" is "0001-01-01"

@wenamun
Copy link

wenamun commented Apr 21, 2017

yeah, to quote Hugh in sosol/sosol#114: "I think we may actually be in Hell"

@t3db0t
Copy link

t3db0t commented Jan 25, 2019

For whatever it's worth, I verified that the largest positive and negative dates representable are "-271820-01-01T00:00:00.000Z" and "+275760-09-12T24:00:00.000Z". So if you've got restaurant reservations in the year 275,761, you're out of luck.

@mbostock
Copy link
Member

Localized formatting of dates in standard JavaScript has improved significantly since this library was first created. For example:

const date = new Date(-50, 0, 1);
date.toLocaleString(undefined, {year: "numeric", era: "short"}); // "51 BC"
date.toLocaleString(undefined, {year: "numeric", era: "long"}); // "51 Before Christ"

More examples in this notebook:

https://beta.observablehq.com/@mbostock/date-formatting

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

5 participants