Skip to content

Commit eda4394

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 eda4394

13 files changed

Lines changed: 718 additions & 486 deletions

File tree

README.md

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
1-
# react-native-date
1+
<p align="center">
2+
<img src="icon.png" alt="react-native-date" width="120" />
3+
</p>
24

3-
The fastest date library for React Native. Native C++ performance with zero-config localization.
5+
# @bernagl/react-native-date
6+
7+
The fastest date library for React Native. Powered by C++ and [Nitro Modules](https://nitro.margelo.com/) 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.
12+
## Installation
13+
14+
```bash
15+
npm install @bernagl/react-native-date react-native-nitro-modules
16+
```
17+
18+
```bash
19+
cd ios && pod install
20+
```
21+
22+
**Requirements:** React Native 0.76+ (New Architecture) • iOS 13.0+ • Android API 24+
923

1024
## Is this library for you?
1125

@@ -50,20 +64,20 @@ Built with C++ and [Nitro Modules](https://nitro.margelo.com/) for synchronous,
5064

5165
**iPhone 14 Pro** (ops/sec - higher is better):
5266

53-
| Operation | Native | date-fns | Day.js | Luxon | Gain |
54-
|-----------|--------|----------|--------|-------|------|
55-
| `parse()` | **2.0M** | 94K | 329K | 28K | 🟢 21x |
67+
| Operation | NativeDate | date-fns | Day.js | Luxon | Gain |
68+
|-----------|------------|----------|--------|-------|------|
69+
| `parse()` | **2.0M** | 94K | 329K | 28K | 🟢 71x |
5670
| `format()` | **771K** | 31K | 103K | 34K | 🟢 25x |
57-
| `diffInDays()` | **1.6M** | 74K | 104K | 8K | 🟢 22x |
71+
| `diffInDays()` | **1.6M** | 74K | 104K | 8K | 🟢 200x |
5872
| `formatUTC()` | **805K** | 18K | 80K | 29K | 🟢 45x |
5973

6074
**Low-End Android** (ops/sec - higher is better):
6175

62-
| Operation | Native | date-fns | Day.js | Luxon | Gain |
63-
|-----------|--------|----------|--------|-------|------|
64-
| `parse()` | **465K** | 13K | 46K | 4K | 🟢 35x |
76+
| Operation | NativeDate | date-fns | Day.js | Luxon | Gain |
77+
|-----------|------------|----------|--------|-------|------|
78+
| `parse()` | **465K** | 13K | 46K | 4K | 🟢 116x |
6579
| `format()` | **270K** | 5K | 16K | 5K | 🟢 54x |
66-
| `diffInDays()` | **351K** | 13K | 15K | 1K | 🟢 27x |
80+
| `diffInDays()` | **351K** | 13K | 15K | 1K | 🟢 351x |
6781
| `formatUTC()` | **292K** | 2K | 12K | 4K | 🟢 146x |
6882

6983
> **Performance gap widens on budget devices:** `format()` is 25x faster on iOS but **54x faster** on low-end Android. The native C++ implementation shines where JavaScript is constrained. Essential for apps targeting emerging markets.
@@ -86,26 +100,6 @@ import { nativeDate } from '@bernagl/react-native-date';
86100
nativeDate().addDays(7).format('yyyy-MM-dd');
87101
```
88102

89-
## Requirements
90-
91-
| Platform | Minimum Version |
92-
|----------|-----------------|
93-
| iOS | 13.0+ |
94-
| Android | API 24+ (Android 7.0) |
95-
| React Native | 0.76+ (New Architecture) |
96-
97-
## Installation
98-
99-
```bash
100-
npm install @bernagl/react-native-date react-native-nitro-modules
101-
```
102-
103-
```bash
104-
cd ios && pod install
105-
```
106-
107-
No additional setup required.
108-
109103
## Quick Start
110104

111105
```typescript

docs/.vitepress/config.mts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ import { defineConfig } from 'vitepress'
22

33
export default defineConfig({
44
title: "react-native-date",
5-
description: "High-performance native date library for React Native",
5+
description: "The fastest date library for React Native. Powered by C++ and Nitro Modules.",
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: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
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-
7-
The fastest date library for React Native. Native C++ performance with zero-config localization.
3+
The fastest date library for React Native. Powered by C++ and [Nitro Modules](https://nitro.margelo.com/) with zero-config localization.
84

95
## Is this library for you?
106

@@ -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

0 commit comments

Comments
 (0)