--[[----------------------------------------------------------------------------

KSDateTest.lua

--------------------------------------------------------------------------------

ADOBE CONFIDENTIAL
------------------
Copyright 2011 Adobe Systems Incorporated
All Rights Reserved.

NOTICE: All information contained herein is, and remain the property of
Adobe Systems Incorporated and its suppliers, if any. The intellectual and
technical concepts contained herein are proprietary to Adobe Systems
Incorporated and its suppliers and may be covered by U.S. and Foreign
Patents, patents in process, and are protected by trade secret or copyright
law.  Dissemination of this information or reproduction of this material is
strictly forbidden unless prior written permission is obtained from
Adobe Systems Incorporated.

------------------------------------------------------------------------------]]

	-- WF.core.os
local KSDate = require 'KSDate'
local WFArguments = require 'WFArguments'
local WFPlatformInfo = require 'WFPlatformInfo'

	-- WF.core.utils
local KSTestUtils = require 'KSTestUtils'

--------------------------------------------------------------------------------

local kSecondsBetweenUnixEpochAndReferenceDate = 978307200

--============================================================================--

local KSDateTest = {}

--------------------------------------------------------------------------------

function KSDateTest.testTimeNow( test )

	local timeNow = KSDate.timeNow()
	local timeNowUnixEpoch = KSDate.timeNow( true )
	
	KSTestUtils.assertEquals( test, 'return value is wrong type' )( 'number' )( type( timeNow ) )
	
	test:log( "timeNow is " .. tostring( timeNow ) )
	
	KSTestUtils.assertEquals( test, 'return value for timeNow( true ) is wrong type' )( 'number' )( type( timeNowUnixEpoch ) )
	
	test:log( "timeNow( true ) is " .. tostring( timeNowUnixEpoch ) )
	
	-- make sure timeNow - which should return seconds since the reference date - and timeNow( true ) - 
	-- which should return seconds since the unix epoch - are the expected distance apart (allowing for some timing slop)
	KSTestUtils.assertEquals( test, 'timeNow and timeNow( true ) are not the expected values' )( true )( math.abs( timeNowUnixEpoch - timeNow - kSecondsBetweenUnixEpochAndReferenceDate ) < 2 )
	
	KSTestUtils.assertEquals( test, 'timeNow( true ) should be larger than timeNow()' )( true )( timeNowUnixEpoch > timeNow )
end

--------------------------------------------------------------------------------

function KSDateTest.testTimeNowForAnimation( test )

	local timeNowForAnimation = KSDate.timeNowForAnimation()
	
	KSTestUtils.assertEquals( test, "return value is wrong type" )( 'number' )( type( timeNowForAnimation ) )

	test:log( "timeNowForAnimation is " .. tostring( timeNowForAnimation ) )

end

--------------------------------------------------------------------------------

function KSDateTest.testTimeInIso8601Format( test )

	local result = KSDate.timeInIso8601Format( 319659304.543 )
	
	KSTestUtils.assertEquals( test, "wrong date" )( '2011-02-17T18:15:04.543Z' )( result )

	test:log( "time at 319659304.54388 in ISO 8601 is " .. tostring( result ) )
	
	result = KSDate.timeInIso8601Format( 319659304.54 )
	
	KSTestUtils.assertEquals( test, "wrong date" )( '2011-02-17T18:15:04.540Z' )( result )
	
	result = KSDate.timeInIso8601Format( 319659304.5436 )
	
	KSTestUtils.assertEquals( test, "wrong date" )( '2011-02-17T18:15:04.544Z' )( result )
end

--------------------------------------------------------------------------------

function KSDateTest.testTimeInIso8601FormatWithUnixEpochInterval( test )

	local diffSinceUnixEpochReference = 978307200

	local result = KSDate.timeInIso8601FormatWithUnixEpochInterval( 319659304.543 + diffSinceUnixEpochReference )
	KSTestUtils.assertEquals( test, "wrong date" )( '2011-02-17T18:15:04.543Z' )( result )

	test:log( "time at 319659304.54388 in ISO 8601 is " .. tostring( result ) )
	
	result = KSDate.timeInIso8601FormatWithUnixEpochInterval( 319659304.54 + diffSinceUnixEpochReference )
	
	KSTestUtils.assertEquals( test, "wrong date" )( '2011-02-17T18:15:04.540Z' )( result )
	
	result = KSDate.timeInIso8601FormatWithUnixEpochInterval( 319659304.5436 + diffSinceUnixEpochReference )
	
	KSTestUtils.assertEquals( test, "wrong date" )( '2011-02-17T18:15:04.544Z' )( result )
end


--------------------------------------------------------------------------------

function KSDateTest.testDifferenceInDays( test )

	local result = KSDate.differenceInDays( "2012-03-03T09:12:34", "2012-03-21T08:34:45" )

	KSTestUtils.assertEquals( test, "difference was wrong" )( 18 )( result )

	test:log( "differenceInDays between 2012-03-03T09:12:34 and 2012-03-21T08:34:45 is "
			  .. tostring( result ) )
	
	-- Make sure the reverse test yields a negative value...
	result = KSDate.differenceInDays( "2012-03-21T08:34:45", "2012-03-03T09:12:34" )

	KSTestUtils.assertEquals( test, "difference was wrong" )( -18 )( result )

	test:log( "differenceInDays between 2012-03-21T08:34:45 and 2012-03-03T09:12:34 is "
			  .. tostring( result ) )
	
end

--------------------------------------------------------------------------------

function KSDateTest.testDifferenceInSeconds( test )

	local result = KSDate.differenceInSeconds( "2012-06-03T09:12:34.0Z",
											   "2012-06-21T08:34:45.0Z" )

	test:log( "differenceInSeconds between 2012-06-03T09:12:34Z and 2012-06-21T08:34:45Z is "
			  .. tostring( result ) )
			  
	KSTestUtils.assertEquals( test, "difference A was wrong" )( 1552931 )( result )

	
	
	-- Now test difference across DST change boundary...UTC should ignore the DST boundary

	result = KSDate.differenceInSeconds( "2012-03-03T09:12:34.0Z",
											 "2012-04-21T08:34:45.0Z" )
	
	test:log( "differenceInSeconds between 2012-03-03T09:12:34.0Z and 2012-04-21T08:34:45.0Z is "
			  .. tostring( result ) )
			  
	KSTestUtils.assertEquals( test, "difference B was wrong" )( 4231331 )( result )

	
	-- local time should observe the DST boundary
	
	-- SPAM [jandrew 2018-04-18]: This test used to use implicit timezones, but that proved
	-- unstable on the build machines.
	
	--[[ result = KSDate.differenceInSeconds( "2012-03-03T09:12:34",
											 "2012-04-21T08:34:45" )]]
	
	result = KSDate.differenceInSeconds( "2012-03-03T09:12:34-08:00",
											 "2012-04-21T08:34:45-07:00" )
	
	test:log( "differenceInSeconds between 2012-03-03T09:12:34-08:00 and 2012-04-21T08:34:45-07:00 is "
				  .. tostring( result ) )
				  
	KSTestUtils.assertEquals( test, "difference C was wrong" )( 4227731 )( result )

	
	
	-- Now reverse the dates
	
	result = KSDate.differenceInSeconds( "2012-06-21T08:34:45", "2012-06-03T09:12:34" )

	test:log( "differenceInSeconds between 2012-06-21T08:34:45 and 2012-06-03T09:12:34 is "
			  .. tostring( result ) )
			  
	KSTestUtils.assertEquals( test, "difference D was wrong" )( -1552931 )( result )

	
	
	
	-- Now test difference across DST change boundary...UTC should ignore the DST boundary
	result = KSDate.differenceInSeconds( "2012-04-21T08:34:45.0Z",
											 "2012-03-03T09:12:34.0Z" )
	
	test:log( "differenceInSeconds between 2012-04-21T08:34:45.0Z and 2012-03-03T09:12:34.0Z is "
				  .. tostring( result ) )
				  
	KSTestUtils.assertEquals( test, "difference B was wrong" )( -4231331 )( result )

	
	-- local time should observe the DST boundary

	result = KSDate.differenceInSeconds( "2012-04-21T08:34:45-07:00",
											 "2012-03-03T09:12:34-08:00" )
	
	test:log( "differenceInSeconds between 2012-04-21T08:34:45-07:00 and 2012-03-03T09:12:34-08:00 is "
				  .. tostring( result ) )
				  
	KSTestUtils.assertEquals( test, "difference F was wrong" )( -4227731 )( result )

end

--------------------------------------------------------------------------------

function KSDateTest.testGetLatestDate( test )

	local setOfDatesToCompare = {
		"2012-06-03T09:12:34.0Z",
		"2012-06-03T09:12:48.0Z",
		"2012-06-03T09:13:48.0Z",
		"2012-06-04T09:13:48.0Z",
		"2012-07-04T09:13:48.0Z",
		"2013-07-04T09:13:48.0Z",
	}
	
	-- Test Expected Date list
	local result
	for i, expectedLatest in ipairs( setOfDatesToCompare ) do
	
		-- set list of dates up to the expected date	
		result = KSDate.getLatestDate( unpack(setOfDatesToCompare, 1, i) )
		KSTestUtils.assertEquals( result, expectedLatest )
		
		-- set a nil entry
		result = KSDate.getLatestDate( nil, nil, nil, unpack(setOfDatesToCompare, 1, i) )
		KSTestUtils.assertEquals( result, expectedLatest )
		
		-- set a non-KSDate entry
		result = KSDate.getLatestDate( 1, "Test", true, {}, unpack(setOfDatesToCompare, 1, i) )
		KSTestUtils.assertEquals( result, expectedLatest )
		
	end
	
	-- Test true Latest Date list
	local latest = setOfDatesToCompare[ #setOfDatesToCompare ]
	for i, upToDate in ipairs( setOfDatesToCompare ) do
		
		-- set list of dates up to the expected date	
		result = KSDate.getLatestDate( unpack(setOfDatesToCompare, 1, i), latest )
		KSTestUtils.assertEquals( result, latest )
		
		-- set a nil entry
		result = KSDate.getLatestDate( nil, nil, nil, unpack(setOfDatesToCompare, 1, i), latest )
		KSTestUtils.assertEquals( result, latest )
		
		-- set a non-KSDate entry
		result = KSDate.getLatestDate( 1, "Test", true, {}, unpack(setOfDatesToCompare, 1, i), latest )
		KSTestUtils.assertEquals( result, latest )
		
	end
	
	-- Test all nil
	result = KSDate.getLatestDate( nil, nil, nil, nil)
	KSTestUtils.assertEquals( result, nil )
	
	-- Test all same
	local testAllSame = "2012-06-04T09:13:48.0Z"
	result = KSDate.getLatestDate( testAllSame, testAllSame, testAllSame, testAllSame, testAllSame )
	KSTestUtils.assertEquals( result, testAllSame )
	
end


--------------------------------------------------------------------------------

function KSDateTest.testLocalizedDiffInAllComponents( test )

	local negative, years, months, days, hours, minutes, seconds
		= KSDate.localizedDiffInAllComponents( "2012-01-01T09:12:34", "2011-12-29T08:11:33" )

	KSTestUtils.assertEquals( test, "time difference was wrong" )(
							  true,     0,     0,      3,    1,     1,       1 )(
							  negative, years, months, days, hours, minutes, seconds )

	-- Sorry for funky formatting, but Lua lint complains about ambiguous syntax
	-- if open (s are on their own lines where I'd want to put them. :-(
	
end

--------------------------------------------------------------------------------

function KSDateTest.testFormatDateTime( test )

	local utcTime = KSDate.formatDateTimeAsUTC( 319659344 )
--	local localTime = KSDate.formatDateTimeAsLocal( 319659344 )

	KSTestUtils.assertEquals( test, "formatDateTimeAsUTC was wrong" )( "2011-02-17T18:15:44" )( utcTime )
	
	-- This test does not work in Minnesota:
	-- KSTestUtils.assertEquals( test, "formatDateTimeAsLocal was wrong" )( "2011-02-17T10:15:44" )( localTime )

end

--------------------------------------------------------------------------------

function KSDateTest.testDateArgumentChecker( test )

	local check = assert( WFArguments.check )

	KSDate.registerArgumentChecker()

	local function testDateArgumentChecker( possibleDate )
		check('KSDate')
	end

	testDateArgumentChecker( "2012-03-03T09:12:34" )

	KSTestUtils.assertFails( testDateArgumentChecker, 3)

end

--------------------------------------------------------------------------------

function KSDateTest.testTimeInIso8601FormatWithTimeZone( test )
	-- in order to pass despite the time zone of the local machine we
	-- are mostly just testing whether the resulting format is correct 
	-- and not actually whether the date with time zone added is 
	-- actually correct for the local machine
	
	local timeWithTZ = KSDate.timeInIso8601FormatWithTimeZone()
	
	assert( not not timeWithTZ:find( "%d%d%d%d%-%d%d%-%d%dT%d%d:%d%d:%d%d(%.?%d*)[+-]%d%d:%d%d" ), 
					string.format( "ISO8601 time with timezone '%s' did not match the expected format", timeWithTZ ) )
	
end

--------------------------------------------------------------------------------

function KSDateTest.testTimeZoneInIso8601Format( test )
	-- in order to pass despite the time zone of the local machine we
	-- are mostly just testing whether the resulting format is correct 
	-- and not actually whether the time zone added is actually correct
	-- for the local machine
	
	local timezone = KSDate.timeZoneInIso8601Format()
	
	assert( not not timezone:find( "^[+-]%d%d:%d%d$" ), string.format( "ISO8601 timezone '%s' did not match the expected format", timezone ) )
	
end

--------------------------------------------------------------------------------

local function compareTimes( time1, time2, optTolerance )
	
	optTolerance = optTolerance or 0
	
	if optTolerance == 0 then
		return time1 == time2
	else 
		return math.abs( time1 - time2 ) < optTolerance
	end
end

----------------------------------------------------------------------------------

function KSDateTest.testIso8601MissingValues( test )
	local strings = {
		"2001-06-09T17:45:15.01+05:30",
		"2001-06-09T17:45:15.01+0530",
		"2001-06-09T17:45:15.01Z",
		"2001-06-09T17:45:15.01",
		
		"2001-06-09T17:45:15+05:30",
		"2001-06-09T17:45:15+0530",
		"2001-06-09T17:45:15Z",
		"2001-06-09T17:45:15",

		"2001-06-09T17:45+05:30",
		"2001-06-09T17:45+0530",
		"2001-06-09T17:45Z",
		"2001-06-09T17:45",
		
		"2001-06-09T17+05:30",
		"2001-06-09T17+0530",
		"2001-06-09T17Z",
		"2001-06-09T17",
		
		"2001-06-09",
		"2001-06",
		"2001",
	}

	local refs = {
		"2001-06-09T17:45:15.01+05:30",
		"2001-06-09T17:45:15.01+0530",
		"2001-06-09T17:45:15.01Z",
		"2001-06-09T17:45:15.01",
		
		"2001-06-09T17:45:15+05:30",
		"2001-06-09T17:45:15+0530",
		"2001-06-09T17:45:15Z",
		"2001-06-09T17:45:15",

		"2001-06-09T17:45:00+05:30",
		"2001-06-09T17:45:00+0530",
		"2001-06-09T17:45:00Z",
		"2001-06-09T17:45:00",
		
		"2001-06-09T17:00:00+05:30",
		"2001-06-09T17:00:00+0530",
		"2001-06-09T17:00:00Z",
		"2001-06-09T17:00:00",
		
		"2001-06-09T00:00:00",
		"2001-06-01T00:00:00",
		"2001-01-01T00:00:00",
	}


	for i = 1, #strings do
		local dateFromString = KSDate.maybeKSDateFromString( strings[ i ] )
		local timeFromString = KSDate.iso8601ToTimeIntervalSinceReference( dateFromString )
		
		local timeFromRef = KSDate.iso8601ToTimeIntervalSinceReference( refs[ i ] )
		assert( compareTimes( timeFromString, timeFromRef ) )
	end

end

--------------------------------------------------------------------------------


function KSDateTest.testRepairMissingOrInvalidSeconds( test )
	local strings = {
		"2001-06-09T17:45:60.10+05:30",
		"2001-06-09T17:45:60.10+0530",
		"2001-06-09T17:45:60.10Z",
		"2001-06-09T17:45:60.10",
		
		"2001-06-09T17:45:60+05:30",
		"2001-06-09T17:45:60+0530",
		"2001-06-09T17:45:60Z",
		"2001-06-09T17:45:60",

		"2001-06-09T17:45+05:30",
		"2001-06-09T17:45+0530",
		"2001-06-09T17:45Z",
		"2001-06-09T17:45",
		
		"2001-06-09T17+05:30",
		"2001-06-09T17+0530",
		"2001-06-09T17Z",
		"2001-06-09T17",
		
		"2001-06-09",
		"2001-06",
		"2001",		
	}

	local refs = {
		"2001-06-09T12:15:59.1Z",
		"2001-06-09T12:15:59.1Z",
		"2001-06-09T17:45:59.1Z",
		"2001-06-09T17:45:59.1",
		
		"2001-06-09T12:15:59Z",
		"2001-06-09T12:15:59Z",
		"2001-06-09T17:45:59Z",
		"2001-06-09T17:45:59",

		"2001-06-09T12:15:00Z",
		"2001-06-09T12:15:00Z",
		"2001-06-09T17:45:00Z",
		"2001-06-09T17:45:00",
		
		"2001-06-09T11:30:00Z",
		"2001-06-09T11:30:00Z",
		"2001-06-09T17:00:00Z",
		"2001-06-09T17:00:00",
		
		"2001-06-09T00:00:00",
		"2001-06-01T00:00:00",
		"2001-01-01T00:00:00",
	}

	local offset, dst = KSDate.timeZoneAdjustmentInSeconds()
	print( offset, dst )

	for i = 1, #strings do
		local dateFromString = KSDate.recoverDateForOz( strings[ i ] )
		
		local timeFromString = KSDate.iso8601ToTimeIntervalSinceReference( dateFromString )		
		local timeFromRef = KSDate.iso8601ToTimeIntervalSinceReference( refs[ i ] )

		assert( compareTimes( timeFromString, timeFromRef ), 
			string.format('[%d] %s should be recovered to: %s (was %s)', i, strings[ i ], refs[ i ], dateFromString ) )
			
	end

end

function KSDateTest.testRepairOutlandishDates( test )

	local strange = {
		false,
		17,
		{ foo = { 'bar' } },
		{ 1, 2, 3 },
		"A tester walks into a bar",
		"2017-06-20T16:09:11.520+00:09:21",
		-- these are actual values we have seen in the wild (on Splunk)
		"610626-01-19T07:14:54.560+07:36:42",
		"4622317-06-20T16:09:11.520+00:09:21",
	}
		
	for i = 1, #strange do
	
		local dateFromString = KSDate.recoverDateForOz( strange[ i ] )
		assert( not dateFromString, string.format( '[%d] %s should not be recoverable: %s', i, strange[ i ], dateFromString ) )

	end

end

--------------------------------------------------------------------------------

function KSDateTest.testRecoverDateForOzTimezoneHandling( test )

    -- Test UTC dates with Z suffix

    local result = KSDate.recoverDateForOz( "2023-03-09T06:45:32.000Z" )

    assert( result == "2023-03-09T06:45:32.000Z",
        string.format( "Failed UTC date with Z suffix: expected=2023-03-09T06:45:32.000Z, got=%s", result ) )

    result = KSDate.recoverDateForOz( "2023-03-09T11:45:32.000Z" )

    assert( result == "2023-03-09T11:45:32.000Z",
        string.format( "Failed UTC date with Z suffix (5 hours later): expected=2023-03-09T11:45:32.000Z, got=%s", result ) )

    result = KSDate.recoverDateForOz( "2023-03-09T16:45:32.000Z" )

    assert( result == "2023-03-09T16:45:32.000Z",
        string.format( "Failed UTC date with Z suffix (10 hours later): expected=2023-03-09T16:45:32.000Z, got=%s", result ) )

    result = KSDate.recoverDateForOz( "2023-03-09T21:45:32.000Z" )

    assert( result == "2023-03-09T21:45:32.000Z",
        string.format( "Failed UTC date with Z suffix (15 hours later): expected=2023-03-09T21:45:32.000Z, got=%s", result ) )

    result = KSDate.recoverDateForOz( "2023-03-10T02:45:32.000Z" )

    assert( result == "2023-03-10T02:45:32.000Z",
        string.format( "Failed UTC date with Z suffix (20 hours later): expected=2023-03-10T02:45:32.000Z, got=%s", result ) )

    -- Test dates with explicit timezone offsets

    result = KSDate.recoverDateForOz( "2023-03-09T06:45:32.000+00:00" )

    assert( result == "2023-03-09T06:45:32.000Z",
        string.format( "Failed UTC date with explicit +00:00 offset: expected=2023-03-09T06:45:32.000Z, got=%s", result ) )

    result = KSDate.recoverDateForOz( "2023-03-09T06:45:32.000-05:00" )

    assert( result == "2023-03-09T11:45:32.000Z",
        string.format( "Failed date with -05:00 offset: expected=2023-03-09T11:45:32.000Z, got=%s", result ) )

	-- Test date without timezone. Note this can be enabled manually, but running
	-- as a part of validation is not advised as the time zone is going to specific
	-- to each individual build node.

--    result = KSDate.recoverDateForOz( "2023-03-09T06:45:32.000" )
--
--    assert( result == "2023-03-09T14:45:32.000Z",
--        string.format( "Failed date without timezone: expected=2023-03-09T14:45:32.000Z, got=%s", result ) )

    -- Test that dates don't shift when processed multiple times

    local date = "2023-03-09T06:45:32.000Z"
    local result1 = KSDate.recoverDateForOz( date )
    local result2 = KSDate.recoverDateForOz( result1 )
    local result3 = KSDate.recoverDateForOz( result2 )

    assert( result1 == result2,
        string.format( "Date shifted after first recovery: original=%s, after1=%s", date, result1 ) )

    assert( result2 == result3,
        string.format( "Date shifted after second recovery: after1=%s, after2=%s", result1, result2 ) )

end

--------------------------------------------------------------------------------

function KSDateTest.testIso8601ToTimeIntervalSinceReference( test )
	
	local ok, _ = pcall( KSDate.iso8601ToTimeIntervalSinceReference ) -- no param should get error
	
	assert( not ok, "iso8601ToTimeIntervalSinceReference should throw error with no parameter" )
	
	ok, _ = pcall( KSDate.iso8601ToTimeIntervalSinceReference, "cannotparsetodate" ) -- unparseable string should get error
	assert( not ok, "iso8601ToTimeIntervalSinceReference should throw error if string cannot be parsed to date" )

	local time = 522244267.310043
	local iso8601utc = KSDate.timeInIso8601Format( time )
	local utcTime = KSDate.iso8601ToTimeIntervalSinceReference( iso8601utc )
	assert( compareTimes( time, utcTime, .001 ), 
			string.format( "ISO8601 UTC fixed round trip failed. UTC: %s, Time: %f, UTC Time: %f", iso8601utc, time, utcTime ) )

	time = KSDate.timeNow()
	
	iso8601utc = KSDate.timeInIso8601Format( time )
	test:log( iso8601utc )
	local iso8601tz = KSDate.timeInIso8601FormatWithTimeZone( time )
	test:log( iso8601tz )
	local iso8601local = KSDate.timeInIso8601Format( time, true )
	test:log( iso8601local )

	utcTime = KSDate.iso8601ToTimeIntervalSinceReference( iso8601utc )
	local tzTime = KSDate.iso8601ToTimeIntervalSinceReference( iso8601tz )
	local localTime = KSDate.iso8601ToTimeIntervalSinceReference( iso8601local )
	
	assert( compareTimes( time, utcTime, .001 ), 
			string.format( "ISO8601 UTC round trip failed. UTC: %s, Time: %f, UTC Time: %f", iso8601utc, time, utcTime ) )
	assert( compareTimes( time, localTime, .001 ), 
			string.format( "ISO8601 local time round trip failed. Local: %s, Time: %f, Local Time: %f", iso8601local, time, localTime ) )
	assert( compareTimes( time, tzTime, .001 ),
			string.format( "ISO8601 TZ round trip failed. TZ: %s, Time: %f, TZ Time: %f", iso8601tz, time, tzTime ) )
	
	-- add tests without ms too
	local iso8601utcNoMs = KSDate.timeInIso8601FormatNearestSecond( time )
	test:log( iso8601utcNoMs )
	local iso8601localNoMs = KSDate.timeInIso8601FormatNearestSecond( time, true )
	test:log( iso8601localNoMs )
	local iso8601tzNoMs = KSDate.timeInIso8601FormatNearestSecond( time, true ) .. KSDate.timeZoneInIso8601Format()
	test:log( iso8601tzNoMs )
	
	local utcTimeNoMs = KSDate.iso8601ToTimeIntervalSinceReference( iso8601utcNoMs )
	local localTimeNoMs = KSDate.iso8601ToTimeIntervalSinceReference( iso8601localNoMs )
	local tzTimeNoMs = KSDate.iso8601ToTimeIntervalSinceReference( iso8601tzNoMs )
	
	local timeNoMs = math.floor( time )
	
	assert( compareTimes( timeNoMs, utcTimeNoMs ), 
			string.format( "ISO8601 UTC round trip without milliseconds failed. UTC: %s, Time: %f, UTC Time: %f", 
							iso8601utcNoMs, timeNoMs, utcTimeNoMs ) )
	assert( compareTimes( timeNoMs, localTimeNoMs ), 
			string.format( "ISO8601 local time round trip without milliseconds failed. Local: %s, Time: %f, Local Time: %f", 
							iso8601localNoMs, timeNoMs, localTimeNoMs ) )
	assert( compareTimes( timeNoMs, tzTimeNoMs ), 
			string.format( "ISO8601 TZ time round trip without milliseconds failed. TZ: %s, Time: %f, TZ Time: %f", 
							iso8601tzNoMs, timeNoMs, tzTimeNoMs ) )
	
	-- this is a TZ that tests are unlikely to run in natively so if we parse this correctly, we're properly
	-- able to take into account the time zone.
	local iso8601TimeWithUnexpectedTZ = "2016-04-05T04:05:34.564+0300"
	local expected = "2016-04-05T01:05:34.564Z"
	
	local timeConvertedToUTC = KSDate.timeInIso8601Format( KSDate.iso8601ToTimeIntervalSinceReference( iso8601TimeWithUnexpectedTZ ) )
	
	assert( timeConvertedToUTC == expected, string.format( "Failed to convert ISO8601 time correctly. Expected: %s, Received: %s", expected, timeConvertedToUTC ) )
	
	
	-- Can we handle sub-millisecond precision (by ignoring the extra if necessary)
	-- 
	local iso8601TimeWithOzStylePrecision = "2016-04-05T01:05:34.564133Z"
	
	ok, time = pcall( KSDate.iso8601ToTimeIntervalSinceReference, iso8601TimeWithOzStylePrecision )
	assert( ok, "iso8601ToTimeIntervalSinceReference should not choke on sub-ms precision" )
	
	local subMsTimeConvertedToUTC =  KSDate.timeInIso8601Format( time )
	test:log( subMsTimeConvertedToUTC )
	assert( subMsTimeConvertedToUTC == expected, 
			string.format( "Failed to convert sub-millisecond ISO8601 time correctly. Expected: %s, Received: %s", expected, subMsTimeConvertedToUTC ) )
			
	-- test times pre-reference since the possible negative time results might be unexpected on different platforms
	local preReferenceIso8601 = "2000-12-29T10:10:00.000Z"
	ok, time = pcall( KSDate.iso8601ToTimeIntervalSinceReference, preReferenceIso8601 )
	
	test:log( time )
	assert( ok, "iso8601ToTimeIntervalSinceReference should handle times pre-OSX reference (00:00:00 UTC on 1 January 2001)" )
	
	local reformattedPreReferenceTime = KSDate.timeInIso8601Format( time )
	test:log( reformattedPreReferenceTime )
	assert( preReferenceIso8601 == reformattedPreReferenceTime, "Failed to format pre-reference time in ISO8601" )
	
	-- this is a particular sub-ms value that Windows parsing choked on and returned an error because it rounded up and caused an issue in native data
	-- structures
	ok, time = pcall( KSDate.iso8601ToTimeIntervalSinceReference, "2016-04-28T20:57:34.999999Z" )
	assert( ok, "iso8601ToTimeIntervalSinceReference should not choke on sub-ms precision  (and should not round up)" )
	assert( math.floor( time ) ~= math.floor( time + 0.5 ), "should not have rounded sub-ms time up" )
	
  	-- this is a particular sub-ms value that was printed as "2017-03-15T19:31:60.000"
	-- Note the seconds value of 60
	local result = KSDate.timeInIso8601Format( 511299119.9999 )
    assert( result == "2017-03-15T19:32:00.000Z", "iso8601ToTimeIntervalSinceReference should not generate seconds values of 60" )

end

--------------------------------------------------------------------------------
if not WFPlatformInfo.isAndroid then
-- FIXME [ashj 2019-02-08]: One of the testcases ("1904-01-01T00:00:00Z")
-- fails for Android. Need to look into the impl of iso8601ToTimeIntervalSinceReference.

function KSDateTest.testShiftDateTime( test )
	
	local timeDelta = 1
		
	local dateStringList = {
	
		-- With timezone
		{ str = "2001-06-09T17:59:59+05:30", 		expected = "2001-06-09T18:00:00+05:30" },
		{ str = "2001-06-09T17:59:59-05:30",		expected = "2001-06-09T18:00:00-05:30", },
		{ str = "2001-06-09T17:59:59+0530",			expected = "2001-06-09T18:00:00+0530"},
		{ str = "2001-06-09T17:59:59-0530",			expected = "2001-06-09T18:00:00-0530"  },
		{ str = "2001-06-09T17:59:59Z",				expected = "2001-06-09T18:00:00Z"},
		
		-- Without timezone
		{ str = "2001-06-09T17:59:59",				expected = "2001-06-09T18:00:00" },
		
		-- Day shift
		{ str = "2001-06-09T23:59:59",				expected = "2001-06-10T00:00:00" },
		
		-- Month shift
		{ str = "2017-02-28T23:59:59",				expected = "2017-03-01T00:00:00" },
		
		-- year shift
		{ str = "2017-12-31T23:59:59",				expected = "2018-01-01T00:00:00" },
		
		
		-- Some user case
		{ str = "2003-09-29T18:31:02-05:00", 		expected = "2003-09-29T18:31:03-05:00" },
		
		{ str = "2017-10-25T13:31:52.000-07:00",	expected = "2017-10-25T13:31:53.000-07:00" },
		
		{ str = "1904-01-01T00:00:00Z",				expected = "1904-01-01T00:00:01Z" },
	
		{ str = "2017-09-06T15:45:37.983",			expected = "2017-09-06T15:45:38.983" },
		
		{ str = "2017-10-16T00:56:04.10",			expected = "2017-10-16T00:56:05.10" },
		
		{ str = "2017-09-03T11:59:03-07:00",		expected = "2017-09-03T11:59:04-07:00" },
	}

	for _, item in ipairs( dateStringList ) do
	
		 local shiftDateString = KSDate.shiftIso8601DateTime( item.str, timeDelta )
		 
		 assert( compareTimes( item.expected, shiftDateString ), 
			string.format( "testShiftDateTime() failed, expected=%s, result=%s ", item.expected, shiftDateString ) )
		
	end
	
	-- backward time shift
	timeDelta = -1

	for _, item in ipairs( dateStringList ) do
	
		 local shiftDateString = KSDate.shiftIso8601DateTime( item.expected, timeDelta )
		 
		 assert( compareTimes( item.str, shiftDateString ), 
			string.format( "testShiftDateTime() failed, expected=%s, result=%s ", item.str, shiftDateString ) )
		
	end
end

end
--------------------------------------------------------------------------------

return KSDateTest
