From 0f984696c7cb2306661ad974d64e96bafa204c0c Mon Sep 17 00:00:00 2001 From: Ronja Date: Sun, 1 Nov 2020 16:44:40 +0100 Subject: [PATCH] time module --- Luxe/math/time.wren | 325 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 325 insertions(+) create mode 100644 Luxe/math/time.wren diff --git a/Luxe/math/time.wren b/Luxe/math/time.wren new file mode 100644 index 0000000..4cdd353 --- /dev/null +++ b/Luxe/math/time.wren @@ -0,0 +1,325 @@ +import "luxe: io" for IO + +//modulo utility function in here to make this pretty self contained for now +var mod = Fn.new{|dividend, divisor| + return (dividend%divisor + divisor) % divisor +} + +//time based on epoch time - assumes time is after 1.1.1970 and doesnt do leap seconds +class DateTime{ + static seconds_per_minute{60} + static seconds_per_hour{3600} + static seconds_per_day{86400} + static minutes_per_hour{60} + static hours_per_day{24} + static epoch{1970} + + toString{ + var month_names = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"] + var week_names = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"] + var ends = ["th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"] + return "%(week_names[day_of_week]), %(days+1)%(ends[(days+1)%10]) of %(month_names[months]) %(years) "+ + "%(hours):%(minutes):%(seconds)" + } + + total_seconds{_time} + total_minutes{(_time / DateTime.seconds_per_minute).truncate} + total_hours{(_time / DateTime.seconds_per_hour).truncate} + total_days{(_time / DateTime.seconds_per_day).truncate} + total_years{years} + + seconds{total_seconds % DateTime.seconds_per_minute} + minutes{(total_minutes % DateTime.minutes_per_hour).truncate} + hours{(total_hours % DateTime.hours_per_day).truncate} + days{ + var months = [31, DateTime.is_leap_year(years)?29:28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + var month = 0 + var days = day_in_year + while(days >= months[month]){ + days = days - months[month] + month = month + 1 + } + return days + } + day_in_year{ + var days = total_days + var year = DateTime.epoch + //while the day is in a later year + while(days >= 365){ + if(DateTime.is_leap_year(year)){ + if(days == 365) return days + days = days - 366 + } else { + days = days - 365 + } + year = year + 1 + } + return days + } + //day of the week from monday=0 to sunday=6 + day_of_week{ + return (total_days + 3) % 7 //+3 is because 1.1.1970 was a thursday + } + week_in_year{ + //what + return ((day_in_year-day_of_week-1)/7).truncate+1 + } + months{ + var months = [31, DateTime.is_leap_year(years)?29:28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + var month = 0 + var days = day_in_year + while(days >= months[month]){ + days = days - months[month] + month = month + 1 + } + return month + } + years{ + return DateTime.epoch + ((total_days - leap_days_since_epoch_start) / 365).truncate + } + leap_days_since_epoch_start{ + var days = total_days + var year = DateTime.epoch + var leaps = 0 + //while the day is in a later year or later than january 29/march 1 + while(days > 58){ + if(DateTime.is_leap_year(year)){ + leaps = leaps + 1 + days = days - 366 + } else { + days = days - 365 + } + year = year + 1 + } + return leaps + } + + is_leap_year{is_leap_year(years)} + + static is_leap_year(year){ + return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) + } + + construct new(epoch){ + _time = epoch + } + + construct now(){ + _time = IO.time_since_epoch_utc() + } + + construct clone(instance){ + _time = instance.total_seconds + } + + //overloads + static from_time(years) {from_time(years, 0, 0, 0, 0, 0)} + static from_time(years, months) {from_time(years, months, 0, 0, 0, 0)} + static from_time(years, months, days) {from_time(years, months, days, 0, 0, 0)} + static from_time(years, months, days, hours) {from_time(years, months, days, hours, 0, 0)} + static from_time(years, months, days, hours, minutes) {from_time(years, months, days, hours, minutes, 0)} + + construct from_time(years, months, days, hours, minutes, seconds){ + if(years < DateTime.epoch) { + System.print("[warn] dates before %(DateTime.epoch) aren't possible yet :(") + return + } + for(i in DateTime.epoch...years){ + days = days + (DateTime.is_leap_year(i)?366:365) + } + var month_days = [31, DateTime.is_leap_year(years)?29:28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + for(i in 0...months){ + days = days + month_days[i] + } + _time = days * DateTime.seconds_per_day + hours * DateTime.seconds_per_hour + minutes * DateTime.seconds_per_minute + seconds + } + + add_seconds(second_diff){ + _time = _time + second_diff + } + + add_minutes(minute_diff){ + _time = _time + minute_diff * DateTime.seconds_per_minute + } + + add_hours(hour_diff){ + _time = _time + hour_diff * DateTime.seconds_per_hour + } + + add_days(day_diff){ + _time = _time + day_diff * DateTime.seconds_per_day + } + + add_weeks(week_diff){ + _time = _time + week_diff * DateTime.seconds_per_day * 7 + } + + add_months(month_diff){ + var month_days = [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + var day_difference = 0 + var month = months + var year = years + var sign = month_diff.sign + var original_days = days + //execute once if counting down to use the next months days, not this months days + if(sign < 0){ + month = month + sign + if(month < 0){ + month = month + 12 + year = year - 1 + } + if(month >= 12){ + month = month - 12 + year = year + 1 + } + } + //collect days we have to move the time + for(i in 0...month_diff){ + var days = month_days[month] + if(days){ + day_difference = day_difference + days * sign + } else { + day_difference = day_difference + (DateTime.is_leap_year(year)?29:28) * sign + } + month = month + sign + if(month < 0){ + month = month + 12 + year = year - 1 + } + if(month >= 12){ + month = month - 12 + year = year + 1 + } + } + add_days(day_difference) + //if the days shifted we can assume we jumped, for example from a 31st of jan to a 2nd of march since the feb is short + //so we just go back until we arrive in the previous month + var new_days = days + if(new_days != original_days){ + add_days(-new_days-1) + } + } + + add_years(year_diff){ + var before_leap = day_in_year <= 59 + var day_difference = 0 + var year = years + var sign = year_diff.sign + if(sign > 0 != before_leap){ + year = year + sign + } + for(i in 0...year_diff){ + day_difference = day_difference + sign * (DateTime.is_leap_year(year)?366:365) + year = year + sign + } + add_days(day_difference) + } + + add(span){ + _time = _time + span.total_seconds + } + + set(time){ + _time = time + } + + update(){ + _time = IO.time_since_epoch_utc() + } + + static difference(time_1, time_2){ + return TimeSpan.new((time_1.total_seconds - time_2.total_seconds).abs) + } + + +(val){ + if(val is TimeSpan){ + return DateTime.new(_time + val.total_seconds) + } + Fiber.abort("Can't add DateTime and %(val.type.name)") + } + + -(val){ + if(val is DateTime){ + return DateTime.difference(this, val) + } + if(val is TimeSpan){ + return DateTime.new(_time - val.total_seconds) + } + Fiber.abort("Can't subtract %(val.type.name) from TimeSpan") + } +} + +class TimeSpan{ + static zero{TimeSpan.new(0)} + + toString{"%(days) days, %(hours) hours, %(minutes) minutes, and %(seconds) seconds"} + + total_seconds{_time} + total_minutes{(_time / DateTime.seconds_per_minute).truncate} + total_hours{(_time / DateTime.seconds_per_hour).truncate} + total_days{(_time / DateTime.seconds_per_day).truncate} + + seconds{total_seconds % DateTime.seconds_per_minute} + minutes{total_minutes % DateTime.minutes_per_hour} + hours{total_hours % DateTime.hours_per_day} + days{total_days} + //months and years are not possible because we don't know which months/years we're talking about + + construct new(time){ + _time = time + } + + construct from_seconds(seconds){ + _time = seconds + } + + construct from_minutes(minutes){ + _time = minutes * DateTime.seconds_per_minute + } + + construct from_hours(hours){ + _time = hours * DateTime.seconds_per_hour + } + + construct from_days(days){ + _time = days * DateTime.seconds_per_day + } + + add_seconds(seconds){ + _time = _time + seconds + } + + add_minutes(minutes){ + _time = _time + minutes * DateTime.seconds_per_minute + } + + add_hours(hours){ + _time = _time + hours * DateTime.seconds_per_hour + } + + add_days(days){ + _time = _time + days * DateTime.seconds_per_day + } + + add(span){ + _time = _time + span.total_seconds + } + + set(time){ + _time = time + } + + +(val){ + if(val is TimeSpan){ + return TimeSpan.new(_time + val.total_seconds) + } + Fiber.abort("Can't add TimeSpan and %(val.type.name)") + } + + -(val){ + if(val is TimeSpan){ + return TimeSpan.new(_time - val.total_seconds) + } + Fiber.abort("Can't add TimeSpan and %(val.type.name)") + } +} \ No newline at end of file