@gobrand/tiempo 2.3.5 → 2.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +23 -986
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -4,1006 +4,43 @@
4
4
  [![CI](https://github.com/go-brand/tiempo/actions/workflows/ci.yml/badge.svg)](https://github.com/go-brand/tiempo/actions/workflows/ci.yml)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
 
7
- Comprehensive datetime utilities for the [Temporal API](https://tc39.es/proposal-temporal/docs/). Full timezone support, nanosecond precision, and a familiar API.
7
+ ![tiempo clock visualization](./clock-visualization.png)
8
8
 
9
- ## Installation
9
+ **Timezone conversions that don't suck.** Built on the [Temporal API](https://tc39.es/proposal-temporal/docs/).
10
10
 
11
- ```bash
12
- npm install @gobrand/tiempo
13
- # or
14
- pnpm add @gobrand/tiempo
15
- # or
16
- yarn add @gobrand/tiempo
17
- ```
18
-
19
- ## Why tiempo?
20
-
21
- JavaScript's `Date` object was modeled after a Java class that was deprecated over 25 years ago. It conflates timestamps with calendar dates, only works in UTC or device-local time, and its mutating methods cause countless bugs. The [Temporal API](https://tc39.es/proposal-temporal/docs/) is the modern replacement, designed from the ground up to fix these problems.
22
-
23
- **tiempo** makes Temporal accessible with an intuitive, date-fns-style API:
24
-
25
- ### The Temporal Advantage
26
-
27
- | Problem with `Date` | Temporal Solution | tiempo API |
28
- |---------------------|-------------------|------------|
29
- | Only UTC or local time | Any IANA timezone | `toZonedTime(date, "America/New_York")` |
30
- | Millisecond precision | Nanosecond precision | `addNanoseconds()`, `differenceInNanoseconds()` |
31
- | Mutating methods | Immutable operations | All functions return new values |
32
- | Ambiguous representations | Distinct types for each use case | `Instant`, `ZonedDateTime`, `PlainDate` |
33
- | DST bugs everywhere | DST-aware arithmetic | `addDays()` respects timezone rules |
34
- | Gregorian calendar only | Multi-calendar support | Works with Hebrew, Chinese, Japanese, and more |
35
-
36
- ### Features
37
-
38
- - **First-class timezone support** - Convert, compare, and calculate across any of 400+ IANA timezones with confidence
39
- - **Nanosecond precision** - Beyond milliseconds for scientific computing, financial systems, and high-frequency operations
40
- - **DST-safe arithmetic** - Adding days means calendar days, not 24-hour periods that break at clock changes
41
- - **Immutable by design** - No mutations, no side effects, no surprises
42
- - **Type-safe** - Full TypeScript support catches datetime errors at compile time
43
- - **Familiar API** - date-fns-style functions built on modern foundations
44
- - **Zero dependencies** - Only the Temporal polyfill for browser compatibility
45
-
46
- ### Perfect for
47
-
48
- - **Scheduling apps** - Social media, meetings, reminders with proper timezone handling
49
- - **Booking systems** - Hotels, flights, events across global timezones
50
- - **Calendar applications** - Date math that respects DST and leap years
51
- - **Time tracking** - Precise duration calculations with nanosecond accuracy
52
- - **Analytics dashboards** - Aggregate by day/week/month in user timezones
53
- - **Global applications** - Any app serving users across multiple timezones
54
-
55
- ## Quick Start
56
-
57
- ```typescript
58
- import { toZonedTime, toUtcString, toUtc, toDate } from '@gobrand/tiempo';
59
-
60
- // From ISO string (typical backend API)
61
- const zoned = toZonedTime("2025-01-20T20:00:00.000Z", "America/New_York");
62
- console.log(zoned.hour); // 15 (3 PM in New York)
63
-
64
- // From Date object (e.g., Drizzle ORM)
65
- const date = new Date("2025-01-20T20:00:00.000Z");
66
- const zonedFromDate = toZonedTime(date, "America/New_York");
67
- console.log(zonedFromDate.hour); // 15 (3 PM in New York)
68
-
69
- // Back to ISO string
70
- const utcString = toUtcString(zoned);
71
- console.log(utcString); // "2025-01-20T20:00:00Z"
72
-
73
- // Back to Date object (for Drizzle ORM)
74
- const backToDate = toDate(zoned);
75
- console.log(backToDate.toISOString()); // "2025-01-20T20:00:00.000Z"
76
- ```
77
-
78
- ## API
79
-
80
- ### Core Conversions
81
-
82
- #### `toZonedTime(input, timezone)`
83
-
84
- Convert a UTC ISO string, Date, Instant, or ZonedDateTime to a ZonedDateTime in the specified timezone.
85
-
86
- **Parameters:**
87
- - `input` (string | Date | Temporal.Instant | Temporal.ZonedDateTime): A UTC ISO 8601 string, Date object, Temporal.Instant, or Temporal.ZonedDateTime
88
- - `timezone` (string): An IANA timezone identifier (e.g., `"America/New_York"`, `"Europe/London"`)
89
-
90
- **Returns:** `Temporal.ZonedDateTime` - The same instant in the specified timezone
91
-
92
- **Example:**
93
- ```typescript
94
- import { toZonedTime } from '@gobrand/tiempo';
95
-
96
- // From ISO string
97
- const zoned = toZonedTime("2025-01-20T20:00:00.000Z", "America/New_York");
98
- console.log(zoned.hour); // 15 (3 PM in New York)
99
- console.log(zoned.toString()); // "2025-01-20T15:00:00-05:00[America/New_York]"
100
-
101
- // From Date (e.g., from Drizzle ORM)
102
- const date = new Date("2025-01-20T20:00:00.000Z");
103
- const zoned2 = toZonedTime(date, "America/New_York");
104
- console.log(zoned2.hour); // 15 (3 PM in New York)
105
-
106
- // From Instant
107
- const instant = Temporal.Instant.from("2025-01-20T20:00:00Z");
108
- const zoned3 = toZonedTime(instant, "Asia/Tokyo");
109
-
110
- // From ZonedDateTime (convert to different timezone)
111
- const nyTime = Temporal.ZonedDateTime.from("2025-01-20T15:00:00-05:00[America/New_York]");
112
- const tokyoTime = toZonedTime(nyTime, "Asia/Tokyo");
113
- ```
114
-
115
- #### `toUtc(input)`
116
-
117
- Convert a UTC ISO string, Date, or ZonedDateTime to a Temporal.Instant (UTC).
118
-
119
- **Parameters:**
120
- - `input` (string | Date | Temporal.ZonedDateTime): A UTC ISO 8601 string, Date object, or Temporal.ZonedDateTime
121
-
122
- **Returns:** `Temporal.Instant` - A Temporal.Instant representing the same moment in UTC
123
-
124
- **Example:**
125
- ```typescript
126
- import { toUtc } from '@gobrand/tiempo';
127
-
128
- // From ISO string
129
- const instant = toUtc("2025-01-20T20:00:00.000Z");
130
-
131
- // From Date (e.g., from Drizzle ORM)
132
- const date = new Date("2025-01-20T20:00:00.000Z");
133
- const instant2 = toUtc(date);
134
-
135
- // From ZonedDateTime
136
- const zoned = Temporal.ZonedDateTime.from("2025-01-20T15:00:00-05:00[America/New_York]");
137
- const instant3 = toUtc(zoned);
138
- // All represent the same UTC moment: 2025-01-20T20:00:00Z
139
- ```
140
-
141
- #### `toUtcString(input)`
142
-
143
- Convert a Temporal.Instant or ZonedDateTime to a UTC ISO 8601 string.
144
-
145
- **Parameters:**
146
- - `input` (Temporal.Instant | Temporal.ZonedDateTime): A Temporal.Instant or Temporal.ZonedDateTime
147
-
148
- **Returns:** `string` - A UTC ISO 8601 string representation
149
-
150
- **Example:**
151
- ```typescript
152
- import { toUtcString } from '@gobrand/tiempo';
153
-
154
- // From ZonedDateTime
155
- const zoned = Temporal.ZonedDateTime.from("2025-01-20T15:00:00-05:00[America/New_York]");
156
- const iso = toUtcString(zoned);
157
- console.log(iso); // "2025-01-20T20:00:00Z"
158
-
159
- // From Instant
160
- const instant = Temporal.Instant.from("2025-01-20T20:00:00Z");
161
- const iso2 = toUtcString(instant);
162
- console.log(iso2); // "2025-01-20T20:00:00Z"
163
- ```
164
-
165
- #### `toDate(input)`
166
-
167
- Convert a Temporal.Instant or ZonedDateTime to a Date object.
168
-
169
- **Parameters:**
170
- - `input` (Temporal.Instant | Temporal.ZonedDateTime): A Temporal.Instant or Temporal.ZonedDateTime
171
-
172
- **Returns:** `Date` - A Date object representing the same moment in time
173
-
174
- **Example:**
175
- ```typescript
176
- import { toDate } from '@gobrand/tiempo';
177
-
178
- // From Instant
179
- const instant = Temporal.Instant.from("2025-01-20T20:00:00Z");
180
- const date = toDate(instant);
181
- console.log(date.toISOString()); // "2025-01-20T20:00:00.000Z"
182
-
183
- // From ZonedDateTime
184
- const zoned = Temporal.ZonedDateTime.from("2025-01-20T15:00:00-05:00[America/New_York]");
185
- const date2 = toDate(zoned);
186
- console.log(date2.toISOString()); // "2025-01-20T20:00:00.000Z"
187
-
188
- // Use with Drizzle ORM (for storing back to database)
189
- const instant = Temporal.Instant.from("2025-01-20T20:00:00Z");
190
- const dateForDb = toDate(instant);
191
- await db.update(posts).set({ publishedAt: dateForDb });
192
- ```
193
-
194
- ### Formatting
195
-
196
- #### `intlFormatDistance(laterDate, earlierDate, options?)`
197
-
198
- Format the distance between two dates as a human-readable, internationalized string using `Intl.RelativeTimeFormat`. Automatically selects the most appropriate unit (seconds, minutes, hours, days, weeks, months, years) based on the distance.
199
-
200
- **Parameters:**
201
- - `laterDate` (Temporal.Instant | Temporal.ZonedDateTime): The later date to compare
202
- - `earlierDate` (Temporal.Instant | Temporal.ZonedDateTime): The earlier date to compare with
203
- - `options` (IntlFormatDistanceOptions, optional): Formatting options
204
- - `unit` (Intl.RelativeTimeFormatUnit): Force a specific unit instead of automatic selection
205
- - `locale` (string | string[]): Locale for formatting (default: system locale)
206
- - `numeric` ('always' | 'auto'): Use numeric values always or allow natural language like "tomorrow" (default: 'auto')
207
- - `style` ('long' | 'short' | 'narrow'): Formatting style (default: 'long')
208
- - `localeMatcher` ('best fit' | 'lookup'): Locale matching algorithm
209
-
210
- **Returns:** `string` - Formatted relative time string
211
-
212
- **Automatic unit selection:**
213
- - < 60 seconds → "in X seconds" / "X seconds ago"
214
- - < 60 minutes → "in X minutes" / "X minutes ago"
215
- - < 24 hours → "in X hours" / "X hours ago"
216
- - < 7 days → "tomorrow" / "yesterday" / "in X days" / "X days ago"
217
- - < 4 weeks → "next week" / "last week" / "in X weeks" / "X weeks ago"
218
- - < 12 months → "next month" / "last month" / "in X months" / "X months ago"
219
- - ≥ 12 months → "next year" / "last year" / "in X years" / "X years ago"
220
-
221
- **Example:**
222
- ```typescript
223
- import { intlFormatDistance } from '@gobrand/tiempo';
224
-
225
- const later = Temporal.Instant.from('2025-01-20T12:00:00Z');
226
- const earlier = Temporal.Instant.from('2025-01-20T11:00:00Z');
227
-
228
- // Basic usage
229
- intlFormatDistance(later, earlier);
230
- // => "in 1 hour"
231
-
232
- intlFormatDistance(earlier, later);
233
- // => "1 hour ago"
234
-
235
- // With different locales
236
- intlFormatDistance(later, earlier, { locale: 'es' });
237
- // => "dentro de 1 hora"
238
-
239
- intlFormatDistance(later, earlier, { locale: 'ja' });
240
- // => "1 時間後"
241
-
242
- // Force numeric format
243
- const tomorrow = Temporal.Instant.from('2025-01-21T00:00:00Z');
244
- const today = Temporal.Instant.from('2025-01-20T00:00:00Z');
245
-
246
- intlFormatDistance(tomorrow, today, { numeric: 'auto' });
247
- // => "tomorrow"
248
-
249
- intlFormatDistance(tomorrow, today, { numeric: 'always' });
250
- // => "in 1 day"
251
-
252
- // Different styles
253
- const future = Temporal.Instant.from('2027-01-20T00:00:00Z');
254
- const now = Temporal.Instant.from('2025-01-20T00:00:00Z');
255
-
256
- intlFormatDistance(future, now, { style: 'long' });
257
- // => "in 2 years"
258
-
259
- intlFormatDistance(future, now, { style: 'short' });
260
- // => "in 2 yr."
261
-
262
- intlFormatDistance(future, now, { style: 'narrow' });
263
- // => "in 2y"
264
-
265
- // Force specific units
266
- intlFormatDistance(future, now, { unit: 'quarter' });
267
- // => "in 8 quarters"
268
-
269
- const dayLater = Temporal.Instant.from('2025-01-21T00:00:00Z');
270
- intlFormatDistance(dayLater, today, { unit: 'hour' });
271
- // => "in 24 hours"
272
- ```
273
-
274
- #### `format(input, formatStr, options?)`
275
-
276
- Format a Temporal.Instant or ZonedDateTime using date-fns-like format tokens.
277
-
278
- **Parameters:**
279
- - `input` (Temporal.Instant | Temporal.ZonedDateTime): A Temporal.Instant or Temporal.ZonedDateTime to format
280
- - `formatStr` (string): Format string using date-fns tokens (e.g., "yyyy-MM-dd HH:mm:ss")
281
- - `options` (FormatOptions, optional): Configuration for locale and timezone
282
- - `locale` (string): BCP 47 language tag (default: "en-US")
283
- - `timeZone` (string): IANA timezone identifier to convert to before formatting
284
-
285
- **Returns:** `string` - Formatted date string
286
-
287
- **Supported tokens:**
288
- - **Year**: `yyyy` (2025), `yy` (25), `y` (2025)
289
- - **Month**: `MMMM` (January), `MMM` (Jan), `MM` (01), `M` (1), `Mo` (1st)
290
- - **Day**: `dd` (20), `d` (20), `do` (20th)
291
- - **Weekday**: `EEEE` (Monday), `EEE` (Mon), `EEEEE` (M)
292
- - **Hour**: `HH` (15, 24h), `H` (15), `hh` (03, 12h), `h` (3)
293
- - **Minute**: `mm` (30), `m` (30)
294
- - **Second**: `ss` (45), `s` (45)
295
- - **AM/PM**: `a` (PM), `aa` (PM), `aaa` (pm), `aaaa` (p.m.), `aaaaa` (p)
296
- - **Timezone**: `xxx` (-05:00), `xx` (-0500), `XXX` (Z or -05:00), `zzzz` (Eastern Standard Time)
297
- - **Quarter**: `Q` (1), `QQQ` (Q1), `QQQQ` (1st quarter)
298
- - **Milliseconds**: `SSS` (123)
299
- - **Timestamp**: `T` (milliseconds), `t` (seconds)
300
- - **Escape text**: Use single quotes `'...'`, double single quotes `''` for literal quote
301
-
302
- **Example:**
303
- ```typescript
304
- import { format, toZonedTime, toUtc } from '@gobrand/tiempo';
305
-
306
- // From ISO string to ZonedDateTime, then format
307
- const isoString = "2025-01-20T20:30:45.000Z";
308
- const zoned = toZonedTime(isoString, "America/New_York");
309
-
310
- format(zoned, "yyyy-MM-dd"); // "2025-01-20"
311
- format(zoned, "MMMM d, yyyy"); // "January 20, 2025"
312
- format(zoned, "h:mm a"); // "3:30 PM"
313
- format(zoned, "EEEE, MMMM do, yyyy 'at' h:mm a"); // "Monday, January 20th, 2025 at 3:30 PM"
314
-
315
- // With locale
316
- format(zoned, "MMMM d, yyyy", { locale: "es-ES" }); // "enero 20, 2025"
317
- format(zoned, "EEEE", { locale: "fr-FR" }); // "lundi"
318
-
319
- // From ISO string to Instant (UTC), then format with timezone conversion
320
- const instant = toUtc(isoString);
321
- format(instant, "yyyy-MM-dd HH:mm", { timeZone: "America/New_York" }); // "2025-01-20 15:30"
322
- format(instant, "yyyy-MM-dd HH:mm", { timeZone: "Asia/Tokyo" }); // "2025-01-21 05:30"
323
- ```
324
-
325
- #### `formatPlainDate(date, formatStr, options?)`
326
-
327
- Format a Temporal.PlainDate using date-fns-like format tokens. Only supports date-related tokens (no time or timezone tokens).
328
-
329
- **Parameters:**
330
- - `date` (Temporal.PlainDate): A Temporal.PlainDate to format
331
- - `formatStr` (string): Format string using date-fns tokens (e.g., "yyyy-MM-dd")
332
- - `options` (FormatPlainDateOptions, optional): Configuration for locale
333
- - `locale` (string): BCP 47 language tag (default: "en-US")
334
-
335
- **Returns:** `string` - Formatted date string
336
-
337
- **Supported tokens:**
338
- - **Year**: `yyyy` (2025), `yy` (25), `y` (2025)
339
- - **Month**: `MMMM` (January), `MMM` (Jan), `MM` (01), `M` (1), `Mo` (1st)
340
- - **Day**: `dd` (20), `d` (20), `do` (20th)
341
- - **Weekday**: `EEEE` (Monday), `EEE` (Mon), `EEEEE` (M)
342
- - **Quarter**: `Q` (1), `QQQ` (Q1), `QQQQ` (1st quarter)
343
- - **Era**: `G` (AD), `GGGG` (Anno Domini)
344
- - **Escape text**: Use single quotes `'...'`, double single quotes `''` for literal quote
345
-
346
- **Example:**
347
- ```typescript
348
- import { formatPlainDate, today } from '@gobrand/tiempo';
349
-
350
- const date = Temporal.PlainDate.from("2025-01-20");
351
-
352
- formatPlainDate(date, "yyyy-MM-dd"); // "2025-01-20"
353
- formatPlainDate(date, "MMMM d, yyyy"); // "January 20, 2025"
354
- formatPlainDate(date, "EEEE, MMMM do, yyyy"); // "Monday, January 20th, 2025"
355
- formatPlainDate(date, "MM/dd/yyyy"); // "01/20/2025"
356
-
357
- // With locale
358
- formatPlainDate(date, "MMMM d, yyyy", { locale: "es-ES" }); // "enero 20, 2025"
359
- formatPlainDate(date, "EEEE", { locale: "de-DE" }); // "Montag"
360
-
361
- // Use with today()
362
- const todayFormatted = formatPlainDate(today(), "EEEE, MMMM do");
363
- // "Thursday, January 23rd"
364
- ```
365
-
366
- #### `simpleFormat(input, options?)`
367
-
368
- Format a Temporal date in a human-friendly way: "Dec 23" or "Dec 23, 2020". By default, shows the year only if the date is not in the current year. Optionally includes time in 12-hour or 24-hour format.
369
-
370
- **Parameters:**
371
- - `input` (Temporal.PlainDate | Temporal.ZonedDateTime | Temporal.Instant): The date to format
372
- - `options` (object, optional): Formatting options
373
- - `locale` (string): BCP 47 language tag (default: "en-US")
374
- - `year` ('auto' | 'always' | 'never'): Control year display (default: 'auto')
375
- - `time` ('12h' | '24h'): Include time in output (not available for PlainDate)
376
- - `timeZone` (string): IANA timezone identifier (required for Instant, optional for ZonedDateTime)
377
-
378
- **Returns:** `string` - Human-friendly formatted date string
379
-
380
- **Example:**
381
- ```typescript
382
- import { simpleFormat, today, now } from '@gobrand/tiempo';
383
-
384
- // Assuming current year is 2026
385
- const date2026 = Temporal.ZonedDateTime.from("2026-12-23T15:30:00[America/New_York]");
386
- const date2020 = Temporal.ZonedDateTime.from("2020-12-23T15:30:00[America/New_York]");
387
-
388
- // Basic usage - year shown only for past years
389
- simpleFormat(date2026); // "Dec 23"
390
- simpleFormat(date2020); // "Dec 23, 2020"
391
-
392
- // With time
393
- simpleFormat(date2026, { time: '12h' }); // "Dec 23, 3:30 PM"
394
- simpleFormat(date2026, { time: '24h' }); // "Dec 23, 15:30"
395
-
396
- // Control year display
397
- simpleFormat(date2026, { year: 'always' }); // "Dec 23, 2026"
398
- simpleFormat(date2020, { year: 'never' }); // "Dec 23"
399
-
400
- // With Instant (timeZone required)
401
- const instant = Temporal.Instant.from("2026-12-23T20:30:00Z");
402
- simpleFormat(instant, { timeZone: 'America/New_York' }); // "Dec 23"
403
- simpleFormat(instant, { timeZone: 'America/New_York', time: '12h' }); // "Dec 23, 3:30 PM"
404
-
405
- // With PlainDate (no time option available)
406
- const plain = Temporal.PlainDate.from("2020-12-23");
407
- simpleFormat(plain); // "Dec 23, 2020"
408
-
409
- // Different locales
410
- simpleFormat(date2020, { locale: 'es-ES' }); // "23 dic 2020"
411
- simpleFormat(date2020, { locale: 'de-DE' }); // "23. Dez. 2020"
412
- ```
413
-
414
- ### Start/End Utilities
415
-
416
- #### `today(timezone?)`
417
-
418
- Get today's date in the system's local timezone or a specified timezone.
419
-
420
- **Parameters:**
421
- - `timezone` (string, optional): An IANA timezone identifier (e.g., `"America/New_York"`, `"Europe/Madrid"`) or `"UTC"`. If not provided, uses the system's local timezone.
422
-
423
- **Returns:** `Temporal.PlainDate` - A Temporal.PlainDate representing today's date
424
-
425
- **Example:**
426
- ```typescript
427
- import { today } from '@gobrand/tiempo';
428
-
429
- const date = today(); // 2025-01-20
430
- date.year; // 2025
431
- date.month; // 1
432
- date.day; // 20
433
-
434
- // Get today in a specific timezone
435
- const dateInTokyo = today("Asia/Tokyo"); // 2025-01-21 (next day due to timezone)
436
- ```
437
-
438
- #### `now(timezone?)`
439
-
440
- Get the current date and time in the system's local timezone or a specified timezone.
441
-
442
- **Parameters:**
443
- - `timezone` (string, optional): An IANA timezone identifier (e.g., `"America/New_York"`, `"Europe/Madrid"`) or `"UTC"`. If not provided, uses the system's local timezone.
444
-
445
- **Returns:** `Temporal.ZonedDateTime` - A Temporal.ZonedDateTime representing the current date and time
446
-
447
- **Example:**
448
- ```typescript
449
- import { now } from '@gobrand/tiempo';
450
-
451
- const current = now(); // 2025-01-20T15:30:45.123-05:00[America/New_York]
452
- current.hour; // 15
453
- current.minute; // 30
454
-
455
- // Get current time in a specific timezone
456
- const currentInTokyo = now("Asia/Tokyo"); // 2025-01-21T05:30:45.123+09:00[Asia/Tokyo]
457
- ```
458
-
459
- #### `startOfDay(input)` / `endOfDay(input)`
460
-
461
- Get the start or end of the day for a given datetime.
462
-
463
- ```typescript
464
- import { startOfDay, endOfDay } from '@gobrand/tiempo';
465
-
466
- const zoned = Temporal.ZonedDateTime.from('2025-01-20T15:30:00-05:00[America/New_York]');
467
-
468
- startOfDay(zoned); // 2025-01-20T00:00:00-05:00[America/New_York]
469
- endOfDay(zoned); // 2025-01-20T23:59:59.999999999-05:00[America/New_York]
470
- ```
471
-
472
- #### `startOfWeek(input)` / `endOfWeek(input)`
473
-
474
- Get the start or end of the week. Uses ISO 8601 week definition (Monday to Sunday).
475
-
476
- ```typescript
477
- import { startOfWeek, endOfWeek } from '@gobrand/tiempo';
478
-
479
- const zoned = Temporal.ZonedDateTime.from('2025-01-20T15:30:00-05:00[America/New_York]'); // Monday
480
-
481
- startOfWeek(zoned); // 2025-01-20T00:00:00-05:00[America/New_York] (Monday)
482
- endOfWeek(zoned); // 2025-01-26T23:59:59.999999999-05:00[America/New_York] (Sunday)
483
- ```
484
-
485
- #### `startOfMonth(input)` / `endOfMonth(input)`
486
-
487
- Get the start or end of the month.
488
-
489
- ```typescript
490
- import { startOfMonth, endOfMonth } from '@gobrand/tiempo';
491
-
492
- const zoned = Temporal.ZonedDateTime.from('2025-01-20T15:30:00-05:00[America/New_York]');
493
-
494
- startOfMonth(zoned); // 2025-01-01T00:00:00-05:00[America/New_York]
495
- endOfMonth(zoned); // 2025-01-31T23:59:59.999999999-05:00[America/New_York]
496
- ```
497
-
498
- #### `startOfYear(input)` / `endOfYear(input)`
499
-
500
- Get the start or end of the year.
501
-
502
- ```typescript
503
- import { startOfYear, endOfYear } from '@gobrand/tiempo';
504
-
505
- const zoned = Temporal.ZonedDateTime.from('2025-01-20T15:30:00-05:00[America/New_York]');
506
-
507
- startOfYear(zoned); // 2025-01-01T00:00:00-05:00[America/New_York]
508
- endOfYear(zoned); // 2025-12-31T23:59:59.999999999-05:00[America/New_York]
509
- ```
510
-
511
- ### Addition/Subtraction Utilities
512
-
513
- tiempo provides comprehensive datetime arithmetic functions for all time units, from nanoseconds to years. All functions:
514
- - Accept `Temporal.Instant` or `Temporal.ZonedDateTime` as input
515
- - Return `Temporal.ZonedDateTime` preserving the original timezone
516
- - Properly handle DST transitions, leap years, and month-end edge cases
517
- - Support negative values for subtraction
518
-
519
- #### Addition Functions
520
-
521
- ##### `addYears(input, years)` / `addMonths(input, months)` / `addWeeks(input, weeks)` / `addDays(input, days)`
522
-
523
- Add calendar units (years, months, weeks, or days) to a datetime.
524
-
525
- ```typescript
526
- import { addYears, addMonths, addWeeks, addDays } from '@gobrand/tiempo';
527
-
528
- const date = Temporal.Instant.from('2025-01-20T12:00:00Z');
529
-
530
- addYears(date, 2); // 2027-01-20T12:00:00Z[UTC] (2 years later)
531
- addMonths(date, 3); // 2025-04-20T12:00:00Z[UTC] (3 months later)
532
- addWeeks(date, 2); // 2025-02-03T12:00:00Z[UTC] (2 weeks later)
533
- addDays(date, 5); // 2025-01-25T12:00:00Z[UTC] (5 days later)
534
-
535
- // Handle month-end edge cases automatically
536
- const endOfJan = Temporal.Instant.from('2025-01-31T12:00:00Z');
537
- addMonths(endOfJan, 1); // 2025-02-28T12:00:00Z[UTC] (Jan 31 → Feb 28)
538
-
539
- // Negative values subtract
540
- addMonths(date, -3); // 2024-10-20T12:00:00Z[UTC] (3 months earlier)
541
- ```
11
+ 👉 **[Documentation](https://eng.gobrand.app/tiempo)**
542
12
 
543
- ##### `addHours(input, hours)` / `addMinutes(input, minutes)` / `addSeconds(input, seconds)`
544
-
545
- Add time units (hours, minutes, or seconds) to a datetime.
546
-
547
- ```typescript
548
- import { addHours, addMinutes, addSeconds } from '@gobrand/tiempo';
549
-
550
- const time = Temporal.Instant.from('2025-01-20T12:00:00Z');
551
-
552
- addHours(time, 3); // 2025-01-20T15:00:00Z[UTC] (3 hours later)
553
- addMinutes(time, 30); // 2025-01-20T12:30:00Z[UTC] (30 minutes later)
554
- addSeconds(time, 45); // 2025-01-20T12:00:45Z[UTC] (45 seconds later)
555
-
556
- // Works with ZonedDateTime and preserves timezone
557
- const ny = Temporal.ZonedDateTime.from('2025-01-20T15:00:00-05:00[America/New_York]');
558
- addHours(ny, 2); // 2025-01-20T17:00:00-05:00[America/New_York]
559
- ```
560
-
561
- ##### `addMilliseconds(input, ms)` / `addMicroseconds(input, μs)` / `addNanoseconds(input, ns)`
562
-
563
- Add sub-second precision units (milliseconds, microseconds, or nanoseconds) to a datetime.
564
-
565
- ```typescript
566
- import { addMilliseconds, addMicroseconds, addNanoseconds } from '@gobrand/tiempo';
567
-
568
- const precise = Temporal.Instant.from('2025-01-20T12:00:00Z');
569
-
570
- addMilliseconds(precise, 500); // 2025-01-20T12:00:00.500Z[UTC]
571
- addMicroseconds(precise, 500); // 2025-01-20T12:00:00.000500Z[UTC]
572
- addNanoseconds(precise, 500); // 2025-01-20T12:00:00.000000500Z[UTC]
573
- ```
574
-
575
- #### Subtraction Functions
576
-
577
- All subtraction functions are convenience wrappers that call their corresponding addition functions with negated values.
578
-
579
- ##### `subYears(input, years)` / `subMonths(input, months)` / `subWeeks(input, weeks)` / `subDays(input, days)`
580
-
581
- ```typescript
582
- import { subYears, subMonths, subWeeks, subDays } from '@gobrand/tiempo';
583
-
584
- const date = Temporal.Instant.from('2025-01-20T12:00:00Z');
585
-
586
- subYears(date, 2); // 2023-01-20T12:00:00Z[UTC] (2 years earlier)
587
- subMonths(date, 3); // 2024-10-20T12:00:00Z[UTC] (3 months earlier)
588
- subWeeks(date, 2); // 2025-01-06T12:00:00Z[UTC] (2 weeks earlier)
589
- subDays(date, 5); // 2025-01-15T12:00:00Z[UTC] (5 days earlier)
590
- ```
591
-
592
- ##### `subHours(input, hours)` / `subMinutes(input, minutes)` / `subSeconds(input, seconds)`
593
-
594
- ```typescript
595
- import { subHours, subMinutes, subSeconds } from '@gobrand/tiempo';
596
-
597
- const time = Temporal.Instant.from('2025-01-20T12:00:00Z');
598
-
599
- subHours(time, 3); // 2025-01-20T09:00:00Z[UTC] (3 hours earlier)
600
- subMinutes(time, 30); // 2025-01-20T11:30:00Z[UTC] (30 minutes earlier)
601
- subSeconds(time, 45); // 2025-01-20T11:59:15Z[UTC] (45 seconds earlier)
602
- ```
603
-
604
- ##### `subMilliseconds(input, ms)` / `subMicroseconds(input, μs)` / `subNanoseconds(input, ns)`
605
-
606
- ```typescript
607
- import { subMilliseconds, subMicroseconds, subNanoseconds } from '@gobrand/tiempo';
608
-
609
- const precise = Temporal.Instant.from('2025-01-20T12:00:00.500Z');
610
-
611
- subMilliseconds(precise, 250); // 2025-01-20T12:00:00.250Z[UTC]
612
- subMicroseconds(precise, 250); // 2025-01-20T12:00:00.499750Z[UTC]
613
- subNanoseconds(precise, 250); // 2025-01-20T12:00:00.499999750Z[UTC]
614
- ```
615
-
616
- #### Real-world Example: Meeting Scheduler
617
-
618
- ```typescript
619
- import {
620
- toZonedTime,
621
- addDays,
622
- addMinutes,
623
- format
624
- } from '@gobrand/tiempo';
625
-
626
- // User's current meeting time
627
- const meeting = toZonedTime('2025-01-20T15:00:00Z', 'America/New_York');
628
- // 2025-01-20T10:00:00-05:00[America/New_York] (10 AM in NY)
629
-
630
- // Reschedule to tomorrow, same time
631
- const tomorrow = addDays(meeting, 1);
632
- format(tomorrow, 'EEEE, MMMM do'); // "Tuesday, January 21st"
633
-
634
- // Add 30-minute buffer before the meeting
635
- const arriveBy = subMinutes(tomorrow, 30);
636
- format(arriveBy, 'h:mm a'); // "9:30 AM"
637
-
638
- // Schedule follow-up 2 weeks later
639
- const followUp = addWeeks(tomorrow, 2);
640
- format(followUp, 'MMM d'); // "Feb 4"
641
- ```
642
-
643
- ### Comparison Utilities
644
-
645
- #### `isBefore(date1, date2)` / `isAfter(date1, date2)`
646
-
647
- Check if the first datetime is before or after the second. Compares the underlying instant in time.
648
-
649
- ```typescript
650
- import { isBefore, isAfter } from '@gobrand/tiempo';
651
-
652
- const earlier = Temporal.ZonedDateTime.from('2025-01-20T10:00:00-05:00[America/New_York]');
653
- const later = Temporal.ZonedDateTime.from('2025-01-20T15:00:00-05:00[America/New_York]');
654
-
655
- isBefore(earlier, later); // true
656
- isBefore(later, earlier); // false
657
-
658
- isAfter(later, earlier); // true
659
- isAfter(earlier, later); // false
660
- ```
661
-
662
- ### Difference Utilities
663
-
664
- All difference functions compare the underlying instant in time and return a positive value if laterDate is after earlierDate, negative if before.
665
-
666
- #### Time-based precision (instant comparison)
667
-
668
- ##### `differenceInNanoseconds(laterDate, earlierDate)`
669
-
670
- Returns the difference in nanoseconds as a BigInt. Provides the highest precision available in Temporal.
671
-
672
- ```typescript
673
- import { differenceInNanoseconds } from '@gobrand/tiempo';
674
-
675
- const later = Temporal.Instant.from('2025-01-20T12:30:20.000000500Z');
676
- const earlier = Temporal.Instant.from('2025-01-20T12:30:20.000000000Z');
677
- differenceInNanoseconds(later, earlier); // 500n
678
- ```
679
-
680
- ##### `differenceInMicroseconds(laterDate, earlierDate)`
681
-
682
- Returns the difference in microseconds (1/1,000,000 second).
683
-
684
- ```typescript
685
- import { differenceInMicroseconds } from '@gobrand/tiempo';
686
-
687
- const later = Temporal.Instant.from('2025-01-20T12:30:20.001000Z');
688
- const earlier = Temporal.Instant.from('2025-01-20T12:30:20.000000Z');
689
- differenceInMicroseconds(later, earlier); // 1000
690
- ```
691
-
692
- ##### `differenceInMilliseconds(laterDate, earlierDate)`
693
-
694
- Returns the difference in milliseconds (1/1,000 second).
695
-
696
- ```typescript
697
- import { differenceInMilliseconds } from '@gobrand/tiempo';
698
-
699
- const later = Temporal.Instant.from('2025-01-20T12:30:21.700Z');
700
- const earlier = Temporal.Instant.from('2025-01-20T12:30:20.600Z');
701
- differenceInMilliseconds(later, earlier); // 1100
702
- ```
703
-
704
- ##### `differenceInSeconds(laterDate, earlierDate)`
705
-
706
- Returns the difference in seconds (truncates sub-second precision).
707
-
708
- ```typescript
709
- import { differenceInSeconds } from '@gobrand/tiempo';
710
-
711
- const later = Temporal.Instant.from('2025-01-20T12:30:25Z');
712
- const earlier = Temporal.Instant.from('2025-01-20T12:30:20Z');
713
- differenceInSeconds(later, earlier); // 5
714
- ```
715
-
716
- ##### `differenceInMinutes(laterDate, earlierDate)`
717
-
718
- Returns the difference in minutes (truncates sub-minute precision).
719
-
720
- ```typescript
721
- import { differenceInMinutes } from '@gobrand/tiempo';
722
-
723
- const later = Temporal.Instant.from('2025-01-20T12:45:00Z');
724
- const earlier = Temporal.Instant.from('2025-01-20T12:30:00Z');
725
- differenceInMinutes(later, earlier); // 15
726
- ```
727
-
728
- ##### `differenceInHours(laterDate, earlierDate)`
729
-
730
- Returns the difference in hours (truncates sub-hour precision).
731
-
732
- ```typescript
733
- import { differenceInHours } from '@gobrand/tiempo';
734
-
735
- const later = Temporal.Instant.from('2025-01-20T18:00:00Z');
736
- const earlier = Temporal.Instant.from('2025-01-20T15:00:00Z');
737
- differenceInHours(later, earlier); // 3
738
- ```
739
-
740
- #### Calendar-aware precision
741
-
742
- These functions use Temporal's `until()` method to account for variable-length calendar units.
743
-
744
- ##### `differenceInDays(laterDate, earlierDate)`
745
-
746
- Returns the difference in days. Calendar-aware, so it properly handles DST transitions where days can be 23, 24, or 25 hours.
747
-
748
- ```typescript
749
- import { differenceInDays } from '@gobrand/tiempo';
750
-
751
- const later = Temporal.Instant.from('2025-01-25T12:00:00Z');
752
- const earlier = Temporal.Instant.from('2025-01-20T12:00:00Z');
753
- differenceInDays(later, earlier); // 5
754
-
755
- // Handles DST transitions correctly
756
- const afterDst = Temporal.ZonedDateTime.from('2025-03-10T12:00:00-04:00[America/New_York]');
757
- const beforeDst = Temporal.ZonedDateTime.from('2025-03-08T12:00:00-05:00[America/New_York]');
758
- differenceInDays(afterDst, beforeDst); // 2 (calendar days, not 48 hours)
759
- ```
760
-
761
- ##### `differenceInWeeks(laterDate, earlierDate)`
762
-
763
- Returns the difference in weeks (7-day periods).
764
-
765
- ```typescript
766
- import { differenceInWeeks } from '@gobrand/tiempo';
767
-
768
- const later = Temporal.Instant.from('2025-02-10T12:00:00Z');
769
- const earlier = Temporal.Instant.from('2025-01-20T12:00:00Z');
770
- differenceInWeeks(later, earlier); // 3
771
- ```
772
-
773
- ##### `differenceInMonths(laterDate, earlierDate)`
774
-
775
- Returns the difference in months. Calendar-aware, properly handling months with different numbers of days (28-31).
776
-
777
- ```typescript
778
- import { differenceInMonths } from '@gobrand/tiempo';
779
-
780
- const later = Temporal.Instant.from('2025-04-20T12:00:00Z');
781
- const earlier = Temporal.Instant.from('2025-01-20T12:00:00Z');
782
- differenceInMonths(later, earlier); // 3
783
- ```
784
-
785
- ##### `differenceInYears(laterDate, earlierDate)`
786
-
787
- Returns the difference in years. Calendar-aware, properly handling leap years (366 days) and regular years (365 days).
788
-
789
- ```typescript
790
- import { differenceInYears } from '@gobrand/tiempo';
791
-
792
- const later = Temporal.Instant.from('2028-01-20T12:00:00Z');
793
- const earlier = Temporal.Instant.from('2025-01-20T12:00:00Z');
794
- differenceInYears(later, earlier); // 3
795
-
796
- // Calculate age
797
- const today = Temporal.ZonedDateTime.from('2025-01-20T12:00:00Z[UTC]');
798
- const birthdate = Temporal.ZonedDateTime.from('1990-01-20T12:00:00Z[UTC]');
799
- differenceInYears(today, birthdate); // 35
800
- ```
801
-
802
- #### `isFuture(date)` / `isPast(date)`
803
-
804
- Check if a datetime is in the future or past relative to the current moment. Compares against `Temporal.Now.instant()`.
13
+ - **Zero timezone bugs** - Real IANA timezone support, not UTC offset hacks
14
+ - **DST-aware math** - `addDays(1)` means tomorrow, even across clock changes
15
+ - **Nanosecond precision** - When milliseconds aren't enough
16
+ - **Type-safe** - Full TypeScript, catches datetime errors at compile time
17
+ - **Familiar API** - If you've used date-fns, you already know this
805
18
 
806
19
  ```typescript
807
- import { isFuture, isPast } from '@gobrand/tiempo';
20
+ import { toZonedTime, addDays, format, toUtcString } from '@gobrand/tiempo';
808
21
 
809
- const tomorrow = Temporal.Now.zonedDateTimeISO().add({ days: 1 });
810
- const yesterday = Temporal.Now.zonedDateTimeISO().subtract({ days: 1 });
22
+ // Backend sends UTC
23
+ const utc = "2025-03-09T07:00:00Z";
811
24
 
812
- isFuture(tomorrow); // true
813
- isFuture(yesterday); // false
25
+ // Convert to user's timezone, manipulate, format
26
+ const userTime = toZonedTime(utc, "America/New_York");
27
+ const tomorrow = addDays(userTime, 1); // DST transition handled correctly
28
+ const display = format(tomorrow, "EEEE 'at' h:mm a"); // "Monday at 2:00 AM"
814
29
 
815
- isPast(yesterday); // true
816
- isPast(tomorrow); // false
30
+ // Send back to backend as UTC
31
+ const payload = toUtcString(tomorrow); // "2025-03-10T06:00:00Z"
817
32
  ```
818
33
 
819
- #### `isSameDay(date1, date2)`
820
-
821
- Check if two datetimes fall on the same calendar day. Compares year, month, and day fields directly.
822
-
823
- **Important:** For ZonedDateTime inputs with different timezones, each date is compared in its own timezone. Convert to a common timezone first if you need a specific perspective.
824
-
825
- ```typescript
826
- import { isSameDay } from '@gobrand/tiempo';
827
-
828
- // Same timezone, same day
829
- const morning = Temporal.ZonedDateTime.from('2025-01-20T08:00:00Z[UTC]');
830
- const evening = Temporal.ZonedDateTime.from('2025-01-20T23:00:00Z[UTC]');
831
- isSameDay(morning, evening); // true
832
-
833
- // Different timezones - compares in their respective timezones
834
- const ny = Temporal.ZonedDateTime.from('2025-01-20T23:00:00-05:00[America/New_York]');
835
- const tokyo = Temporal.ZonedDateTime.from('2025-01-21T13:00:00+09:00[Asia/Tokyo]');
836
- isSameDay(ny, tokyo); // false (Jan 20 in NY, Jan 21 in Tokyo)
34
+ ## Install
837
35
 
838
- // Convert to UTC to compare in UTC timezone
839
- isSameDay(ny.withTimeZone('UTC'), tokyo.withTimeZone('UTC')); // true (both Jan 21 in UTC)
840
-
841
- // Instant inputs are automatically converted to UTC
842
- const instant1 = Temporal.Instant.from('2025-01-20T10:00:00Z');
843
- const instant2 = Temporal.Instant.from('2025-01-20T23:00:00Z');
844
- isSameDay(instant1, instant2); // true (both Jan 20 in UTC)
845
- ```
846
-
847
- ## Drizzle ORM Integration
848
-
849
- tiempo seamlessly integrates with Drizzle ORM for database datetime operations. When using Drizzle with PostgreSQL `timestamptz` columns and `mode: 'date'`, tiempo provides utilities to convert between Date objects and Temporal.
850
-
851
- ```typescript
852
- import { toZonedTime, toUtc, toDate } from '@gobrand/tiempo';
853
-
854
- // 1. Reading from database (Drizzle returns Date with mode: 'date')
855
- const post = await db.query.posts.findFirst();
856
- const publishedAt = post.publishedAt; // Date object
857
-
858
- // 2. Convert to user's timezone for display
859
- const userTimezone = "America/New_York";
860
- const zonedTime = toZonedTime(publishedAt, userTimezone);
861
- console.log(zonedTime.hour); // Local hour in user's timezone
862
-
863
- // 3. User reschedules post to tomorrow at 3 PM their time
864
- const rescheduled = zonedTime.add({ days: 1 }).with({ hour: 15, minute: 0 });
865
-
866
- // 4. Convert back to Date for database storage
867
- const dateForDb = toDate(rescheduled);
868
- await db.update(posts).set({ publishedAt: dateForDb }).where(eq(posts.id, post.id));
869
- ```
870
-
871
- **Why this works:**
872
- - Drizzle's `mode: 'date'` returns JavaScript `Date` objects (timestamps)
873
- - `toZonedTime(date, tz)` converts the Date to a timezone-aware Temporal object
874
- - Work with Temporal's powerful API for date arithmetic and manipulation
875
- - `toDate(temporal)` converts back to Date for Drizzle storage
876
-
877
- ## Real World Examples
878
-
879
- ### Example 1: Social Media Post Scheduler
880
-
881
- This example demonstrates the complete workflow of working with datetimes in a frontend application: receiving a UTC ISO 8601 string from your backend, converting it to a Temporal object in the user's timezone, formatting it for display, manipulating it with tiempo's arithmetic functions, and sending it back to your backend as a UTC ISO 8601 string.
882
-
883
- ```typescript
884
- import {
885
- toZonedTime,
886
- toUtcString,
887
- format,
888
- addDays,
889
- addHours,
890
- subMinutes
891
- } from '@gobrand/tiempo';
892
-
893
- // 1. Receive UTC ISO 8601 string from backend
894
- const scheduledAtUTC = "2025-01-20T20:00:00.000Z";
895
-
896
- // 2. Convert to user's timezone (Temporal.ZonedDateTime)
897
- const userTimezone = "America/New_York";
898
- const zonedDateTime = toZonedTime(scheduledAtUTC, userTimezone);
899
-
900
- // 3. Format for display in the UI
901
- const displayTime = format(zonedDateTime, "EEEE, MMMM do 'at' h:mm a");
902
- console.log(displayTime); // "Monday, January 20th at 3:00 PM"
903
-
904
- // 4. User wants to reschedule to tomorrow, 2 hours later
905
- const tomorrow = addDays(zonedDateTime, 1);
906
- const twoHoursLater = addHours(tomorrow, 2);
907
-
908
- // 5. Format the updated time for confirmation
909
- const confirmTime = format(twoHoursLater, "EEEE 'at' h:mm a");
910
- console.log(`Rescheduled to ${confirmTime}`); // "Tuesday at 5:00 PM"
911
-
912
- // 6. Convert back to UTC ISO 8601 string for backend
913
- const updatedUTC = toUtcString(twoHoursLater);
914
- console.log(updatedUTC); // "2025-01-21T22:00:00Z"
915
- ```
916
-
917
- ### Example 2: Meeting Reminder System
918
-
919
- ```typescript
920
- import {
921
- toZonedTime,
922
- format,
923
- subMinutes,
924
- subHours,
925
- differenceInMinutes,
926
- isFuture
927
- } from '@gobrand/tiempo';
928
-
929
- // Meeting scheduled for 2 PM
930
- const meeting = toZonedTime('2025-01-20T19:00:00Z', 'America/New_York');
931
- // 2025-01-20T14:00:00-05:00[America/New_York]
932
-
933
- // Send reminders at multiple intervals
934
- const reminders = [
935
- { time: subMinutes(meeting, 15), label: '15 minutes before' },
936
- { time: subHours(meeting, 1), label: '1 hour before' },
937
- { time: subHours(meeting, 24), label: '1 day before' },
938
- ];
939
-
940
- reminders.forEach(({ time, label }) => {
941
- if (isFuture(time)) {
942
- const formatted = format(time, 'MMM d, h:mm a');
943
- console.log(`Send reminder "${label}" at ${formatted}`);
944
- }
945
- });
946
-
947
- // Calculate time until meeting
948
- const now = Temporal.Now.zonedDateTimeISO('America/New_York');
949
- const minutesUntil = differenceInMinutes(meeting, now);
950
- console.log(`Meeting starts in ${minutesUntil} minutes`);
951
- ```
952
-
953
- ### PlainDate Comparisons
954
-
955
- For comparing calendar dates without time or timezone considerations, tiempo provides dedicated PlainDate comparison functions. These are useful for calendars, date pickers, and scheduling UIs where you work with pure dates.
956
-
957
- #### `isPlainDateBefore(date1, date2)` / `isPlainDateAfter(date1, date2)` / `isPlainDateEqual(date1, date2)`
958
-
959
- Compare two `Temporal.PlainDate` values. Unlike `isBefore`/`isAfter` which compare instants in time, these functions compare pure calendar dates.
960
-
961
- ```typescript
962
- import { isPlainDateBefore, isPlainDateAfter, isPlainDateEqual } from '@gobrand/tiempo';
963
-
964
- const jan20 = Temporal.PlainDate.from('2025-01-20');
965
- const jan25 = Temporal.PlainDate.from('2025-01-25');
966
-
967
- isPlainDateBefore(jan20, jan25); // true
968
- isPlainDateBefore(jan25, jan20); // false
969
-
970
- isPlainDateAfter(jan25, jan20); // true
971
- isPlainDateAfter(jan20, jan25); // false
972
-
973
- isPlainDateEqual(jan20, Temporal.PlainDate.from('2025-01-20')); // true
974
- isPlainDateEqual(jan20, jan25); // false
975
- ```
976
-
977
- **When to use PlainDate vs Instant/ZonedDateTime:**
978
- - Use `isPlainDateBefore`/`isPlainDateAfter` when comparing calendar dates (e.g., "Is January 20th before January 25th?")
979
- - Use `isBefore`/`isAfter` when comparing specific moments in time (e.g., "Did event A happen before event B?")
980
-
981
- ```typescript
982
- // Calendar UI: Disable dates before today
983
- const isDateDisabled = (date: Temporal.PlainDate) =>
984
- isPlainDateBefore(date, today());
985
-
986
- // Booking system: Check if selected date is in the past
987
- const isPastDate = (date: Temporal.PlainDate) =>
988
- isPlainDateBefore(date, today());
989
- ```
990
-
991
- ## Browser Support
992
-
993
- The Temporal API is a Stage 3 TC39 proposal. The polyfill `@js-temporal/polyfill` is included as a dependency, so you're ready to go!
994
-
995
- ```typescript
996
- import { Temporal } from '@js-temporal/polyfill';
36
+ ```bash
37
+ npm install @gobrand/tiempo
997
38
  ```
998
39
 
999
- ## Contributing
40
+ ## Docs
1000
41
 
1001
- Contributions are welcome! Please feel free to submit a Pull Request.
42
+ **[eng.gobrand.app/tiempo](https://eng.gobrand.app/tiempo)** Full API reference, examples, and guides.
1002
43
 
1003
44
  ## License
1004
45
 
1005
- MIT © [Go Brand](https://github.com/go-brand)
1006
-
1007
- ## Built by Go Brand
1008
-
1009
- tiempo is built and maintained by [Go Brand](https://gobrand.app) - a modern social media management platform.
46
+ MIT © [Ruben Costa](https://x.com/PonziChad) / [Go Brand](https://gobrand.app)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobrand/tiempo",
3
- "version": "2.3.5",
3
+ "version": "2.3.6",
4
4
  "description": "Lightweight utility functions for converting between UTC and timezone-aware datetimes using the Temporal API",
5
5
  "private": false,
6
6
  "publishConfig": {