1. 16

  2. 9

    What is your favorite pitfall in Date?

    Has to be toISOString(). Claims to return ISO8601, which contains the timezone offset, but instead it just gives you the GMT string, even though it’s perfectly aware of the timezone information:

    // It's 15.44 in Europe/Warsaw
    > dt.getTimezoneOffset()
    > dt.toISOString()
    1. 5

      That is a valid ISO 8601 timestamp. The ‘Z’ (“zulu”) means zero UTC offset, so it’s equivalent to 2020-08-02T15:44:03.936+02:00.

      1. 3

        Oh, it is valid, yes. It’s just less useful than one containing the TZ information that is stored in that Date object. It’s correct, but less useful than it could be (and with little extra effort).

        1. 3

          Ah, I misunderstood you, then. When you wrote “claims to return ISO 8601” I thought you meant that it wasn’t actually an ISO 8601 string.

          So what you mean is that the “encoding” of the of the ISO 8601 string should reflect the local timezone of the system where you call .toISOString()? I.e. 2020-08-02T15:44:03.936+02:00 if you called .toISOString() on a CEST system and 2020-08-02T09:44:03.936-04:00 if you called it on an EDT system?

          1. 2

            I’d expect it to not lose the timezone information, given that it already uses a format that supports that information. It’s not incorrect, it’s just less useful that it could be. Perhaps that’s just the implementation, not the spec – but I’m yet to see it implemented differently. It’s not a huge deal, it’s just frustrating that it could’ve been better at a little cost and yet no one bothered, apparently.

            It’s not about the system it’s called on – that determines the timezone that’s already in the object, as my code snipped showed. I’d expect the data that’s already there to be included in the formatting, instead of being converted to UTC, lost and disregarded. If implemented correctly better, toISOString could’ve been a nice, portable, lossless serialization format for Dates – but as it is, a roundtrip gives you a different date than you started with, because it will now always come back as UTC.

            1. 2

              I would actually assume that getTimezoneOffset is a class method that just looks at your system’s configured time zone and does not read anything from the Date object. I’m pretty sure the object does not store information about the timezone of the system in which it was generated, because it’s never needed. You can always convert to the timezone you want at read time.

              This is also what PostgreSQL does. If you create a column for “timestamps with timezone” it will discard the timezone information at write time and just use UTC (because why not?). The only thing that is different when you choose a timestamp column with timezone is that at read time it will convert values from columns to the configured timezone. All it stores is the number of seconds since the epoch.

              If you look at Firefox’s JS source, it looks like they also just store the seconds since the Unix epoch in a Date object, no timezone information: https://github.com/mozilla/gecko-dev/blob/d9f92154813fbd4a528453c33886dc3a74f27abb/js/src/vm/DateObject.h

          2. 3

            I don’t believe Date contains a time offset. As far as I’m aware, like many languages, the problem is not that the APIs ignore the time offset - they would have to silently reach into the client locale to get it, which would be misleading and make it easy to create bugs. the problem is that they named it “Date” when it’s really just a point in absolute time. Combine a Date with the client locale’s time offset and you’ve got yourself a date, but a Date is not a date.

        2. 5

          This is a namespacing error that’s common when methods are on objects like this. getTimezoneOffset is a property here of the client locale, not of the date time object.

        3. 9

          What is your favorite pitfall in Date?

          Well, the usual magic type conversion applies of course :-)

          >>> var d = new Date();
          >>> var d2 = new Date();
          >>> d - d2
          >>> d + d2
          "Mon Aug 03 2020 03:19:54 GMT+0800 (WITA)Mon Aug 03 2020 03:19:56 GMT+0800 (WITA)"

          It’s especially surprising/confusing since - works as expected (you get a difference between the dates) but + acts like string concatenation.

          So want to add 30 minutes to a date? Use new Date(d - -1300000) as d + 1300000 will result in the formatted date with 1300000 appended as a string. You need to re-parse the result in a new Date object as well, as it returns a timestamp rather than a date object; another weird thing IMO.

          By the way, getYear() was the only method included in the original Netscape 2 release. That’s right, in 1995 they released a programming language with y2k issues 🙃 It wasn’t until Netscape 4 in 1997 that this was fixed with the addition of getFullYear(). People complain about Internet Explorer, but let’s not forget what an absolute mess Netscape was as well.

          Perhaps designing a programming language in 10 days wasn’t the best of ideas. Then again, I doubt anyone 25 years ago could foresee what a success it would become; hindsight 20/20 and all that.

          1. 2

            It’s especially surprising/confusing since - works as expected (you get a difference between the dates) but + acts like string concatenation.

            What exactly would you expect from adding two dates together?

            1. 4

              I would expect new Date() + 1300000 to add 30 minutes, and new Date() + new Date() to either throw an error, or be some time in March 2071 (i.e. do: new Date(new Date().getTime() + new Date().getTime())).

              1. 1

                This is sort of what Julia does, which I like. There are separate types for dates, datetimes, and amounts of time (Days, Hours, Minutes, etc).

                You can add an amount of time to a date or datetime and get a new date or datetime. You can add amounts of time together sensibly, but you can’t add two dates together directly (that’s a type error). You can subtract them, to get an amount of time.

          2. 4

            Moment.js is a much nicer experience in general when dealing with time and dates, and has many saner defaults and helper functions, although it’s ultimately backed by the native date object so it doesn’t paper over everything perfectly.

            I’ve found https://js-joda.github.io/js-joda/ to be a really robust library for more complex date time stuff, it is especially easier to reason about and manipulate date times with time zone offsets. It’s a port of a java library and the api reflects this and is quite clunky, but it’s also using its own immutable representation instead of the native date.

            1. 8

              I’d recommend going with date-fns instead of Moment.js, for reasons like immutability, tree-shaking and performance.

              1. 2

                I regretted going with moment.js for an angular app. It is HUGE and isn’t really easy to reduce size. I shouldn’t have to do some fancy build steps to do it.

                1. 1

                  That’s fair I use it on the backend mostly less of an issue there

                  1. 1

                    Yeah, I wouldn’t mind that on the backend.

              2. 2

                Why are they calling the new API Temporal instead of just Time?

                1. 7

                  Not breaking widely used scripts. Look for Array.prototype.flatten (aka smooshgate) in your favorite search engine.

                  1. 1

                    Totally get that, and I understand why they’re not just changing the semantics of Date. But Time isn’t in use, so why not use that?

                    1. 3

                      With Array.flatten they had broken websites with JS libraries as old as MooTools (iirc), I wouldn’t be surprised if there was a popular libraries that breaks when there’s a built in Time. That all being said, I am just guessing.

                      1. 2

                        Time appears to be part of it.

                  2. 1

                    My favourite shorthand trick—especially for making little creative/hacky animation experiments is:

                    const ms = +new Date

                    It’s using the unary + operator on a Date to get the number of milliseconds since epoch. Pretty neat hack I reckon. :-)