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

static-functions Date.datePlus and Date.timePlus as JSON-friendly alternatives for classes CivilDate and CivilTime #82

Closed
kaizhu256 opened this issue Aug 16, 2018 · 8 comments

Comments

@kaizhu256
Copy link
Contributor

CivilDate and CivilTime are both classes that only have one method each (dealing with date and time arithmetic respectively). Would replacing these 2 classes and 2 methods with 2 static-functions (e.g. Date.datePlus and Date.timePlus that manipulate ISOStrings) be simpler?

it could solve the following issues for CivilDate and CivilTime:

  1. ambiguous .valueOf() (Consider removing toString and valueOf methods #74 (comment))?
  2. JSON-roundtrip (Consider removing toString and valueOf methods #74 (comment))
  3. polluting global-namespace or need to spec-out standard-modules for CivilDate and CivilTime (rwaldron/tc39-notes@51f2ae8#diff-b44ce5f0c2c0281137b7c01453115401R109)

here are the a/b code comparisons:

// a/b code-comparison between class CivilDate and static-function Date.datePlus

// 1. date-arithmetic
new CivilDate(2018, 8, 16).plus({ months: 1 }) === new CivilDate(2018, 9, 16);
Date.datePlus('2018-08-16', { months: 1 }) === '2018-09-16';

// 2. constructor
new CivilDate(2018, 8, 16);
'2018' + '-' + '08' + '-' + '16';  // must follow [comparison-friendly] RegExp pattern (/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/)

// 3. JSON-roundtrip
new CivilDate(2018, 8, 16) === ???(JSON.parse(JSON.stringify(new CivilDate(2018, 8, 16))));
'2018-08-16' === JSON.parse(JSON.stringify('2018-08-16'));

// 4. .valueOf()
new CivilDate(2018, 8, 16).valueOf(); // ???
'2018-08-16'.valueOf(); // '2018-08-16'

// 5. properties
var year = new CivilDate(2018, 8, 16).year; // 2018
var month = new CivilDate(2018, 8, 16).month; // 8
var day = new CivilDate(2018, 8, 16).day; // 16
var year = '2018-08-16'.split('-')[0]; // '2018'
var month = '2018-08-16'.split('-')[1]; // '08'
var day = '2018-08-16'.split('-')[2]; // '16'



// a/b code-comparison between class CivilTime and static-function Date.timePlus

// 1. time-arithmetic
new CivilTime(6, 10, 33, 999, 123456).plus({ hours: 2, minutes: 4 }) === new CivilTime(8, 14, 33, 999, 123456);
Date.datePlus('06:10:33.999123456', { hours: 2, minutes: 4 }) === '08:14:33.999123456';

// 2. constructor
new CivilTime(6, 10, 33, 999, 123456);
'06' + ':' + '10' + ':' + '33' + '.' + '999' + '123456';  // must follow [comparison-friendly] RegExp pattern (/^[0-9]{2}:[0-9]{2}:[0-9]{2}\.?[0-9]{0,9}$/)

// 3. JSON-roundtrip
new CivilTime(6, 10, 33, 999, 123456) === ???(JSON.parse(JSON.stringify(new CivilTime(6, 10, 33, 999, 123456))));
'06:10:33.999123456' === JSON.parse(JSON.stringify('06:10:33.999123456'));

// 4. .valueOf()
new CivilTime(6, 10, 33, 999, 123456).valueOf(); // ???
'06:10:33.999123456'.valueOf(); // '06:10:33.999123456'

// 5. properties
var hour = new CivilTime(6, 10, 33, 999, 123456).hour; // 6
var minute = new CivilTime(6, 10, 33, 999, 123456).minute; // 10
var second = new CivilTime(6, 10, 33, 999, 123456).second; // 33
var millisecond = new CivilTime(6, 10, 33, 999, 123456).millisecond; // 999
var nanosecond = new CivilTime(6, 10, 33, 999, 123456).nanosecond; // 123456
var hour = '06:10:33.999123456'.slice(0, 2); // '06'
var minute = '06:10:33.999123456'.slice(3, 5); // '10'
var second = '06:10:33.999123456'.slice(6, 8); // '33'
var millisecond = '06:10:33.999123456'.slice(9, 12); // '999'
var nanosecond = '06:10:33.999123456'.slice(9, 12); // '123456'
@ljharb
Copy link
Member

ljharb commented Aug 16, 2018

No, it wouldn't be simpler; having primitive object types is much simpler than passing around strings that you have to implicitly know the type of.

@gilmoreorless
Copy link
Contributor

We should avoid creating new APIs that base everything on manipulating strings. "Stringly-typed" APIs are usually regarded as a code smell. As an example, the MDN page on types says:

With conventions, it is possible to represent any data structure in a string. This does not make it a good idea. For instance, with a separator, one could emulate a list (while a JavaScript array would be more suitable). Unfortunately, when the separator is used in one of the "list" elements, then, the list is broken. An escape character can be chosen, etc. All of this requires conventions and creates an unnecessary maintenance burden.

Use strings for textual data. When representing complex data, parse strings and use the appropriate abstraction.

Your examples above represent everything as strings. Years, months, days, hours, minutes, seconds, milliseconds, and nanoseconds are numbers and they should be represented as such.

You've also presented a straw man argument regarding the JSON round trip. The current Date object doesn't get restored via JSON.parse(JSON.stringify(new Date())) either. In fact, every argument you've put forward so far as to why CivilDate shouldn't exist also applies to Date — if you really wanted to, you could replace every Date prototype method with a static method that manipulates strings. Why not go further and suggest that all non-primitive objects be represented as strings?

The rise of type checkers such as TypeScript and Flow have shown that developers want more control over types, not less. If you only use strings for dates/times, how do you get a type checker to verify that your code is correctly passing around dates?

@kaizhu256 kaizhu256 reopened this Aug 25, 2018
@kaizhu256
Copy link
Contributor Author

  • the mdn page argues against strings for complex data (which it later gives as an example, comma-separated lists) . date-string is not complex-data.

  • i actually am open to the idea of string-based datetime as well. but one thing at a time ; ) lets see where Date.datePlus and Date.timePlus leads first.

  • we have fundamental philosophical disagreements. the most useful insight about javascript-code is not about the low-level internal-workings of libraries through static-type analysis with TypeScript or Flow. its rather high-level insight about which parts of your library-code are relevant to the UX-behavior of the final-product. that kind of insight can only be gained through code-coverage of high-level end-to-end integration-tests, like these real-world, code-coveraged end-to-end tests (of both browser and server code) on user-login or file-uploading
    [1], [2], [3].

image
image
image

  • and the only "type-checker" that really matters is the one at the client <-> server endpoint-interfaces. at integration-level, no frontend-engineer cares about some silly TypeScript/Flow error in your library. they only care that your backend-server validated their JSON-data/URLString client-request (perhap with a swagger/jsonschema validator)

image

  • obviously CivilDate and CivilTime don't have much meaning at the JSON-serialized integration-level. you can't distinguish them from date-strings and time-string in JSON. TypeScript and Flow (unlike json-schema validators) are not going to help you as a senior-engineer responsible for debugging-and-fixing end-to-end communication-errors which are far more problematic to web-projects, than simple, low-level type-errors.

@ljharb
Copy link
Member

ljharb commented Aug 25, 2018

at integration-level, no frontend-engineer cares about some silly TypeScript/Flow error in your library

This is wildly false. I care about type errors (with or without a type system) at every single level - you continue to parrot the false premise that the only thing that matters is a webpage, and the JSON interaction between a webpage and a web server. These things are important but they are not the entirety of JavaScript, not by a long shot.

Any argument framed by this false premise is not going to go far.

@kaizhu256
Copy link
Contributor Author

@ljharb, web-projects are complicated beasts. does static-type-checking tell you whether or not whatever low-level library-code you're writing is actually useful, product-related code ... vs needless bikeshed/technical-debt? no it does not. what are common reasons why many web-projects fail? because of some silly type-error that wasn't caught because you weren't using TypeScript/Flow? of course not. they fail largely because programmers wasted their time writing low-level code in the dark (without code-coverage insight from end-to-end integration-tests) that ended up being technical debt. TypeScript/Flow only gives you a false sense of security that you're writing useful code - when you really don't have a clue, unless you have hard-data from integration-level test-coverage to back it up.

@ljharb
Copy link
Member

ljharb commented Aug 25, 2018

@kaizhu256 yes, i happen to agree, which is why i want the ability to do stronger and clearer runtime operations by having first-class date/time primitives, instead of passing around opaque strings or object literals (which are a frequent cause of failures in web apps due to the lack of developer clarity involved).

@kaizhu256
Copy link
Contributor Author

no we disagree.

  • you're view is extra-structure from Civil-classes (over string-representations) creates more maintainable code.

  • my view is extra-structure from Civil-classes (over string-representations) creates more code-inertia/technical-debt.

and my conclusion comes from 6 years of analyzing javascript end-to-end code-coverage of integrated-systems. the typical code half-life for web-projects (where 50% of the code gets rewritten) ranges from 3-6 months for the the 1st year. after the 1st year, the half-life slows to 12-24 months, but only if the web-project is successful and passes integration/qa/deployment trials during the 1st year, which very few do.

why do they fail in 1st year? because of integration-problems dealing with technical-debt. and there is surprisingly more of it from my code-coverage analysis, in javascrip-projects that extensively use class-based design-patterns ... vs ones derided as having "code-smell", because they avoided class-structures/meta-programming, and worked mostly with basic JSON-friendly data-structures, manipulated by static-functions.

@gilmoreorless, in an ideal-world where all code you write is correct the 1st time around, then yea, Civil-classes and having structures makes sense. but that is not the reality for most javascript-based web-projects. they have incredible amounts of code-churn, and like i said, 50% or more it would be completely rewritten by the time it passes integration-trials dealing with end-to-end communication.

as an example of real-world code-churn, code related to frontend-presentation is notoriously unstable. if you constantly had to rewrite the presentation-formats for date/time due to fluid design-specs, its more flexible and less technical-debty to due so with string-manipulations of date-strings/time-strings than with class-methods on CivilDate/CivilTime.

@maggiepint
Copy link
Member

Given we have advanced this proposal, with the five types described, to stage 2, I believe this issue can be closed.

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

No branches or pull requests

4 participants