-local function normalise ( year , month , day , hour , min , sec )
- min , sec = increment ( min , sec , 60 ) -- TODO: consider leap seconds?
- hour , min = increment ( hour , min , 60 )
- day , hour = increment ( day , hour , 24 )
-
- while day <= 0 do
- year = year - 1
- day = day + year_length ( year )
+local function normalise(year, month, day, hour, min, sec)
+ -- `month` and `day` start from 1, need -1 and +1 so it works modulo
+ month, day = month - 1, day - 1
+
+ -- Convert everything (except seconds) to an integer
+ -- by propagating fractional components down.
+ year , month = borrow(year , month, 12)
+ -- Carry from month to year first, so we get month length correct in next line around leap years
+ year , month = carry(year, month, 12)
+ month, day = borrow(month, day , month_length(floor(month + 1), year))
+ day , hour = borrow(day , hour , 24)
+ hour , min = borrow(hour , min , 60)
+ min , sec = borrow(min , sec , 60)
+
+ -- Propagate out of range values up
+ -- e.g. if `min` is 70, `hour` increments by 1 and `min` becomes 10
+ -- This has to happen for all columns after borrowing, as lower radixes may be pushed out of range
+ min , sec = carry(min , sec , 60) -- TODO: consider leap seconds?
+ hour , min = carry(hour, min , 60)
+ day , hour = carry(day , hour, 24)
+ -- Ensure `day` is not underflowed
+ -- Add a whole year of days at a time, this is later resolved by adding months
+ -- TODO[OPTIMIZE]: This could be slow if `day` is far out of range
+ while day < 0 do
+ month = month - 1
+ if month < 0 then
+ year = year - 1
+ month = 11
+ end
+ day = day + month_length(month + 1, year)