Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b4f8884
Import asserts from dev deps
bjuppa Mar 29, 2025
1fa9b03
Import testing asserts with jsr
bjuppa Mar 29, 2025
5309555
Add props for jsr publishing
bjuppa Mar 29, 2025
392280d
Import types with keyword
bjuppa Mar 29, 2025
3eb1ddf
Add missing explicit return types
bjuppa Mar 30, 2025
f316252
Format code
bjuppa Mar 30, 2025
d4f8431
Add jsr publishing git workflow
bjuppa Mar 30, 2025
e8b26e8
Document JSR publish flow
bjuppa Mar 30, 2025
9a0bad7
Import dnt from jsr
bjuppa Mar 30, 2025
608c599
Add compilerOptions to fix typechecking in asserts
bjuppa Mar 30, 2025
12a05ee
Format code
bjuppa Mar 30, 2025
cefeeff
Exclude tests from publishing
bjuppa Mar 30, 2025
2da7ed4
Exclude unnecessary files when publishing to jsr
bjuppa Mar 30, 2025
d16a7fb
Exclude npm directory from all deno operations
bjuppa Mar 30, 2025
a1bf65a
Indicate publish exclude directories
bjuppa Mar 30, 2025
325e6bf
Exclude deno.json when publishing to jsr
bjuppa Mar 30, 2025
c9a4511
Reorder excludes
bjuppa Mar 30, 2025
2dd15bb
Improve jsr publishing docs
bjuppa Mar 30, 2025
610a7e4
Point to new API docs at jsr.io
bjuppa Mar 30, 2025
91e0454
Update link to specific API docs at jsr.io
bjuppa Mar 30, 2025
b7ad85a
Import with jsr specifier
bjuppa Mar 30, 2025
97562f5
Point to jsr installation, and remove deno.land
bjuppa Mar 30, 2025
0fee849
Document publishing to npm before deno.land
bjuppa Mar 30, 2025
00e86e8
Use deno v2 in github actions
bjuppa Mar 30, 2025
584f57c
Use node v22 in github actions
bjuppa Mar 30, 2025
69d63f5
Format markdown
bjuppa Mar 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ jobs:
run: echo TAG_VERSION=${GITHUB_REF/refs\/tags\//} >> $GITHUB_OUTPUT

- name: Setup Deno
uses: denoland/setup-deno@v1
uses: denoland/setup-deno@v2
with:
deno-version: v1.x # Run with latest stable Deno.
deno-version: v2.x # Run with latest stable Deno.

- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 18
registry-url: 'https://registry.npmjs.org'
node-version: 22
registry-url: "https://registry.npmjs.org"

- name: Build package
run: deno run -A ./scripts/build_npm.ts ${{steps.get_tag_version.outputs.TAG_VERSION}}
Expand Down
19 changes: 19 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Publish
on:
push:
branches:
- main

jobs:
publish:
runs-on: ubuntu-latest

permissions:
contents: read
id-token: write

steps:
- uses: actions/checkout@v4

- name: Publish package
run: npx jsr publish
4 changes: 2 additions & 2 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ jobs:
uses: actions/checkout@v3

- name: Setup Deno
uses: denoland/setup-deno@v1
uses: denoland/setup-deno@v2
with:
deno-version: v1.x # Run with latest stable Deno.
deno-version: v2.x # Run with latest stable Deno.

- name: Check formatting
run: deno fmt --check
Expand Down
13 changes: 9 additions & 4 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,19 @@ deno fmt

## Releases

Set a new `version` in [`deno.json`](deno.json) and push to `main` branch to
[automatically publish](https://jsr.io/docs/publishing-packages#publishing-from-github-actions)
to [jsr](https://jsr.io/@bjuppa/complaindate) with a
[GitHub Action](https://github.com/bjuppa/com-plain-date/actions/).

Releases are to be created and managed through
[GitHub Releases](https://github.com/bjuppa/com-plain-date/releases).

New releases are automatically published to
[deno.land](https://deno.land/x/complaindate) with a
[GitHub Webhook](https://github.com/bjuppa/com-plain-date/settings/hooks) and to
When created, GitHub releases are automatically published to
[npm](https://www.npmjs.com/package/complaindate) with a
[GitHub Action](https://github.com/bjuppa/com-plain-date/actions).
[GitHub Action](https://github.com/bjuppa/com-plain-date/actions) and to
[deno.land](https://deno.land/x/complaindate) with a
[GitHub Webhook](https://github.com/bjuppa/com-plain-date/settings/hooks).

## Testing the package

Expand Down
12 changes: 10 additions & 2 deletions ExPlainDate.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { ComPlainDate, PlainDate, PlainDateFactory } from "./PlainDate.ts";
import {
type ComPlainDate,
PlainDate,
type PlainDateFactory,
} from "./PlainDate.ts";
import { createLocalInstant } from "./utils/createLocalInstant.ts";
import { createInstant } from "./utils/createInstant.ts";
import { QuarterNumber, WeekDay, WeekDayNumber } from "./constants.ts";
import {
type QuarterNumber,
WeekDay,
type WeekDayNumber,
} from "./constants.ts";
import { addDays } from "./utils/addDays.ts";
import { addBusinessDays } from "./utils/addBusinessDays.ts";
import { addMonths } from "./utils/addMonths.ts";
Expand Down
4 changes: 2 additions & 2 deletions ExPlainDate_test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { ExPlainDate, ExtendedPlainDate } from "./ExPlainDate.ts";
import { ExPlainDate, type ExtendedPlainDate } from "./ExPlainDate.ts";
import { PlainDate } from "./PlainDate.ts";
import {
assertEquals,
assertStringIncludes,
assertThrows,
} from "./dev_deps.ts";
import { PlainDateMapFn } from "./support/function-signatures.ts";
import type { PlainDateMapFn } from "./support/function-signatures.ts";

Deno.test("factory accepts PlainDate", () => {
const exPlainDate = ExPlainDate(PlainDate({ year: 2022, month: 2, day: 2 }));
Expand Down
4 changes: 2 additions & 2 deletions PlainDate.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createUtcInstant } from "./utils/createUtcInstant.ts";
import { MonthNumber } from "./constants.ts";
import { RequireAtLeastOne } from "./support/utility-types.ts";
import type { MonthNumber } from "./constants.ts";
import type { RequireAtLeastOne } from "./support/utility-types.ts";

export type FormatPlainDateOptions = Omit<
Intl.DateTimeFormatOptions,
Expand Down
4 changes: 2 additions & 2 deletions PlainDate_test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ComPlainDate, PlainDate } from "./PlainDate.ts";
import { type ComPlainDate, PlainDate } from "./PlainDate.ts";
import { assert, assertEquals, assertFalse, assertThrows } from "./dev_deps.ts";
import { PlainDateMapFn } from "./support/function-signatures.ts";
import type { PlainDateMapFn } from "./support/function-signatures.ts";

Deno.test("factory accepts number date parts", () => {
const plainDate = PlainDate({ year: 2022, month: 2, day: 2 });
Expand Down
2 changes: 1 addition & 1 deletion PlainTime.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { HOURS_IN_DAY, MS_IN_HOUR } from "./constants.ts";
import { tallyMilliseconds } from "./support/tallyMilliseconds.ts";
import { RequireAtLeastOne } from "./support/utility-types.ts";
import type { RequireAtLeastOne } from "./support/utility-types.ts";

export type FormatPlainTimeOptions =
& Omit<
Expand Down
71 changes: 32 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,17 @@ timezones on the surface_.

This readme aims to explain how ComPlainDate works, but doesn't contain full
explanations of all available operations. The detailed documentation and
categorized lists of available functions were once available at the _deno.land_
website, but that is no longer the case.
categorized lists of available functions are available at the _jsr.io_ website:

We hope to regain documentation once this package is published at JSR instead,
see [Issue #88](https://github.com/bjuppa/com-plain-date/issues/88) for
progress.

Please note that all the links to the _deno.land_ docs from this readme are
currently not working. The simplest workaround is to browse the source files on
GitHub instead, and read the docblocks straight from the source. Not ideal, we
know, sorry.
Visit [the ComPlainDate API documentation](https://jsr.io/@bjuppa/complaindate)
to explore which specific utilities you can use to solve your problem.

## Installation

ComPlainDate is distributed as an **npm** package as well as a **Deno** module:
ComPlainDate is distributed as packages via both **jsr** and **npm**:

- [jsr.io/@bjuppa/complaindate](https://jsr.io/@bjuppa/complaindate)
- [npmjs.com/package/complaindate](https://www.npmjs.com/package/complaindate)
- [deno.land/x/complaindate](https://deno.land/x/complaindate)

## Table of contents

Expand Down Expand Up @@ -107,7 +100,7 @@ time to reach for the other concepts, described below.
### `PlainDate` for _calendar dates_

Plain-date objects adhere to the
[`ComPlainDate` contract](https://deno.land/x/complaindate/mod.ts?s=ComPlainDate)
[`ComPlainDate` contract](https://jsr.io/@bjuppa/complaindate/doc/~/ComPlainDate)
and have three numeric properties (`year`, `month`, and `day`) used for most
operations.

Expand All @@ -127,7 +120,7 @@ encouraged to build your own mapper functions on top of the existing ones.
### `PlainTime` for _time-of-day_

Plain-time objects adhere to the
[`ComPlainTime` contract](https://deno.land/x/complaindate/mod.ts?s=ComPlainTime)
[`ComPlainTime` contract](https://jsr.io/@bjuppa/complaindate/doc/~/ComPlainTime)
and have four numeric properties (`hour`, `minute`, `second`, and
`millisecond`), that may be used for operations, but those are surprisingly
uncommon.
Expand All @@ -141,8 +134,8 @@ method is best for controlled formatting in user interfaces.
## Creating plain-date and plain-time objects

Pass any _calendar-date_ or _wall-time_ **shaped** objects to the factory
functions [`PlainDate`](https://deno.land/x/complaindate/mod.ts?s=PlainDate) and
[`PlainTime`](https://deno.land/x/complaindate/mod.ts?s=PlainTime):
functions [`PlainDate`](https://jsr.io/@bjuppa/complaindate/doc/~/PlainDate) and
[`PlainTime`](https://jsr.io/@bjuppa/complaindate/doc/~/PlainTime):

```ts
const someDate = PlainDate({
Expand Down Expand Up @@ -170,8 +163,8 @@ const midnight = PlainTime({}); // 00:00
### Extraction from strings

Functions
[`parsePlainDate`](https://deno.land/x/complaindate/mod.ts?s=parsePlainDate) and
[`parsePlainTime`](https://deno.land/x/complaindate/mod.ts?s=parsePlainTime)
[`parsePlainDate`](https://jsr.io/@bjuppa/complaindate/doc/~/parsePlainDate) and
[`parsePlainTime`](https://jsr.io/@bjuppa/complaindate/doc/~/parsePlainTime)
creates objects from **strings**:

```ts
Expand All @@ -185,7 +178,7 @@ const midday = parsePlainTime("12:00");
### Extraction from JavaScript `Date` objects

If you have a JavaScript `Date` object, calling
[`splitDateTime`](https://deno.land/x/complaindate/mod.ts?s=splitDateTime) will
[`splitDateTime`](https://jsr.io/@bjuppa/complaindate/doc/~/splitDateTime) will
extract separate plain-date and plain-time objects for a given **timezone**:

```ts
Expand All @@ -196,9 +189,9 @@ const [june6, time1337] = splitDateTime("Europe/Stockholm")(aJsDate);
```

A `Date` can also be split in UTC using
[`splitUtcDateTime`](https://deno.land/x/complaindate/mod.ts?s=splitUtcDateTime)
[`splitUtcDateTime`](https://jsr.io/@bjuppa/complaindate/doc/~/splitUtcDateTime)
or the system's timezone with
[`splitLocalDateTime`](https://deno.land/x/complaindate/mod.ts?s=splitLocalDateTime):
[`splitLocalDateTime`](https://jsr.io/@bjuppa/complaindate/doc/~/splitLocalDateTime):

```ts
const [june6, time1137] = splitUtcDateTime(aJsDate);
Expand Down Expand Up @@ -280,15 +273,15 @@ a timezone supported in your backend may not be supported in the user's current
browser.

Before using a timezone string in frontend code, pass it through the
[`safeTimezone`](https://deno.land/x/complaindate/mod.ts?s=safeTimezone) utility
[`safeTimezone`](https://jsr.io/@bjuppa/complaindate/doc/~/safeTimezone) utility
to get a string guaranteed to be a valid timezone in the local system. Should
the given timezone name be unsuitable, it will return the operating system's
named timezone instead, or `"UTC"` if no timezone can be determined. For the
user, this should make for the best possible graceful degradation when their
preferred timezone is unavailable.

When your application doesn't support timezone as a user preference, the
[`localTimezone`](https://deno.land/x/complaindate/mod.ts?s=localTimezone)
[`localTimezone`](https://jsr.io/@bjuppa/complaindate/doc/~/localTimezone)
utility can be used to retrieve a relevant timezone for the current view.
Although, be careful with server side rendering here&hellip;

Expand All @@ -298,7 +291,7 @@ Because the timezone used may be a fallback and not what the user expects, it's
important to _always_ display the actual timezone name whenever time information
is present in the user interface.

The [`formatTimezone`](https://deno.land/x/complaindate/mod.ts?s=formatTimezone)
The [`formatTimezone`](https://jsr.io/@bjuppa/complaindate/doc/~/formatTimezone)
utility will make a timezone name look pretty for the user. It replaces
underscores with spaces to give a less technical impression, for example
`"Africa/Dar es Salaam"` instead of `"Africa/Dar_es_Salaam"`.
Expand All @@ -307,7 +300,7 @@ underscores with spaces to give a less technical impression, for example

If your user interface provides a way for users to select their preferred
timezone, use
[`supportedCanonicalTimezones`](https://deno.land/x/complaindate/mod.ts?s=supportedCanonicalTimezones)
[`supportedCanonicalTimezones`](https://jsr.io/@bjuppa/complaindate/doc/~/supportedCanonicalTimezones)
to get a list of all the named timezones in the system. You may even create an
endpoint that returns the timezones supported by your backend and intersect that
with the browser's timezones to really make sure no unhandled timezone is
Expand All @@ -320,20 +313,20 @@ to become an autocomplete "combobox" for the user to select from. See the
for details and examples.

Don't forget to use
[`localTimezone`](https://deno.land/x/complaindate/mod.ts?s=localTimezone) to
[`localTimezone`](https://jsr.io/@bjuppa/complaindate/doc/~/localTimezone) to
set a sensible initial value for the input!

### Validating timezones

User input can be run through
[`sanitizeTimezone`](https://deno.land/x/complaindate/mod.ts?s=sanitizeTimezone)
[`sanitizeTimezone`](https://jsr.io/@bjuppa/complaindate/doc/~/sanitizeTimezone)
to clean up a timezone string, removing some common user typos and converting
whitespace to underscore. The result can be checked with
[`isTimezone`](https://deno.land/x/complaindate/mod.ts?s=isTimezone).
[`isTimezone`](https://jsr.io/@bjuppa/complaindate/doc/~/isTimezone).

If you'd rather throw `RangeError` on failure, or want to extract a timezone
name that is part of a longer string, use
[`parseTimezone`](https://deno.land/x/complaindate/mod.ts?s=parseTimezone)
[`parseTimezone`](https://jsr.io/@bjuppa/complaindate/doc/~/parseTimezone)
directly to both sanitize and validate the result.

## Working with JavaScript `Date` objects
Expand All @@ -350,10 +343,10 @@ const instant = new Date(...);

With ComPlainDate `Date` objects can also be created from any date-time
**shaped** objects in a specified timezone with
[`createInstant`](https://deno.land/x/complaindate/mod.ts?s=createInstant),
[`createLocalInstant`](https://deno.land/x/complaindate/mod.ts?s=createLocalInstant)
[`createInstant`](https://jsr.io/@bjuppa/complaindate/doc/~/createInstant),
[`createLocalInstant`](https://jsr.io/@bjuppa/complaindate/doc/~/createLocalInstant)
and
[`createUtcInstant`](https://deno.land/x/complaindate/mod.ts?s=createUtcInstant):
[`createUtcInstant`](https://jsr.io/@bjuppa/complaindate/doc/~/createUtcInstant):

```ts
const noon2023Feb3InSweden = createInstant("Europe/Stockholm")({
Expand Down Expand Up @@ -387,7 +380,7 @@ const jsDateInUtc = createUtcInstant({
```

For UTC, that last example can also be written using the
[`toUtcInstant`](https://deno.land/x/complaindate/mod.ts?s=ComPlainDate#prop_toUtcInstant)
[`toUtcInstant`](https://jsr.io/@bjuppa/complaindate/doc/~/ComPlainDate#property_toutcinstant)
method of the plain-date object, passing an optional wall-time shaped object:

```ts
Expand All @@ -396,7 +389,7 @@ jan1.toUtcInstant(...midday); // 2023-01-01T12:00:00.000Z

### Displaying a `Date` to users

The [`formatInstant`](https://deno.land/x/complaindate/mod.ts?s=formatInstant)
The [`formatInstant`](https://jsr.io/@bjuppa/complaindate/doc/~/formatInstant)
utility generates formatting functions to reuse for consistency throughout a
user interface. It is curried in three rounds with a locale, format options, and
a timezone. Each parameter has a sensible default if left out, using the
Expand All @@ -422,8 +415,8 @@ format24hDateTimeForUser(aJsDate); // "6/13/2023, 08:00:00 EDT"

### Operations on `Date`

Use functions [`addTime`](https://deno.land/x/complaindate/mod.ts?s=addTime) and
[`subtractTime`](https://deno.land/x/complaindate/mod.ts?s=subtractTime) to get
Use functions [`addTime`](https://jsr.io/@bjuppa/complaindate/doc/~/addTime) and
[`subtractTime`](https://jsr.io/@bjuppa/complaindate/doc/~/subtractTime) to get
a new `Date` object shifted some **duration** from an existing one. Units up to
`hours` make sense here because an hour is exactly 60 minutes no matter what
timezone you're in. These methods just sum up the total milliseconds before
Expand Down Expand Up @@ -580,14 +573,14 @@ use in a non-functional paradigm too!

First of all, there are no external dependencies, and there will never be any.

The base [`PlainDate`](https://deno.land/x/complaindate/mod.ts?s=ComPlainDate)
and [`PlainTime`](https://deno.land/x/complaindate/mod.ts?s=ComPlainTime)
The base [`PlainDate`](https://jsr.io/@bjuppa/complaindate/doc/~/ComPlainDate)
and [`PlainTime`](https://jsr.io/@bjuppa/complaindate/doc/~/ComPlainTime)
objects are carefully composed to be _minimal loveable objects_, containing only
what is needed for a neat developer experience. The utility functions are meant
to be imported and applied with these base objects when required.

When bundle size is not an issue (i.e. server-side), you can work with full
[`ExPlainDate`](https://deno.land/x/complaindate/mod.ts?s=ExtendedPlainDate)
[`ExPlainDate`](https://jsr.io/@bjuppa/complaindate/doc/~/ExtendedPlainDate)
objects if you want to call available operations directly on the plain-date
object. This may sound convenient, but it is very hard to tree-shake, making
your bundle size unnecessary big when used.
Expand Down
26 changes: 14 additions & 12 deletions deno.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
{
"name": "@bjuppa/complaindate",
"version": "1.0.1",
"license": "MIT",
"exports": "./mod.ts",
"lock": false,
"lint": {
"publish": {
"exclude": [
"npm/"
"**/*_test.ts",
".github/",
".vscode/",
"examples/",
"scripts/",
"deno.json",
"dev_deps.ts",
"DEVELOPMENT.md"
]
},
"fmt": {
"exclude": [
"npm/"
]
},
"test": {
"exclude": [
"npm/"
]
}
"exclude": ["npm/"]
}
2 changes: 1 addition & 1 deletion dev_deps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ export {
assertStrictEquals,
assertStringIncludes,
assertThrows,
} from "https://deno.land/std@0.168.0/testing/asserts.ts";
} from "jsr:@std/assert";
2 changes: 1 addition & 1 deletion examples/quick-start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
startOfYear,
WeekDay,
weekDayNumber,
} from "https://deno.land/x/complaindate/mod.ts";
} from "jsr:@bjuppa/complaindate";

// Extract a plain-date and a plain-time from any JS `Date`
const [june6, time1337] = splitDateTime(
Expand Down
Loading