From 1a308eadf53fe2ffbc6a36e2ad13d29f213f1925 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Tue, 9 Dec 2025 08:35:39 -0800 Subject: [PATCH] [prelude] add `date` functions --- src/prelude/date.ts | 12 +++ src/prelude/index.ts | 2 + src/prelude/tests/date.test.ts | 170 +++++++++++++++++++++++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 src/prelude/date.ts create mode 100644 src/prelude/tests/date.test.ts diff --git a/src/prelude/date.ts b/src/prelude/date.ts new file mode 100644 index 0000000..dda92ef --- /dev/null +++ b/src/prelude/date.ts @@ -0,0 +1,12 @@ +export const date = { + now: () => Date.now(), + year: (time: number) => (new Date(time)).getFullYear(), + month: (time: number) => (new Date(time)).getMonth(), + date: (time: number) => (new Date(time)).getDate(), + hour: (time: number) => (new Date(time)).getHours(), + minute: (time: number) => (new Date(time)).getMinutes(), + second: (time: number) => (new Date(time)).getSeconds(), + ms: (time: number) => (new Date(time)).getMilliseconds(), + new: (year: number, month: number, day: number, hour = 0, minute = 0, second = 0, ms = 0) => + new Date(year, month, day, hour, minute, second, ms).getTime() +} \ No newline at end of file diff --git a/src/prelude/index.ts b/src/prelude/index.ts index 2a01236..d69cfc4 100644 --- a/src/prelude/index.ts +++ b/src/prelude/index.ts @@ -6,6 +6,7 @@ import { extractParamInfo, isWrapped, getOriginalFunction, } from 'reefvm' +import { date } from './date' import { dict } from './dict' import { fs } from './fs' import { json } from './json' @@ -16,6 +17,7 @@ import { str } from './str' import { types } from './types' export const globals: Record = { + date, dict, fs, json, diff --git a/src/prelude/tests/date.test.ts b/src/prelude/tests/date.test.ts new file mode 100644 index 0000000..9a2f4a5 --- /dev/null +++ b/src/prelude/tests/date.test.ts @@ -0,0 +1,170 @@ +import { expect, describe, test } from 'bun:test' + +describe('date', () => { + test('date.now returns current timestamp', () => { + expect(`date.now | number?`).toEvaluateTo(true) + + expect(`(date.now) > 1577836800000`).toEvaluateTo(true) + }) + + test('date.new creates timestamp from components', () => { + expect(` + t = date.new 2024 0 1 12 0 0 500 + [ + (date.year t) + (date.month t) + (date.date t) + (date.hour t) + (date.minute t) + (date.second t) + (date.ms t) + ] + `).toEvaluateTo([2024, 0, 1, 12, 0, 0, 500]) + }) + + test('date.new with minimal arguments', () => { + expect(` + t = date.new 2024 5 15 + [ + (date.year t) + (date.month t) + (date.date t) + (date.hour t) + (date.minute t) + (date.second t) + (date.ms t) + ] + `).toEvaluateTo([2024, 5, 15, 0, 0, 0, 0]) + }) + + test('date.year extracts year', () => { + expect(` + t = date.new 2024 0 1 + date.year t + `).toEvaluateTo(2024) + + expect(` + t = date.new 1999 11 31 + date.year t + `).toEvaluateTo(1999) + }) + + test('date.month extracts month (0-indexed)', () => { + // January = 0, December = 11 + expect(` + jan = date.new 2024 0 1 + dec = date.new 2024 11 31 + [(date.month jan) (date.month dec)] + `).toEvaluateTo([0, 11]) + }) + + test('date.date extracts day of month', () => { + expect(` + t = date.new 2024 5 15 + date.date t + `).toEvaluateTo(15) + + expect(` + date.new 2024 0 1 | date.date + `).toEvaluateTo(1) + }) + + test('date.hour extracts hour', () => { + expect(` + t = date.new 2024 0 1 14 30 45 + date.hour t + `).toEvaluateTo(14) + + expect(` + t = date.new 2024 0 1 0 0 0 + date.hour t + `).toEvaluateTo(0) + }) + + test('date.minute extracts minute', () => { + expect(` + t = date.new 2024 0 1 14 30 45 + date.minute t + `).toEvaluateTo(30) + }) + + test('date.second extracts second', () => { + expect(` + t = date.new 2024 0 1 14 30 45 + date.second t + `).toEvaluateTo(45) + }) + + test('date.ms extracts milliseconds', () => { + expect(` + t = date.new 2024 0 1 14 30 45 250 + date.ms t + `).toEvaluateTo(250) + }) + + test('round-trip: create and extract components', () => { + expect(` + t = date.new 2024 6 4 15 30 45 123 + year = date.year t + month = date.month t + day = date.date t + hour = date.hour t + min = date.minute t + sec = date.second t + ms = date.ms t + [year month day hour min sec ms] + `).toEvaluateTo([2024, 6, 4, 15, 30, 45, 123]) + }) + + test('edge cases - midnight', () => { + expect(` + t = date.new 2024 0 1 0 0 0 0 + [ + (date.hour t) + (date.minute t) + (date.second t) + (date.ms t) + ] + `).toEvaluateTo([0, 0, 0, 0]) + }) + + test('edge cases - end of day', () => { + expect(` + t = date.new 2024 0 1 23 59 59 999 + [ + (date.hour t) + (date.minute t) + (date.second t) + (date.ms t) + ] + `).toEvaluateTo([23, 59, 59, 999]) + }) + + test('edge cases - leap year', () => { + expect(` + t = date.new 2024 1 29 + [ + (date.year t) + (date.month t) + (date.date t) + ] + `).toEvaluateTo([2024, 1, 29]) + }) + + test('combining date functions with arithmetic', () => { + expect(` + t = date.new 2024 5 15 10 30 0 + next-hour = date.new 2024 5 15 11 30 0 + (date.hour next-hour) - (date.hour t) + `).toEvaluateTo(1) + }) + + test('using date.now in calculations', () => { + // Check that date.now is in the past compared to a future timestamp + expect(` + now = (date.now) + future = date.new 2030 0 1 + future > now + `).toEvaluateTo(true) + }) +}) \ No newline at end of file