• aebletrae [she/her]
    ·
    1 year ago

    You want to create the date "31st February", but it's JavaScript that's cursed?

    Write a less side-effecty function.

    function getMonthName(monthNumber) {
        const date = new Date(2023, monthNumber - 1, 1);
        return date.toLocaleString([], { month: 'long' });
    }
    
    • ☆ Yσɠƚԋσʂ ☆@lemmy.ml
      hexagon
      ·
      edit-2
      1 year ago

      The point is that this scenario exists in Js in the first place. It's a completely unnecessary rake left around for people to step on. Also, the function isn't side effecty since it doesn't make implicit references outside its scope. The fact that the date is mutable is an internal concern there. You could just as easily do

      function getMonthName(monthNumber) {
        const date = new Date();
        date.setDate(1);  
        date.setMonth(monthNumber - 1);
      
        return date.toLocaleString([], { month: 'long' });
      }
      

      The problem here isn't with side effects, but with having to know that you want to set your date to first day to get the next month reliably.

      • aebletrae [she/her]
        ·
        edit-2
        1 year ago

        The rake has nothing to do with JS (which I agree is cursed, but for its own reasons, not this).

        You have called a function in a way that does not give a consistent value (Date()). Such functions are hardly the preserve of JavaScript. You've failed to adequately deal with the range of values produced, with code that tries to insist that the "31st February" can be a meaningful date in February. You should accept that this is your mistake and learn to (better) avoid side effects where possible.

        Also, the function isn't side effecty since it doesn't make implicit references outside its scope.

        Edit responding to your edit:

        Also, the function isn't side effecty since it doesn't make implicit references outside its scope.

        The Date() function's output varies according to something other than its input (and even the rest of your program). Using its output without accounting for that variation means that your function, as originally written, also gives inconsistent return values, varying according to something other than its input, because it does, in fact, reference something outside the function. If it did not, the results would only depend on the monthNumber argument, and would always be consistent. I don't know what you call that, but I view it as a side effect.

        As you have said, the rake is that months have different lengths, and you need to account for that. But that's not one of JavaScript's many issues.

        • ☆ Yσɠƚԋσʂ ☆@lemmy.ml
          hexagon
          ·
          1 year ago

          The idea is to get the current data that will have the current year, month, day in it, and then to query this date for the previous month. A sane API would just throw an error when the date is out of range. A Js API will quitely give you nonsense instead. Again, side effects have absolutely nothing to do with anything here.

          • aebletrae [she/her]
            ·
            1 year ago

            You've replied while I was editing, so see that regarding what I mean by side effects.

            As far as throwing an error when you try to create "31st February", this wouldn't actually help much, since the error would still only occur on some days of the year, because your original code doesn't account for the range of outputs from Date() when called without arguments.

            To perform correctly, your code needs to normalise the day of the month, or just create the date more explicitly to begin with, but this is a calendrical issue, not a JavaScript one.

            • ☆ Yσɠƚԋσʂ ☆@lemmy.ml
              hexagon
              ·
              1 year ago

              Side effects are when your function has a reference to some state outside its scope and modifies that state. A function that produces different outputs when it's called, such as getting a current time is not an example of a side effect. Again, the issue here is that Js tries to infer what to do with a bad input, a number outside acceptable range, instead of simply rejecting it.

              My point isn't that you can't write a better function that's less error prone, but the fact that Js allows such things to happen in the first place. It's a very easily avoidable problem at the API level.

              • aebletrae [she/her]
                ·
                1 year ago

                I was taught that side effects are not so one-sided, and that changing output in response to outside state (such as the date) is also a side effect, a side effect on the function, rather than a side effect of the function, but I'm happy to use other definitions so long as they're commonly understood.

                As I said before, though, even if JavaScript did throw an error as you'd prefer, it would still allow your function to have date-based problems. They'd be a bit noisier perhaps but no less present, and just as "well it's worked fine so far". And that's because, as I keep saying, the real problem here is using a function with inconsistent output and not thoroughly dealing with the possibilities. An API change wouldn't alter that. Most of the time it would still let you write bad code.

                I also probably agree with you that errors are generally better than silence in response to bad input but, as someone else has said (more or less) it's not always unreasonable to consider "31st [Month]" as 31 days after the end of [Previous Month]. Without throwing errors, this flexibility is possible. Perhaps the creators believed having to mutate the day-of-month first was an acceptable trade-off for that.

                • ☆ Yσɠƚԋσʂ ☆@lemmy.ml
                  hexagon
                  ·
                  1 year ago

                  Right, my only point here is that it's better to throw an error when encountering bad or ambiguous input than trying to infer what should happen. I think tha a lot of problems in Js come from it being too accommodating regarding input, and the just trying to figure out what you might've meant. In vast majority of cases, an input of this kind if an indication of an error in the program logic and it's better to fail on such inputs than to accept them. If somebody passes a date that doesn't make sense for a current month, it's almost certainly because they have some logic error in their code. Accepting this date as a parameter simply results in creating a subtle bug in a program that the user likely won't be aware of and that's going to be difficult to find in testing.

  • kinttach@lemm.ee
    ·
    1 year ago

    The legacy Date object has many problems and this is one of them. Another infamous one is that it uses zero-based month numbers: January is the zeroth month and December the 11th month.

    This will be fixed Any Day Now™️ when Temporal is released. This is a carefully designed library that supersedes Date and is currently waiting on some standards to be finalized.

    • hh93@lemm.ee
      ·
      1 year ago

      What would you expect "-1 month" to do for a date like 31st of March? Would the result be the same as for "-1 month" on 29th of March?

      If you go back 2 months so the 31st is existing again - should that mean that the result of using -1 month twice should be different to using -2 months?

      I think it's just a stupid way to implement something like this as "month" isn't a defined size so defining it with a fixed value and documenting it properly is a decent solution but noone should use that kind of function in the first place

      • ☆ Yσɠƚԋσʂ ☆@lemmygrad.ml
        ·
        1 year ago

        This is literally how every sane API works in languages built by adults. For example, here's what happens in Java:

        java.time.LocalDate.of(2023, 3, 31)
        > #object[java.time.LocalDate 0x2bc77260 "2023-03-31"]
        java.time.LocalDate.of(2023, 3, 31).minusMonths(1)
        > #object[java.time.LocalDate 0xac0dc15 "2023-02-28"]
        java.time.LocalDate.of(2023, 3, 31).minusMonths(2)
        > #object[java.time.LocalDate 0x44b9305f "2023-01-31"]
        

        I have no idea where people get this notion that a month isn't a defined size. Do people just not understand the concept of a month?

      • mattreb@feddit.it
        ·
        edit-2
        1 year ago

        It is a stupid way to implement it, but the called function is named setMonth()! The minus one is performed externally, so if you set February you expect February, validation should adjust the other fields...

    • ftatateeta@lemmy.ml
      ·
      1 year ago

      I would expect the month to increment by one and the day to be clamped to the valid days for the month.

      • erogenouswarzone@lemmy.ml
        ·
        1 year ago

        I love js. But the date object has always been a total pain. Moment.js is a good package to deal with it, but yeah, it's currently deprecated, but it would be nice if it or something like it became part of ECMAScript.

        I have no idea why it hasn't yet, except that it might be that js needs to work for everyone, not just the us. So time is not standard.

        • towerful@programming.dev
          ·
          edit-2
          1 year ago

          The date API is like the original rip of the Java date API. Barely changed, and totally backwards compatible nonsense.

          Temporal is the new JavaScript/ECMAScript date API.
          It's stage 3, and likely stable (just a few kinks being worked out). So you could polyfill it for production.
          https://github.com/tc39/proposal-temporal

          • erogenouswarzone@lemmy.ml
            ·
            1 year ago

            Speaking of Java RipS. How annoying is it the JS has left Java in the dust as far as looser standards?

            Developing in Java: YOU FORGOT A SEMI-COLON ARE YOU CRAZY?! HOW IS THE COMPILER SUPPOSED TO KNOW WHAT TO DO?!

            Developing in JS: Who gives a fuck about semi-colons?