From bb5af13ddc981ccac1a2fdcd66c87dcf2442d838 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Mon, 23 May 2022 13:44:41 -0400 Subject: [PATCH] Moved DateTime functionality into separate file --- .../ReducerInterface_DateTime.res | 48 +++++------ .../ReducerInterface_ExpressionValue.res | 4 +- .../src/rescript/Utility/DateTime.res | 79 ++++++++++++++++++ .../squiggle-lang/src/rescript/Utility/E.res | 82 +------------------ 4 files changed, 106 insertions(+), 107 deletions(-) create mode 100644 packages/squiggle-lang/src/rescript/Utility/DateTime.res diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_DateTime.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_DateTime.res index a0059392..ee7b1a7c 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_DateTime.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_DateTime.res @@ -5,22 +5,22 @@ let dateDispatch = (call: ExpressionValue.functionCall, env: DistributionOperati result, > => { switch call { - | ("toString", [EvDate(t)]) => EvString(E.Date.toString(t))->Ok->Some + | ("toString", [EvDate(t)]) => EvString(DateTime.Date.toString(t))->Ok->Some | ("makeDateFromYear", [EvNumber(year)]) => - switch E.Date.makeFromYear(year) { + switch DateTime.Date.makeFromYear(year) { | Ok(t) => EvDate(t)->Ok->Some | Error(e) => RETodo(e)->Error->Some } - | ("dateFromNumber", [EvNumber(f)]) => EvDate(E.Date.fromFloat(f))->Ok->Some - | ("toNumber", [EvDate(f)]) => EvNumber(E.Date.toFloat(f))->Ok->Some + | ("dateFromNumber", [EvNumber(f)]) => EvDate(DateTime.Date.fromFloat(f))->Ok->Some + | ("toNumber", [EvDate(f)]) => EvNumber(DateTime.Date.toFloat(f))->Ok->Some | ("subtract", [EvDate(d1), EvDate(d2)]) => - switch E.Date.subtract(d1, d2) { + switch DateTime.Date.subtract(d1, d2) { | Ok(d) => EvTimeDuration(d)->Ok | Error(e) => Error(RETodo(e)) }->Some | ("subtract", [EvDate(d1), EvTimeDuration(d2)]) => - EvDate(E.Date.subtractDuration(d1, d2))->Ok->Some - | ("add", [EvDate(d1), EvTimeDuration(d2)]) => EvDate(E.Date.addDuration(d1, d2))->Ok->Some + EvDate(DateTime.Date.subtractDuration(d1, d2))->Ok->Some + | ("add", [EvDate(d1), EvTimeDuration(d2)]) => EvDate(DateTime.Date.addDuration(d1, d2))->Ok->Some | _ => None } } @@ -29,23 +29,23 @@ let durationDispatch = (call: ExpressionValue.functionCall, _: DistributionOpera result, > => { switch call { - | ("toString", [EvTimeDuration(t)]) => EvString(E.Duration.toString(t))->Ok->Some - | ("hours", [EvNumber(f)]) => EvTimeDuration(E.Duration.fromHours(f))->Ok->Some - | ("minutes", [EvNumber(f)]) => EvTimeDuration(E.Duration.fromMinutes(f))->Ok->Some - | ("days", [EvNumber(f)]) => EvTimeDuration(E.Duration.fromDays(f))->Ok->Some - | ("years", [EvNumber(f)]) => EvTimeDuration(E.Duration.fromYears(f))->Ok->Some - | ("toHours", [EvTimeDuration(f)]) => EvNumber(E.Duration.toHours(f))->Ok->Some - | ("toMinutes", [EvTimeDuration(f)]) => EvNumber(E.Duration.toMinutes(f))->Ok->Some - | ("toDays", [EvTimeDuration(f)]) => EvNumber(E.Duration.toDays(f))->Ok->Some - | ("toYears", [EvTimeDuration(f)]) => EvNumber(E.Duration.toYears(f))->Ok->Some - | ("add", [EvTimeDuration(d1), EvTimeDuration(d2)]) => - EvTimeDuration(E.Duration.add(d1, d2))->Ok->Some - | ("subtract", [EvTimeDuration(d1), EvTimeDuration(d2)]) => - EvTimeDuration(E.Duration.subtract(d1, d2))->Ok->Some - | ("multiply", [EvTimeDuration(d1), EvNumber(d2)]) => - EvTimeDuration(E.Duration.multiply(d1, d2))->Ok->Some - | ("divide", [EvTimeDuration(d1), EvNumber(d2)]) => - EvTimeDuration(E.Duration.divide(d1, d2))->Ok->Some + | ("toString", [EvTimeDuration(t)]) => EvString(DateTime.Duration.toString(t))->Ok->Some + | ("hours", [EvNumber(f)]) => EvTimeDuration(DateTime.Duration.fromHours(f))->Ok->Some + | ("minutes", [EvNumber(f)]) => EvTimeDuration(DateTime.Duration.fromMinutes(f))->Ok->Some + | ("days", [EvNumber(f)]) => EvTimeDuration(DateTime.Duration.fromDays(f))->Ok->Some + | ("years", [EvNumber(f)]) => EvTimeDuration(DateTime.Duration.fromYears(f))->Ok->Some + | ("toHours", [EvTimeDuration(f)]) => EvNumber(DateTime.Duration.toHours(f))->Ok->Some + | ("toMinutes", [EvTimeDuration(f)]) => EvNumber(DateTime.Duration.toMinutes(f))->Ok->Some + | ("toDays", [EvTimeDuration(f)]) => EvNumber(DateTime.Duration.toDays(f))->Ok->Some + | ("toYears", [EvTimeDuration(f)]) => EvNumber(DateTime.Duration.toYears(f))->Ok->Some + | ("add", [EvTimeDuration(d1), EvTimeDuration(d2)]) => + EvTimeDuration(DateTime.Duration.add(d1, d2))->Ok->Some + | ("subtract", [EvTimeDuration(d1), EvTimeDuration(d2)]) => + EvTimeDuration(DateTime.Duration.subtract(d1, d2))->Ok->Some + | ("multiply", [EvTimeDuration(d1), EvNumber(d2)]) => + EvTimeDuration(DateTime.Duration.multiply(d1, d2))->Ok->Some + | ("divide", [EvTimeDuration(d1), EvNumber(d2)]) => + EvTimeDuration(DateTime.Duration.divide(d1, d2))->Ok->Some | _ => None } } diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res index 6da34db1..c9363606 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res @@ -53,8 +53,8 @@ let rec toString = aValue => | EvSymbol(aString) => `:${aString}` | EvRecord(aRecord) => aRecord->toStringRecord | EvDistribution(dist) => GenericDist.toString(dist) - | EvDate(date) => E.Date.toString(date) - | EvTimeDuration(t) => E.Duration.toString(t) + | EvDate(date) => DateTime.Date.toString(date) + | EvTimeDuration(t) => DateTime.Duration.toString(t) } and toStringRecord = aRecord => { let pairs = diff --git a/packages/squiggle-lang/src/rescript/Utility/DateTime.res b/packages/squiggle-lang/src/rescript/Utility/DateTime.res new file mode 100644 index 00000000..344c8760 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/Utility/DateTime.res @@ -0,0 +1,79 @@ +module Duration = { + //Stores in Unix milliseconds + type t = float + let minute = Belt.Float.fromInt(60 * 1000) + let hour = Belt.Float.fromInt(60 * 60 * 1000) + let day = Belt.Float.fromInt(24 * 60 * 60 * 1000) + let year = Belt.Float.fromInt(24 * 60 * 60 * 1000) *. 365.25 + let fromFloat = (f: float): t => f + let toFloat = (d: t): float => d + let fromMinutes = (h: float): t => h *. minute + let fromHours = (h: float): t => h *. hour + let fromDays = (d: float): t => d *. day + let fromYears = (y: float): t => y *. year + let toMinutes = (t: t): float => t /. minute + let toHours = (t: t): float => t /. hour + let toDays = (t: t): float => t /. day + let toYears = (t: t): float => t /. year + + let toString = (t: t): string => { + let shouldPluralize = f => f != 1.0 + let display = (f: float, s: string) => + `${E.Float.with3DigitsPrecision(f)} ${s}${shouldPluralize(f) ? "s" : ""}` + let abs = Js.Math.abs_float(t) + if abs >= year { + display(t /. year, "year") + } else if abs >= day { + display(t /. day, "day") + } else if abs >= hour { + display(t /. hour, "hour") + } else if abs >= minute { + display(t /. minute, "minute") + } else { + E.Float.toFixed(t) ++ "ms" + } + } + let add = (t1: t, t2: t): t => t1 +. t2 + let subtract = (t1: t, t2: t): t => t1 -. t2 + let multiply = (t1: t, t2: float): t => t1 *. t2 + let divide = (t1: t, t2: float): t => t1 /. t2 +} + +module Date = { + //The Rescript/JS implementation of Date is pretty mediocre. It would be good to improve upon later. + type t = Js.Date.t + let toFloat = Js.Date.getTime + let getFullYear = Js.Date.getFullYear + let toString = Js.Date.toDateString + let fromFloat = Js.Date.fromFloat + let fmap = (t: t, fn: float => float) => t->toFloat->fn->fromFloat + let subtract = (t1: t, t2: t) => { + let (f1, f2) = (toFloat(t1), toFloat(t2)) + let diff = f1 -. f2 + if diff < 0.0 { + Error("Cannot subtract a date by one that is in its future") + } else { + Ok(Duration.fromFloat(diff)) + } + } + let addDuration = (t: t, duration: Duration.t) => fmap(t, t => t +. duration) + let subtractDuration = (t: t, duration: Duration.t) => fmap(t, t => t -. duration) + //The Js.Date.makeWithYM function accepts a float, but only treats it as a whole number. + //Our version takes an integer to make this distinction clearer. + let makeWithYearInt = (y: int): result => { + if y < 100 { + Error("Year must be over 100") + } else if y > 200000 { + Error("Year must be less than 200000") + } else { + Ok(Js.Date.makeWithYM(~year=Belt.Float.fromInt(y), ~month=0.0, ())) + } + } + let makeFromYear = (year: float): result => { + let floor = year->Js.Math.floor_float + makeWithYearInt(Belt.Float.toInt(floor))->E.R2.fmap(earlyDate => { + let diff = year -. floor + earlyDate->addDuration(diff *. Duration.year) + }) + } +} diff --git a/packages/squiggle-lang/src/rescript/Utility/E.res b/packages/squiggle-lang/src/rescript/Utility/E.res index 46787134..77b5c5e4 100644 --- a/packages/squiggle-lang/src/rescript/Utility/E.res +++ b/packages/squiggle-lang/src/rescript/Utility/E.res @@ -832,84 +832,4 @@ module JsArray = { |> Js.Array.filter(O.isSome) |> Js.Array.map(O.toExn("Warning: This should not have happened")) let filter = Js.Array.filter -} - -module Duration = { - //Stores in Unix milliseconds - type t = float - let minute = Belt.Float.fromInt(60 * 1000) - let hour = Belt.Float.fromInt(60 * 60 * 1000) - let day = Belt.Float.fromInt(24 * 60 * 60 * 1000) - let year = Belt.Float.fromInt(24 * 60 * 60 * 1000) *. 365.25 - let fromFloat = (f: float): t => f - let toFloat = (d: t): float => d - let fromMinutes = (h: float): t => h *. minute - let fromHours = (h: float): t => h *. hour - let fromDays = (d: float): t => d *. day - let fromYears = (y: float): t => y *. year - let toMinutes = (t: t): float => t /. minute - let toHours = (t: t): float => t /. hour - let toDays = (t: t): float => t /. day - let toYears = (t: t): float => t /. year - - let toString = (t: t): string => { - let shouldPluralize = f => f != 1.0 - let display = (f: float, s: string) => - `${Float.with3DigitsPrecision(f)} ${s}${shouldPluralize(f) ? "s" : ""}` - let abs = Js.Math.abs_float(t) - if abs >= year { - display(t /. year, "year") - } else if abs >= day { - display(t /. day, "day") - } else if abs >= hour { - display(t /. hour, "hour") - } else if abs >= minute { - display(t /. minute, "minute") - } else { - Float.toFixed(t) ++ "ms" - } - } - let add = (t1: t, t2: t): t => t1 +. t2 - let subtract = (t1: t, t2: t): t => t1 -. t2 - let multiply = (t1: t, t2: float): t => t1 *. t2 - let divide = (t1: t, t2: float): t => t1 /. t2 -} - -module Date = { - //The Rescript/JS implementation of Date is pretty mediocre. It would be good to improve upon later. - type t = Js.Date.t - let toFloat = Js.Date.getTime - let getFullYear = Js.Date.getFullYear - let toString = Js.Date.toDateString - let fromFloat = Js.Date.fromFloat - let fmap = (t: t, fn: float => float) => t->toFloat->fn->fromFloat - let subtract = (t1: t, t2: t) => { - let (f1, f2) = (toFloat(t1), toFloat(t2)) - let diff = f1 -. f2 - if diff < 0.0 { - Error("Cannot subtract a date by one that is in its future") - } else { - Ok(Duration.fromFloat(diff)) - } - } - let addDuration = (t: t, duration: Duration.t) => fmap(t, t => t +. duration) - let subtractDuration = (t: t, duration: Duration.t) => fmap(t, t => t -. duration) - //The Js.Date.makeWithYM function accepts a float, but only treats it as a whole number. - //Our version takes an integer to make this distinction clearer. - let makeWithYearInt = (y: int): result => { - if y < 100 { - Error("Year must be over 100") - } else if y > 200000 { - Error("Year must be less than 200000") - } else { - Ok(Js.Date.makeWithYM(~year=Belt.Float.fromInt(y), ~month=0.0, ())) - } - } - let makeFromYear = (year: float): result => { - let floor = year->Js.Math.floor_float - makeWithYearInt(Belt.Float.toInt(floor))->R2.fmap(earlyDate => { - let diff = year -. floor - earlyDate->addDuration(diff *. Duration.year) - }) - } -} +} \ No newline at end of file