Ticket Created
over 5 years ago

WERETECH-8841 (for Time.Moment/Duration constructor not being type safe)

Moment Seconds limited to Number / Signed 32bit Integer

Am I right in thinking there is an issue with the Garmin O/S when we reach the rollover of seconds within a signed 32bit int.  This is happening with seconds greater than 2147483647 at Tue, 19 Jan 2038 03:14:07 GMT.

Or is there a recommended workaround either external or within the system.

I discovered issues today while testing some solar calculation code today with dates beyond the above.

Parents
  • This is supposed to be a developer.garmin.com/.../Moment.html limitation. Unfortunately, the documentation here isn't entirely accurate. The documentation claims that we store a 32-bit signed integer internally, but we actually store whatever type you put in. The following code demonstrates:
    using Toybox.Time;
    
    (:test)
    module Test_Moment
    {
    
        function do_test_Moment(value) {
        	var moment = new Time.Moment(value);
        	pretty.dump(moment.value());
        }
        
        function do_test_Duration(value) {
        	var duration = new Time.Duration(value);
        	pretty.dump(duration.value());
        }
        
        function do_test_Moment_add_Duration(value) {
        	var moment = new Time.Moment(value).add(new Time.Duration(value));
        	pretty.dump(moment.value());
        }
        
    	(:test)
    	function test_Moment(logger)
    	{
        	do_test_Moment(1);
        	do_test_Moment(1L);
        	do_test_Moment(1.0);
        	do_test_Moment(1.0d);
        	do_test_Moment("754");
        	
        	return true;
    	}
    	
    	(:test)
    	function test_Duration(logger)
    	{
        	do_test_Duration(1);
        	do_test_Duration(1L);
        	do_test_Duration(1.0);
        	do_test_Duration(1.0d);
        	do_test_Duration("754");
        	
        	return true;
    	}
    	
    	(:test)
    	function test_Moment_add_Duration(logger)
    	{
        	do_test_Moment_add_Duration(1);
        	do_test_Moment_add_Duration(1L);
        	do_test_Moment_add_Duration(1.0);
        	do_test_Moment_add_Duration(1.0d);
        	do_test_Moment_add_Duration("754");
        	
        	return true;
    	}
    }
    Note that the pretty.dump() function called above just dumps the value in a format that indicates the type of the value to make it possible to see what you're actually getting. The output I see is:
    Executing test Test_Moment.test_Moment...
    1
    1L
    1.000000f
    1.000000d
    "754"
    PASS
    ------------------------------------------------------------------------------
    Executing test Test_Moment.test_Duration...
    1
    1L
    1.000000f
    1.000000d
    "754"
    PASS
    ------------------------------------------------------------------------------
    Executing test Test_Moment.test_Moment_add_Duration...
    2L
    2L
    2.000000d
    2.000000d
    "754754"
    PASS
    So, you can see that the value returned by Moment.value() and Duration.value() is the same type as whatever was passed into the constructor. When adding a Moment and a Duration that are the same type the result is slightly different. Given Numbers, you get a Long. Given Floats, you get a Double... so the Moment.add() function is taking the value from the Duration, and converting it to a Long. All of this is seems to be very sketchy behavior. We probably should be type checking the value passed in to initialize for both Moment and Duration. At the very least we should ensure that the value is a Number or Long. Eventually, when you try to do something useful with the Moment, the system will try to get the stored value out as a Number. If it is a Number. If the value stored internally is a numeric type, the value will be truncated down to a signed 32-bit integer. If is some other type (String), You'll get an exception. I'll be filing an issue for this behavior. I don't believe that we should be storing non-numeric types in Moment or Duration, and I don't think we should allow anything that isn't a 32-bit signed value. Once that is fixed we could update our implementation to internally store a 64-bit signed value and allow you to pass a Number or Long. This would expand the range of supported values without breaking apps.
Comment
  • This is supposed to be a developer.garmin.com/.../Moment.html limitation. Unfortunately, the documentation here isn't entirely accurate. The documentation claims that we store a 32-bit signed integer internally, but we actually store whatever type you put in. The following code demonstrates:
    using Toybox.Time;
    
    (:test)
    module Test_Moment
    {
    
        function do_test_Moment(value) {
        	var moment = new Time.Moment(value);
        	pretty.dump(moment.value());
        }
        
        function do_test_Duration(value) {
        	var duration = new Time.Duration(value);
        	pretty.dump(duration.value());
        }
        
        function do_test_Moment_add_Duration(value) {
        	var moment = new Time.Moment(value).add(new Time.Duration(value));
        	pretty.dump(moment.value());
        }
        
    	(:test)
    	function test_Moment(logger)
    	{
        	do_test_Moment(1);
        	do_test_Moment(1L);
        	do_test_Moment(1.0);
        	do_test_Moment(1.0d);
        	do_test_Moment("754");
        	
        	return true;
    	}
    	
    	(:test)
    	function test_Duration(logger)
    	{
        	do_test_Duration(1);
        	do_test_Duration(1L);
        	do_test_Duration(1.0);
        	do_test_Duration(1.0d);
        	do_test_Duration("754");
        	
        	return true;
    	}
    	
    	(:test)
    	function test_Moment_add_Duration(logger)
    	{
        	do_test_Moment_add_Duration(1);
        	do_test_Moment_add_Duration(1L);
        	do_test_Moment_add_Duration(1.0);
        	do_test_Moment_add_Duration(1.0d);
        	do_test_Moment_add_Duration("754");
        	
        	return true;
    	}
    }
    Note that the pretty.dump() function called above just dumps the value in a format that indicates the type of the value to make it possible to see what you're actually getting. The output I see is:
    Executing test Test_Moment.test_Moment...
    1
    1L
    1.000000f
    1.000000d
    "754"
    PASS
    ------------------------------------------------------------------------------
    Executing test Test_Moment.test_Duration...
    1
    1L
    1.000000f
    1.000000d
    "754"
    PASS
    ------------------------------------------------------------------------------
    Executing test Test_Moment.test_Moment_add_Duration...
    2L
    2L
    2.000000d
    2.000000d
    "754754"
    PASS
    So, you can see that the value returned by Moment.value() and Duration.value() is the same type as whatever was passed into the constructor. When adding a Moment and a Duration that are the same type the result is slightly different. Given Numbers, you get a Long. Given Floats, you get a Double... so the Moment.add() function is taking the value from the Duration, and converting it to a Long. All of this is seems to be very sketchy behavior. We probably should be type checking the value passed in to initialize for both Moment and Duration. At the very least we should ensure that the value is a Number or Long. Eventually, when you try to do something useful with the Moment, the system will try to get the stored value out as a Number. If it is a Number. If the value stored internally is a numeric type, the value will be truncated down to a signed 32-bit integer. If is some other type (String), You'll get an exception. I'll be filing an issue for this behavior. I don't believe that we should be storing non-numeric types in Moment or Duration, and I don't think we should allow anything that isn't a 32-bit signed value. Once that is fixed we could update our implementation to internally store a 64-bit signed value and allow you to pass a Number or Long. This would expand the range of supported values without breaking apps.
Children
No Data