Proposal-temporal: integration with <input type="time"> and <input type="date">

Created on 20 Feb 2019  Â·  20Comments  Â·  Source: tc39/proposal-temporal

referenced from w3ctag-review

according to MDN, the input-values would be [1], [2]:

<!-- date -->
<input id="date" type="date" value="2017-06-01">
<!-- time -->
<input id="time" type="time" value="13:30">

what would UX-workflow integration look like? how would i use temporals to transform <input type="date">, <input type="time"> into JSONdatetime_utc, timezoneOffset?

var datetime_local;
var datetime_utc;
var timezoneOffset;

datetime_local = CivilDateTime.fromString(
    document.querySelector("#date").value
    + "T"
    + document.querySelector("#time").value
);
timezoneOffset = ???  // new Date().getTimezoneOffset()
datetime_utc = ??? // JSON.stringify(new Date(datetime_local))

ajax({
    "method": "POST",
    "url": "/api/schedule-appointment",
    "payload": JSON.stringify({
        datetime_utc: datetime_utc,
        timezoneOffset: timezoneOffset,
        ...
    })
});

[1] https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date
[2] https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/time

behavior integration

Most helpful comment

Strings are brittle. They are hard to validate, hard to identify, there’s no way of attaching methods/behavior to them; programmer intention is hard to make clear, as there is often overlap for a given string between more than one potential domain out of infinite domains.

All 20 comments

let date = CivilDate.from(doument.getElementById('date').value);
let time = CivilTime.from(doument.getElementById('time').value);
let datetime = CivilDateTime.from(date, time);
let localdatetime = datetime.withZone('SYSTEM');
let timezoneOffsetSeconds = localdatetime.offsetSeconds;
let timezoneOffsetString = localdatetime.offsetString;
let timezoneName = localdatetime.timeZone;

ajax({
  "method": "POST",
  "url": "/api/schedule-appointment",
  "payload": localdatetime // serializes to "2019-03-21T09:32:24.012307238-04:00[America/New_York]" (as of this post)
});

// No need to pass the timezone separately. A ZonedDateTime has both the absolute time and zone

thx. but say i want to directly save ajax-result to sql-table that manages appointments across the u.s. i obviously want the dates normalized to utc so they're sortable (regardless of region).

can you fill in the ???:

var localdatetime;

localdatetime = CivilDateTime.from(
    CivilDate.from(document.getElementById("date").value),
    CivilTime.from(document.getElementById("time").value)
).withZone("SYSTEM");

ajax({
    "method": "POST",
    "url": "/api/schedule-appointment",
    "payload": JSON.stringify({
        // sortable utc-datetime saved to sql-table
        datetime_utc: ???,
        // metadata for future presentation-logic
        timezoneOffset: localdatetime.offsetSeconds / 60,
        ...
    })
});
ajax({
    "method": "POST",
    "url": "/api/schedule-appointment",
    "payload": {
        utcdate: localdatetime.instant, // stringifies to 2019-03-21T14:47:01.012352312Z
        timezone: localdatetime.timeZone // either IANA-Name or the offsetString (-05:00),
        offset: localdatetime.offsetSeconds
    }
});

suggest this common use-case be added to README

To be honest, I was contemplating not putting it in this issue, because it's very likely to be a bad idea. The way you do this by keeping it as the localdatetime.toString() which includes both the time and the zone. Most databases worth their salt can accept this format into a datetime field. That makes it sortable in the db, yet still be filterable in the db by date.

Take the following example:

Your DB stores appointments. You want "all appointments on a specific date". With just the UTC date you can't do that selection even when storing timezone. So keeping the timezone and time in one value is fairly important. https://docs.microsoft.com/en-us/sql/t-sql/data-types/datetimeoffset-transact-sql?view=sql-server-2017 would be an example.

I'll be updating the FAQ soonish though

different perspective. my experience has made me distrustful of datetime-magic, and keep most "business-logic" utc-only.

for example in texas (+05:00 DST), i would want "all appointments on a specific [utc] datetime-range", e.g.:

-- find all appointments on date 2019-03-22 in texas
SELECT * FROM appointment
    -- filter by [timzoneoffset'd] utc-datetime-range
    WHERE
        '2019-03-22T05:00:00' <= appointment.utcdatetime
        AND appointment.utcdatetime < '2019-03-23T05:00:00'

@kaizhu256 That sounds like a good use case for the Instant class, rather than ZonedDateTime with a UTC timezone.

@littledan, yes Instant's ISOString and timezoneOffset are of most value to me when baton-passing datetime between no-magic/zero-config systems.

ZonedDateTime in my scenario is primarily for UI-logic with humans.

@kaizhu256 your use-case actually screams for OffsetDateTime, which is something we talked about last week. It’s bascially an Instant with a timezone offset that stringifies to an ISO-string with the offset (rather than IANA zone). No magic involved.
I’ll @-mention you in the PR when I get the state of that discussion written up so you can go over it.

@pipobscure, are OffsetDateTime utc-based when serialized? if not, they are not sortable across timezones :`(

javascript commonly interfaces (via JSON) with "dumb" systems/shell-scripts,
that cannot easily sort non-utc-based ISO-strings like these:

2017‑12‑30T19:00:00-05:00        // [US/CDT]
2017-12-31T00:00:00.000000+00:00 // [UTC]
2017‑12‑31T09:00:00.000+09:00    // [Asia/Tokyo]

The creation of Duration, CivilYearMonth and CivilMonthDay address the original issue.

Creating types is half of this; the other half would be making a proposal in HTML for the new input methods. Let's continue tracking this.

Here is a strawperson proposal:

  • Add a valueAsTemporal getter/setter to HTMLInputElement

    • <input type=date> accepts/returns CivilDates

    • <input type=time> accepts/returns CivilTimes

    • <input type=datetime-local> accepts/returns CivilDateTimes

    • <input type=month> accepts/returns CivilYearMonths

    • (Unlike valueAsDate) The same object must always be returned until the value changes, and setting to a specific Temporal object means the getter must return that object. (This works, because temporal objects are immutable.)

    • If type="" is in a different state, the getter returns null and the setter throws an "InvalidStateError" DOMException.

  • <input type=week> does not have a mapping
  • Instant, OffsetDateTime, ZonedDateTime do not have a mapping

Other ideas:

  • Use separate accessors for each type. HTMLInputElement.prototype is full of stuff that only works for one type="" anyway; 4 new accessors vs. 1 new accessor isn't a big deal.
  • Use functions instead of accessors. (I don't think this is necessary; the valueAsDate sucks as an accessor because Date is mutable, but we don't have that problem here.)

Here is a strawperson proposal:

-1 for overengineering and unecessary coupling with temporal-proposal.

here's a simpler strawperson-proposal using ISOStrings thats more cost-effective/library-agnostic:

<!--
  -- strawperson-proposal with simpler spec and UX
  -- by introducing 2 extra isostrings
  -- `valueZoned` and `valueUTC` for <input type="datetime-local">
  -->

<input id="input1" type="datetime-local"
    value      = "2019-07-21T00:00:00"
    valueZoned = "2019-07-21T00:00:00-05:00[America/Houston]"
    valueUTC   = "2019-07-21T05:00:00Z"
>

<script>
document.querySelector(
    "#input1"
).addEventListener("change", function (evt) {
/*
 * this function will handle change <evt> to get
 * 1. value
 * 2. valueZoned
 * 3. valueUTC
 */
    var civilZonedInstant;
    var civilInstant;
    var civilDatetime;

    // get civil-object from #input1
    civilDatetime     = CivilDateTime.fromString( evt.target.value      );
    civilZonedInstant = ZonedInstant.fromString(  evt.target.valueZoned );
    civilInstant      = Instant.fromString(       evt.target.valueUTC   );

    // output civil-object to #input1
    evt.target.value      = JSON.stringify(civilDatetime);
    evt.target.valueZoned = JSON.stringify(civilZonedInstant);
    evt.target.valueUTC   = JSON.stringify(civilInstant);
    ...
});
</script>

this is perfect for the common-scenario where you're just baton-passing isostrings between user <-> dom <-> backend (and bypass civil-classes entirely).

that came off harsh. i don't want heavy-handed coupling between and temporal-proposal that locks them into future-interoperability commitments that can be avoided. i do appreciate @domenic's original strawman for getting the ball-rolling and understanding the UX.

Anything that requires passing around strings, instead of instances, isn’t going to be viable.

Yes. We have no intention of adding more stringly-typed APIs to HTML or ES.

why is it not viable?

Strings are brittle. They are hard to validate, hard to identify, there’s no way of attaching methods/behavior to them; programmer intention is hard to make clear, as there is often overlap for a given string between more than one potential domain out of infinite domains.

@domenic presented a good proposal in https://github.com/tc39/proposal-temporal/issues/107#issuecomment-510946109 . Thanks for the help! I think this is all we need for now; after Stage 3, we can raise this as an HTML proposal. We should also cross-reference @domenic 's proposal when we file again for a TAG review (#102 ). Other than that, I don't think any more action is needed for now. So, I'm removing this from the Stage 3 milestone and Finalize spec text project.

Was this page helpful?
0 / 5 - 0 ratings