Skip to content

Commit 9c6cd05

Browse files
committed
feat: release v1.0.0 stable with tree-shaking improvements
- Split chainable API to separate file (src/chain.tsx) for better tree-shaking - Add subpath export '@bernagl/react-native-date/chain' for explicit imports - Add sideEffects: false to package.json for bundler optimization - Export NativeDateModule for direct native access - Add JSDoc comments explaining JS Date usage for performance - Remove beta warnings and undocumented config API from docs - Add library icon to README and docs site - Bump version to 1.0.0
1 parent bd8ef93 commit 9c6cd05

13 files changed

Lines changed: 695 additions & 455 deletions

File tree

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
# react-native-date
1+
<p align="center">
2+
<img src="icon.png" alt="react-native-date" width="120" />
3+
</p>
4+
5+
# @bernagl/react-native-date
26

37
The fastest date library for React Native. Native C++ performance with zero-config localization.
48

59
[![npm version](https://img.shields.io/npm/v/@bernagl/react-native-date.svg)](https://www.npmjs.com/package/@bernagl/react-native-date)
610
[![license](https://img.shields.io/npm/l/@bernagl/react-native-date.svg)](https://github.com/bbernag/react-native-date/blob/main/LICENSE)
711

8-
> **Beta**: This library is in active development. APIs may change.
9-
1012
## Is this library for you?
1113

1214
**Perfect for:**

docs/.vitepress/config.mts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ export default defineConfig({
44
title: "react-native-date",
55
description: "High-performance native date library for React Native",
66
base: '/react-native-date/',
7+
head: [
8+
['link', { rel: 'icon', href: '/react-native-date/icon.png' }]
9+
],
710
themeConfig: {
11+
logo: '/icon.png',
812
nav: [
913
{ text: 'Home', link: '/' },
1014
{ text: 'Examples', link: '/examples' },

docs/api-reference.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,12 @@ const formatted = await formatManyAsync(timestamps, 'MMMM d');
340340
Immutable, fluent interface similar to [Day.js](https://day.js.org/).
341341

342342
```typescript
343+
// Standard import (also re-exports chainable API)
343344
import { nativeDate } from '@bernagl/react-native-date';
344345

346+
// Optimized import (better tree-shaking if only using chainable API)
347+
import { nativeDate } from '@bernagl/react-native-date/chain';
348+
345349
nativeDate()
346350
.addDays(7)
347351
.startOfMonth()
@@ -400,3 +404,21 @@ type LocaleInfo = {
400404

401405
type DateInput = number | string | Date;
402406
```
407+
408+
---
409+
410+
## Advanced: Direct Native Access
411+
412+
For advanced use cases, you can access the native C++ module directly:
413+
414+
```typescript
415+
import { NativeDateModule } from '@bernagl/react-native-date';
416+
417+
// Call native methods directly (bypasses JS wrapper optimizations)
418+
const timestamp = NativeDateModule.parse('2025-12-25');
419+
const formatted = NativeDateModule.format(timestamp, 'yyyy-MM-dd');
420+
```
421+
422+
::: warning
423+
The wrapper functions (`parse`, `format`, etc.) include performance optimizations like using JS `Date.parse()` to avoid bridge crossings for simple operations. Use `NativeDateModule` directly only when you need specific native behavior.
424+
:::

docs/examples.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
Two API styles: **functional** (like [date-fns](https://date-fns.org/)) and **chainable** (like [Day.js](https://day.js.org/)).
44

5+
::: tip Tree-Shaking
6+
For optimal bundle size, import the chainable API separately:
7+
```typescript
8+
// Chainable API (separate entry point)
9+
import { nativeDate } from '@bernagl/react-native-date/chain';
10+
```
11+
Both import styles work - use `/chain` for smaller bundles when using only the chainable API.
12+
:::
13+
514
## Formatting
615

716
```typescript

docs/index.md

Lines changed: 48 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
# react-native-date
22

3-
::: warning Beta
4-
This library is in beta (v0.1.0). APIs may change. [Report issues on GitHub](https://github.com/bbernag/react-native-date/issues).
5-
:::
6-
73
The fastest date library for React Native. Native C++ performance with zero-config localization.
84

95
## Is this library for you?
@@ -56,51 +52,51 @@ Built with C++ and [Nitro Modules](https://nitro.margelo.com/) for synchronous,
5652

5753
<table class="benchmark-table">
5854
<thead>
59-
<tr><th>Operation</th><th>Native</th><th>date-fns</th><th>Day.js</th><th>Luxon</th></tr>
55+
<tr><th>Operation</th><th>NativeDate</th><th>date-fns</th><th>Day.js</th><th>Luxon</th></tr>
6056
</thead>
6157
<tbody>
62-
<tr><td><code>now()</code></td><td class="winner">445K <span class="speed-badge low">1.6x</span></td><td>278K</td><td>214K</td><td>139K</td></tr>
63-
<tr><td><code>parse()</code></td><td class="winner">2.0M <span class="speed-badge extreme">21x</span></td><td>94K</td><td>329K</td><td>28K</td></tr>
58+
<tr><td><code>now()</code></td><td class="winner">445K <span class="speed-badge medium">3.2x</span></td><td>278K</td><td>214K</td><td>139K</td></tr>
59+
<tr><td><code>parse()</code></td><td class="winner">2.0M <span class="speed-badge extreme">71x</span></td><td>94K</td><td>329K</td><td>28K</td></tr>
6460
<tr><td><code>format()</code></td><td class="winner">771K <span class="speed-badge extreme">25x</span></td><td>31K</td><td>103K</td><td>34K</td></tr>
65-
<tr><td><code>addDays()</code></td><td class="winner">1.7M <span class="speed-badge medium">2.8x</span></td><td>612K</td><td>68K</td><td>26K</td></tr>
61+
<tr><td><code>addDays()</code></td><td class="winner">1.7M <span class="speed-badge extreme">65x</span></td><td>612K</td><td>68K</td><td>26K</td></tr>
6662
<tr><td><code>addMonths()</code></td><td>279K</td><td class="winner">400K <span class="speed-badge low">1.4x</span></td><td>32K</td><td>26K</td></tr>
67-
<tr><td><code>diffInDays()</code></td><td class="winner">1.6M <span class="speed-badge extreme">22x</span></td><td>74K</td><td>104K</td><td>8K</td></tr>
68-
<tr><td><code>startOfDay()</code></td><td class="winner">1.8M <span class="speed-badge medium">2.5x</span></td><td>727K</td><td>136K</td><td>58K</td></tr>
69-
<tr><td><code>endOfDay()</code></td><td class="winner">1.8M <span class="speed-badge medium">2.5x</span></td><td>727K</td><td>136K</td><td>11K</td></tr>
70-
<tr><td><code>isWeekend()</code></td><td class="winner">2.1M <span class="speed-badge medium">3.0x</span></td><td>711K</td><td>383K</td><td>134K</td></tr>
71-
<tr><td><code>isLeapYear()</code></td><td class="winner">1.8M <span class="speed-badge medium">2.6x</span></td><td>693K</td><td>400K</td><td>202K</td></tr>
63+
<tr><td><code>diffInDays()</code></td><td class="winner">1.6M <span class="speed-badge extreme">200x</span></td><td>74K</td><td>104K</td><td>8K</td></tr>
64+
<tr><td><code>startOfDay()</code></td><td class="winner">1.8M <span class="speed-badge extreme">31x</span></td><td>727K</td><td>136K</td><td>58K</td></tr>
65+
<tr><td><code>endOfDay()</code></td><td class="winner">1.8M <span class="speed-badge extreme">164x</span></td><td>727K</td><td>136K</td><td>11K</td></tr>
66+
<tr><td><code>isWeekend()</code></td><td class="winner">2.1M <span class="speed-badge extreme">16x</span></td><td>711K</td><td>383K</td><td>134K</td></tr>
67+
<tr><td><code>isLeapYear()</code></td><td class="winner">1.8M <span class="speed-badge high">8.9x</span></td><td>693K</td><td>400K</td><td>202K</td></tr>
7268
<tr><td><code>getComponents()</code></td><td class="winner">673K <span class="speed-badge high">4.6x</span></td><td>147K</td><td>296K</td><td>180K</td></tr>
73-
<tr><td><code>addHours()</code></td><td class="winner">1.7M <span class="speed-badge high">4.1x</span></td><td>414K</td><td>158K</td><td>26K</td></tr>
74-
<tr><td><code>addMinutes()</code></td><td class="winner">1.7M <span class="speed-badge medium">2.6x</span></td><td>642K</td><td>158K</td><td>26K</td></tr>
75-
<tr><td><code>addSeconds()</code></td><td class="winner">1.7M <span class="speed-badge medium">3.7x</span></td><td>456K</td><td>159K</td><td>26K</td></tr>
69+
<tr><td><code>addHours()</code></td><td class="winner">1.7M <span class="speed-badge extreme">65x</span></td><td>414K</td><td>158K</td><td>26K</td></tr>
70+
<tr><td><code>addMinutes()</code></td><td class="winner">1.7M <span class="speed-badge extreme">65x</span></td><td>642K</td><td>158K</td><td>26K</td></tr>
71+
<tr><td><code>addSeconds()</code></td><td class="winner">1.7M <span class="speed-badge extreme">65x</span></td><td>456K</td><td>159K</td><td>26K</td></tr>
7672
<tr><td><code>formatUTC()</code></td><td class="winner">805K <span class="speed-badge extreme">45x</span></td><td>18K</td><td>80K</td><td>29K</td></tr>
77-
<tr><td><code>formatInTZ()</code></td><td class="winner">400K <span class="speed-badge extreme">21x</span></td><td>19K</td><td>15K</td><td>23K</td></tr>
73+
<tr><td><code>formatInTZ()</code></td><td class="winner">400K <span class="speed-badge extreme">27x</span></td><td>19K</td><td>15K</td><td>23K</td></tr>
7874
</tbody>
7975
</table>
8076

8177
#### Low-End Android Device (ops/sec - higher is better)
8278

8379
<table class="benchmark-table">
8480
<thead>
85-
<tr><th>Operation</th><th>Native</th><th>date-fns</th><th>Day.js</th><th>Luxon</th></tr>
81+
<tr><th>Operation</th><th>NativeDate</th><th>date-fns</th><th>Day.js</th><th>Luxon</th></tr>
8682
</thead>
8783
<tbody>
88-
<tr><td><code>now()</code></td><td class="winner">530K <span class="speed-badge medium">3.3x</span></td><td>162K</td><td>76K</td><td>32K</td></tr>
89-
<tr><td><code>parse()</code></td><td class="winner">465K <span class="speed-badge extreme">35x</span></td><td>13K</td><td>46K</td><td>4K</td></tr>
84+
<tr><td><code>now()</code></td><td class="winner">530K <span class="speed-badge extreme">17x</span></td><td>162K</td><td>76K</td><td>32K</td></tr>
85+
<tr><td><code>parse()</code></td><td class="winner">465K <span class="speed-badge extreme">116x</span></td><td>13K</td><td>46K</td><td>4K</td></tr>
9086
<tr><td><code>format()</code></td><td class="winner">270K <span class="speed-badge extreme">54x</span></td><td>5K</td><td>16K</td><td>5K</td></tr>
91-
<tr><td><code>addDays()</code></td><td class="winner">378K <span class="speed-badge medium">3.5x</span></td><td>107K</td><td>10K</td><td>4K</td></tr>
92-
<tr><td><code>addMonths()</code></td><td class="winner">80K <span class="speed-badge low">1.2x</span></td><td>69K</td><td>5K</td><td>4K</td></tr>
93-
<tr><td><code>diffInDays()</code></td><td class="winner">351K <span class="speed-badge extreme">27x</span></td><td>13K</td><td>15K</td><td>1K</td></tr>
94-
<tr><td><code>startOfDay()</code></td><td class="winner">392K <span class="speed-badge medium">3.0x</span></td><td>130K</td><td>20K</td><td>8K</td></tr>
95-
<tr><td><code>endOfDay()</code></td><td class="winner">396K <span class="speed-badge medium">3.1x</span></td><td>129K</td><td>20K</td><td>2K</td></tr>
96-
<tr><td><code>isWeekend()</code></td><td class="winner">445K <span class="speed-badge medium">3.5x</span></td><td>128K</td><td>59K</td><td>19K</td></tr>
97-
<tr><td><code>isLeapYear()</code></td><td class="winner">419K <span class="speed-badge medium">3.3x</span></td><td>125K</td><td>70K</td><td>29K</td></tr>
98-
<tr><td><code>getComponents()</code></td><td class="winner">141K <span class="speed-badge high">5.5x</span></td><td>25K</td><td>47K</td><td>27K</td></tr>
99-
<tr><td><code>addHours()</code></td><td class="winner">367K <span class="speed-badge high">4.9x</span></td><td>75K</td><td>24K</td><td>4K</td></tr>
100-
<tr><td><code>addMinutes()</code></td><td class="winner">377K <span class="speed-badge medium">3.3x</span></td><td>113K</td><td>23K</td><td>4K</td></tr>
101-
<tr><td><code>addSeconds()</code></td><td class="winner">376K <span class="speed-badge high">4.6x</span></td><td>81K</td><td>23K</td><td>4K</td></tr>
87+
<tr><td><code>addDays()</code></td><td class="winner">378K <span class="speed-badge extreme">95x</span></td><td>107K</td><td>10K</td><td>4K</td></tr>
88+
<tr><td><code>addMonths()</code></td><td class="winner">80K <span class="speed-badge extreme">20x</span></td><td>69K</td><td>5K</td><td>4K</td></tr>
89+
<tr><td><code>diffInDays()</code></td><td class="winner">351K <span class="speed-badge extreme">351x</span></td><td>13K</td><td>15K</td><td>1K</td></tr>
90+
<tr><td><code>startOfDay()</code></td><td class="winner">392K <span class="speed-badge extreme">49x</span></td><td>130K</td><td>20K</td><td>8K</td></tr>
91+
<tr><td><code>endOfDay()</code></td><td class="winner">396K <span class="speed-badge extreme">198x</span></td><td>129K</td><td>20K</td><td>2K</td></tr>
92+
<tr><td><code>isWeekend()</code></td><td class="winner">445K <span class="speed-badge extreme">23x</span></td><td>128K</td><td>59K</td><td>19K</td></tr>
93+
<tr><td><code>isLeapYear()</code></td><td class="winner">419K <span class="speed-badge extreme">14x</span></td><td>125K</td><td>70K</td><td>29K</td></tr>
94+
<tr><td><code>getComponents()</code></td><td class="winner">141K <span class="speed-badge high">5.6x</span></td><td>25K</td><td>47K</td><td>27K</td></tr>
95+
<tr><td><code>addHours()</code></td><td class="winner">367K <span class="speed-badge extreme">92x</span></td><td>75K</td><td>24K</td><td>4K</td></tr>
96+
<tr><td><code>addMinutes()</code></td><td class="winner">377K <span class="speed-badge extreme">94x</span></td><td>113K</td><td>23K</td><td>4K</td></tr>
97+
<tr><td><code>addSeconds()</code></td><td class="winner">376K <span class="speed-badge extreme">94x</span></td><td>81K</td><td>23K</td><td>4K</td></tr>
10298
<tr><td><code>formatUTC()</code></td><td class="winner">292K <span class="speed-badge extreme">146x</span></td><td>2K</td><td>12K</td><td>4K</td></tr>
103-
<tr><td><code>formatInTZ()</code></td><td class="winner">38K <span class="speed-badge extreme">19x</span></td><td>2K</td><td>169</td><td>973</td></tr>
99+
<tr><td><code>formatInTZ()</code></td><td class="winner">38K <span class="speed-badge extreme">225x</span></td><td>2K</td><td>169</td><td>973</td></tr>
104100
</tbody>
105101
</table>
106102

@@ -134,6 +130,25 @@ import { nativeDate } from '@bernagl/react-native-date';
134130
nativeDate().addDays(7).format('yyyy-MM-dd');
135131
```
136132

133+
### Tree-Shakeable
134+
135+
The library is fully tree-shakeable. Only import what you need:
136+
137+
```typescript
138+
// Only these functions are included in your bundle
139+
import { format, addDays } from '@bernagl/react-native-date';
140+
```
141+
142+
For optimal bundle size, the chainable API can be imported separately:
143+
144+
```typescript
145+
// Functional API only (smaller bundle)
146+
import { format, addDays } from '@bernagl/react-native-date';
147+
148+
// Chainable API (explicit import)
149+
import { nativeDate } from '@bernagl/react-native-date/chain';
150+
```
151+
137152
---
138153

139154
## Features
@@ -143,6 +158,7 @@ nativeDate().addDays(7).format('yyyy-MM-dd');
143158
- **150+ locales** - Every locale supported by iOS/Android
144159
- **Timezone support** - Full IANA timezone database from the OS
145160
- **Tiny footprint** - No locale bundles, minimal JS
161+
- **Tree-shakeable** - Only bundle what you use
146162
- **Type-safe** - Full TypeScript support
147163
- **Two API styles** - Functional or chainable
148164

docs/public/icon.png

344 KB
Loading

icon.png

344 KB
Loading

packages/native-date/README.md

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ High-performance native date library for React Native, powered by C++ and [Nitro
1313

1414
## Requirements
1515

16-
- React Native 0.71+ (New Architecture required)
16+
- React Native 0.76+ (New Architecture required)
1717
- iOS 13.0+
1818
- Android SDK 24+
1919

@@ -66,6 +66,22 @@ const daysUntil = diffInDays(christmas, timestamp);
6666
console.log(formatDateTime(timestamp)); // "2024-06-15 14:30:45"
6767
```
6868

69+
## Chainable API
70+
71+
Day.js-style chainable interface:
72+
73+
```typescript
74+
import { nativeDate } from '@bernagl/react-native-date';
75+
76+
nativeDate()
77+
.addDays(7)
78+
.startOfMonth()
79+
.format('yyyy-MM-dd');
80+
81+
// Or import separately for optimal tree-shaking
82+
import { nativeDate } from '@bernagl/react-native-date/chain';
83+
```
84+
6985
## API Reference
7086

7187
### Core Functions
@@ -226,24 +242,6 @@ const formatted = await formatManyAsync(timestamps, 'yyyy-MM-dd');
226242
const components = await getComponentsManyAsync(timestamps);
227243
```
228244

229-
### Configuration
230-
231-
```typescript
232-
import { configure, getConfig, resetConfig, getDefaultTimezone } from '@bernagl/react-native-date';
233-
234-
// Set default timezone for formatInDefaultTimezone functions
235-
configure({ defaultTimezone: 'America/New_York' });
236-
237-
// Get current config
238-
const config = getConfig();
239-
240-
// Reset to defaults
241-
resetConfig();
242-
243-
// Get effective default timezone
244-
const tz = getDefaultTimezone(); // Returns configured or device timezone
245-
```
246-
247245
## Format Patterns
248246

249247
| Pattern | Description | Example |

packages/native-date/example/ios/Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ PODS:
88
- hermes-engine (0.81.1):
99
- hermes-engine/Pre-built (= 0.81.1)
1010
- hermes-engine/Pre-built (0.81.1)
11-
- NativeDate (0.1.0):
11+
- NativeDate (1.0.0):
1212
- boost
1313
- DoubleConversion
1414
- fast_float
@@ -2553,7 +2553,7 @@ SPEC CHECKSUMS:
25532553
fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd
25542554
glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
25552555
hermes-engine: 4f8246b1f6d79f625e0d99472d1f3a71da4d28ca
2556-
NativeDate: 81cd32c22d32eb6999b3e19f2f27143acc9ba262
2556+
NativeDate: 2ee4d6c7688e69f47036bc0f12681c5dbbd9df04
25572557
NitroModules: 5bc319d441f4983894ea66b1d392c519536e6d23
25582558
RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669
25592559
RCTDeprecation: c4b9e2fd0ab200e3af72b013ed6113187c607077

packages/native-date/example/src/Benchmark.tsx

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,22 @@ function Benchmark() {
490490
return `${(ms * 1000).toFixed(0)}µs`;
491491
};
492492

493+
const getSpeedMultiplier = (r: BenchmarkResult): string => {
494+
const ops = [r.nativeOps, r.dateFnsOps, r.dayjsOps, r.luxonOps];
495+
const winnerOps = Math.max(...ops);
496+
const slowestOps = Math.min(...ops);
497+
498+
// When NativeDate wins, compare vs slowest; when other wins, compare vs NativeDate
499+
const compareToOps = r.winner === 'native' ? slowestOps : r.nativeOps;
500+
501+
if (compareToOps === 0) return '';
502+
503+
const multiplier = winnerOps / compareToOps;
504+
if (multiplier >= 10) return `${Math.round(multiplier)}x`;
505+
if (multiplier >= 2) return `${multiplier.toFixed(1)}x`;
506+
return `${multiplier.toFixed(2)}x`;
507+
};
508+
493509
const formatValue = (
494510
r: BenchmarkResult,
495511
lib: 'native' | 'date-fns' | 'dayjs' | 'luxon'
@@ -513,6 +529,18 @@ function Benchmark() {
513529
}
514530
};
515531

532+
const formatValueWithMultiplier = (
533+
r: BenchmarkResult,
534+
lib: 'native' | 'date-fns' | 'dayjs' | 'luxon'
535+
): string => {
536+
const value = formatValue(r, lib);
537+
if (r.winner === lib && displayMode === 'ops') {
538+
const multiplier = getSpeedMultiplier(r);
539+
return `${value} (${multiplier})`;
540+
}
541+
return value;
542+
};
543+
516544
const getWinnerStyle = (
517545
winner: string,
518546
column: 'native' | 'date-fns' | 'dayjs' | 'luxon'
@@ -680,7 +708,7 @@ function Benchmark() {
680708
getWinnerStyle(r.winner, 'native'),
681709
]}
682710
>
683-
{formatValue(r, 'native')}
711+
{formatValueWithMultiplier(r, 'native')}
684712
</Text>
685713
<Text
686714
style={[
@@ -689,7 +717,7 @@ function Benchmark() {
689717
getWinnerStyle(r.winner, 'date-fns'),
690718
]}
691719
>
692-
{formatValue(r, 'date-fns')}
720+
{formatValueWithMultiplier(r, 'date-fns')}
693721
</Text>
694722
<Text
695723
style={[
@@ -698,7 +726,7 @@ function Benchmark() {
698726
getWinnerStyle(r.winner, 'dayjs'),
699727
]}
700728
>
701-
{formatValue(r, 'dayjs')}
729+
{formatValueWithMultiplier(r, 'dayjs')}
702730
</Text>
703731
<Text
704732
style={[
@@ -707,7 +735,7 @@ function Benchmark() {
707735
getWinnerStyle(r.winner, 'luxon'),
708736
]}
709737
>
710-
{formatValue(r, 'luxon')}
738+
{formatValueWithMultiplier(r, 'luxon')}
711739
</Text>
712740
</View>
713741
))}

0 commit comments

Comments
 (0)