diff --git a/src/lib/utils/conversions.test.ts b/src/lib/utils/conversions.test.ts new file mode 100644 index 0000000..1a7c532 --- /dev/null +++ b/src/lib/utils/conversions.test.ts @@ -0,0 +1,50 @@ +import { describe, it, expect } from 'vitest'; +import { cmToFeetInches, feetInchesToCm, kgToLb, lbToKg } from './conversions'; + +describe('cmToFeetInches', () => { + it('converts 180 cm', () => { + expect(cmToFeetInches(180)).toBe('5\'11"'); + }); + + it('converts 152 cm', () => { + expect(cmToFeetInches(152)).toBe('5\'0"'); + }); + + it('converts 0 cm', () => { + expect(cmToFeetInches(0)).toBe('0\'0"'); + }); + + it('converts 30 cm (just inches)', () => { + expect(cmToFeetInches(30)).toBe('1\'0"'); + }); +}); + +describe('feetInchesToCm', () => { + it('converts 5\'11" back', () => { + expect(feetInchesToCm(5, 11)).toBeCloseTo(180.34, 0); + }); + + it('converts 0\'0"', () => { + expect(feetInchesToCm(0, 0)).toBe(0); + }); +}); + +describe('kgToLb', () => { + it('converts 75 kg', () => { + expect(kgToLb(75)).toBeCloseTo(165.35, 0); + }); + + it('converts 0 kg', () => { + expect(kgToLb(0)).toBe(0); + }); +}); + +describe('lbToKg', () => { + it('converts 165 lb', () => { + expect(lbToKg(165)).toBeCloseTo(74.84, 0); + }); + + it('converts 0 lb', () => { + expect(lbToKg(0)).toBe(0); + }); +}); diff --git a/src/lib/utils/conversions.ts b/src/lib/utils/conversions.ts new file mode 100644 index 0000000..9655ccf --- /dev/null +++ b/src/lib/utils/conversions.ts @@ -0,0 +1,22 @@ +const CM_PER_INCH = 2.54; +const INCHES_PER_FOOT = 12; +const LB_PER_KG = 2.20462; + +export function cmToFeetInches(cm: number): string { + const totalInches = Math.round(cm / CM_PER_INCH); + const feet = Math.floor(totalInches / INCHES_PER_FOOT); + const inches = totalInches % INCHES_PER_FOOT; + return `${feet}'${inches}"`; +} + +export function feetInchesToCm(feet: number, inches: number): number { + return (feet * INCHES_PER_FOOT + inches) * CM_PER_INCH; +} + +export function kgToLb(kg: number): number { + return kg * LB_PER_KG; +} + +export function lbToKg(lb: number): number { + return lb / LB_PER_KG; +} diff --git a/src/lib/utils/dates.test.ts b/src/lib/utils/dates.test.ts new file mode 100644 index 0000000..06b148e --- /dev/null +++ b/src/lib/utils/dates.test.ts @@ -0,0 +1,33 @@ +import { describe, it, expect } from 'vitest'; +import { icYear, formatICDate } from './dates'; + +describe('icYear', () => { + it('adds 442 to the real year', () => { + expect(icYear(2026)).toBe(2468); + }); + + it('works for other years', () => { + expect(icYear(2000)).toBe(2442); + }); +}); + +describe('formatICDate', () => { + it('formats with ordinal suffixes', () => { + expect(formatICDate(new Date(2026, 2, 1))).toBe('March 1st, 2468'); + expect(formatICDate(new Date(2026, 2, 2))).toBe('March 2nd, 2468'); + expect(formatICDate(new Date(2026, 2, 3))).toBe('March 3rd, 2468'); + expect(formatICDate(new Date(2026, 2, 4))).toBe('March 4th, 2468'); + }); + + it('handles 11th, 12th, 13th', () => { + expect(formatICDate(new Date(2026, 0, 11))).toBe('January 11th, 2468'); + expect(formatICDate(new Date(2026, 0, 12))).toBe('January 12th, 2468'); + expect(formatICDate(new Date(2026, 0, 13))).toBe('January 13th, 2468'); + }); + + it('handles 21st, 22nd, 23rd', () => { + expect(formatICDate(new Date(2026, 5, 21))).toBe('June 21st, 2468'); + expect(formatICDate(new Date(2026, 5, 22))).toBe('June 22nd, 2468'); + expect(formatICDate(new Date(2026, 5, 23))).toBe('June 23rd, 2468'); + }); +}); diff --git a/src/lib/utils/dates.ts b/src/lib/utils/dates.ts new file mode 100644 index 0000000..c68a1df --- /dev/null +++ b/src/lib/utils/dates.ts @@ -0,0 +1,24 @@ +const IC_OFFSET = 442; + +const MONTHS = [ + 'January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December' +]; + +function ordinal(n: number): string { + if (n >= 11 && n <= 13) return n + 'th'; + switch (n % 10) { + case 1: return n + 'st'; + case 2: return n + 'nd'; + case 3: return n + 'rd'; + default: return n + 'th'; + } +} + +export function icYear(realYear: number): number { + return realYear + IC_OFFSET; +} + +export function formatICDate(date: Date): string { + return `${MONTHS[date.getMonth()]} ${ordinal(date.getDate())}, ${icYear(date.getFullYear())}`; +}