electricessence / TypeScript.NET

A JavaScript-Friendly .NET Based TypeScript Library (Moved)
https://github.com/electricessence/TypeScript.NET-Core
Other
251 stars 36 forks source link

How to compare DateTimes and ClockTimes? #58

Closed shaulbehr closed 7 years ago

shaulbehr commented 7 years ago

It appears the comparison operators haven't been implemented for DateTime, ClockTime etc. Thus:

let lt = dateA < dateB; // evaluates to false
let gt = dateA > dateB; // evaluates to false
let eq = dateA == dateB; // evaluates to false

How should I compare these DateTime values?

electricessence commented 7 years ago

I will add IEquatable interface to these. Should be ready soon.

electricessence commented 7 years ago

Some of these classes already have IEquatable.

electricessence commented 7 years ago

BTW, in JS you can't compare two Date objects either:

var d1 = new Date(2012,12,11);
var d2 = new Date(2012,12,11);
var e = d1==d2; /* false */
var ev = d1.getTime()==d2.getTime(); /* true */
electricessence commented 7 years ago

Please review. Dates have always been a pain in JS because of time zones. Take a look at this code and see what you think. The equals is simple and stupid but maybe the equivalent method could be more useful.


    /**
     * Compares a JS Date with the current instance.  Does not evaluate the kind.
     * @param other
     * @returns {boolean}
     */
    equals(other:Date):boolean

    /**
     * Compares another IDateTime object and returns true if they or their value are equal.
     * @param other The other IDateTime object.
     * @param strict When strict is true, the 'kind' also must match.
     * @returns {boolean}
     */
    equals(other:IDateTime, strict?:boolean):boolean

    equals(other:IDateTime | Date, strict:boolean = false):boolean
    {
        if(!other) return false;
        if(other==this) return true;

        if(other instanceof Date)
        {
            let v = this._value;
            return other==v || other.getTime()==v.getTime();
        }

        if(other instanceof DateTime)
        {
            if(strict)
            {
                let ok = other._kind;
                if(!ok && this._kind || ok!=this._kind) return false;
            }

            return this.equals(other._value);
        }
        else if(strict)
            return false;

        return this.equals(other.toJsDate());

    }

    equivalent(other:IDateTime | Date):boolean
    {
        if(!other) return false;
        if(other==this) return true;

        if(other instanceof Date)
        {
            let v = this._value;
            // TODO: What is the best way to handle this when kinds match or don't?
            return v.toUTCString()==other.toUTCString();
        }

        if(other instanceof DateTime)
        {
            if(this.equals(other, true)) return true;
        }

        return this.equivalent(other.toJsDate());
    }
electricessence commented 7 years ago

Here's the actual change: https://github.com/electricessence/TypeScript.NET/commit/c6072d83fb7b95eeff8811539673da2ec78d3534#diff-f5c5c14b1129c431e6a0a61f9634559e It has been published to NPM and can be used now. Would still appreciate a review.

shaulbehr commented 7 years ago

Thanks for this! Can I also request implementations of <, <=, > and >=, please? :-)

electricessence commented 7 years ago

Ok so an IComparable interface... Will do. Again would appreciate some review and tell me if you think the way I'm implementing makes sense to developers. The equals method is a bit strict here.

electricessence commented 7 years ago

But there's no operator extensions in JS so I can't just add them like in C#.

shaulbehr commented 7 years ago

But there's no operator extensions in JS so I can't just add them like in C#.

Rats. That's a bit of a bummer. So even for equivalence comparison, I have to use .equals() or .equivalent(), not == or ===? (As you can see, I'm kinda new to TypeScript.)

electricessence commented 7 years ago

Well... Again already in existence is... https://www.w3schools.com/jsref/jsref_getTime.asp You get the milliseconds (.getTime()) from any Date and you can compare them. So as a fallback all DateTime objects in TypeScript .NET have .toJsDate() which gives you a copy of the internal date object. With the milliseconds you can easily >=, <=, <, > or whatever.

For sorting purposes, DateTime objects should have IComparable otherwise you'd have to write your own comparer.

electricessence commented 7 years ago

Here's the tests I added for that.

    it("should compare correctly",()=>{
        const x = new DateTime(d1), y = new DateTime(d2), z = new DateTime(d2);
        assert.ok(x.compareTo(y)<0);
        assert.ok(y.compareTo(x)>0);
        assert.ok(y.compareTo(z)==0);
    });
electricessence commented 7 years ago

New patched version is now published. Note that all TimeQuantity classes already have IComparable implemented.

electricessence commented 7 years ago

@shaulbehr If you want to support my efforts, take my Udemy course: :) https://www.udemy.com/stepping-up-to-typescript-fundamentals/ Could use another positive review or two.

shaulbehr commented 7 years ago

New patched version is now published. Note that all TimeQuantity classes already have IComparable implemented.

Terrific! Thanks for your super-speedy responses - really appreciated. What version contains these patches?

electricessence commented 7 years ago

@shaulbehr latest on NPM

shaulbehr commented 7 years ago

Hm, comparing two ClockTimes is causing an exception: My code:

if (now.compareTo(todayHours.localStartTime) < 0) { ... }

Exception:

TypeError: Can't add property _total, object is not extensible at n.get [as total] (TimeQuantity.js:formatted:1) at n.t.compareTo (TimeQuantity.js:formatted:1) at SchoolHoursManagementService.setOutsideSchoolHours (school-hours-management.service.ts:112) at school-hours-management.service.ts:55 at ZoneDelegate.invoke (TimeQuantity.js:formatted:1) at Object.onInvoke (TimeQuantity.js:formatted:1) at ZoneDelegate.invoke (TimeQuantity.js:formatted:1) at Zone.run (TimeQuantity.js:formatted:1) at TimeQuantity.js:formatted:1 at ZoneDelegate.invokeTask (TimeQuantity.js:formatted:1) at Object.onInvokeTask (TimeQuantity.js:formatted:1) at ZoneDelegate.invokeTask (TimeQuantity.js:formatted:1) at Zone.runTask (TimeQuantity.js:formatted:1) at drainMicroTaskQueue (TimeQuantity.js:formatted:1) at IDBRequest.ZoneTask.invoke (TimeQuantity.js:formatted:1)

electricessence commented 7 years ago

Hey sorry for the delay on this. I'll get to it asap.

electricessence commented 7 years ago

Ok. This is fixed and published as 4.8.0. I added a couple more unit tests to make sure that comparisons are covered in Time classes other than DateTime.