Examples
These are some longer examples than on the home page.
A complex loop
This example is intended to demonstrate how continue
is translated, as well as breaking out of a function from within a loop.
import other.module (name1, name2); func my_func(a: [Int], b: Str) -> Value { x = 1; y = 0; for i in a ++ [5, 6, 7] { y += i; x *= y; func flip(x, y) { return (x:y, y:x); } if i == 5 { continue; } (x, y) = flip(x, y); if x > 100 { return x; } } z = y + x; return z; }
import other.module (name1, name2); func my_func(a: [Int], b: Str) -> Value { x := 1; y := 0; for i in a ++ [5, 6, 7] { y += i; x *= y; func flip(x, y) { return (x: y, y: x); } if i == 5 { continue } (x, y) := flip(left: x, right: y); if x > 100 { return x; } } z := y + x; return z; }
import other.module (name1, name2); func my_func(a: [Int], b: Str) -> Value { x := 1; y := 0; flag_ := #pass_; for i in a ++ [5, 6, 7] { y += i; x *= y; func flip(x, y) { return (x: y, y: x); } if i == 5 { continue } (x, y) := flip(left: x, right: y); if x > 100 { flag_ := return_ ~ x; break } } switch flag_ { case #pass_: pass case return_ ~ temp_: return temp_; } z := y + x; return z; }
import other.module (name1, name2); func my_func(a: [Int], b: Str) -> Value { x := 1; y := 0; flag_ := #pass_; func body_( iter__, accum__: (flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) ) -> ( cont~(flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) | stop~(flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) ) { (flag_, flip, x, y) := accum__; i := iter__; y += i; x *= y; func flip(x, y) { return (x: y, y: x); } if i == 5 { return cont ~ (flag_:, flip:, x:, y:); } (x, y) := flip(left: x, right: y); if x > 100 { flag_ := return_ ~ x; return stop ~ (flag_:, flip:, x:, y:); } return cont ~ (flag_:, flip:, x:, y:); } (flag_, flip, x, y) := !!for_loop( body_, a ++ [5, 6, 7], (flag_:, flip:, x:, y:) ); switch flag_ { case #pass_: pass case return_ ~ temp_: return temp_; } z := y + x; return z; }
import other.module (name1, name2); func my_func(a: [Int], b: Str) -> Value { x := 1; y := 0; flag_ := #pass_; func body_( iter__, accum__: (flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) ) -> ( cont~(flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) | stop~(flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) ) { (flag_, flip, x, y) := accum__; i := iter__; y += i; x *= y; func flip(x, y) { return (x: y, y: x); } if i == 5 { return cont ~ (flag_:, flip:, x:, y:); (x, y) := flip(left: x, right: y); if x > 100 { flag_ := return_ ~ x; return stop ~ (flag_:, flip:, x:, y:); return cont ~ (flag_:, flip:, x:, y:); } else { return cont ~ (flag_:, flip:, x:, y:); } } else { (x, y) := flip(left: x, right: y); if x > 100 { flag_ := return_ ~ x; return stop ~ (flag_:, flip:, x:, y:); return cont ~ (flag_:, flip:, x:, y:); } else { return cont ~ (flag_:, flip:, x:, y:); } } } (flag_, flip, x, y) := !!for_loop( body_, a ++ [5, 6, 7], (flag_:, flip:, x:, y:) ); switch flag_ { case #pass_: pass z := y + x; return z; case return_ ~ temp_: return temp_; z := y + x; return z; } }
import other.module (name1, name2); func my_func(a: [Int], b: Str) -> Value { x := 1; y := 0; flag_ := #pass_; func body_( iter__, accum__: (flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) ) -> ( cont~(flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) | stop~(flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) ) { (flag_, flip, x, y) := accum__; i := iter__; y += i; x *= y; func flip(x, y) { return (x: y, y: x); } if i == 5 { return cont ~ (flag_:, flip:, x:, y:); } else { (x, y) := flip(left: x, right: y); if x > 100 { flag_ := return_ ~ x; return stop ~ (flag_:, flip:, x:, y:); } else { return cont ~ (flag_:, flip:, x:, y:); } } } (flag_, flip, x, y) := !!for_loop( body_, a ++ [5, 6, 7], (flag_:, flip:, x:, y:) ); switch flag_ { case #pass_: z := y + x; return z; case return_ ~ temp_: return temp_; } }
import other.module (name1, name2); func my_func(a: [Int], b: Str) -> Value { x := 1; y := 0; flag_ := #pass_; func body_( iter__, accum__: (flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) ) -> ( cont~(flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) | stop~(flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) ) { (flag_, flip, x, y) := accum__; i := iter__; y += i; x *= y; func flip(x, y) { return (x: y, y: x); } if i == 5 { return cont ~ (flag_:, flip:, x:, y:); } else { (x, y) := flip(left: x, right: y); if x > 100 { flag_ := return_ ~ x; return stop ~ (flag_:, flip:, x:, y:); } else { return cont ~ (flag_:, flip:, x:, y:); } } } (flag_, flip, x, y) := !!for_loop( body_, a ++ [5, 6, 7], (flag_:, flip:, x:, y:) ); switch flag_ { case pass_ ~ var_: z := y + x; return z; case return_ ~ var_a: switch var_a { case temp_: return temp_; } } }
import other.module (name1, name2); func my_func(a: [Int], b: Str) -> Value { x := 1; y := 0; flag_ := #pass_; func body_( iter__, accum__: (flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) ) -> ( cont~(flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) | stop~(flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) ) { (flag_, flip, x, y) := accum__; i := iter__; y += i; x *= y; func flip(x, y) { return (x: y, y: x); } if i == 5 { return cont ~ (flag_:, flip:, x:, y:); } else { (x, y) := flip(left: x, right: y); if x > 100 { flag_ := return_ ~ x; return stop ~ (flag_:, flip:, x:, y:); } else { return cont ~ (flag_:, flip:, x:, y:); } } } (flag_, flip, x, y) := !!for_loop( body_, a ++ [5, 6, 7], (flag_:, flip:, x:, y:) ); switch flag_ { case pass_ ~ *: var_ := flag_ ? pass_; z := y + x; return z; case return_ ~ *: var_a := flag_ ? return_; switch var_a { case *: temp_ := var_a; return temp_; } } }
import other.module (name1, name2); func my_func(a: [Int], b: Str) -> Value { x := 1; y := 0; flag_ := #pass_; func body_( iter__, accum__: (flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) ) -> ( cont~(flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) | stop~(flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) ) { temp_n := accum__; flag_ := temp_n.flag_; flip := temp_n.flip; x := temp_n.x; y := temp_n.y; i := iter__; y := y + i; x := x * y; func flip(x, y) { return (x: y, y: x); } if i == 5 { return cont ~ (flag_:, flip:, x:, y:); } else { temp_a := flip(left: x, right: y); x := temp_a.x; y := temp_a.y; if x > 100 { flag_ := return_ ~ x; return stop ~ (flag_:, flip:, x:, y:); } else { return cont ~ (flag_:, flip:, x:, y:); } } } temp_t := !!for_loop(body_, a ++ [5, 6, 7], (flag_:, flip:, x:, y:)); flag_ := temp_t.flag_; flip := temp_t.flip; x := temp_t.x; y := temp_t.y; switch flag_ { case pass_ ~ *: var_ := flag_ ? pass_; z := y + x; return z; case return_ ~ *: var_a := flag_ ? return_; switch var_a { case *: temp_ := var_a; return temp_; } } }
import other.module (name1, name2); func my_func(a: [Int], b: Str) -> Value { x := 1; y := 0; flag_ := #pass_; func body_( iter__, accum__: (flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) ) -> ( cont~(flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) | stop~(flag_: !Unspec, flip: !Unspec, x: !Unspec, y: !Unspec) ) { temp_n := accum__; flag_0 := temp_n.flag_; flip := temp_n.flip; x_0 := temp_n.x; y_0 := temp_n.y; i := iter__; y_1 := y_0 + i; x_1 := x_0 * y_1; func flip_0(x, y) { return (x: y, y: x); } if i == 5 { return cont ~ (flag_: flag_0, flip: flip_0, x: x_1, y: y_1); } else { temp_a := flip_0(left: x_1, right: y_1); x_2 := temp_a.x; y_2 := temp_a.y; if x_2 > 100 { flag_1 := return_ ~ x_2; return stop ~ (flag_: flag_1, flip: flip_0, x: x_2, y: y_2); } else { return cont ~ (flag_: flag_0, flip: flip_0, x: x_2, y: y_2); } } } temp_t := !!for_loop(body_, a ++ [5, 6, 7], (flag_:, flip:, x:, y:)); flag_2 := temp_t.flag_; flip := temp_t.flip; x_3 := temp_t.x; y_3 := temp_t.y; switch flag_2 { case pass_ ~ *: var_ := flag_2 ? pass_; z := y_3 + x_3; return z; case return_ ~ *: var_a := flag_2 ? return_; switch var_a { case *: temp_ := var_a; return temp_; } } }
import other.module(name1 as name1); import other.module(name2 as name2); def my_func := {def a:[!List !Int] b:!Str ::Value (let x:1 (let y:0 (let flag_:#pass_ (let body_:{def accum__:[!Record flag_:!Unspec flip:!Unspec x:!Unspec y:!Unspec] iter__:: ::[!Union cont:[!Record flag_:!Unspec flip:!Unspec x:!Unspec y:!Unspec] stop:[!Record flag_:!Unspec flip:!Unspec x:!Unspec y:!Unspec] ] (let temp_n:accum__ (let flag_0:(!get_slot flag_ temp_n) (let flip:(!get_slot flip temp_n) (let x_0:(!get_slot x temp_n) (let y_0:(!get_slot y temp_n) (let i:iter__ (let y_1:(!bin_plus y_0 i) (let x_1:(!bin_times x_0 y_1) (let flip_0:{def x:: y:: [. x:y y:x]} (case tags (!bin_equals i 5) false:(let temp_a:(flip_0 left:x_1 right:y_1) (let x_2:(!get_slot x temp_a) (let y_2:(!get_slot y temp_a) (case tags (!bin_greater_than x_2 100) false:cont~[. flag_:flag_0 flip:flip_0 x:x_2 y:y_2 ] true:(let flag_1:return_~x_2 stop~[. flag_:flag_1 flip:flip_0 x:x_2 y:y_2 ] ) ) ) ) ) true:cont~[. flag_:flag_0 flip:flip_0 x:x_1 y:y_1 ] ) ) ) ) ) ) ) ) ) ) } (let temp_t:(!for_loop body_ (!bin_concat a [list 5 6 7]) [. flag_:flag_ flip:flip x:x y:y] ) (let flag_2:(!get_slot flag_ temp_t) (let flip:(!get_slot flip temp_t) (let x_3:(!get_slot x temp_t) (let y_3:(!get_slot y temp_t) (case tags flag_2 pass_:(let var_:(!get_variant pass_ flag_2) (let z:(!bin_plus y_3 x_3) z) ) return_:(let var_a:(!get_variant return_ flag_2) (let temp_:var_a temp_) ) ) ) ) ) ) ) ) ) ) ) };
import other.module(name1 as name1); import other.module(name2 as name2); def my_func := {def a:[!List !Int] b:!Str ::Value (let x:1 y:0 (let body_:{def accum__:[!Record flag_:!Unspec flip:!Unspec x:!Unspec y:!Unspec] iter__:: ::[!Union cont:[!Record flag_:!Unspec flip:!Unspec x:!Unspec y:!Unspec] stop:[!Record flag_:!Unspec flip:!Unspec x:!Unspec y:!Unspec] ] (let temp_n:accum__ (let flag_0:(!get_slot flag_ temp_n) flip:(!get_slot flip temp_n) i:iter__ x_0:(!get_slot x temp_n) y_0:(!get_slot y temp_n) (let y_1:(!bin_plus y_0 i) (let flip_0:{def x:: y:: [. x:y y:x]} x_1:(!bin_times x_0 y_1) (case tags (!bin_equals i 5) false:(let temp_a:(flip_0 left:x_1 right:y_1) (let x_2:(!get_slot x temp_a) y_2:(!get_slot y temp_a) (case tags (!bin_greater_than x_2 100) false:cont~[. flag_:flag_0 flip:flip_0 x:x_2 y:y_2] true:(let flag_1:return_~x_2 stop~[. flag_:flag_1 flip:flip_0 x:x_2 y:y_2] ) ) ) ) true:cont~[. flag_:flag_0 flip:flip_0 x:x_1 y:y_1] ) ) ) ) ) } flag_:#pass_ (let temp_t:(!for_loop body_ (!bin_concat a [list 5 6 7]) [. flag_:flag_ flip:flip x:x y:y] ) (let flag_2:(!get_slot flag_ temp_t) flip:(!get_slot flip temp_t) x_3:(!get_slot x temp_t) y_3:(!get_slot y temp_t) (case tags flag_2 pass_:(let var_:(!get_variant pass_ flag_2) z:(!bin_plus y_3 x_3) z) return_:(let var_a:(!get_variant return_ flag_2) (let temp_:var_a temp_) ) ) ) ) ) ) };
import other.module(name1 as name1); import other.module(name2 as name2); def body_ := {def accum__:[!Record flag_:!Unspec flip:!Unspec x:!Unspec y:!Unspec] iter__:: ::[!Union cont:[!Record flag_:!Unspec flip:!Unspec x:!Unspec y:!Unspec] stop:[!Record flag_:!Unspec flip:!Unspec x:!Unspec y:!Unspec] ] (let temp_n:accum__ (let flag_0:(!get_slot flag_ temp_n) flip:(!get_slot flip temp_n) i:iter__ x_0:(!get_slot x temp_n) y_0:(!get_slot y temp_n) (let y_1:(!bin_plus y_0 i) (let flip_0:{flip_0} x_1:(!bin_times x_0 y_1) (case tags (!bin_equals i 5) false:(let temp_a:(flip_0 left:x_1 right:y_1) (let x_2:(!get_slot x temp_a) y_2:(!get_slot y temp_a) (case tags (!bin_greater_than x_2 100) false:cont~[. flag_:flag_0 flip:flip_0 x:x_2 y:y_2] true:(let flag_1:return_~x_2 stop~[. flag_:flag_1 flip:flip_0 x:x_2 y:y_2] ) ) ) ) true:cont~[. flag_:flag_0 flip:flip_0 x:x_1 y:y_1] ) ) ) ) ) }; def flip_0 := {def x:: y:: [. x:y y:x]}; def my_func := {def a:[!List !Int] b:!Str ::Value (let x:1 y:0 (let body_:{body_} flag_:#pass_ (let temp_t:(!for_loop body_ (!bin_concat a [list 5 6 7]) [. flag_:flag_ flip:flip x:x y:y] ) (let flag_2:(!get_slot flag_ temp_t) flip:(!get_slot flip temp_t) x_3:(!get_slot x temp_t) y_3:(!get_slot y temp_t) (case tags flag_2 pass_:(let var_:(!get_variant pass_ flag_2) z:(!bin_plus y_3 x_3) z) return_:(let var_a:(!get_variant return_ flag_2) (let temp_:var_a temp_) ) ) ) ) ) ) };
A search syntax
A language describing searching would typically allow arbitrarily nested parentheses, but that requires a feature, cyclic types, that won’t be immediately available. We can still describe and even evaluate fairly complex expressions, though.
;; In a Union type, a tag, here "name" or "literal" determines what the variant stores. type Value_Expr := name ~ Str | literal ~ Int | #zero ;; The sharp means the variant type is the "unit" type, which has only one value. let example_value_1 := name ~ "age" let example_value_2 := name ~ "count" let example_value_3 := literal ~ 55 ;; We use a Tuple to store possible arguments for a binary relation. type Binary_Relation := ( left: Value_Expr, right: Value_Expr, ) ;; And another union distinguishes our types of search operators. type Search_Operator := equals ~ Binary_Relation | not_equals ~ Binary_Relation | positive ~ Value_Expr type Search_Expression := [Search_Operator] let example := [ equals ~ (left: name ~ "age", right: literal ~ 55), positive ~ name ~ "count", ] func evaluate(expr, names : {Str: Int}) { let result := true let eval := eval_val(names:, ...) for op in expr { switch op { case equals ~ (left:, right:): let result := result and eval(expr: left) == eval(expr: right) case not_equals ~ (left:, right:): let result := result and eval(expr: left) != eval(expr: right) case positive ~ val: let result := result and eval_val(val) > 0 } } return result } func eval_val(expr, names : {Str: Int}) { switch expr { case name ~ name: return names[name] case literal ~ num: return num case #zero: return 0 } }
type Value_Expr := (name~Str | literal~Int | #zero); example_value_1 := name ~ "age"; example_value_2 := name ~ "count"; example_value_3 := literal ~ 55; type Binary_Relation := (left: Value_Expr, right: Value_Expr); type Search_Operator := ( equals~Binary_Relation | not_equals~Binary_Relation | positive~Value_Expr ); type Search_Expression := [Search_Operator]; example := [ equals ~ (left: name ~ "age", right: literal ~ 55), positive ~ name ~ "count" ]; func evaluate(expr, names: { Str: Int }) { result := true; eval := eval_val(names:, ...); for op in expr { switch op { case equals ~ (left:, right:): result := result and eval(expr: left) == eval(expr: right); case not_equals ~ (left:, right:): result := result and eval(expr: left) != eval(expr: right); case positive ~ val: result := result and eval_val(value: val) > 0; } } return result; } func eval_val(expr, names: { Str: Int }) { switch expr { case name ~ name: return names[name]; case literal ~ num: return num; case #zero: return 0; } }
type Value_Expr := (name~Str | literal~Int | #zero); example_value_1 := name ~ "age"; example_value_2 := name ~ "count"; example_value_3 := literal ~ 55; type Binary_Relation := (left: Value_Expr, right: Value_Expr); type Search_Operator := ( equals~Binary_Relation | not_equals~Binary_Relation | positive~Value_Expr ); type Search_Expression := [Search_Operator]; example := [ equals ~ (left: name ~ "age", right: literal ~ 55), positive ~ name ~ "count" ]; func evaluate(expr, names: { Str: Int }) { result := true; eval := eval_val(names:, ...); func body_(iter__, accum__: (result: !Unspec)) -> ( cont~(result: !Unspec) | stop~(result: !Unspec) ) { (result) := accum__; op := iter__; switch op { case equals ~ (left:, right:): result := result and eval(expr: left) == eval(expr: right); case not_equals ~ (left:, right:): result := result and eval(expr: left) != eval(expr: right); case positive ~ val: result := result and eval_val(value: val) > 0; } return cont ~ (result:); } (result) := !!for_loop(body_, expr, (result:)); return result; } func eval_val(expr, names: { Str: Int }) { switch expr { case name ~ name: return names[name]; case literal ~ num: return num; case #zero: return 0; } }
type Value_Expr := (name~Str | literal~Int | #zero); example_value_1 := name ~ "age"; example_value_2 := name ~ "count"; example_value_3 := literal ~ 55; type Binary_Relation := (left: Value_Expr, right: Value_Expr); type Search_Operator := ( equals~Binary_Relation | not_equals~Binary_Relation | positive~Value_Expr ); type Search_Expression := [Search_Operator]; example := [ equals ~ (left: name ~ "age", right: literal ~ 55), positive ~ name ~ "count" ]; func evaluate(expr, names: { Str: Int }) { result := true; eval := eval_val(names:, ...); func body_(iter__, accum__: (result: !Unspec)) -> ( cont~(result: !Unspec) | stop~(result: !Unspec) ) { (result) := accum__; op := iter__; switch op { case equals ~ (left:, right:): result := result and eval(expr: left) == eval(expr: right); return cont ~ (result:); case not_equals ~ (left:, right:): result := result and eval(expr: left) != eval(expr: right); return cont ~ (result:); case positive ~ val: result := result and eval_val(value: val) > 0; return cont ~ (result:); } } (result) := !!for_loop(body_, expr, (result:)); return result; } func eval_val(expr, names: { Str: Int }) { switch expr { case name ~ name: return names[name]; case literal ~ num: return num; case #zero: return 0; } }
type Value_Expr := (name~Str | literal~Int | #zero); example_value_1 := name ~ "age"; example_value_2 := name ~ "count"; example_value_3 := literal ~ 55; type Binary_Relation := (left: Value_Expr, right: Value_Expr); type Search_Operator := ( equals~Binary_Relation | not_equals~Binary_Relation | positive~Value_Expr ); type Search_Expression := [Search_Operator]; example := [ equals ~ (left: name ~ "age", right: literal ~ 55), positive ~ name ~ "count" ]; func evaluate(expr, names: { Str: Int }) { result := true; eval := eval_val(names:, ...); func body_(iter__, accum__: (result: !Unspec)) -> ( cont~(result: !Unspec) | stop~(result: !Unspec) ) { (result) := accum__; op := iter__; switch op { case equals ~ var_: (left : slot_, right : slot_a) := var_; switch slot_ { case left: switch slot_a { case right: result := result and eval(expr: left) == eval(expr: right); return cont ~ (result:); } } case not_equals ~ var_a: (left : slot_n, right : slot_t) := var_a; switch slot_n { case left: switch slot_t { case right: result := result and eval(expr: left) != eval(expr: right); return cont ~ (result:); } } case positive ~ var_n: switch var_n { case val: result := result and eval_val(value: val) > 0; return cont ~ (result:); } } } (result) := !!for_loop(body_, expr, (result:)); return result; } func eval_val(expr, names: { Str: Int }) { switch expr { case literal ~ var_t: switch var_t { case num: return num; } case name ~ var_g: switch var_g { case name: return names[name]; } case zero ~ var_w: return 0; } }
type Value_Expr := (name~Str | literal~Int | #zero); example_value_1 := name ~ "age"; example_value_2 := name ~ "count"; example_value_3 := literal ~ 55; type Binary_Relation := (left: Value_Expr, right: Value_Expr); type Search_Operator := ( equals~Binary_Relation | not_equals~Binary_Relation | positive~Value_Expr ); type Search_Expression := [Search_Operator]; example := [ equals ~ (left: name ~ "age", right: literal ~ 55), positive ~ name ~ "count" ]; func evaluate(expr, names: { Str: Int }) { result := true; eval := eval_val(names:, ...); func body_(iter__, accum__: (result: !Unspec)) -> ( cont~(result: !Unspec) | stop~(result: !Unspec) ) { (result) := accum__; op := iter__; switch op { case equals ~ *: var_ := op ? equals; (left : slot_, right : slot_a) := var_; switch slot_ { case *: left := slot_; switch slot_a { case *: right := slot_a; result := result and eval(expr: left) == eval(expr: right); return cont ~ (result:); } } case not_equals ~ *: var_a := op ? not_equals; (left : slot_n, right : slot_t) := var_a; switch slot_n { case *: left := slot_n; switch slot_t { case *: right := slot_t; result := result and eval(expr: left) != eval(expr: right); return cont ~ (result:); } } case positive ~ *: var_n := op ? positive; switch var_n { case *: val := var_n; result := result and eval_val(value: val) > 0; return cont ~ (result:); } } } (result) := !!for_loop(body_, expr, (result:)); return result; } func eval_val(expr, names: { Str: Int }) { switch expr { case literal ~ *: var_t := expr ? literal; switch var_t { case *: num := var_t; return num; } case name ~ *: var_g := expr ? name; switch var_g { case *: name := var_g; return names[name]; } case zero ~ *: var_w := expr ? zero; return 0; } }
type Value_Expr := (name~Str | literal~Int | #zero); example_value_1 := name ~ "age"; example_value_2 := name ~ "count"; example_value_3 := literal ~ 55; type Binary_Relation := (left: Value_Expr, right: Value_Expr); type Search_Operator := ( equals~Binary_Relation | not_equals~Binary_Relation | positive~Value_Expr ); type Search_Expression := [Search_Operator]; example := [ equals ~ (left: name ~ "age", right: literal ~ 55), positive ~ name ~ "count" ]; func evaluate(expr, names: { Str: Int }) { result := true; eval := eval_val(names:, ...); func body_(iter__, accum__: (result: !Unspec)) -> ( cont~(result: !Unspec) | stop~(result: !Unspec) ) { temp_n := accum__; result := temp_n.result; op := iter__; switch op { case equals ~ *: var_ := op ? equals; temp_ := var_; slot_ := temp_.left; slot_a := temp_.right; switch slot_ { case *: left := slot_; switch slot_a { case *: right := slot_a; result := result and eval(expr: left) == eval(expr: right); return cont ~ (result:); } } case not_equals ~ *: var_a := op ? not_equals; temp_a := var_a; slot_n := temp_a.left; slot_t := temp_a.right; switch slot_n { case *: left := slot_n; switch slot_t { case *: right := slot_t; result := result and eval(expr: left) != eval(expr: right); return cont ~ (result:); } } case positive ~ *: var_n := op ? positive; switch var_n { case *: val := var_n; result := result and eval_val(value: val) > 0; return cont ~ (result:); } } } temp_t := !!for_loop(body_, expr, (result:)); result := temp_t.result; return result; } func eval_val(expr, names: { Str: Int }) { switch expr { case literal ~ *: var_t := expr ? literal; switch var_t { case *: num := var_t; return num; } case name ~ *: var_g := expr ? name; switch var_g { case *: name := var_g; return names[name]; } case zero ~ *: var_w := expr ? zero; return 0; } }
type Value_Expr := (name~Str | literal~Int | #zero); example_value_1 := name ~ "age"; example_value_2 := name ~ "count"; example_value_3 := literal ~ 55; type Binary_Relation := (left: Value_Expr, right: Value_Expr); type Search_Operator := ( equals~Binary_Relation | not_equals~Binary_Relation | positive~Value_Expr ); type Search_Expression := [Search_Operator]; example := [ equals ~ (left: name ~ "age", right: literal ~ 55), positive ~ name ~ "count" ]; func evaluate(expr, names: { Str: Int }) { result := true; eval := eval_val(names:, ...); func body_(iter__, accum__: (result: !Unspec)) -> ( cont~(result: !Unspec) | stop~(result: !Unspec) ) { temp_n := accum__; result_0 := temp_n.result; op := iter__; switch op { case equals ~ *: var_ := op ? equals; temp_ := var_; slot_ := temp_.left; slot_a := temp_.right; switch slot_ { case *: left := slot_; switch slot_a { case *: right := slot_a; result_1 := result_0 and eval(expr: left) == eval( expr: right ); return cont ~ (result: result_1); } } case not_equals ~ *: var_a := op ? not_equals; temp_a := var_a; slot_n := temp_a.left; slot_t := temp_a.right; switch slot_n { case *: left := slot_n; switch slot_t { case *: right := slot_t; result_2 := result_0 and eval(expr: left) != eval( expr: right ); return cont ~ (result: result_2); } } case positive ~ *: var_n := op ? positive; switch var_n { case *: val := var_n; result_3 := result_0 and eval_val(value: val) > 0; return cont ~ (result: result_3); } } } temp_t := !!for_loop(body_, expr, (result:)); result_4 := temp_t.result; return result_4; } func eval_val(expr, names: { Str: Int }) { switch expr { case literal ~ *: var_t := expr ? literal; switch var_t { case *: num := var_t; return num; } case name ~ *: var_g := expr ? name; switch var_g { case *: name := var_g; return names[name]; } case zero ~ *: var_w := expr ? zero; return 0; } }
type Binary_Relation := [!Record left:Value_Expr right:Value_Expr]; type Search_Expression := [!List Search_Operator]; type Search_Operator := [!Union equals:Binary_Relation not_equals:Binary_Relation positive:Value_Expr ]; type Value_Expr := [!Union literal:!Int name:!Str zero:[!Record]]; def eval_val := {def expr:: names:[!Map !Str !Int] (case tags expr literal:(let var_t:(!get_variant literal expr) (let num:var_t num)) name:(let var_g:(!get_variant name expr) (let name:var_g (!get_index names name)) ) zero:(let var_w:(!get_variant zero expr) 0) ) }; def evaluate := {def expr:: names:[!Map !Str !Int] (let result:true (let eval:{eval_val names:names} (let body_:{def accum__:[!Record result:!Unspec] iter__:: ::[!Union cont:[!Record result:!Unspec] stop:[!Record result:!Unspec]] (let temp_n:accum__ (let result_0:(!get_slot result temp_n) (let op:iter__ (case tags op equals:(let var_:(!get_variant equals op) (let temp_:var_ (let slot_:(!get_slot left temp_) (let slot_a:(!get_slot right temp_) (let left:slot_ (let right:slot_a (let result_1:(!bin_and result_0 (!bin_equals (eval expr:left) (eval expr:right) ) ) cont~[. result:result_1] ) ) ) ) ) ) ) not_equals:(let var_a:(!get_variant not_equals op) (let temp_a:var_a (let slot_n:(!get_slot left temp_a) (let slot_t:(!get_slot right temp_a) (let left:slot_n (let right:slot_t (let result_2:(!bin_and result_0 (!bin_not_equal (eval expr:left) (eval expr:right) ) ) cont~[. result:result_2] ) ) ) ) ) ) ) positive:(let var_n:(!get_variant positive op) (let val:var_n (let result_3:(!bin_and result_0 (!bin_greater_than (eval_val value:val) 0) ) cont~[. result:result_3] ) ) ) ) ) ) ) } (let temp_t:(!for_loop body_ expr [. result:result]) (let result_4:(!get_slot result temp_t) result_4) ) ) ) ) }; def example := [list equals~[. left:name~"age" right:literal~55] positive~name~"count" ]; def example_value_1 := name~"age"; def example_value_2 := name~"count"; def example_value_3 := literal~55;
type Binary_Relation := [!Record left:Value_Expr right:Value_Expr]; type Search_Expression := [!List Search_Operator]; type Search_Operator := [!Union equals:Binary_Relation not_equals:Binary_Relation positive:Value_Expr ]; type Value_Expr := [!Union literal:!Int name:!Str zero:[!Record]]; def eval_val := {def expr:: names:[!Map !Str !Int] (case tags expr literal:(let var_t:(!get_variant literal expr) (let num:var_t num)) name:(let var_g:(!get_variant name expr) (let name:var_g (!get_index names name)) ) zero:(let var_w:(!get_variant zero expr) 0) ) }; def evaluate := {def expr:: names:[!Map !Str !Int] (let eval:{eval_val names:names} result:true (let body_:{def accum__:[!Record result:!Unspec] iter__:: ::[!Union cont:[!Record result:!Unspec] stop:[!Record result:!Unspec]] (let temp_n:accum__ (let op:iter__ result_0:(!get_slot result temp_n) (case tags op equals:(let var_:(!get_variant equals op) (let temp_:var_ (let slot_:(!get_slot left temp_) slot_a:(!get_slot right temp_) (let left:slot_ right:slot_a (let result_1:(!bin_and result_0 (!bin_equals (eval expr:left) (eval expr:right)) ) cont~[. result:result_1] ) ) ) ) ) not_equals:(let var_a:(!get_variant not_equals op) (let temp_a:var_a (let slot_n:(!get_slot left temp_a) slot_t:(!get_slot right temp_a) (let left:slot_n right:slot_t (let result_2:(!bin_and result_0 (!bin_not_equal (eval expr:left) (eval expr:right)) ) cont~[. result:result_2] ) ) ) ) ) positive:(let var_n:(!get_variant positive op) (let val:var_n (let result_3:(!bin_and result_0 (!bin_greater_than (eval_val value:val) 0) ) cont~[. result:result_3] ) ) ) ) ) ) } (let temp_t:(!for_loop body_ expr [. result:result]) (let result_4:(!get_slot result temp_t) result_4) ) ) ) }; def example := [list equals~[. left:name~"age" right:literal~55] positive~name~"count" ]; def example_value_1 := name~"age"; def example_value_2 := name~"count"; def example_value_3 := literal~55;
type Binary_Relation := [!Record left:Value_Expr right:Value_Expr]; type Search_Expression := [!List Search_Operator]; type Search_Operator := [!Union equals:Binary_Relation not_equals:Binary_Relation positive:Value_Expr ]; type Value_Expr := [!Union literal:!Int name:!Str zero:[!Record]]; def body_ := {def accum__:[!Record result:!Unspec] eval:: iter__:: ::[!Union cont:[!Record result:!Unspec] stop:[!Record result:!Unspec]] (let temp_n:accum__ (let op:iter__ result_0:(!get_slot result temp_n) (case tags op equals:(let var_:(!get_variant equals op) (let temp_:var_ (let slot_:(!get_slot left temp_) slot_a:(!get_slot right temp_) (let left:slot_ right:slot_a (let result_1:(!bin_and result_0 (!bin_equals (eval expr:left) (eval expr:right)) ) cont~[. result:result_1] ) ) ) ) ) not_equals:(let var_a:(!get_variant not_equals op) (let temp_a:var_a (let slot_n:(!get_slot left temp_a) slot_t:(!get_slot right temp_a) (let left:slot_n right:slot_t (let result_2:(!bin_and result_0 (!bin_not_equal (eval expr:left) (eval expr:right)) ) cont~[. result:result_2] ) ) ) ) ) positive:(let var_n:(!get_variant positive op) (let val:var_n (let result_3:(!bin_and result_0 (!bin_greater_than (eval_val value:val) 0) ) cont~[. result:result_3] ) ) ) ) ) ) }; def eval_val := {def expr:: names:[!Map !Str !Int] (case tags expr literal:(let var_t:(!get_variant literal expr) (let num:var_t num)) name:(let var_g:(!get_variant name expr) (let name:var_g (!get_index names name)) ) zero:(let var_w:(!get_variant zero expr) 0) ) }; def evaluate := {def expr:: names:[!Map !Str !Int] (let eval:{eval_val names:names} result:true (let body_:{body_ eval:eval} (let temp_t:(!for_loop body_ expr [. result:result]) (let result_4:(!get_slot result temp_t) result_4) ) ) ) }; def example := [list equals~[. left:name~"age" right:literal~55] positive~name~"count" ]; def example_value_1 := name~"age"; def example_value_2 := name~"count"; def example_value_3 := literal~55;
A date-time library
This is a comprehensive example of Tenet code and shows what it can do even within current limitations.
;; I wanted to put together an example tenet model based on an earlier project I had done ;; that implemented an organizer. That, of couse, depends pretty heavily on dates and times, ;; and I realized I'll need at least rudimentary support for it. Poking around, it seems ;; like a naive (in that it doesn't handle timezones) implementation of iso8601 gets you ;; 99% of what you need. It also stresses our ability to do math and such, even if the math ;; is suprisingly easy. Those monks were some clever bastards! ;; The two epochs here are posix, which most people are familiar with, and 'CE' meaning ;; Jan 1 of year 1 of the common era. Actual dates are implemented according to the ISO 8601 ;; standard, which is the proleptic Gregorian calendar, except that years are astronomical, ;; year +0001 corresponds with 1 AD, year 0000 corresponds with 1 BC, -0001 is 2 BC, etc. ;; Timezone support would just be adding more fields. ;; Define an epoch date; if we want to improve our date handling ;; methods later, they can be backwards compatible. type Iso8601Date = (year: Int, month: Int, day: Int) type Iso8601Time = (hour: Int, minute: Int, second: Int) type Duration = seconds ~ Int ;; type Iso8601Simple = Iso8601Date * Iso8601Time type Iso8601Simple = ( year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int, ) type DateTime = epoch_ce ~ Int | ;; 0001-Jan-01 = 0 epoch_posix ~ Int | ;; 1970-Jan-01 = 0 iso8601 ~ Iso8601Simple ;; ;; Datetime arithmetic ;; func dt_plus_dur(left: DateTime, right: Duration) { let dt_seconds = as_epoch_ce(left) let dur_seconds = as_dur_seconds(right) return epoch_ce ~ (dt_seconds + dur_seconds) } func dt_minus_dur(left: DateTime, right: Duration) { let dt_seconds = as_epoch_ce(left) let dur_seconds = as_dur_seconds(right) return epoch_ce ~ (dt_seconds - dur_seconds) } func dt_minus_dt(left:DateTime, right:DateTime) { let left_seconds = as_epoch_ce(left) let right_seconds = as_epoch_ce(right) return dur_seconds ~ (left_seconds - right_seconds) } func dur_plus_dur(left: Duration, right: Duration) { return dur_seconds ~ (as_dur_seconds(left) + as_dur_seconds(right)) } ;; ;; Conversion between forms. ;; func as_iso8601(value: DateTime) { switch value { case epoch_posix ~ epoch_seconds: return epoch_posix_to_iso8601(epoch_seconds) case epoch_ce ~ epoch_seconds: return epoch_ce_to_iso8601(seoncds) case iso8601 ~ datetime: return datetime } } func as_epoch_ce(value: DateTime) { switch value { case epoch_posix ~ epoch_seconds: return epoch_posix_to_ce(epoch_seconds) case epoch_ce ~ epoch_seconds: return epoch_seconds case iso8601 ~ datetime: return iso8601_to_epoch_ce(datetime) } } func as_epoch_posix(value: DateTime) { switch value { case epoch_posix ~ epoch_seconds: return epoch_seconds case epoch_ce ~ epoch_seconds: return epoch_ce_to_posix(epoch_seconds) case iso8601 ~ datetime: return iso8601_to_epoch_posix(datetime) } } func as_dur_seconds(value: Duration) { return value ? seconds } ;; ;; Internal DateTime conversion functions ;; func epoch_ce_to_iso8601(epoch_seconds: Int) { let (epoch_days, day_seconds) = split_epoch_seconds(epoch_seconds:) let (year, month, day) = epoch_days_to_year_month_day(epoch_days:) let (hour, minute, second) = seconds_to_hour_minute_second(day_seconds:) return (year:, month:, day:, hour:, minute:, second:) } func epoch_ce_to_posix(epoch_seconds: Int) { return epoch_seconds + ce_to_posix_offset } func epoch_posix_to_ce(epoch_seconds: Int) { return epoch_seconds - ce_to_posix_offset } func epoch_posix_to_iso8601(epoch_seconds: Int) { return epoch_ce_to_iso8601(epoch_posix_to_ce(epoch_seconds)) } func iso8601_to_epoch_ce(datetime: Iso8601Simple) { let (year, month, day, hour, minute, second) = datetime let epoch_days = year_month_day_to_epoch_days(year:, month:, day:) let day_seconds = time_of_day_to_seconds(hour:, minute:, second:) return combine_epoch_date(epoch_days:, day_seconds:) } func iso8601_to_epoch_posix(datetime: Iso8601Simple) { return epoch_ce_to_posix(iso8601_to_epoch_ce(datetime)) } let ce_to_posix_offset = 719162 * seconds_in_day ;; ;; Converting seconds from an epoch to epoch days and time of day ;; let seconds_in_day = 24 * 60 * 60 func split_epoch_seconds(epoch_seconds: Int) { let (div, mod) = div_mod(epoch_seconds, seconds_in_day) return (epoch_days: div, day_seconds: mod) } func split_epoch_ce(value: DateTime) { return split_epoch_seconds(as_epoch_ce(value)) } func combine_epoch_date(epoch_days: Int, day_seconds: Int) { return seconds_in_day * epoch_days + day_seconds } func seconds_to_hour_minute_second(seconds: Int) { let (div: minutes, mod: second) = div_mod(seconds, 60) let (div: hour, mod: minute) = div_mod(minutes, 60) return (hour:, minute:, second:) } func hour_minute_second_to_seconds(hour:Int, minute:Int, second:Int) { return 3600 * hour + 60 * minute + second } ;; ;; Converting days to y-m-d ;; ;; Note: year, month, day are all 0-based in calculations, but 1-based when exposed to the user. ;; Epochs are at 0 seconds. func month_start(month: Int, is_leap_year: Boolean) { if is_leap_year { let starts = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335] } else { let starts = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334] } return starts[month] } let days_in_quad_century = 146097 let days_in_century = 36524 let days_in_quad_year = 1461 let days_in_std_year = 365 func epoch_day_to_year_month_day(epoch_days: Int) -> Iso8601Date { let (div: quad_century, mod: quad_centry_day) = div_mod(epoch_days, days_in_quad_century) ;; The first, second and third century have 36524, ;; the fourth has 36525. let (div: century_p, mod: century_day) = div_mod(quad_century_day, days_in_century) ;; Thus, century_p may be 0, 1, 2, 3 or 4. ;; 4 is only reported on the last day of a "leap century." if century_p == 4 { let century = 3 } else { let century = century_p } let (div: quad_year, mod: quad_year_day) = div_mod(century_day, days_in_quad_year) ;; As before, year will be 4 on dec 31 of the leap year let (div: year_p, mod: year_day) = div_mod(quad_year_day, days_in_year) if year_p == 4 { let year = 3 } else { let year = year_p } ;; Real day of the year, allowing Dec 31 on a leap year if century_p == 4 or year_p == 4 { let year_day = 365 } else { let year_day = year_day_p } let is_leap_year = century_p == 4 or year_p > 2 or quad_year == 24 ;; This clever approximation came from python's datetime. let month_p = (year_day + 50) // 32 ;; We then get when the estimated month starts... let month_start_day = month_start(month:month_p, is_leap_year:) ;; ... which may be over by one if month_start_day > year_day { let month = month_p - 1 let month_start_day = month_start(month:, is_leap_year:) } else { let month = month_p } let month_day = year_day - month_start_day return (year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1) } ;; ;; Convert year-month-day to seconds after the epoch. ;; func year_month_day_to_epoch_days(year:Int, month:Int, day:Int) -> Index { let (div: quad_century, mod: quad_century_year) = div_mod(year - 1, 400) let (div: century, mod: century_year) = div_mod(quad_century_year, 100) let (div: quad_years, mod: quad_year) = div_mod(century_year, 4) let is_leap_year = quad_year == 3 and (century_year != 99 or century == 3) let month_start_day = month_start(month:, is_leap_year:) return (day - 1 + month_start_day + days_in_year * quad_year + days_in_quad_year * quad_years + days_in_century * century + days_in_quad_century * quad_century) } ;; ;; Month name functions ;; type Month = #jan | #feb | #mar | #apr | #may | #jun | #jul | #aug | #sep | #oct | #nov | #dec func month_from_index(index:Int) -> Month { return [#jan, #feb, #mar, #apr, #may, #jun, #jul, #aug, #sep, #oct, #nov, #dec][index - 1] } func index_of_month(month: Month) -> Int { return { #jan: 1, #feb: 2, #mar: 3, #apr: 4, #may: 5, #jun: 6, #jul: 7, #aug: 8, #sep: 9, #oct: 10, #nov: 11, #dec: 12 }[month] } ;; ;; Days of the week and business day calculations ;; type DayOfWeek = #sun | #mon | #tue | #wed | #thu | #fri | #sat func day_of_week_from_index(index: Int) -> DayOfWeek { return [#sun, #mon, #tue, #wed, #thu, #fri, #sat][index] } func index_of_day_of_week(day: DayOfWeek) -> Int { return { #sun: 0, #mon: 1, #tue: 2, #wed: 3, #thu: 4, #fri: 5, #sat: 6 }[day] } ;; Get the index of the day of week, where Sunday is 0. func day_of_week_of_epoch_days(epoch_days: Int) { ;; Jan 1, 0001 is a Saturday. return (6 + epoch_days) % 7 } func day_of_week_date_time(value: DateTime) { return day_of_week_of_epoch_days(split_epoch_ce(value).epoch_days) } func closest_business_day_forwards(value: Int) { switch day_of_week_of_epoch_days(epoch_days:value) { case 0: return value + 1 case 6: return value + 2 default: return value } } func closest_business_day_backwards(value: Int) { switch day_of_week_of_epoch_days(epoch_days:value) { case 0: return value - 2 case 6: return value - 1 default: return value } } ;; Increments a time by a number of business days, ensuring that the specific number of business days exist ;; within the interval. Incrementing by 0 will force the time to the open of the next business day. ;; open-seconds and close-seconds allow you to pick a window during which business is conducted, so it ;; handles "lunch hours" reasonably well. func business_days_after(point:DateTime, days_change:Int, open_seconds:Int, close_seconds:Int) { let (epoch_days, day_seconds) = split_epoch_ce(point) let after_hours = day_seconds > close_seconds let before_hours = day_seconds < open_seconds if after_hours { let day = 1 + epoch_days } else { let day = epoch_days } if after_hours or before_hours { let seconds_p = open_seconds } else { let seconds_p = day_seconds } ;; Bump our answer to the next business day if we've landed on a weekend. let day = closest_business_day_forwards(day) ;; Figure out how many weeks we're adding, and the remaining days let (div: weeks_change, mod: days_change) = div_mod(days_change, 5) let day += 7 * weeks_change + days_change ;; And now bump our answer to the next business day again. let day = closest_business_day_forwards_p(day) ;; And then combine the day and time. return epoch_ce ~ combine_epoch_date(epoch_days:day, day_seconds:seconds_p) } ;; Decrements a time by a number of business days, ensuring that the specific number of business days exist ;; within the interval. Decrementing by 0 will force the time to the close of the previous business day. ;; open-seconds and close-seconds allow you to pick a window during which business is conducted, so it ;; handles "lunch hours" reasonably well. func business_days_prior(point:DateTime, days_change:Int, open_seconds:Int, close_seconds:Int) { let (epoch_days, day_seconds) = split_epoch_ce(point) let after_hours = day_seconds > close_seconds let before_hours = day_seconds < open_seconds if before_hours { let day = epoch_days - 1 } else { let day = epoch_days } if after_hours or before_hours { let seconds_p = close_seconds } else { let seconds_p = day_seconds } ;; Bump our answer to the previous business day if we've landed on a weekend. let day = closest_business_day_backwards(day) ;; Figure out how many weeks we're subtracting, and the remaining days let (div: weeks_change, mod: days_change) = div_mod(days_change, 5) let day += 7 * weeks_change + days_change ;; And now bump our answer to the previous business day again. let day = closest_business_day_backwards(day) ;; And then combine the day and time. return epoch_ce ~ combine_epoch_date(epoch_days:day, day_seconds:seconds_p) }
type Iso8601Date := (year: Int, month: Int, day: Int); type Iso8601Time := (hour: Int, minute: Int, second: Int); type Duration := (seconds~Int); type Iso8601Simple := ( year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int ); type DateTime := (epoch_ce~Int | epoch_posix~Int | iso8601~Iso8601Simple); func dt_plus_dur(left: DateTime, right: Duration) { dt_seconds := as_epoch_ce(value: left); dur_seconds := as_dur_seconds(value: right); return epoch_ce ~ (dt_seconds + dur_seconds); } func dt_minus_dur(left: DateTime, right: Duration) { dt_seconds := as_epoch_ce(value: left); dur_seconds := as_dur_seconds(value: right); return epoch_ce ~ (dt_seconds - dur_seconds); } func dt_minus_dt(left: DateTime, right: DateTime) { left_seconds := as_epoch_ce(value: left); right_seconds := as_epoch_ce(value: right); return dur_seconds ~ (left_seconds - right_seconds); } func dur_plus_dur(left: Duration, right: Duration) { return dur_seconds ~ ( as_dur_seconds(value: left) + as_dur_seconds(value: right) ); } func as_iso8601(value: DateTime) { switch value { case epoch_posix ~ epoch_seconds: return epoch_posix_to_iso8601(value: epoch_seconds); case epoch_ce ~ epoch_seconds: return epoch_ce_to_iso8601(value: seoncds); case iso8601 ~ datetime: return datetime; } } func as_epoch_ce(value: DateTime) { switch value { case epoch_posix ~ epoch_seconds: return epoch_posix_to_ce(value: epoch_seconds); case epoch_ce ~ epoch_seconds: return epoch_seconds; case iso8601 ~ datetime: return iso8601_to_epoch_ce(value: datetime); } } func as_epoch_posix(value: DateTime) { switch value { case epoch_posix ~ epoch_seconds: return epoch_seconds; case epoch_ce ~ epoch_seconds: return epoch_ce_to_posix(value: epoch_seconds); case iso8601 ~ datetime: return iso8601_to_epoch_posix(value: datetime); } } func as_dur_seconds(value: Duration) { return value ? seconds; } func epoch_ce_to_iso8601(epoch_seconds: Int) { (epoch_days, day_seconds) := split_epoch_seconds(epoch_seconds:); (year, month, day) := epoch_days_to_year_month_day(epoch_days:); (hour, minute, second) := seconds_to_hour_minute_second(day_seconds:); return (year:, month:, day:, hour:, minute:, second:); } func epoch_ce_to_posix(epoch_seconds: Int) { return epoch_seconds + ce_to_posix_offset; } func epoch_posix_to_ce(epoch_seconds: Int) { return epoch_seconds - ce_to_posix_offset; } func epoch_posix_to_iso8601(epoch_seconds: Int) { return epoch_ce_to_iso8601(value: epoch_posix_to_ce(value: epoch_seconds)); } func iso8601_to_epoch_ce(datetime: Iso8601Simple) { (year, month, day, hour, minute, second) := datetime; epoch_days := year_month_day_to_epoch_days(year:, month:, day:); day_seconds := time_of_day_to_seconds(hour:, minute:, second:); return combine_epoch_date(epoch_days:, day_seconds:); } func iso8601_to_epoch_posix(datetime: Iso8601Simple) { return epoch_ce_to_posix(value: iso8601_to_epoch_ce(value: datetime)); } ce_to_posix_offset := 719162 * seconds_in_day; seconds_in_day := 24 * 60 * 60; func split_epoch_seconds(epoch_seconds: Int) { (div, mod) := div_mod(left: epoch_seconds, right: seconds_in_day); return (epoch_days: div, day_seconds: mod); } func split_epoch_ce(value: DateTime) { return split_epoch_seconds(value: as_epoch_ce(value:)); } func combine_epoch_date(epoch_days: Int, day_seconds: Int) { return seconds_in_day * epoch_days + day_seconds; } func seconds_to_hour_minute_second(seconds: Int) { (div : minutes, mod : second) := div_mod(left: seconds, right: 60); (div : hour, mod : minute) := div_mod(left: minutes, right: 60); return (hour:, minute:, second:); } func hour_minute_second_to_seconds(hour: Int, minute: Int, second: Int) { return 3600 * hour + 60 * minute + second; } func month_start(month: Int, is_leap_year: Boolean) { if is_leap_year { starts := [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]; } else { starts := [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; } return starts[month]; } days_in_quad_century := 146097; days_in_century := 36524; days_in_quad_year := 1461; days_in_std_year := 365; func epoch_day_to_year_month_day(epoch_days: Int) -> Iso8601Date { (div : quad_century, mod : quad_centry_day) := div_mod( left: epoch_days, right: days_in_quad_century ); (div : century_p, mod : century_day) := div_mod( left: quad_century_day, right: days_in_century ); if century_p == 4 { century := 3; } else { century := century_p; } (div : quad_year, mod : quad_year_day) := div_mod( left: century_day, right: days_in_quad_year ); (div : year_p, mod : year_day) := div_mod( left: quad_year_day, right: days_in_year ); if year_p == 4 { year := 3; } else { year := year_p; } if century_p == 4 or year_p == 4 { year_day := 365; } else { year_day := year_day_p; } is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); } else { month := month_p; } month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } func year_month_day_to_epoch_days( year: Int, month: Int, day: Int ) -> Index { (div : quad_century, mod : quad_century_year) := div_mod( left: year - 1, right: 400 ); (div : century, mod : century_year) := div_mod( left: quad_century_year, right: 100 ); (div : quad_years, mod : quad_year) := div_mod( left: century_year, right: 4 ); is_leap_year := quad_year == 3 and (century_year != 99 or century == 3); month_start_day := month_start(month:, is_leap_year:); return day - 1 + month_start_day + days_in_year * quad_year + days_in_quad_year * quad_years + days_in_century * century + days_in_quad_century * quad_century; } type Month := ( #jan | #feb | #mar | #apr | #may | #jun | #jul | #aug | #sep | #oct | #nov | #dec ); func month_from_index(index: Int) -> Month { return [ #jan, #feb, #mar, #apr, #may, #jun, #jul, #aug, #sep, #oct, #nov, #dec ][index - 1]; } func index_of_month(month: Month) -> Int { return { #jan:1, #feb:2, #mar:3, #apr:4, #may:5, #jun:6, #jul:7, #aug:8, #sep:9, #oct:10, #nov:11, #dec:12 }[month]; } type DayOfWeek := (#sun | #mon | #tue | #wed | #thu | #fri | #sat); func day_of_week_from_index(index: Int) -> DayOfWeek { return [#sun, #mon, #tue, #wed, #thu, #fri, #sat][index]; } func index_of_day_of_week(day: DayOfWeek) -> Int { return { #sun:0, #mon:1, #tue:2, #wed:3, #thu:4, #fri:5, #sat:6 }[day]; } func day_of_week_of_epoch_days(epoch_days: Int) { return (6 + epoch_days) % 7; } func day_of_week_date_time(value: DateTime) { return day_of_week_of_epoch_days(value: split_epoch_ce(value:).epoch_days); } func closest_business_day_forwards(value: Int) { switch day_of_week_of_epoch_days(epoch_days: value) { case 0: return value + 1; case 6: return value + 2; default: return value; } } func closest_business_day_backwards(value: Int) { switch day_of_week_of_epoch_days(epoch_days: value) { case 0: return value - 2; case 6: return value - 1; default: return value; } } func business_days_after( point: DateTime, days_change: Int, open_seconds: Int, close_seconds: Int ) { (epoch_days, day_seconds) := split_epoch_ce(value: point); after_hours := day_seconds > close_seconds; before_hours := day_seconds < open_seconds; if after_hours { day := 1 + epoch_days; } else { day := epoch_days; } if after_hours or before_hours { seconds_p := open_seconds; } else { seconds_p := day_seconds; } day := closest_business_day_forwards(value: day); (div : weeks_change, mod : days_change) := div_mod( left: days_change, right: 5 ); day += 7 * weeks_change + days_change; day := closest_business_day_forwards_p(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } func business_days_prior( point: DateTime, days_change: Int, open_seconds: Int, close_seconds: Int ) { (epoch_days, day_seconds) := split_epoch_ce(value: point); after_hours := day_seconds > close_seconds; before_hours := day_seconds < open_seconds; if before_hours { day := epoch_days - 1; } else { day := epoch_days; } if after_hours or before_hours { seconds_p := close_seconds; } else { seconds_p := day_seconds; } day := closest_business_day_backwards(value: day); (div : weeks_change, mod : days_change) := div_mod( left: days_change, right: 5 ); day += 7 * weeks_change + days_change; day := closest_business_day_backwards(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); }
type Iso8601Date := (year: Int, month: Int, day: Int); type Iso8601Time := (hour: Int, minute: Int, second: Int); type Duration := (seconds~Int); type Iso8601Simple := ( year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int ); type DateTime := (epoch_ce~Int | epoch_posix~Int | iso8601~Iso8601Simple); func dt_plus_dur(left: DateTime, right: Duration) { dt_seconds := as_epoch_ce(value: left); dur_seconds := as_dur_seconds(value: right); return epoch_ce ~ (dt_seconds + dur_seconds); } func dt_minus_dur(left: DateTime, right: Duration) { dt_seconds := as_epoch_ce(value: left); dur_seconds := as_dur_seconds(value: right); return epoch_ce ~ (dt_seconds - dur_seconds); } func dt_minus_dt(left: DateTime, right: DateTime) { left_seconds := as_epoch_ce(value: left); right_seconds := as_epoch_ce(value: right); return dur_seconds ~ (left_seconds - right_seconds); } func dur_plus_dur(left: Duration, right: Duration) { return dur_seconds ~ ( as_dur_seconds(value: left) + as_dur_seconds(value: right) ); } func as_iso8601(value: DateTime) { switch value { case epoch_posix ~ epoch_seconds: return epoch_posix_to_iso8601(value: epoch_seconds); case epoch_ce ~ epoch_seconds: return epoch_ce_to_iso8601(value: seoncds); case iso8601 ~ datetime: return datetime; } } func as_epoch_ce(value: DateTime) { switch value { case epoch_posix ~ epoch_seconds: return epoch_posix_to_ce(value: epoch_seconds); case epoch_ce ~ epoch_seconds: return epoch_seconds; case iso8601 ~ datetime: return iso8601_to_epoch_ce(value: datetime); } } func as_epoch_posix(value: DateTime) { switch value { case epoch_posix ~ epoch_seconds: return epoch_seconds; case epoch_ce ~ epoch_seconds: return epoch_ce_to_posix(value: epoch_seconds); case iso8601 ~ datetime: return iso8601_to_epoch_posix(value: datetime); } } func as_dur_seconds(value: Duration) { return value ? seconds; } func epoch_ce_to_iso8601(epoch_seconds: Int) { (epoch_days, day_seconds) := split_epoch_seconds(epoch_seconds:); (year, month, day) := epoch_days_to_year_month_day(epoch_days:); (hour, minute, second) := seconds_to_hour_minute_second(day_seconds:); return (year:, month:, day:, hour:, minute:, second:); } func epoch_ce_to_posix(epoch_seconds: Int) { return epoch_seconds + ce_to_posix_offset; } func epoch_posix_to_ce(epoch_seconds: Int) { return epoch_seconds - ce_to_posix_offset; } func epoch_posix_to_iso8601(epoch_seconds: Int) { return epoch_ce_to_iso8601(value: epoch_posix_to_ce(value: epoch_seconds)); } func iso8601_to_epoch_ce(datetime: Iso8601Simple) { (year, month, day, hour, minute, second) := datetime; epoch_days := year_month_day_to_epoch_days(year:, month:, day:); day_seconds := time_of_day_to_seconds(hour:, minute:, second:); return combine_epoch_date(epoch_days:, day_seconds:); } func iso8601_to_epoch_posix(datetime: Iso8601Simple) { return epoch_ce_to_posix(value: iso8601_to_epoch_ce(value: datetime)); } ce_to_posix_offset := 719162 * seconds_in_day; seconds_in_day := 24 * 60 * 60; func split_epoch_seconds(epoch_seconds: Int) { (div, mod) := div_mod(left: epoch_seconds, right: seconds_in_day); return (epoch_days: div, day_seconds: mod); } func split_epoch_ce(value: DateTime) { return split_epoch_seconds(value: as_epoch_ce(value:)); } func combine_epoch_date(epoch_days: Int, day_seconds: Int) { return seconds_in_day * epoch_days + day_seconds; } func seconds_to_hour_minute_second(seconds: Int) { (div : minutes, mod : second) := div_mod(left: seconds, right: 60); (div : hour, mod : minute) := div_mod(left: minutes, right: 60); return (hour:, minute:, second:); } func hour_minute_second_to_seconds(hour: Int, minute: Int, second: Int) { return 3600 * hour + 60 * minute + second; } func month_start(month: Int, is_leap_year: Boolean) { if is_leap_year { starts := [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]; return starts[month]; } else { starts := [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; return starts[month]; } } days_in_quad_century := 146097; days_in_century := 36524; days_in_quad_year := 1461; days_in_std_year := 365; func epoch_day_to_year_month_day(epoch_days: Int) -> Iso8601Date { (div : quad_century, mod : quad_centry_day) := div_mod( left: epoch_days, right: days_in_quad_century ); (div : century_p, mod : century_day) := div_mod( left: quad_century_day, right: days_in_century ); if century_p == 4 { century := 3; (div : quad_year, mod : quad_year_day) := div_mod( left: century_day, right: days_in_quad_year ); (div : year_p, mod : year_day) := div_mod( left: quad_year_day, right: days_in_year ); if year_p == 4 { year := 3; if century_p == 4 or year_p == 4 { year_day := 365; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } else { year_day := year_day_p; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } } else { year := year_p; if century_p == 4 or year_p == 4 { year_day := 365; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } else { year_day := year_day_p; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } } } else { century := century_p; (div : quad_year, mod : quad_year_day) := div_mod( left: century_day, right: days_in_quad_year ); (div : year_p, mod : year_day) := div_mod( left: quad_year_day, right: days_in_year ); if year_p == 4 { year := 3; if century_p == 4 or year_p == 4 { year_day := 365; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } else { year_day := year_day_p; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } } else { year := year_p; if century_p == 4 or year_p == 4 { year_day := 365; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } else { year_day := year_day_p; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } } } } func year_month_day_to_epoch_days( year: Int, month: Int, day: Int ) -> Index { (div : quad_century, mod : quad_century_year) := div_mod( left: year - 1, right: 400 ); (div : century, mod : century_year) := div_mod( left: quad_century_year, right: 100 ); (div : quad_years, mod : quad_year) := div_mod( left: century_year, right: 4 ); is_leap_year := quad_year == 3 and (century_year != 99 or century == 3); month_start_day := month_start(month:, is_leap_year:); return day - 1 + month_start_day + days_in_year * quad_year + days_in_quad_year * quad_years + days_in_century * century + days_in_quad_century * quad_century; } type Month := ( #jan | #feb | #mar | #apr | #may | #jun | #jul | #aug | #sep | #oct | #nov | #dec ); func month_from_index(index: Int) -> Month { return [ #jan, #feb, #mar, #apr, #may, #jun, #jul, #aug, #sep, #oct, #nov, #dec ][index - 1]; } func index_of_month(month: Month) -> Int { return { #jan:1, #feb:2, #mar:3, #apr:4, #may:5, #jun:6, #jul:7, #aug:8, #sep:9, #oct:10, #nov:11, #dec:12 }[month]; } type DayOfWeek := (#sun | #mon | #tue | #wed | #thu | #fri | #sat); func day_of_week_from_index(index: Int) -> DayOfWeek { return [#sun, #mon, #tue, #wed, #thu, #fri, #sat][index]; } func index_of_day_of_week(day: DayOfWeek) -> Int { return { #sun:0, #mon:1, #tue:2, #wed:3, #thu:4, #fri:5, #sat:6 }[day]; } func day_of_week_of_epoch_days(epoch_days: Int) { return (6 + epoch_days) % 7; } func day_of_week_date_time(value: DateTime) { return day_of_week_of_epoch_days(value: split_epoch_ce(value:).epoch_days); } func closest_business_day_forwards(value: Int) { switch day_of_week_of_epoch_days(epoch_days: value) { case 0: return value + 1; case 6: return value + 2; default: return value; } } func closest_business_day_backwards(value: Int) { switch day_of_week_of_epoch_days(epoch_days: value) { case 0: return value - 2; case 6: return value - 1; default: return value; } } func business_days_after( point: DateTime, days_change: Int, open_seconds: Int, close_seconds: Int ) { (epoch_days, day_seconds) := split_epoch_ce(value: point); after_hours := day_seconds > close_seconds; before_hours := day_seconds < open_seconds; if after_hours { day := 1 + epoch_days; if after_hours or before_hours { seconds_p := open_seconds; day := closest_business_day_forwards(value: day); (div : weeks_change, mod : days_change) := div_mod( left: days_change, right: 5 ); day += 7 * weeks_change + days_change; day := closest_business_day_forwards_p(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } else { seconds_p := day_seconds; day := closest_business_day_forwards(value: day); (div : weeks_change, mod : days_change) := div_mod( left: days_change, right: 5 ); day += 7 * weeks_change + days_change; day := closest_business_day_forwards_p(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } } else { day := epoch_days; if after_hours or before_hours { seconds_p := open_seconds; day := closest_business_day_forwards(value: day); (div : weeks_change, mod : days_change) := div_mod( left: days_change, right: 5 ); day += 7 * weeks_change + days_change; day := closest_business_day_forwards_p(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } else { seconds_p := day_seconds; day := closest_business_day_forwards(value: day); (div : weeks_change, mod : days_change) := div_mod( left: days_change, right: 5 ); day += 7 * weeks_change + days_change; day := closest_business_day_forwards_p(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } } } func business_days_prior( point: DateTime, days_change: Int, open_seconds: Int, close_seconds: Int ) { (epoch_days, day_seconds) := split_epoch_ce(value: point); after_hours := day_seconds > close_seconds; before_hours := day_seconds < open_seconds; if before_hours { day := epoch_days - 1; if after_hours or before_hours { seconds_p := close_seconds; day := closest_business_day_backwards(value: day); (div : weeks_change, mod : days_change) := div_mod( left: days_change, right: 5 ); day += 7 * weeks_change + days_change; day := closest_business_day_backwards(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } else { seconds_p := day_seconds; day := closest_business_day_backwards(value: day); (div : weeks_change, mod : days_change) := div_mod( left: days_change, right: 5 ); day += 7 * weeks_change + days_change; day := closest_business_day_backwards(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } } else { day := epoch_days; if after_hours or before_hours { seconds_p := close_seconds; day := closest_business_day_backwards(value: day); (div : weeks_change, mod : days_change) := div_mod( left: days_change, right: 5 ); day += 7 * weeks_change + days_change; day := closest_business_day_backwards(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } else { seconds_p := day_seconds; day := closest_business_day_backwards(value: day); (div : weeks_change, mod : days_change) := div_mod( left: days_change, right: 5 ); day += 7 * weeks_change + days_change; day := closest_business_day_backwards(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } } }
type Iso8601Date := (year: Int, month: Int, day: Int); type Iso8601Time := (hour: Int, minute: Int, second: Int); type Duration := (seconds~Int); type Iso8601Simple := ( year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int ); type DateTime := (epoch_ce~Int | epoch_posix~Int | iso8601~Iso8601Simple); func dt_plus_dur(left: DateTime, right: Duration) { dt_seconds := as_epoch_ce(value: left); dur_seconds := as_dur_seconds(value: right); return epoch_ce ~ (dt_seconds + dur_seconds); } func dt_minus_dur(left: DateTime, right: Duration) { dt_seconds := as_epoch_ce(value: left); dur_seconds := as_dur_seconds(value: right); return epoch_ce ~ (dt_seconds - dur_seconds); } func dt_minus_dt(left: DateTime, right: DateTime) { left_seconds := as_epoch_ce(value: left); right_seconds := as_epoch_ce(value: right); return dur_seconds ~ (left_seconds - right_seconds); } func dur_plus_dur(left: Duration, right: Duration) { return dur_seconds ~ ( as_dur_seconds(value: left) + as_dur_seconds(value: right) ); } func as_iso8601(value: DateTime) { switch value { case epoch_posix ~ *: epoch_seconds := value ? epoch_posix; return epoch_posix_to_iso8601(value: epoch_seconds); case epoch_ce ~ *: epoch_seconds := value ? epoch_ce; return epoch_ce_to_iso8601(value: seoncds); case iso8601 ~ *: datetime := value ? iso8601; return datetime; } } func as_epoch_ce(value: DateTime) { switch value { case epoch_posix ~ *: epoch_seconds := value ? epoch_posix; return epoch_posix_to_ce(value: epoch_seconds); case epoch_ce ~ *: epoch_seconds := value ? epoch_ce; return epoch_seconds; case iso8601 ~ *: datetime := value ? iso8601; return iso8601_to_epoch_ce(value: datetime); } } func as_epoch_posix(value: DateTime) { switch value { case epoch_posix ~ *: epoch_seconds := value ? epoch_posix; return epoch_seconds; case epoch_ce ~ *: epoch_seconds := value ? epoch_ce; return epoch_ce_to_posix(value: epoch_seconds); case iso8601 ~ *: datetime := value ? iso8601; return iso8601_to_epoch_posix(value: datetime); } } func as_dur_seconds(value: Duration) { return value ? seconds; } func epoch_ce_to_iso8601(epoch_seconds: Int) { (epoch_days, day_seconds) := split_epoch_seconds(epoch_seconds:); (year, month, day) := epoch_days_to_year_month_day(epoch_days:); (hour, minute, second) := seconds_to_hour_minute_second(day_seconds:); return (year:, month:, day:, hour:, minute:, second:); } func epoch_ce_to_posix(epoch_seconds: Int) { return epoch_seconds + ce_to_posix_offset; } func epoch_posix_to_ce(epoch_seconds: Int) { return epoch_seconds - ce_to_posix_offset; } func epoch_posix_to_iso8601(epoch_seconds: Int) { return epoch_ce_to_iso8601(value: epoch_posix_to_ce(value: epoch_seconds)); } func iso8601_to_epoch_ce(datetime: Iso8601Simple) { (year, month, day, hour, minute, second) := datetime; epoch_days := year_month_day_to_epoch_days(year:, month:, day:); day_seconds := time_of_day_to_seconds(hour:, minute:, second:); return combine_epoch_date(epoch_days:, day_seconds:); } func iso8601_to_epoch_posix(datetime: Iso8601Simple) { return epoch_ce_to_posix(value: iso8601_to_epoch_ce(value: datetime)); } ce_to_posix_offset := 719162 * seconds_in_day; seconds_in_day := 24 * 60 * 60; func split_epoch_seconds(epoch_seconds: Int) { (div, mod) := div_mod(left: epoch_seconds, right: seconds_in_day); return (epoch_days: div, day_seconds: mod); } func split_epoch_ce(value: DateTime) { return split_epoch_seconds(value: as_epoch_ce(value:)); } func combine_epoch_date(epoch_days: Int, day_seconds: Int) { return seconds_in_day * epoch_days + day_seconds; } func seconds_to_hour_minute_second(seconds: Int) { (div : minutes, mod : second) := div_mod(left: seconds, right: 60); (div : hour, mod : minute) := div_mod(left: minutes, right: 60); return (hour:, minute:, second:); } func hour_minute_second_to_seconds(hour: Int, minute: Int, second: Int) { return 3600 * hour + 60 * minute + second; } func month_start(month: Int, is_leap_year: Boolean) { if is_leap_year { starts := [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]; return starts[month]; } else { starts := [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; return starts[month]; } } days_in_quad_century := 146097; days_in_century := 36524; days_in_quad_year := 1461; days_in_std_year := 365; func epoch_day_to_year_month_day(epoch_days: Int) -> Iso8601Date { (div : quad_century, mod : quad_centry_day) := div_mod( left: epoch_days, right: days_in_quad_century ); (div : century_p, mod : century_day) := div_mod( left: quad_century_day, right: days_in_century ); if century_p == 4 { century := 3; (div : quad_year, mod : quad_year_day) := div_mod( left: century_day, right: days_in_quad_year ); (div : year_p, mod : year_day) := div_mod( left: quad_year_day, right: days_in_year ); if year_p == 4 { year := 3; if century_p == 4 or year_p == 4 { year_day := 365; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } else { year_day := year_day_p; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } } else { year := year_p; if century_p == 4 or year_p == 4 { year_day := 365; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } else { year_day := year_day_p; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } } } else { century := century_p; (div : quad_year, mod : quad_year_day) := div_mod( left: century_day, right: days_in_quad_year ); (div : year_p, mod : year_day) := div_mod( left: quad_year_day, right: days_in_year ); if year_p == 4 { year := 3; if century_p == 4 or year_p == 4 { year_day := 365; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } else { year_day := year_day_p; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } } else { year := year_p; if century_p == 4 or year_p == 4 { year_day := 365; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } else { year_day := year_day_p; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } } } } func year_month_day_to_epoch_days( year: Int, month: Int, day: Int ) -> Index { (div : quad_century, mod : quad_century_year) := div_mod( left: year - 1, right: 400 ); (div : century, mod : century_year) := div_mod( left: quad_century_year, right: 100 ); (div : quad_years, mod : quad_year) := div_mod( left: century_year, right: 4 ); is_leap_year := quad_year == 3 and (century_year != 99 or century == 3); month_start_day := month_start(month:, is_leap_year:); return day - 1 + month_start_day + days_in_year * quad_year + days_in_quad_year * quad_years + days_in_century * century + days_in_quad_century * quad_century; } type Month := ( #jan | #feb | #mar | #apr | #may | #jun | #jul | #aug | #sep | #oct | #nov | #dec ); func month_from_index(index: Int) -> Month { return [ #jan, #feb, #mar, #apr, #may, #jun, #jul, #aug, #sep, #oct, #nov, #dec ][index - 1]; } func index_of_month(month: Month) -> Int { return { #jan:1, #feb:2, #mar:3, #apr:4, #may:5, #jun:6, #jul:7, #aug:8, #sep:9, #oct:10, #nov:11, #dec:12 }[month]; } type DayOfWeek := (#sun | #mon | #tue | #wed | #thu | #fri | #sat); func day_of_week_from_index(index: Int) -> DayOfWeek { return [#sun, #mon, #tue, #wed, #thu, #fri, #sat][index]; } func index_of_day_of_week(day: DayOfWeek) -> Int { return { #sun:0, #mon:1, #tue:2, #wed:3, #thu:4, #fri:5, #sat:6 }[day]; } func day_of_week_of_epoch_days(epoch_days: Int) { return (6 + epoch_days) % 7; } func day_of_week_date_time(value: DateTime) { return day_of_week_of_epoch_days(value: split_epoch_ce(value:).epoch_days); } func closest_business_day_forwards(value: Int) { switch day_of_week_of_epoch_days(epoch_days: value) { case 0: return value + 1; case 6: return value + 2; default: return value; } } func closest_business_day_backwards(value: Int) { switch day_of_week_of_epoch_days(epoch_days: value) { case 0: return value - 2; case 6: return value - 1; default: return value; } } func business_days_after( point: DateTime, days_change: Int, open_seconds: Int, close_seconds: Int ) { (epoch_days, day_seconds) := split_epoch_ce(value: point); after_hours := day_seconds > close_seconds; before_hours := day_seconds < open_seconds; if after_hours { day := 1 + epoch_days; if after_hours or before_hours { seconds_p := open_seconds; day := closest_business_day_forwards(value: day); (div : weeks_change, mod : days_change) := div_mod( left: days_change, right: 5 ); day += 7 * weeks_change + days_change; day := closest_business_day_forwards_p(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } else { seconds_p := day_seconds; day := closest_business_day_forwards(value: day); (div : weeks_change, mod : days_change) := div_mod( left: days_change, right: 5 ); day += 7 * weeks_change + days_change; day := closest_business_day_forwards_p(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } } else { day := epoch_days; if after_hours or before_hours { seconds_p := open_seconds; day := closest_business_day_forwards(value: day); (div : weeks_change, mod : days_change) := div_mod( left: days_change, right: 5 ); day += 7 * weeks_change + days_change; day := closest_business_day_forwards_p(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } else { seconds_p := day_seconds; day := closest_business_day_forwards(value: day); (div : weeks_change, mod : days_change) := div_mod( left: days_change, right: 5 ); day += 7 * weeks_change + days_change; day := closest_business_day_forwards_p(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } } } func business_days_prior( point: DateTime, days_change: Int, open_seconds: Int, close_seconds: Int ) { (epoch_days, day_seconds) := split_epoch_ce(value: point); after_hours := day_seconds > close_seconds; before_hours := day_seconds < open_seconds; if before_hours { day := epoch_days - 1; if after_hours or before_hours { seconds_p := close_seconds; day := closest_business_day_backwards(value: day); (div : weeks_change, mod : days_change) := div_mod( left: days_change, right: 5 ); day += 7 * weeks_change + days_change; day := closest_business_day_backwards(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } else { seconds_p := day_seconds; day := closest_business_day_backwards(value: day); (div : weeks_change, mod : days_change) := div_mod( left: days_change, right: 5 ); day += 7 * weeks_change + days_change; day := closest_business_day_backwards(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } } else { day := epoch_days; if after_hours or before_hours { seconds_p := close_seconds; day := closest_business_day_backwards(value: day); (div : weeks_change, mod : days_change) := div_mod( left: days_change, right: 5 ); day += 7 * weeks_change + days_change; day := closest_business_day_backwards(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } else { seconds_p := day_seconds; day := closest_business_day_backwards(value: day); (div : weeks_change, mod : days_change) := div_mod( left: days_change, right: 5 ); day += 7 * weeks_change + days_change; day := closest_business_day_backwards(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } } }
type Iso8601Date := (year: Int, month: Int, day: Int); type Iso8601Time := (hour: Int, minute: Int, second: Int); type Duration := (seconds~Int); type Iso8601Simple := ( year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int ); type DateTime := (epoch_ce~Int | epoch_posix~Int | iso8601~Iso8601Simple); func dt_plus_dur(left: DateTime, right: Duration) { dt_seconds := as_epoch_ce(value: left); dur_seconds := as_dur_seconds(value: right); return epoch_ce ~ (dt_seconds + dur_seconds); } func dt_minus_dur(left: DateTime, right: Duration) { dt_seconds := as_epoch_ce(value: left); dur_seconds := as_dur_seconds(value: right); return epoch_ce ~ (dt_seconds - dur_seconds); } func dt_minus_dt(left: DateTime, right: DateTime) { left_seconds := as_epoch_ce(value: left); right_seconds := as_epoch_ce(value: right); return dur_seconds ~ (left_seconds - right_seconds); } func dur_plus_dur(left: Duration, right: Duration) { return dur_seconds ~ ( as_dur_seconds(value: left) + as_dur_seconds(value: right) ); } func as_iso8601(value: DateTime) { switch value { case epoch_posix ~ *: epoch_seconds := value ? epoch_posix; return epoch_posix_to_iso8601(value: epoch_seconds); case epoch_ce ~ *: epoch_seconds := value ? epoch_ce; return epoch_ce_to_iso8601(value: seoncds); case iso8601 ~ *: datetime := value ? iso8601; return datetime; } } func as_epoch_ce(value: DateTime) { switch value { case epoch_posix ~ *: epoch_seconds := value ? epoch_posix; return epoch_posix_to_ce(value: epoch_seconds); case epoch_ce ~ *: epoch_seconds := value ? epoch_ce; return epoch_seconds; case iso8601 ~ *: datetime := value ? iso8601; return iso8601_to_epoch_ce(value: datetime); } } func as_epoch_posix(value: DateTime) { switch value { case epoch_posix ~ *: epoch_seconds := value ? epoch_posix; return epoch_seconds; case epoch_ce ~ *: epoch_seconds := value ? epoch_ce; return epoch_ce_to_posix(value: epoch_seconds); case iso8601 ~ *: datetime := value ? iso8601; return iso8601_to_epoch_posix(value: datetime); } } func as_dur_seconds(value: Duration) { return value ? seconds; } func epoch_ce_to_iso8601(epoch_seconds: Int) { temp_n := split_epoch_seconds(epoch_seconds:); epoch_days := temp_n.epoch_days; day_seconds := temp_n.day_seconds; temp_a := epoch_days_to_year_month_day(epoch_days:); year := temp_a.year; month := temp_a.month; day := temp_a.day; temp_ := seconds_to_hour_minute_second(day_seconds:); hour := temp_.hour; minute := temp_.minute; second := temp_.second; return (year:, month:, day:, hour:, minute:, second:); } func epoch_ce_to_posix(epoch_seconds: Int) { return epoch_seconds + ce_to_posix_offset; } func epoch_posix_to_ce(epoch_seconds: Int) { return epoch_seconds - ce_to_posix_offset; } func epoch_posix_to_iso8601(epoch_seconds: Int) { return epoch_ce_to_iso8601(value: epoch_posix_to_ce(value: epoch_seconds)); } func iso8601_to_epoch_ce(datetime: Iso8601Simple) { temp_t := datetime; year := temp_t.year; month := temp_t.month; day := temp_t.day; hour := temp_t.hour; minute := temp_t.minute; second := temp_t.second; epoch_days := year_month_day_to_epoch_days(year:, month:, day:); day_seconds := time_of_day_to_seconds(hour:, minute:, second:); return combine_epoch_date(epoch_days:, day_seconds:); } func iso8601_to_epoch_posix(datetime: Iso8601Simple) { return epoch_ce_to_posix(value: iso8601_to_epoch_ce(value: datetime)); } ce_to_posix_offset := 719162 * seconds_in_day; seconds_in_day := 24 * 60 * 60; func split_epoch_seconds(epoch_seconds: Int) { temp_g := div_mod(left: epoch_seconds, right: seconds_in_day); div := temp_g.div; mod := temp_g.mod; return (epoch_days: div, day_seconds: mod); } func split_epoch_ce(value: DateTime) { return split_epoch_seconds(value: as_epoch_ce(value:)); } func combine_epoch_date(epoch_days: Int, day_seconds: Int) { return seconds_in_day * epoch_days + day_seconds; } func seconds_to_hour_minute_second(seconds: Int) { temp_j := div_mod(left: seconds, right: 60); minutes := temp_j.div; second := temp_j.mod; temp_w := div_mod(left: minutes, right: 60); hour := temp_w.div; minute := temp_w.mod; return (hour:, minute:, second:); } func hour_minute_second_to_seconds(hour: Int, minute: Int, second: Int) { return 3600 * hour + 60 * minute + second; } func month_start(month: Int, is_leap_year: Boolean) { if is_leap_year { starts := [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]; return starts[month]; } else { starts := [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; return starts[month]; } } days_in_quad_century := 146097; days_in_century := 36524; days_in_quad_year := 1461; days_in_std_year := 365; func epoch_day_to_year_month_day(epoch_days: Int) -> Iso8601Date { temp_e := div_mod(left: epoch_days, right: days_in_quad_century); quad_century := temp_e.div; quad_centry_day := temp_e.mod; temp_r := div_mod(left: quad_century_day, right: days_in_century); century_p := temp_r.div; century_day := temp_r.mod; if century_p == 4 { century := 3; temp_d := div_mod(left: century_day, right: days_in_quad_year); quad_year := temp_d.div; quad_year_day := temp_d.mod; temp_q := div_mod(left: quad_year_day, right: days_in_year); year_p := temp_q.div; year_day := temp_q.mod; if year_p == 4 { year := 3; if century_p == 4 or year_p == 4 { year_day := 365; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } else { year_day := year_day_p; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } } else { year := year_p; if century_p == 4 or year_p == 4 { year_day := 365; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } else { year_day := year_day_p; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } } } else { century := century_p; temp_l := div_mod(left: century_day, right: days_in_quad_year); quad_year := temp_l.div; quad_year_day := temp_l.mod; temp_y := div_mod(left: quad_year_day, right: days_in_year); year_p := temp_y.div; year_day := temp_y.mod; if year_p == 4 { year := 3; if century_p == 4 or year_p == 4 { year_day := 365; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } else { year_day := year_day_p; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } } else { year := year_p; if century_p == 4 or year_p == 4 { year_day := 365; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } else { year_day := year_day_p; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day { month := month_p - 1; month_start_day := month_start(month:, is_leap_year:); month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } } } } func year_month_day_to_epoch_days( year: Int, month: Int, day: Int ) -> Index { temp_o := div_mod(left: year - 1, right: 400); quad_century := temp_o.div; quad_century_year := temp_o.mod; temp_h := div_mod(left: quad_century_year, right: 100); century := temp_h.div; century_year := temp_h.mod; temp_u := div_mod(left: century_year, right: 4); quad_years := temp_u.div; quad_year := temp_u.mod; is_leap_year := quad_year == 3 and (century_year != 99 or century == 3); month_start_day := month_start(month:, is_leap_year:); return day - 1 + month_start_day + days_in_year * quad_year + days_in_quad_year * quad_years + days_in_century * century + days_in_quad_century * quad_century; } type Month := ( #jan | #feb | #mar | #apr | #may | #jun | #jul | #aug | #sep | #oct | #nov | #dec ); func month_from_index(index: Int) -> Month { return [ #jan, #feb, #mar, #apr, #may, #jun, #jul, #aug, #sep, #oct, #nov, #dec ][index - 1]; } func index_of_month(month: Month) -> Int { return { #jan:1, #feb:2, #mar:3, #apr:4, #may:5, #jun:6, #jul:7, #aug:8, #sep:9, #oct:10, #nov:11, #dec:12 }[month]; } type DayOfWeek := (#sun | #mon | #tue | #wed | #thu | #fri | #sat); func day_of_week_from_index(index: Int) -> DayOfWeek { return [#sun, #mon, #tue, #wed, #thu, #fri, #sat][index]; } func index_of_day_of_week(day: DayOfWeek) -> Int { return { #sun:0, #mon:1, #tue:2, #wed:3, #thu:4, #fri:5, #sat:6 }[day]; } func day_of_week_of_epoch_days(epoch_days: Int) { return (6 + epoch_days) % 7; } func day_of_week_date_time(value: DateTime) { return day_of_week_of_epoch_days(value: split_epoch_ce(value:).epoch_days); } func closest_business_day_forwards(value: Int) { switch day_of_week_of_epoch_days(epoch_days: value) { case 0: return value + 1; case 6: return value + 2; default: return value; } } func closest_business_day_backwards(value: Int) { switch day_of_week_of_epoch_days(epoch_days: value) { case 0: return value - 2; case 6: return value - 1; default: return value; } } func business_days_after( point: DateTime, days_change: Int, open_seconds: Int, close_seconds: Int ) { temp_f := split_epoch_ce(value: point); epoch_days := temp_f.epoch_days; day_seconds := temp_f.day_seconds; after_hours := day_seconds > close_seconds; before_hours := day_seconds < open_seconds; if after_hours { day := 1 + epoch_days; if after_hours or before_hours { seconds_p := open_seconds; day := closest_business_day_forwards(value: day); temp_b := div_mod(left: days_change, right: 5); weeks_change := temp_b.div; days_change := temp_b.mod; day := day + 7 * weeks_change + days_change; day := closest_business_day_forwards_p(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } else { seconds_p := day_seconds; day := closest_business_day_forwards(value: day); temp_z := div_mod(left: days_change, right: 5); weeks_change := temp_z.div; days_change := temp_z.mod; day := day + 7 * weeks_change + days_change; day := closest_business_day_forwards_p(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } } else { day := epoch_days; if after_hours or before_hours { seconds_p := open_seconds; day := closest_business_day_forwards(value: day); temp_m := div_mod(left: days_change, right: 5); weeks_change := temp_m.div; days_change := temp_m.mod; day := day + 7 * weeks_change + days_change; day := closest_business_day_forwards_p(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } else { seconds_p := day_seconds; day := closest_business_day_forwards(value: day); temp_s := div_mod(left: days_change, right: 5); weeks_change := temp_s.div; days_change := temp_s.mod; day := day + 7 * weeks_change + days_change; day := closest_business_day_forwards_p(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } } } func business_days_prior( point: DateTime, days_change: Int, open_seconds: Int, close_seconds: Int ) { temp_x := split_epoch_ce(value: point); epoch_days := temp_x.epoch_days; day_seconds := temp_x.day_seconds; after_hours := day_seconds > close_seconds; before_hours := day_seconds < open_seconds; if before_hours { day := epoch_days - 1; if after_hours or before_hours { seconds_p := close_seconds; day := closest_business_day_backwards(value: day); temp_v := div_mod(left: days_change, right: 5); weeks_change := temp_v.div; days_change := temp_v.mod; day := day + 7 * weeks_change + days_change; day := closest_business_day_backwards(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } else { seconds_p := day_seconds; day := closest_business_day_backwards(value: day); temp_i := div_mod(left: days_change, right: 5); weeks_change := temp_i.div; days_change := temp_i.mod; day := day + 7 * weeks_change + days_change; day := closest_business_day_backwards(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } } else { day := epoch_days; if after_hours or before_hours { seconds_p := close_seconds; day := closest_business_day_backwards(value: day); temp_p := div_mod(left: days_change, right: 5); weeks_change := temp_p.div; days_change := temp_p.mod; day := day + 7 * weeks_change + days_change; day := closest_business_day_backwards(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } else { seconds_p := day_seconds; day := closest_business_day_backwards(value: day); temp_c := div_mod(left: days_change, right: 5); weeks_change := temp_c.div; days_change := temp_c.mod; day := day + 7 * weeks_change + days_change; day := closest_business_day_backwards(value: day); return epoch_ce ~ combine_epoch_date( epoch_days: day, day_seconds: seconds_p ); } } }
type Iso8601Date := (year: Int, month: Int, day: Int); type Iso8601Time := (hour: Int, minute: Int, second: Int); type Duration := (seconds~Int); type Iso8601Simple := ( year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int ); type DateTime := (epoch_ce~Int | epoch_posix~Int | iso8601~Iso8601Simple); func dt_plus_dur(left: DateTime, right: Duration) { dt_seconds := as_epoch_ce(value: left); dur_seconds := as_dur_seconds(value: right); return epoch_ce ~ (dt_seconds + dur_seconds); } func dt_minus_dur(left: DateTime, right: Duration) { dt_seconds := as_epoch_ce(value: left); dur_seconds := as_dur_seconds(value: right); return epoch_ce ~ (dt_seconds - dur_seconds); } func dt_minus_dt(left: DateTime, right: DateTime) { left_seconds := as_epoch_ce(value: left); right_seconds := as_epoch_ce(value: right); return dur_seconds ~ (left_seconds - right_seconds); } func dur_plus_dur(left: Duration, right: Duration) { return dur_seconds ~ ( as_dur_seconds(value: left) + as_dur_seconds(value: right) ); } func as_iso8601(value: DateTime) { switch value { case epoch_posix ~ *: epoch_seconds := value ? epoch_posix; return epoch_posix_to_iso8601(value: epoch_seconds); case epoch_ce ~ *: epoch_seconds := value ? epoch_ce; return epoch_ce_to_iso8601(value: seoncds); case iso8601 ~ *: datetime := value ? iso8601; return datetime; } } func as_epoch_ce(value: DateTime) { switch value { case epoch_posix ~ *: epoch_seconds := value ? epoch_posix; return epoch_posix_to_ce(value: epoch_seconds); case epoch_ce ~ *: epoch_seconds := value ? epoch_ce; return epoch_seconds; case iso8601 ~ *: datetime := value ? iso8601; return iso8601_to_epoch_ce(value: datetime); } } func as_epoch_posix(value: DateTime) { switch value { case epoch_posix ~ *: epoch_seconds := value ? epoch_posix; return epoch_seconds; case epoch_ce ~ *: epoch_seconds := value ? epoch_ce; return epoch_ce_to_posix(value: epoch_seconds); case iso8601 ~ *: datetime := value ? iso8601; return iso8601_to_epoch_posix(value: datetime); } } func as_dur_seconds(value: Duration) { return value ? seconds; } func epoch_ce_to_iso8601(epoch_seconds: Int) { temp_n := split_epoch_seconds(epoch_seconds:); epoch_days := temp_n.epoch_days; day_seconds := temp_n.day_seconds; temp_a := epoch_days_to_year_month_day(epoch_days:); year := temp_a.year; month := temp_a.month; day := temp_a.day; temp_ := seconds_to_hour_minute_second(day_seconds:); hour := temp_.hour; minute := temp_.minute; second := temp_.second; return (year:, month:, day:, hour:, minute:, second:); } func epoch_ce_to_posix(epoch_seconds: Int) { return epoch_seconds + ce_to_posix_offset; } func epoch_posix_to_ce(epoch_seconds: Int) { return epoch_seconds - ce_to_posix_offset; } func epoch_posix_to_iso8601(epoch_seconds: Int) { return epoch_ce_to_iso8601(value: epoch_posix_to_ce(value: epoch_seconds)); } func iso8601_to_epoch_ce(datetime: Iso8601Simple) { temp_t := datetime; year := temp_t.year; month := temp_t.month; day := temp_t.day; hour := temp_t.hour; minute := temp_t.minute; second := temp_t.second; epoch_days := year_month_day_to_epoch_days(year:, month:, day:); day_seconds := time_of_day_to_seconds(hour:, minute:, second:); return combine_epoch_date(epoch_days:, day_seconds:); } func iso8601_to_epoch_posix(datetime: Iso8601Simple) { return epoch_ce_to_posix(value: iso8601_to_epoch_ce(value: datetime)); } ce_to_posix_offset := 719162 * seconds_in_day; seconds_in_day := 24 * 60 * 60; func split_epoch_seconds(epoch_seconds: Int) { temp_g := div_mod(left: epoch_seconds, right: seconds_in_day); div := temp_g.div; mod := temp_g.mod; return (epoch_days: div, day_seconds: mod); } func split_epoch_ce(value: DateTime) { return split_epoch_seconds(value: as_epoch_ce(value:)); } func combine_epoch_date(epoch_days: Int, day_seconds: Int) { return seconds_in_day * epoch_days + day_seconds; } func seconds_to_hour_minute_second(seconds: Int) { temp_j := div_mod(left: seconds, right: 60); minutes := temp_j.div; second := temp_j.mod; temp_w := div_mod(left: minutes, right: 60); hour := temp_w.div; minute := temp_w.mod; return (hour:, minute:, second:); } func hour_minute_second_to_seconds(hour: Int, minute: Int, second: Int) { return 3600 * hour + 60 * minute + second; } func month_start(month: Int, is_leap_year: Boolean) { if is_leap_year { starts := [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]; return starts[month]; } else { starts := [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; return starts[month]; } } days_in_quad_century := 146097; days_in_century := 36524; days_in_quad_year := 1461; days_in_std_year := 365; func epoch_day_to_year_month_day(epoch_days: Int) -> Iso8601Date { temp_e := div_mod(left: epoch_days, right: days_in_quad_century); quad_century := temp_e.div; quad_centry_day := temp_e.mod; temp_r := div_mod(left: quad_century_day, right: days_in_century); century_p := temp_r.div; century_day := temp_r.mod; if century_p == 4 { century := 3; temp_d := div_mod(left: century_day, right: days_in_quad_year); quad_year := temp_d.div; quad_year_day := temp_d.mod; temp_q := div_mod(left: quad_year_day, right: days_in_year); year_p := temp_q.div; year_day := temp_q.mod; if year_p == 4 { year := 3; if century_p == 4 or year_p == 4 { year_day_0 := 365; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day_0 + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day_0 { month := month_p - 1; month_start_day_0 := month_start(month:, is_leap_year:); month_day := year_day_0 - month_start_day_0; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day_0 - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } else { year_day_1 := year_day_p; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day_1 + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day_1 { month := month_p - 1; month_start_day_1 := month_start(month:, is_leap_year:); month_day := year_day_1 - month_start_day_1; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day_1 - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } } else { year := year_p; if century_p == 4 or year_p == 4 { year_day_2 := 365; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day_2 + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day_2 { month := month_p - 1; month_start_day_2 := month_start(month:, is_leap_year:); month_day := year_day_2 - month_start_day_2; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day_2 - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } else { year_day_3 := year_day_p; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day_3 + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day_3 { month := month_p - 1; month_start_day_3 := month_start(month:, is_leap_year:); month_day := year_day_3 - month_start_day_3; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day_3 - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } } } else { century := century_p; temp_l := div_mod(left: century_day, right: days_in_quad_year); quad_year := temp_l.div; quad_year_day := temp_l.mod; temp_y := div_mod(left: quad_year_day, right: days_in_year); year_p := temp_y.div; year_day := temp_y.mod; if year_p == 4 { year := 3; if century_p == 4 or year_p == 4 { year_day_4 := 365; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day_4 + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day_4 { month := month_p - 1; month_start_day_4 := month_start(month:, is_leap_year:); month_day := year_day_4 - month_start_day_4; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day_4 - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } else { year_day_5 := year_day_p; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day_5 + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day_5 { month := month_p - 1; month_start_day_5 := month_start(month:, is_leap_year:); month_day := year_day_5 - month_start_day_5; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day_5 - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } } else { year := year_p; if century_p == 4 or year_p == 4 { year_day_6 := 365; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day_6 + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day_6 { month := month_p - 1; month_start_day_6 := month_start(month:, is_leap_year:); month_day := year_day_6 - month_start_day_6; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day_6 - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } else { year_day_7 := year_day_p; is_leap_year := century_p == 4 or year_p > 2 or quad_year == 24; month_p := (year_day_7 + 50) // 32; month_start_day := month_start(month: month_p, is_leap_year:); if month_start_day > year_day_7 { month := month_p - 1; month_start_day_7 := month_start(month:, is_leap_year:); month_day := year_day_7 - month_start_day_7; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } else { month := month_p; month_day := year_day_7 - month_start_day; return ( year: 400 * quad_century + 100 * century + year + 1, month: month + 1, day: month_day + 1 ); } } } } } func year_month_day_to_epoch_days( year: Int, month: Int, day: Int ) -> Index { temp_o := div_mod(left: year - 1, right: 400); quad_century := temp_o.div; quad_century_year := temp_o.mod; temp_h := div_mod(left: quad_century_year, right: 100); century := temp_h.div; century_year := temp_h.mod; temp_u := div_mod(left: century_year, right: 4); quad_years := temp_u.div; quad_year := temp_u.mod; is_leap_year := quad_year == 3 and (century_year != 99 or century == 3); month_start_day := month_start(month:, is_leap_year:); return day - 1 + month_start_day + days_in_year * quad_year + days_in_quad_year * quad_years + days_in_century * century + days_in_quad_century * quad_century; } type Month := ( #jan | #feb | #mar | #apr | #may | #jun | #jul | #aug | #sep | #oct | #nov | #dec ); func month_from_index(index: Int) -> Month { return [ #jan, #feb, #mar, #apr, #may, #jun, #jul, #aug, #sep, #oct, #nov, #dec ][index - 1]; } func index_of_month(month: Month) -> Int { return { #jan:1, #feb:2, #mar:3, #apr:4, #may:5, #jun:6, #jul:7, #aug:8, #sep:9, #oct:10, #nov:11, #dec:12 }[month]; } type DayOfWeek := (#sun | #mon | #tue | #wed | #thu | #fri | #sat); func day_of_week_from_index(index: Int) -> DayOfWeek { return [#sun, #mon, #tue, #wed, #thu, #fri, #sat][index]; } func index_of_day_of_week(day: DayOfWeek) -> Int { return { #sun:0, #mon:1, #tue:2, #wed:3, #thu:4, #fri:5, #sat:6 }[day]; } func day_of_week_of_epoch_days(epoch_days: Int) { return (6 + epoch_days) % 7; } func day_of_week_date_time(value: DateTime) { return day_of_week_of_epoch_days(value: split_epoch_ce(value:).epoch_days); } func closest_business_day_forwards(value: Int) { switch day_of_week_of_epoch_days(epoch_days: value) { case 0: return value + 1; case 6: return value + 2; default: return value; } } func closest_business_day_backwards(value: Int) { switch day_of_week_of_epoch_days(epoch_days: value) { case 0: return value - 2; case 6: return value - 1; default: return value; } } func business_days_after( point: DateTime, days_change: Int, open_seconds: Int, close_seconds: Int ) { temp_f := split_epoch_ce(value: point); epoch_days := temp_f.epoch_days; day_seconds := temp_f.day_seconds; after_hours := day_seconds > close_seconds; before_hours := day_seconds < open_seconds; if after_hours { day := 1 + epoch_days; if after_hours or before_hours { seconds_p := open_seconds; day_0 := closest_business_day_forwards(value: day); temp_b := div_mod(left: days_change, right: 5); weeks_change := temp_b.div; days_change_0 := temp_b.mod; day_1 := day_0 + 7 * weeks_change + days_change_0; day_2 := closest_business_day_forwards_p(value: day_1); return epoch_ce ~ combine_epoch_date( epoch_days: day_2, day_seconds: seconds_p ); } else { seconds_p := day_seconds; day_3 := closest_business_day_forwards(value: day); temp_z := div_mod(left: days_change, right: 5); weeks_change := temp_z.div; days_change_1 := temp_z.mod; day_4 := day_3 + 7 * weeks_change + days_change_1; day_5 := closest_business_day_forwards_p(value: day_4); return epoch_ce ~ combine_epoch_date( epoch_days: day_5, day_seconds: seconds_p ); } } else { day := epoch_days; if after_hours or before_hours { seconds_p := open_seconds; day_6 := closest_business_day_forwards(value: day); temp_m := div_mod(left: days_change, right: 5); weeks_change := temp_m.div; days_change_2 := temp_m.mod; day_7 := day_6 + 7 * weeks_change + days_change_2; day_8 := closest_business_day_forwards_p(value: day_7); return epoch_ce ~ combine_epoch_date( epoch_days: day_8, day_seconds: seconds_p ); } else { seconds_p := day_seconds; day_9 := closest_business_day_forwards(value: day); temp_s := div_mod(left: days_change, right: 5); weeks_change := temp_s.div; days_change_3 := temp_s.mod; day_10 := day_9 + 7 * weeks_change + days_change_3; day_11 := closest_business_day_forwards_p(value: day_10); return epoch_ce ~ combine_epoch_date( epoch_days: day_11, day_seconds: seconds_p ); } } } func business_days_prior( point: DateTime, days_change: Int, open_seconds: Int, close_seconds: Int ) { temp_x := split_epoch_ce(value: point); epoch_days := temp_x.epoch_days; day_seconds := temp_x.day_seconds; after_hours := day_seconds > close_seconds; before_hours := day_seconds < open_seconds; if before_hours { day := epoch_days - 1; if after_hours or before_hours { seconds_p := close_seconds; day_12 := closest_business_day_backwards(value: day); temp_v := div_mod(left: days_change, right: 5); weeks_change := temp_v.div; days_change_4 := temp_v.mod; day_13 := day_12 + 7 * weeks_change + days_change_4; day_14 := closest_business_day_backwards(value: day_13); return epoch_ce ~ combine_epoch_date( epoch_days: day_14, day_seconds: seconds_p ); } else { seconds_p := day_seconds; day_15 := closest_business_day_backwards(value: day); temp_i := div_mod(left: days_change, right: 5); weeks_change := temp_i.div; days_change_5 := temp_i.mod; day_16 := day_15 + 7 * weeks_change + days_change_5; day_17 := closest_business_day_backwards(value: day_16); return epoch_ce ~ combine_epoch_date( epoch_days: day_17, day_seconds: seconds_p ); } } else { day := epoch_days; if after_hours or before_hours { seconds_p := close_seconds; day_18 := closest_business_day_backwards(value: day); temp_p := div_mod(left: days_change, right: 5); weeks_change := temp_p.div; days_change_6 := temp_p.mod; day_19 := day_18 + 7 * weeks_change + days_change_6; day_20 := closest_business_day_backwards(value: day_19); return epoch_ce ~ combine_epoch_date( epoch_days: day_20, day_seconds: seconds_p ); } else { seconds_p := day_seconds; day_21 := closest_business_day_backwards(value: day); temp_c := div_mod(left: days_change, right: 5); weeks_change := temp_c.div; days_change_7 := temp_c.mod; day_22 := day_21 + 7 * weeks_change + days_change_7; day_23 := closest_business_day_backwards(value: day_22); return epoch_ce ~ combine_epoch_date( epoch_days: day_23, day_seconds: seconds_p ); } } }
type DateTime := [!Union epoch_ce:!Int epoch_posix:!Int iso8601:Iso8601Simple ]; type DayOfWeek := [!Union fri:[!Record] mon:[!Record] sat:[!Record] sun:[!Record] thu:[!Record] tue:[!Record] wed:[!Record] ]; type Duration := [!Union seconds:!Int]; type Iso8601Date := [!Record day:!Int month:!Int year:!Int]; type Iso8601Simple := [!Record day:!Int hour:!Int minute:!Int month:!Int second:!Int year:!Int ]; type Iso8601Time := [!Record hour:!Int minute:!Int second:!Int]; type Month := [!Union apr:[!Record] aug:[!Record] dec:[!Record] feb:[!Record] jan:[!Record] jul:[!Record] jun:[!Record] mar:[!Record] may:[!Record] nov:[!Record] oct:[!Record] sep:[!Record] ]; def as_dur_seconds := {def value:Duration (!get_variant seconds value)}; def as_epoch_ce := {def value:DateTime (case tags value epoch_ce:(let epoch_seconds:(!get_variant epoch_ce value) epoch_seconds) epoch_posix:(let epoch_seconds:(!get_variant epoch_posix value) (epoch_posix_to_ce value:epoch_seconds) ) iso8601:(let datetime:(!get_variant iso8601 value) (iso8601_to_epoch_ce value:datetime) ) ) }; def as_epoch_posix := {def value:DateTime (case tags value epoch_ce:(let epoch_seconds:(!get_variant epoch_ce value) (epoch_ce_to_posix value:epoch_seconds) ) epoch_posix:(let epoch_seconds:(!get_variant epoch_posix value) epoch_seconds ) iso8601:(let datetime:(!get_variant iso8601 value) (iso8601_to_epoch_posix value:datetime) ) ) }; def as_iso8601 := {def value:DateTime (case tags value epoch_ce:(let epoch_seconds:(!get_variant epoch_ce value) (epoch_ce_to_iso8601 value:seoncds) ) epoch_posix:(let epoch_seconds:(!get_variant epoch_posix value) (epoch_posix_to_iso8601 value:epoch_seconds) ) iso8601:(let datetime:(!get_variant iso8601 value) datetime) ) }; def business_days_after := {def close_seconds:!Int days_change:!Int open_seconds:!Int point:DateTime (let temp_f:(split_epoch_ce value:point) (let epoch_days:(!get_slot epoch_days temp_f) (let day_seconds:(!get_slot day_seconds temp_f) (let after_hours:(!bin_greater_than day_seconds close_seconds) (let before_hours:(!bin_less_than day_seconds open_seconds) (case tags after_hours false:(let day:epoch_days (case tags (!bin_or after_hours before_hours) false:(let seconds_p:day_seconds (let day_9:(closest_business_day_forwards value:day) (let temp_s:(div_mod left:days_change right:5) (let weeks_change:(!get_slot div temp_s) (let days_change_3:(!get_slot mod temp_s) (let day_10:(!bin_plus day_9 (!bin_plus (!bin_times 7 weeks_change) days_change_3 ) ) (let day_11:(closest_business_day_forwards_p value:day_10 ) epoch_ce~(combine_epoch_date day_seconds:seconds_p epoch_days:day_11 ) ) ) ) ) ) ) ) true:(let seconds_p:open_seconds (let day_6:(closest_business_day_forwards value:day) (let temp_m:(div_mod left:days_change right:5) (let weeks_change:(!get_slot div temp_m) (let days_change_2:(!get_slot mod temp_m) (let day_7:(!bin_plus day_6 (!bin_plus (!bin_times 7 weeks_change) days_change_2 ) ) (let day_8:(closest_business_day_forwards_p value:day_7 ) epoch_ce~(combine_epoch_date day_seconds:seconds_p epoch_days:day_8 ) ) ) ) ) ) ) ) ) ) true:(let day:(!bin_plus 1 epoch_days) (case tags (!bin_or after_hours before_hours) false:(let seconds_p:day_seconds (let day_3:(closest_business_day_forwards value:day) (let temp_z:(div_mod left:days_change right:5) (let weeks_change:(!get_slot div temp_z) (let days_change_1:(!get_slot mod temp_z) (let day_4:(!bin_plus day_3 (!bin_plus (!bin_times 7 weeks_change) days_change_1 ) ) (let day_5:(closest_business_day_forwards_p value:day_4 ) epoch_ce~(combine_epoch_date day_seconds:seconds_p epoch_days:day_5 ) ) ) ) ) ) ) ) true:(let seconds_p:open_seconds (let day_0:(closest_business_day_forwards value:day) (let temp_b:(div_mod left:days_change right:5) (let weeks_change:(!get_slot div temp_b) (let days_change_0:(!get_slot mod temp_b) (let day_1:(!bin_plus day_0 (!bin_plus (!bin_times 7 weeks_change) days_change_0 ) ) (let day_2:(closest_business_day_forwards_p value:day_1 ) epoch_ce~(combine_epoch_date day_seconds:seconds_p epoch_days:day_2 ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) }; def business_days_prior := {def close_seconds:!Int days_change:!Int open_seconds:!Int point:DateTime (let temp_x:(split_epoch_ce value:point) (let epoch_days:(!get_slot epoch_days temp_x) (let day_seconds:(!get_slot day_seconds temp_x) (let after_hours:(!bin_greater_than day_seconds close_seconds) (let before_hours:(!bin_less_than day_seconds open_seconds) (case tags before_hours false:(let day:epoch_days (case tags (!bin_or after_hours before_hours) false:(let seconds_p:day_seconds (let day_21:(closest_business_day_backwards value:day) (let temp_c:(div_mod left:days_change right:5) (let weeks_change:(!get_slot div temp_c) (let days_change_7:(!get_slot mod temp_c) (let day_22:(!bin_plus day_21 (!bin_plus (!bin_times 7 weeks_change) days_change_7 ) ) (let day_23:(closest_business_day_backwards value:day_22 ) epoch_ce~(combine_epoch_date day_seconds:seconds_p epoch_days:day_23 ) ) ) ) ) ) ) ) true:(let seconds_p:close_seconds (let day_18:(closest_business_day_backwards value:day) (let temp_p:(div_mod left:days_change right:5) (let weeks_change:(!get_slot div temp_p) (let days_change_6:(!get_slot mod temp_p) (let day_19:(!bin_plus day_18 (!bin_plus (!bin_times 7 weeks_change) days_change_6 ) ) (let day_20:(closest_business_day_backwards value:day_19 ) epoch_ce~(combine_epoch_date day_seconds:seconds_p epoch_days:day_20 ) ) ) ) ) ) ) ) ) ) true:(let day:(!bin_minus epoch_days 1) (case tags (!bin_or after_hours before_hours) false:(let seconds_p:day_seconds (let day_15:(closest_business_day_backwards value:day) (let temp_i:(div_mod left:days_change right:5) (let weeks_change:(!get_slot div temp_i) (let days_change_5:(!get_slot mod temp_i) (let day_16:(!bin_plus day_15 (!bin_plus (!bin_times 7 weeks_change) days_change_5 ) ) (let day_17:(closest_business_day_backwards value:day_16 ) epoch_ce~(combine_epoch_date day_seconds:seconds_p epoch_days:day_17 ) ) ) ) ) ) ) ) true:(let seconds_p:close_seconds (let day_12:(closest_business_day_backwards value:day) (let temp_v:(div_mod left:days_change right:5) (let weeks_change:(!get_slot div temp_v) (let days_change_4:(!get_slot mod temp_v) (let day_13:(!bin_plus day_12 (!bin_plus (!bin_times 7 weeks_change) days_change_4 ) ) (let day_14:(closest_business_day_backwards value:day_13 ) epoch_ce~(combine_epoch_date day_seconds:seconds_p epoch_days:day_14 ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) }; def ce_to_posix_offset := (!bin_times 719162 seconds_in_day); def closest_business_day_backwards := {def value:!Int (case integers (day_of_week_of_epoch_days epoch_days:value) 0:(!bin_minus value 2) 6:(!bin_minus value 1) ::value ) }; def closest_business_day_forwards := {def value:!Int (case integers (day_of_week_of_epoch_days epoch_days:value) 0:(!bin_plus value 1) 6:(!bin_plus value 2) ::value ) }; def combine_epoch_date := {def day_seconds:!Int epoch_days:!Int (!bin_plus (!bin_times seconds_in_day epoch_days) day_seconds) }; def day_of_week_date_time := {def value:DateTime (day_of_week_of_epoch_days value:(!get_slot epoch_days (split_epoch_ce value:value)) ) }; def day_of_week_from_index := {def index:!Int ::DayOfWeek (!get_index [list #sun #mon #tue #wed #thu #fri #sat] index) }; def day_of_week_of_epoch_days := {def epoch_days:!Int (!bin_modulo (!bin_plus 6 epoch_days) 7) }; def days_in_century := 36524; def days_in_quad_century := 146097; def days_in_quad_year := 1461; def days_in_std_year := 365; def dt_minus_dt := {def left:DateTime right:DateTime (let left_seconds:(as_epoch_ce value:left) (let right_seconds:(as_epoch_ce value:right) dur_seconds~(!bin_minus left_seconds right_seconds) ) ) }; def dt_minus_dur := {def left:DateTime right:Duration (let dt_seconds:(as_epoch_ce value:left) (let dur_seconds:(as_dur_seconds value:right) epoch_ce~(!bin_minus dt_seconds dur_seconds) ) ) }; def dt_plus_dur := {def left:DateTime right:Duration (let dt_seconds:(as_epoch_ce value:left) (let dur_seconds:(as_dur_seconds value:right) epoch_ce~(!bin_plus dt_seconds dur_seconds) ) ) }; def dur_plus_dur := {def left:Duration right:Duration dur_seconds~(!bin_plus (as_dur_seconds value:left) (as_dur_seconds value:right) ) }; def epoch_ce_to_iso8601 := {def epoch_seconds:!Int (let temp_n:(split_epoch_seconds epoch_seconds:epoch_seconds) (let epoch_days:(!get_slot epoch_days temp_n) (let day_seconds:(!get_slot day_seconds temp_n) (let temp_a:(epoch_days_to_year_month_day epoch_days:epoch_days) (let year:(!get_slot year temp_a) (let month:(!get_slot month temp_a) (let day:(!get_slot day temp_a) (let temp_:(seconds_to_hour_minute_second day_seconds:day_seconds) (let hour:(!get_slot hour temp_) (let minute:(!get_slot minute temp_) (let second:(!get_slot second temp_) [. day:day hour:hour minute:minute month:month second:second year:year ] ) ) ) ) ) ) ) ) ) ) ) }; def epoch_ce_to_posix := {def epoch_seconds:!Int (!bin_plus epoch_seconds ce_to_posix_offset) }; def epoch_day_to_year_month_day := {def epoch_days:!Int ::Iso8601Date (let temp_e:(div_mod left:epoch_days right:days_in_quad_century) (let quad_century:(!get_slot div temp_e) (let quad_centry_day:(!get_slot mod temp_e) (let temp_r:(div_mod left:quad_century_day right:days_in_century) (let century_p:(!get_slot div temp_r) (let century_day:(!get_slot mod temp_r) (case tags (!bin_equals century_p 4) false:(let century:century_p (let temp_l:(div_mod left:century_day right:days_in_quad_year) (let quad_year:(!get_slot div temp_l) (let quad_year_day:(!get_slot mod temp_l) (let temp_y:(div_mod left:quad_year_day right:days_in_year) (let year_p:(!get_slot div temp_y) (let year_day:(!get_slot mod temp_y) (case tags (!bin_equals year_p 4) false:(let year:year_p (case tags (!bin_or (!bin_equals century_p 4) (!bin_equals year_p 4) ) false:(let year_day_7:year_day_p (let is_leap_year:(!bin_or (!bin_or (!bin_equals century_p 4) (!bin_greater_than year_p 2) ) (!bin_equals quad_year 24) ) (let month_p:(!bin_floor_divide (!bin_plus year_day_7 50) 32 ) (let month_start_day:(month_start is_leap_year:is_leap_year month:month_p ) (case tags (!bin_greater_than month_start_day year_day_7 ) false:(let month:month_p (let month_day:(!bin_minus year_day_7 month_start_day ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century ) (!bin_times 100 century ) ) year ) 1 ) ] ) ) true:(let month:(!bin_minus month_p 1) (let month_start_day_7:(month_start is_leap_year:is_leap_year month:month ) (let month_day:(!bin_minus year_day_7 month_start_day_7 ) [. day:(!bin_plus month_day 1 ) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century ) (!bin_times 100 century ) ) year ) 1 ) ] ) ) ) ) ) ) ) ) true:(let year_day_6:365 (let is_leap_year:(!bin_or (!bin_or (!bin_equals century_p 4) (!bin_greater_than year_p 2) ) (!bin_equals quad_year 24) ) (let month_p:(!bin_floor_divide (!bin_plus year_day_6 50) 32 ) (let month_start_day:(month_start is_leap_year:is_leap_year month:month_p ) (case tags (!bin_greater_than month_start_day year_day_6 ) false:(let month:month_p (let month_day:(!bin_minus year_day_6 month_start_day ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century ) (!bin_times 100 century ) ) year ) 1 ) ] ) ) true:(let month:(!bin_minus month_p 1) (let month_start_day_6:(month_start is_leap_year:is_leap_year month:month ) (let month_day:(!bin_minus year_day_6 month_start_day_6 ) [. day:(!bin_plus month_day 1 ) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century ) (!bin_times 100 century ) ) year ) 1 ) ] ) ) ) ) ) ) ) ) ) ) true:(let year:3 (case tags (!bin_or (!bin_equals century_p 4) (!bin_equals year_p 4) ) false:(let year_day_5:year_day_p (let is_leap_year:(!bin_or (!bin_or (!bin_equals century_p 4) (!bin_greater_than year_p 2) ) (!bin_equals quad_year 24) ) (let month_p:(!bin_floor_divide (!bin_plus year_day_5 50) 32 ) (let month_start_day:(month_start is_leap_year:is_leap_year month:month_p ) (case tags (!bin_greater_than month_start_day year_day_5 ) false:(let month:month_p (let month_day:(!bin_minus year_day_5 month_start_day ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century ) (!bin_times 100 century ) ) year ) 1 ) ] ) ) true:(let month:(!bin_minus month_p 1) (let month_start_day_5:(month_start is_leap_year:is_leap_year month:month ) (let month_day:(!bin_minus year_day_5 month_start_day_5 ) [. day:(!bin_plus month_day 1 ) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century ) (!bin_times 100 century ) ) year ) 1 ) ] ) ) ) ) ) ) ) ) true:(let year_day_4:365 (let is_leap_year:(!bin_or (!bin_or (!bin_equals century_p 4) (!bin_greater_than year_p 2) ) (!bin_equals quad_year 24) ) (let month_p:(!bin_floor_divide (!bin_plus year_day_4 50) 32 ) (let month_start_day:(month_start is_leap_year:is_leap_year month:month_p ) (case tags (!bin_greater_than month_start_day year_day_4 ) false:(let month:month_p (let month_day:(!bin_minus year_day_4 month_start_day ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century ) (!bin_times 100 century ) ) year ) 1 ) ] ) ) true:(let month:(!bin_minus month_p 1) (let month_start_day_4:(month_start is_leap_year:is_leap_year month:month ) (let month_day:(!bin_minus year_day_4 month_start_day_4 ) [. day:(!bin_plus month_day 1 ) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century ) (!bin_times 100 century ) ) year ) 1 ) ] ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) true:(let century:3 (let temp_d:(div_mod left:century_day right:days_in_quad_year) (let quad_year:(!get_slot div temp_d) (let quad_year_day:(!get_slot mod temp_d) (let temp_q:(div_mod left:quad_year_day right:days_in_year) (let year_p:(!get_slot div temp_q) (let year_day:(!get_slot mod temp_q) (case tags (!bin_equals year_p 4) false:(let year:year_p (case tags (!bin_or (!bin_equals century_p 4) (!bin_equals year_p 4) ) false:(let year_day_3:year_day_p (let is_leap_year:(!bin_or (!bin_or (!bin_equals century_p 4) (!bin_greater_than year_p 2) ) (!bin_equals quad_year 24) ) (let month_p:(!bin_floor_divide (!bin_plus year_day_3 50) 32 ) (let month_start_day:(month_start is_leap_year:is_leap_year month:month_p ) (case tags (!bin_greater_than month_start_day year_day_3 ) false:(let month:month_p (let month_day:(!bin_minus year_day_3 month_start_day ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century ) (!bin_times 100 century ) ) year ) 1 ) ] ) ) true:(let month:(!bin_minus month_p 1) (let month_start_day_3:(month_start is_leap_year:is_leap_year month:month ) (let month_day:(!bin_minus year_day_3 month_start_day_3 ) [. day:(!bin_plus month_day 1 ) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century ) (!bin_times 100 century ) ) year ) 1 ) ] ) ) ) ) ) ) ) ) true:(let year_day_2:365 (let is_leap_year:(!bin_or (!bin_or (!bin_equals century_p 4) (!bin_greater_than year_p 2) ) (!bin_equals quad_year 24) ) (let month_p:(!bin_floor_divide (!bin_plus year_day_2 50) 32 ) (let month_start_day:(month_start is_leap_year:is_leap_year month:month_p ) (case tags (!bin_greater_than month_start_day year_day_2 ) false:(let month:month_p (let month_day:(!bin_minus year_day_2 month_start_day ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century ) (!bin_times 100 century ) ) year ) 1 ) ] ) ) true:(let month:(!bin_minus month_p 1) (let month_start_day_2:(month_start is_leap_year:is_leap_year month:month ) (let month_day:(!bin_minus year_day_2 month_start_day_2 ) [. day:(!bin_plus month_day 1 ) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century ) (!bin_times 100 century ) ) year ) 1 ) ] ) ) ) ) ) ) ) ) ) ) true:(let year:3 (case tags (!bin_or (!bin_equals century_p 4) (!bin_equals year_p 4) ) false:(let year_day_1:year_day_p (let is_leap_year:(!bin_or (!bin_or (!bin_equals century_p 4) (!bin_greater_than year_p 2) ) (!bin_equals quad_year 24) ) (let month_p:(!bin_floor_divide (!bin_plus year_day_1 50) 32 ) (let month_start_day:(month_start is_leap_year:is_leap_year month:month_p ) (case tags (!bin_greater_than month_start_day year_day_1 ) false:(let month:month_p (let month_day:(!bin_minus year_day_1 month_start_day ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century ) (!bin_times 100 century ) ) year ) 1 ) ] ) ) true:(let month:(!bin_minus month_p 1) (let month_start_day_1:(month_start is_leap_year:is_leap_year month:month ) (let month_day:(!bin_minus year_day_1 month_start_day_1 ) [. day:(!bin_plus month_day 1 ) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century ) (!bin_times 100 century ) ) year ) 1 ) ] ) ) ) ) ) ) ) ) true:(let year_day_0:365 (let is_leap_year:(!bin_or (!bin_or (!bin_equals century_p 4) (!bin_greater_than year_p 2) ) (!bin_equals quad_year 24) ) (let month_p:(!bin_floor_divide (!bin_plus year_day_0 50) 32 ) (let month_start_day:(month_start is_leap_year:is_leap_year month:month_p ) (case tags (!bin_greater_than month_start_day year_day_0 ) false:(let month:month_p (let month_day:(!bin_minus year_day_0 month_start_day ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century ) (!bin_times 100 century ) ) year ) 1 ) ] ) ) true:(let month:(!bin_minus month_p 1) (let month_start_day_0:(month_start is_leap_year:is_leap_year month:month ) (let month_day:(!bin_minus year_day_0 month_start_day_0 ) [. day:(!bin_plus month_day 1 ) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century ) (!bin_times 100 century ) ) year ) 1 ) ] ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) }; def epoch_posix_to_ce := {def epoch_seconds:!Int (!bin_minus epoch_seconds ce_to_posix_offset) }; def epoch_posix_to_iso8601 := {def epoch_seconds:!Int (epoch_ce_to_iso8601 value:(epoch_posix_to_ce value:epoch_seconds)) }; def hour_minute_second_to_seconds := {def hour:!Int minute:!Int second:!Int (!bin_plus (!bin_plus (!bin_times 3600 hour) (!bin_times 60 minute)) second) }; def index_of_day_of_week := {def day:DayOfWeek ::!Int (!get_index [map #sun:0 #mon:1 #tue:2 #wed:3 #thu:4 #fri:5 #sat:6] day) }; def index_of_month := {def month:Month ::!Int (!get_index [map #jan:1 #feb:2 #mar:3 #apr:4 #may:5 #jun:6 #jul:7 #aug:8 #sep:9 #oct:10 #nov:11 #dec:12 ] month ) }; def iso8601_to_epoch_ce := {def datetime:Iso8601Simple (let temp_t:datetime (let year:(!get_slot year temp_t) (let month:(!get_slot month temp_t) (let day:(!get_slot day temp_t) (let hour:(!get_slot hour temp_t) (let minute:(!get_slot minute temp_t) (let second:(!get_slot second temp_t) (let epoch_days:(year_month_day_to_epoch_days day:day month:month year:year ) (let day_seconds:(time_of_day_to_seconds hour:hour minute:minute second:second ) (combine_epoch_date day_seconds:day_seconds epoch_days:epoch_days ) ) ) ) ) ) ) ) ) ) }; def iso8601_to_epoch_posix := {def datetime:Iso8601Simple (epoch_ce_to_posix value:(iso8601_to_epoch_ce value:datetime)) }; def month_from_index := {def index:!Int ::Month (!get_index [list #jan #feb #mar #apr #may #jun #jul #aug #sep #oct #nov #dec] (!bin_minus index 1) ) }; def month_start := {def is_leap_year:Boolean month:!Int (case tags is_leap_year false:(let starts:[list 0 31 59 90 120 151 181 212 243 273 304 334] (!get_index starts month) ) true:(let starts:[list 0 31 60 91 121 152 182 213 244 274 305 335] (!get_index starts month) ) ) }; def seconds_in_day := (!bin_times (!bin_times 24 60) 60); def seconds_to_hour_minute_second := {def seconds:!Int (let temp_j:(div_mod left:seconds right:60) (let minutes:(!get_slot div temp_j) (let second:(!get_slot mod temp_j) (let temp_w:(div_mod left:minutes right:60) (let hour:(!get_slot div temp_w) (let minute:(!get_slot mod temp_w) [. hour:hour minute:minute second:second] ) ) ) ) ) ) }; def split_epoch_ce := {def value:DateTime (split_epoch_seconds value:(as_epoch_ce value:value)) }; def split_epoch_seconds := {def epoch_seconds:!Int (let temp_g:(div_mod left:epoch_seconds right:seconds_in_day) (let div:(!get_slot div temp_g) (let mod:(!get_slot mod temp_g) [. day_seconds:mod epoch_days:div]) ) ) }; def year_month_day_to_epoch_days := {def day:!Int month:!Int year:!Int ::Index (let temp_o:(div_mod left:(!bin_minus year 1) right:400) (let quad_century:(!get_slot div temp_o) (let quad_century_year:(!get_slot mod temp_o) (let temp_h:(div_mod left:quad_century_year right:100) (let century:(!get_slot div temp_h) (let century_year:(!get_slot mod temp_h) (let temp_u:(div_mod left:century_year right:4) (let quad_years:(!get_slot div temp_u) (let quad_year:(!get_slot mod temp_u) (let is_leap_year:(!bin_and (!bin_equals quad_year 3) (!bin_or (!bin_not_equal century_year 99) (!bin_equals century 3) ) ) (let month_start_day:(month_start is_leap_year:is_leap_year month:month ) (!bin_plus (!bin_plus (!bin_plus (!bin_plus (!bin_plus (!bin_minus day 1) month_start_day) (!bin_times days_in_year quad_year) ) (!bin_times days_in_quad_year quad_years) ) (!bin_times days_in_century century) ) (!bin_times days_in_quad_century quad_century) ) ) ) ) ) ) ) ) ) ) ) ) };
type DateTime := [!Union epoch_ce:!Int epoch_posix:!Int iso8601:Iso8601Simple ]; type DayOfWeek := [!Union fri:[!Record] mon:[!Record] sat:[!Record] sun:[!Record] thu:[!Record] tue:[!Record] wed:[!Record] ]; type Duration := [!Union seconds:!Int]; type Iso8601Date := [!Record day:!Int month:!Int year:!Int]; type Iso8601Simple := [!Record day:!Int hour:!Int minute:!Int month:!Int second:!Int year:!Int ]; type Iso8601Time := [!Record hour:!Int minute:!Int second:!Int]; type Month := [!Union apr:[!Record] aug:[!Record] dec:[!Record] feb:[!Record] jan:[!Record] jul:[!Record] jun:[!Record] mar:[!Record] may:[!Record] nov:[!Record] oct:[!Record] sep:[!Record] ]; def as_dur_seconds := {def value:Duration (!get_variant seconds value)}; def as_epoch_ce := {def value:DateTime (case tags value epoch_ce:(let epoch_seconds:(!get_variant epoch_ce value) epoch_seconds) epoch_posix:(let epoch_seconds:(!get_variant epoch_posix value) (epoch_posix_to_ce value:epoch_seconds) ) iso8601:(let datetime:(!get_variant iso8601 value) (iso8601_to_epoch_ce value:datetime) ) ) }; def as_epoch_posix := {def value:DateTime (case tags value epoch_ce:(let epoch_seconds:(!get_variant epoch_ce value) (epoch_ce_to_posix value:epoch_seconds) ) epoch_posix:(let epoch_seconds:(!get_variant epoch_posix value) epoch_seconds ) iso8601:(let datetime:(!get_variant iso8601 value) (iso8601_to_epoch_posix value:datetime) ) ) }; def as_iso8601 := {def value:DateTime (case tags value epoch_ce:(let epoch_seconds:(!get_variant epoch_ce value) (epoch_ce_to_iso8601 value:seoncds) ) epoch_posix:(let epoch_seconds:(!get_variant epoch_posix value) (epoch_posix_to_iso8601 value:epoch_seconds) ) iso8601:(let datetime:(!get_variant iso8601 value) datetime) ) }; def business_days_after := {def close_seconds:!Int days_change:!Int open_seconds:!Int point:DateTime (let temp_f:(split_epoch_ce value:point) (let day_seconds:(!get_slot day_seconds temp_f) epoch_days:(!get_slot epoch_days temp_f) (let after_hours:(!bin_greater_than day_seconds close_seconds) before_hours:(!bin_less_than day_seconds open_seconds) (case tags after_hours false:(let day:epoch_days (case tags (!bin_or after_hours before_hours) false:(let day_9:(closest_business_day_forwards value:day) seconds_p:day_seconds temp_s:(div_mod left:days_change right:5) (let days_change_3:(!get_slot mod temp_s) weeks_change:(!get_slot div temp_s) (let day_10:(!bin_plus day_9 (!bin_plus (!bin_times 7 weeks_change) days_change_3) ) (let day_11:(closest_business_day_forwards_p value:day_10) epoch_ce~(combine_epoch_date day_seconds:seconds_p epoch_days:day_11 ) ) ) ) ) true:(let day_6:(closest_business_day_forwards value:day) seconds_p:open_seconds temp_m:(div_mod left:days_change right:5) (let days_change_2:(!get_slot mod temp_m) weeks_change:(!get_slot div temp_m) (let day_7:(!bin_plus day_6 (!bin_plus (!bin_times 7 weeks_change) days_change_2) ) (let day_8:(closest_business_day_forwards_p value:day_7) epoch_ce~(combine_epoch_date day_seconds:seconds_p epoch_days:day_8 ) ) ) ) ) ) ) true:(let day:(!bin_plus 1 epoch_days) (case tags (!bin_or after_hours before_hours) false:(let day_3:(closest_business_day_forwards value:day) seconds_p:day_seconds temp_z:(div_mod left:days_change right:5) (let days_change_1:(!get_slot mod temp_z) weeks_change:(!get_slot div temp_z) (let day_4:(!bin_plus day_3 (!bin_plus (!bin_times 7 weeks_change) days_change_1) ) (let day_5:(closest_business_day_forwards_p value:day_4) epoch_ce~(combine_epoch_date day_seconds:seconds_p epoch_days:day_5 ) ) ) ) ) true:(let day_0:(closest_business_day_forwards value:day) seconds_p:open_seconds temp_b:(div_mod left:days_change right:5) (let days_change_0:(!get_slot mod temp_b) weeks_change:(!get_slot div temp_b) (let day_1:(!bin_plus day_0 (!bin_plus (!bin_times 7 weeks_change) days_change_0) ) (let day_2:(closest_business_day_forwards_p value:day_1) epoch_ce~(combine_epoch_date day_seconds:seconds_p epoch_days:day_2 ) ) ) ) ) ) ) ) ) ) ) }; def business_days_prior := {def close_seconds:!Int days_change:!Int open_seconds:!Int point:DateTime (let temp_x:(split_epoch_ce value:point) (let day_seconds:(!get_slot day_seconds temp_x) epoch_days:(!get_slot epoch_days temp_x) (let after_hours:(!bin_greater_than day_seconds close_seconds) before_hours:(!bin_less_than day_seconds open_seconds) (case tags before_hours false:(let day:epoch_days (case tags (!bin_or after_hours before_hours) false:(let day_21:(closest_business_day_backwards value:day) seconds_p:day_seconds temp_c:(div_mod left:days_change right:5) (let days_change_7:(!get_slot mod temp_c) weeks_change:(!get_slot div temp_c) (let day_22:(!bin_plus day_21 (!bin_plus (!bin_times 7 weeks_change) days_change_7) ) (let day_23:(closest_business_day_backwards value:day_22) epoch_ce~(combine_epoch_date day_seconds:seconds_p epoch_days:day_23 ) ) ) ) ) true:(let day_18:(closest_business_day_backwards value:day) seconds_p:close_seconds temp_p:(div_mod left:days_change right:5) (let days_change_6:(!get_slot mod temp_p) weeks_change:(!get_slot div temp_p) (let day_19:(!bin_plus day_18 (!bin_plus (!bin_times 7 weeks_change) days_change_6) ) (let day_20:(closest_business_day_backwards value:day_19) epoch_ce~(combine_epoch_date day_seconds:seconds_p epoch_days:day_20 ) ) ) ) ) ) ) true:(let day:(!bin_minus epoch_days 1) (case tags (!bin_or after_hours before_hours) false:(let day_15:(closest_business_day_backwards value:day) seconds_p:day_seconds temp_i:(div_mod left:days_change right:5) (let days_change_5:(!get_slot mod temp_i) weeks_change:(!get_slot div temp_i) (let day_16:(!bin_plus day_15 (!bin_plus (!bin_times 7 weeks_change) days_change_5) ) (let day_17:(closest_business_day_backwards value:day_16) epoch_ce~(combine_epoch_date day_seconds:seconds_p epoch_days:day_17 ) ) ) ) ) true:(let day_12:(closest_business_day_backwards value:day) seconds_p:close_seconds temp_v:(div_mod left:days_change right:5) (let days_change_4:(!get_slot mod temp_v) weeks_change:(!get_slot div temp_v) (let day_13:(!bin_plus day_12 (!bin_plus (!bin_times 7 weeks_change) days_change_4) ) (let day_14:(closest_business_day_backwards value:day_13) epoch_ce~(combine_epoch_date day_seconds:seconds_p epoch_days:day_14 ) ) ) ) ) ) ) ) ) ) ) }; def ce_to_posix_offset := (!bin_times 719162 seconds_in_day); def closest_business_day_backwards := {def value:!Int (case integers (day_of_week_of_epoch_days epoch_days:value) 0:(!bin_minus value 2) 6:(!bin_minus value 1) ::value ) }; def closest_business_day_forwards := {def value:!Int (case integers (day_of_week_of_epoch_days epoch_days:value) 0:(!bin_plus value 1) 6:(!bin_plus value 2) ::value ) }; def combine_epoch_date := {def day_seconds:!Int epoch_days:!Int (!bin_plus (!bin_times seconds_in_day epoch_days) day_seconds) }; def day_of_week_date_time := {def value:DateTime (day_of_week_of_epoch_days value:(!get_slot epoch_days (split_epoch_ce value:value)) ) }; def day_of_week_from_index := {def index:!Int ::DayOfWeek (!get_index [list #sun #mon #tue #wed #thu #fri #sat] index) }; def day_of_week_of_epoch_days := {def epoch_days:!Int (!bin_modulo (!bin_plus 6 epoch_days) 7) }; def days_in_century := 36524; def days_in_quad_century := 146097; def days_in_quad_year := 1461; def days_in_std_year := 365; def dt_minus_dt := {def left:DateTime right:DateTime (let left_seconds:(as_epoch_ce value:left) right_seconds:(as_epoch_ce value:right) dur_seconds~(!bin_minus left_seconds right_seconds) ) }; def dt_minus_dur := {def left:DateTime right:Duration (let dt_seconds:(as_epoch_ce value:left) dur_seconds:(as_dur_seconds value:right) epoch_ce~(!bin_minus dt_seconds dur_seconds) ) }; def dt_plus_dur := {def left:DateTime right:Duration (let dt_seconds:(as_epoch_ce value:left) dur_seconds:(as_dur_seconds value:right) epoch_ce~(!bin_plus dt_seconds dur_seconds) ) }; def dur_plus_dur := {def left:Duration right:Duration dur_seconds~(!bin_plus (as_dur_seconds value:left) (as_dur_seconds value:right) ) }; def epoch_ce_to_iso8601 := {def epoch_seconds:!Int (let temp_n:(split_epoch_seconds epoch_seconds:epoch_seconds) (let epoch_days:(!get_slot epoch_days temp_n) (let day_seconds:(!get_slot day_seconds temp_n) temp_a:(epoch_days_to_year_month_day epoch_days:epoch_days) (let day:(!get_slot day temp_a) month:(!get_slot month temp_a) temp_:(seconds_to_hour_minute_second day_seconds:day_seconds) year:(!get_slot year temp_a) (let hour:(!get_slot hour temp_) minute:(!get_slot minute temp_) second:(!get_slot second temp_) [. day:day hour:hour minute:minute month:month second:second year:year ] ) ) ) ) ) }; def epoch_ce_to_posix := {def epoch_seconds:!Int (!bin_plus epoch_seconds ce_to_posix_offset) }; def epoch_day_to_year_month_day := {def epoch_days:!Int ::Iso8601Date (let temp_e:(div_mod left:epoch_days right:days_in_quad_century) (let quad_centry_day:(!get_slot mod temp_e) quad_century:(!get_slot div temp_e) temp_r:(div_mod left:quad_century_day right:days_in_century) (let century_day:(!get_slot mod temp_r) century_p:(!get_slot div temp_r) (case tags (!bin_equals century_p 4) false:(let century:century_p temp_l:(div_mod left:century_day right:days_in_quad_year) (let quad_year:(!get_slot div temp_l) quad_year_day:(!get_slot mod temp_l) (let temp_y:(div_mod left:quad_year_day right:days_in_year) (let year_day:(!get_slot mod temp_y) year_p:(!get_slot div temp_y) (case tags (!bin_equals year_p 4) false:(let year:year_p (case tags (!bin_or (!bin_equals century_p 4) (!bin_equals year_p 4) ) false:(let year_day_7:year_day_p (let is_leap_year:(!bin_or (!bin_or (!bin_equals century_p 4) (!bin_greater_than year_p 2) ) (!bin_equals quad_year 24) ) month_p:(!bin_floor_divide (!bin_plus year_day_7 50) 32 ) (let month_start_day:(month_start is_leap_year:is_leap_year month:month_p ) (case tags (!bin_greater_than month_start_day year_day_7 ) false:(let month:month_p month_day:(!bin_minus year_day_7 month_start_day ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century) (!bin_times 100 century) ) year ) 1 ) ] ) true:(let month:(!bin_minus month_p 1) (let month_start_day_7:(month_start is_leap_year:is_leap_year month:month ) (let month_day:(!bin_minus year_day_7 month_start_day_7 ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century) (!bin_times 100 century) ) year ) 1 ) ] ) ) ) ) ) ) ) true:(let year_day_6:365 (let is_leap_year:(!bin_or (!bin_or (!bin_equals century_p 4) (!bin_greater_than year_p 2) ) (!bin_equals quad_year 24) ) month_p:(!bin_floor_divide (!bin_plus year_day_6 50) 32 ) (let month_start_day:(month_start is_leap_year:is_leap_year month:month_p ) (case tags (!bin_greater_than month_start_day year_day_6 ) false:(let month:month_p month_day:(!bin_minus year_day_6 month_start_day ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century) (!bin_times 100 century) ) year ) 1 ) ] ) true:(let month:(!bin_minus month_p 1) (let month_start_day_6:(month_start is_leap_year:is_leap_year month:month ) (let month_day:(!bin_minus year_day_6 month_start_day_6 ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century) (!bin_times 100 century) ) year ) 1 ) ] ) ) ) ) ) ) ) ) ) true:(let year:3 (case tags (!bin_or (!bin_equals century_p 4) (!bin_equals year_p 4) ) false:(let year_day_5:year_day_p (let is_leap_year:(!bin_or (!bin_or (!bin_equals century_p 4) (!bin_greater_than year_p 2) ) (!bin_equals quad_year 24) ) month_p:(!bin_floor_divide (!bin_plus year_day_5 50) 32 ) (let month_start_day:(month_start is_leap_year:is_leap_year month:month_p ) (case tags (!bin_greater_than month_start_day year_day_5 ) false:(let month:month_p month_day:(!bin_minus year_day_5 month_start_day ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century) (!bin_times 100 century) ) year ) 1 ) ] ) true:(let month:(!bin_minus month_p 1) (let month_start_day_5:(month_start is_leap_year:is_leap_year month:month ) (let month_day:(!bin_minus year_day_5 month_start_day_5 ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century) (!bin_times 100 century) ) year ) 1 ) ] ) ) ) ) ) ) ) true:(let year_day_4:365 (let is_leap_year:(!bin_or (!bin_or (!bin_equals century_p 4) (!bin_greater_than year_p 2) ) (!bin_equals quad_year 24) ) month_p:(!bin_floor_divide (!bin_plus year_day_4 50) 32 ) (let month_start_day:(month_start is_leap_year:is_leap_year month:month_p ) (case tags (!bin_greater_than month_start_day year_day_4 ) false:(let month:month_p month_day:(!bin_minus year_day_4 month_start_day ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century) (!bin_times 100 century) ) year ) 1 ) ] ) true:(let month:(!bin_minus month_p 1) (let month_start_day_4:(month_start is_leap_year:is_leap_year month:month ) (let month_day:(!bin_minus year_day_4 month_start_day_4 ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century) (!bin_times 100 century) ) year ) 1 ) ] ) ) ) ) ) ) ) ) ) ) ) ) ) ) true:(let century:3 temp_d:(div_mod left:century_day right:days_in_quad_year) (let quad_year:(!get_slot div temp_d) quad_year_day:(!get_slot mod temp_d) (let temp_q:(div_mod left:quad_year_day right:days_in_year) (let year_day:(!get_slot mod temp_q) year_p:(!get_slot div temp_q) (case tags (!bin_equals year_p 4) false:(let year:year_p (case tags (!bin_or (!bin_equals century_p 4) (!bin_equals year_p 4) ) false:(let year_day_3:year_day_p (let is_leap_year:(!bin_or (!bin_or (!bin_equals century_p 4) (!bin_greater_than year_p 2) ) (!bin_equals quad_year 24) ) month_p:(!bin_floor_divide (!bin_plus year_day_3 50) 32 ) (let month_start_day:(month_start is_leap_year:is_leap_year month:month_p ) (case tags (!bin_greater_than month_start_day year_day_3 ) false:(let month:month_p month_day:(!bin_minus year_day_3 month_start_day ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century) (!bin_times 100 century) ) year ) 1 ) ] ) true:(let month:(!bin_minus month_p 1) (let month_start_day_3:(month_start is_leap_year:is_leap_year month:month ) (let month_day:(!bin_minus year_day_3 month_start_day_3 ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century) (!bin_times 100 century) ) year ) 1 ) ] ) ) ) ) ) ) ) true:(let year_day_2:365 (let is_leap_year:(!bin_or (!bin_or (!bin_equals century_p 4) (!bin_greater_than year_p 2) ) (!bin_equals quad_year 24) ) month_p:(!bin_floor_divide (!bin_plus year_day_2 50) 32 ) (let month_start_day:(month_start is_leap_year:is_leap_year month:month_p ) (case tags (!bin_greater_than month_start_day year_day_2 ) false:(let month:month_p month_day:(!bin_minus year_day_2 month_start_day ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century) (!bin_times 100 century) ) year ) 1 ) ] ) true:(let month:(!bin_minus month_p 1) (let month_start_day_2:(month_start is_leap_year:is_leap_year month:month ) (let month_day:(!bin_minus year_day_2 month_start_day_2 ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century) (!bin_times 100 century) ) year ) 1 ) ] ) ) ) ) ) ) ) ) ) true:(let year:3 (case tags (!bin_or (!bin_equals century_p 4) (!bin_equals year_p 4) ) false:(let year_day_1:year_day_p (let is_leap_year:(!bin_or (!bin_or (!bin_equals century_p 4) (!bin_greater_than year_p 2) ) (!bin_equals quad_year 24) ) month_p:(!bin_floor_divide (!bin_plus year_day_1 50) 32 ) (let month_start_day:(month_start is_leap_year:is_leap_year month:month_p ) (case tags (!bin_greater_than month_start_day year_day_1 ) false:(let month:month_p month_day:(!bin_minus year_day_1 month_start_day ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century) (!bin_times 100 century) ) year ) 1 ) ] ) true:(let month:(!bin_minus month_p 1) (let month_start_day_1:(month_start is_leap_year:is_leap_year month:month ) (let month_day:(!bin_minus year_day_1 month_start_day_1 ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century) (!bin_times 100 century) ) year ) 1 ) ] ) ) ) ) ) ) ) true:(let year_day_0:365 (let is_leap_year:(!bin_or (!bin_or (!bin_equals century_p 4) (!bin_greater_than year_p 2) ) (!bin_equals quad_year 24) ) month_p:(!bin_floor_divide (!bin_plus year_day_0 50) 32 ) (let month_start_day:(month_start is_leap_year:is_leap_year month:month_p ) (case tags (!bin_greater_than month_start_day year_day_0 ) false:(let month:month_p month_day:(!bin_minus year_day_0 month_start_day ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century) (!bin_times 100 century) ) year ) 1 ) ] ) true:(let month:(!bin_minus month_p 1) (let month_start_day_0:(month_start is_leap_year:is_leap_year month:month ) (let month_day:(!bin_minus year_day_0 month_start_day_0 ) [. day:(!bin_plus month_day 1) month:(!bin_plus month 1) year:(!bin_plus (!bin_plus (!bin_plus (!bin_times 400 quad_century) (!bin_times 100 century) ) year ) 1 ) ] ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) }; def epoch_posix_to_ce := {def epoch_seconds:!Int (!bin_minus epoch_seconds ce_to_posix_offset) }; def epoch_posix_to_iso8601 := {def epoch_seconds:!Int (epoch_ce_to_iso8601 value:(epoch_posix_to_ce value:epoch_seconds)) }; def hour_minute_second_to_seconds := {def hour:!Int minute:!Int second:!Int (!bin_plus (!bin_plus (!bin_times 3600 hour) (!bin_times 60 minute)) second) }; def index_of_day_of_week := {def day:DayOfWeek ::!Int (!get_index [map #sun:0 #mon:1 #tue:2 #wed:3 #thu:4 #fri:5 #sat:6] day) }; def index_of_month := {def month:Month ::!Int (!get_index [map #jan:1 #feb:2 #mar:3 #apr:4 #may:5 #jun:6 #jul:7 #aug:8 #sep:9 #oct:10 #nov:11 #dec:12 ] month ) }; def iso8601_to_epoch_ce := {def datetime:Iso8601Simple (let temp_t:datetime (let day:(!get_slot day temp_t) hour:(!get_slot hour temp_t) minute:(!get_slot minute temp_t) month:(!get_slot month temp_t) second:(!get_slot second temp_t) year:(!get_slot year temp_t) (let day_seconds:(time_of_day_to_seconds hour:hour minute:minute second:second ) epoch_days:(year_month_day_to_epoch_days day:day month:month year:year) (combine_epoch_date day_seconds:day_seconds epoch_days:epoch_days) ) ) ) }; def iso8601_to_epoch_posix := {def datetime:Iso8601Simple (epoch_ce_to_posix value:(iso8601_to_epoch_ce value:datetime)) }; def month_from_index := {def index:!Int ::Month (!get_index [list #jan #feb #mar #apr #may #jun #jul #aug #sep #oct #nov #dec] (!bin_minus index 1) ) }; def month_start := {def is_leap_year:Boolean month:!Int (case tags is_leap_year false:(let starts:[list 0 31 59 90 120 151 181 212 243 273 304 334] (!get_index starts month) ) true:(let starts:[list 0 31 60 91 121 152 182 213 244 274 305 335] (!get_index starts month) ) ) }; def seconds_in_day := (!bin_times (!bin_times 24 60) 60); def seconds_to_hour_minute_second := {def seconds:!Int (let temp_j:(div_mod left:seconds right:60) (let minutes:(!get_slot div temp_j) (let second:(!get_slot mod temp_j) temp_w:(div_mod left:minutes right:60) (let hour:(!get_slot div temp_w) minute:(!get_slot mod temp_w) [. hour:hour minute:minute second:second] ) ) ) ) }; def split_epoch_ce := {def value:DateTime (split_epoch_seconds value:(as_epoch_ce value:value)) }; def split_epoch_seconds := {def epoch_seconds:!Int (let temp_g:(div_mod left:epoch_seconds right:seconds_in_day) (let div:(!get_slot div temp_g) mod:(!get_slot mod temp_g) [. day_seconds:mod epoch_days:div] ) ) }; def year_month_day_to_epoch_days := {def day:!Int month:!Int year:!Int ::Index (let temp_o:(div_mod left:(!bin_minus year 1) right:400) (let quad_century:(!get_slot div temp_o) quad_century_year:(!get_slot mod temp_o) (let temp_h:(div_mod left:quad_century_year right:100) (let century:(!get_slot div temp_h) century_year:(!get_slot mod temp_h) (let temp_u:(div_mod left:century_year right:4) (let quad_year:(!get_slot mod temp_u) quad_years:(!get_slot div temp_u) (let is_leap_year:(!bin_and (!bin_equals quad_year 3) (!bin_or (!bin_not_equal century_year 99) (!bin_equals century 3) ) ) (let month_start_day:(month_start is_leap_year:is_leap_year month:month ) (!bin_plus (!bin_plus (!bin_plus (!bin_plus (!bin_plus (!bin_minus day 1) month_start_day) (!bin_times days_in_year quad_year) ) (!bin_times days_in_quad_year quad_years) ) (!bin_times days_in_century century) ) (!bin_times days_in_quad_century quad_century) ) ) ) ) ) ) ) ) ) };