time module

This commit is contained in:
Ronja 2020-11-01 16:44:40 +01:00
parent ed41828ecb
commit 0f984696c7

325
Luxe/math/time.wren Normal file
View file

@ -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)")
}
}