7 ms·
A big step in the right direction, but I still don't like the API, here's why: Especially in JavaScript where I often share a lot of code between the client and
by VanCoding 6mo ago
A big step in the right direction, but I still don't like the API, here's why:
Especially in JavaScript where I often share a lot of code between the client and the server and therefore also transfer data between them, I like to strictly separate data from logic. What i mean by this is that all my data is plain JSON and no class instances or objects that have function properties, so that I can serialize/deserialize it easily.
This is not the case for Temporal objects. Also, the temporal objects have functions on them, which, granted, makes it convenient to use, but a pain to pass it over the wire.
I'd clearly prefer a set of pure functions, into which I can pass data-only temporal objects, quite a bit like date-fns did it.
- qcoret 6mo agoAll Temporal objects are easily (de)serializable, though. `.toString` and `Temporal.from` work great.
- VanCoding 6mo agoThat's not what I mean. Even though it is serializable, it's still not the same when you serialize/deserialize it. For example `JSON.parse(JSON.stringify(Temporal.PlainYearMonth.from({year:2026,month:1}))).subtract({ years: 1})` won't work, because it misses the prototype and is no longer an instance of Temporal.PlainYearMonth. This is problematic if you use tRPC for example.
- gowld 6mo agoWould a plain data object be an instance of PlainYearMonth? If not, that regardless of being plain data or a serialized object with functions, you'd still need to convert it to the type you want.
- flyingmeteor 6mo agoYou would need to use the `reviver` parameter of `JSON.parse()` to revive your date strings to Temporal objects. As others have said, it's a simple `Temporal.from()` https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
- cyral 6mo agoI've been doing this for so long and never knew there was a reviver param, thanks - that is super useful.
- Bratmon 6mo agoHaving to provide a complete schema of your json everywhere your json gets parsed negates the advantages of json.
- true_religion 6mo agoThe main advantage of json is that it’s human readable and writable. Beyond that, it has no notion of user created data types so anyone using it has to do custom unmarshalling to get a type apart from sting, number, dict and list.
- hueho 6mo agoMost JSON libraries in typed languages require this for data binding to complex types though.
- Bratmon 6mo agoWhich is why many developers only use JavaScript Object Notation for JavaScript objects, and only JavaScript objects that can losslessly be written as JSON. Which this proposal explicitly does not support.
- afiori 6mo ago{"$temporal_type":"PlainYearMonth","$data":"........"}
- hdjrudni 6mo agoEven if you don't explicitly provide a schema, you implicitly still have one. The recipient needs to know what you're sending them. Unless maybe you want to start parsing JSON payloads with an LLM.
- foresterre 6mo ago
- rimunroe 6mo ago> For example `JSON.parse(JSON.stringify(Temporal.PlainYearMonth.from({year:2026,month:1}))).subtract({ years: 1})` won't work, because it misses the prototype and is no longer an instance of Temporal.PlainYearMonth. I don't know if I'm missing something, but that's exactly how I'd expect it to compose. Does the following do what you wanted your snippet to do? Temporal.PlainYearMonth.from(JSON.parse(JSON.stringify(Temporal.PlainYearMonth.from({year:2026,month:1})))) JSON.stringify and JSON.parse should not be viewed as strict inverses of each other. `JSON.parse(JSON.stringify(x)) = x` is only true for a for a small category of values. That category is even smaller if parsing is happening in a different place than stringification because JSON doesn't specify runtime characteristics. This can lead to things like JSON parsing incorrect in JS because they're too large for JS to represent as a number.
- tshaddox 6mo agoThis is also true of JavaScript Date instances, so I’m curious what solution you had that did work with raw JSON stringify and parse.
- aubergene 6mo agoThis seems more to do with how JSON works than Temporal. There are libraries such as Devalue which will handle this for you `devalue.parse(devalue.stringify(Temporal.PlainYearMonth.from({year:2026,month:1}))).subtract({ years: 1})` https://www.npmjs.com/package/devalue https://www.npmjs.com/package/devalue
- cyberrock 6mo agoThen you're talking about changing JSON.parse to start parsing some schema as a type instead of object, which would break compatibility.
- jonathrg 6mo agoThe JSON types are string, number, boolean, null, object and array. So how could the suggested code possibly work? Do you want JSON.parse to do arbitrary code execution like Python's pickle?
- deleted 6mo ago[deleted]
- perfmode 6mo agoThis is a real pain point and I run into the same tension in systems where data crosses serialization boundaries constantly. The prototype-stripping problem you're describing with JSON.parse/stringify is a specific case of a more general issue: rich domain objects don't survive wire transfer without a reconstitution step. That said, I think the Temporal team made the right call here. Date-time logic is one of those domains where the "bag of data plus free functions" approach leads to subtle bugs because callers forget to pass the right context (calendar system, timezone) to the right function. Binding the operations to the object means the type system can enforce that a PlainDate never accidentally gets treated as a ZonedDateTime. date-fns is great but it can't give you that. The serialization issue is solvable at the boundary. If you're using tRPC or similar, a thin transform layer that calls Temporal.Whatever.from() on the way in and .toString() on the way out is pretty minimal overhead. Same pattern people use with Decimal types or any value object that doesn't roundtrip through JSON natively. Annoying, sure, but the alternative is giving up the type safety that makes the API worth having in the first place.
- VanCoding 6mo agoIt's not that much about type safety. Since TypeScript uses duck typing, a DateTime could not be used as a ZonedDateTime because it'd lack the "timezone" property. The other way around, though, it would work. But I wouldn't even mind that, honestly. The real drawback of the functional approach is UX, because it's harder to code and you don't get nice auto-complete. But I'd easily pay that price.
- TimTheTinker 6mo agoSounds like we need an extended JSON with the express intent of conveying common extended values and rich objects: DateTime instants (with calendar system & timezone), Decimal, BigInt, etc.
- sheept 6mo agoI disagree: this is not unlike including the schema in the JSON itself. This should be handled by the apps themselves, since they would have to know what the keys mean regardless. If you do want the interchange format to be the one deserializing into specific runtime data structures, use YAML. YAML's tag syntax allows you to run arbitrary code inside YAML, which can be used for what you want.
- chrisweekly 6mo agoIt should still be possible to continue using date-fns (or a similar lib) to suit your preference, right?
- VanCoding 6mo agoyes, sure. probably there will even pop up a functional wrapper around the temporal API occasionally. But would've been nice if it was like this from the start.
- causal 6mo agoI'm with you on this. I worked on a big Temporal project briefly and I was really turned off by how much of the codebase was just rote mapping properties from one layer to the next.
- Avamander 6mo ago> Especially in JavaScript where I often share a lot of code between the client and the server and therefore also transfer data between them, I like to strictly separate data from logic Which makes me wonder how it'll look like when interfacing with WASM. Better than Date?
- jayflux 6mo agoThis was an intentional design decision. We wanted to make sure all the temporal types could be serialize/deserializable, but as you mentioned, you couldn't implicitly go back to the object you started with as JSON.parse doesn't support that. Instead the onus is on the developer to re-create the correct object they need on the other side. I don't believe this is problematic because if you know you're sending a Date, DateTime, MonthDay, YearMonth type from one side, then you know what type to rebuild from the ISO string on the other. Having it be automatic could be an issue if you receive unexpected values and are now dealing with the wrong types. There is an example here in the docs of a reviver being used for Temporal.Instant https://tc39.es/proposal-temporal/docs/instant.html#toJSON https://tc39.es/proposal-temporal/docs/instant.html#toJSON
- frde_me 6mo agoSo it's intentional to make people pass down raw strings versus making the communication safe(er) by default?
- Manishearth 6mo ago.... we're talking about serialization here. "convert to a raw string" is sort of the name of the game. It's a string in a well specified string format. That's typically what you want for serialization. Temporal is typed; but its serialization helpers aren't, because there's no single way to talk about types across serialization. That's functionality a serialization library may choose to provide, but can't really be designed into the language.
- Hasnep 6mo agoThere are no date, time or datetime types in JSON, so you'll have to serialise it to a string or an int anyway, and then when deserialising you'll need to identify explicitly which values should be parsed as dates.
- zelphirkalt 6mo agoWell, you could still have a compound object in JSON, that is output by the Temporal API, and which given as input is guaranteed to result in an equal object it was created/serialized from. This compound object must contain all required infos about timezones and such stuff.
- TimTheTinker 6mo agoUpdating JSON.parse() to automatically create Temporal objects (from what shape of JSON value?) without a custom reviver would be a step too far, in my opinion. This is effectively no different from Date: serialize: date.toJSON() deserialize: new Date(jsonDate) in Temporal: serialize: instant.toJSON() deserialize: Temporal.Instant.from(jsonDate)
- tshaddox 6mo agoDon’t JavaScript Date instances have the same problem? Date implements toJSON, but when parsing JSON you’ll have to manually identify which string values represent Dates and convert them back to Date instances. The exact same is true of Temporal (e.g. Instant). And as far as I know, date-fns deals with native Date instances, not “data-only objects.”
- nulltrace 6mo agoThe serialization thing is real but I don't think OOP vs functional is the actual issue here. JSON has no date type, period. You JSON.stringify a Date, get an ISO string, and hope whoever's parsing remembers to reconstruct it. Temporal doesn't fix that part, but at least when you do reconstruct you're saying "this is a ZonedDateTime" vs "this is an Instant" instead of everything being one ambiguous Date object.
- pwdisswordfishy 6mo ago> What i mean by this is that all my data is plain JSON and no class instances or objects that have function properties, so that I can serialize/deserialize it easily. This is known as the "primitive obsession" anti-pattern.