A powerful and intuitive date library for React Native and Expo, inspired by PHP's Carbon
npm install momento-rn
# or
yarn add momento-rn- π Full Localization - Native support for Portuguese, English, and more
- β° Fluent API - Intuitive date manipulation like PHP's Carbon
- π± React Native First - Optimized specifically for RN/Expo
- π Immutable - All operations return new instances
- π― TypeScript - Complete typing and IntelliSense
- π Advanced Comparisons - Rich methods for date comparison
- π Timezone Support - Complete timezone handling
- π¨ Flexible Formatting - Custom formatting in any language
- β‘ Performance - Optimized for mobile applications
- π§ͺ Tested - Comprehensive test coverage
If you've used Carbon in PHP, you'll feel at home! Momento brings all the elegance and power of Carbon to the React Native world, with some mobile-specific improvements.
import Momento from 'momento-rn';
// Intuitive creation
const now = Momento.now();
const tomorrow = Momento.tomorrow();
const birthday = Momento.create(1995, 8, 15);
// Fluent manipulation
const future = now
.addYears(1)
.addMonths(6)
.addDays(15)
.startOfDay();
// Natural comparisons
if (birthday.isToday()) {
console.log('Happy birthday! π');
}
// Localized formatting
console.log(now.locale('pt').format('DDDD, D [de] MMMM [de] YYYY'));
// "Monday, 15 de July de 2024"
// Humanized differences
console.log(tomorrow.diffForHumans()); // "in 1 day"// Various ways to create dates
const now = Momento.now();
const today = Momento.today();
const tomorrow = Momento.tomorrow();
const yesterday = Momento.yesterday();
// Specific date
const date = Momento.create(2024, 12, 25, 14, 30, 0);
// From string
const parsed = Momento.parse('2024-12-25');
// From timestamp
const fromTimestamp = Momento.createFromTimestamp(1640995200);const date = Momento.create(2024, 1, 15);
// Addition
const future = date
.addYears(1)
.addMonths(2)
.addWeeks(3)
.addDays(4)
.addHours(5);
// Subtraction
const past = date
.subYears(1)
.subMonths(1)
.subDays(10);
// Start/end of periods
const startOfYear = date.startOfYear();
const endOfMonth = date.endOfMonth();
const startOfWeek = date.startOfWeek();const date1 = Momento.create(2024, 6, 15);
const date2 = Momento.create(2024, 6, 20);
// Basic comparisons
date1.isBefore(date2); // true
date1.isAfter(date2); // false
date1.equals(date2); // false
// Type checks
date1.isToday(); // false
date1.isFuture(); // true/false depending on when executed
date1.isWeekend(); // true if Saturday/Sunday
date1.isLeapYear(); // true if leap year
// Same period
date1.isSameDay(date2); // false
date1.isSameMonth(date2); // true
date1.isSameYear(date2); // trueconst date = Momento.create(2024, 3, 15, 14, 30, 45);
// Standard formats
date.toDateString(); // "2024-03-15"
date.toTimeString(); // "14:30:45"
date.toDateTimeString(); // "2024-03-15 14:30:45"
date.toISOString(); // ISO 8601
// Custom formatting
date.format('DD/MM/YYYY HH:mm'); // "15/03/2024 14:30"
date.format('MMMM D, YYYY [at] h:mm A'); // "March 15, 2024 at 2:30 PM"
date.format('DDDD [the] D[th] [of] MMMM'); // "Friday the 15th of March"
// With localization
date.locale('pt').format('MMMM'); // "MarΓ§o"
date.locale('en').format('MMMM'); // "March"const start = Momento.create(2024, 1, 1);
const end = Momento.create(2024, 12, 31);
// Numeric differences
start.diffInDays(end); // 365
start.diffInMonths(end); // 11
start.diffInHours(end); // 8760
// Humanized differences
end.diffForHumans(); // "in 5 months" (depending on current date)
start.diffForHumans(end); // "11 months before"
// With precision
start.diffInHours(end, { float: true }); // 8760.0// Global configuration
Momento.setLocale('pt');
// Per instance
const datePortuguese = Momento.now().locale('pt');
const dateEnglish = Momento.now().locale('en');
console.log(datePortuguese.format('DDDD, MMMM'));
// "Segunda-feira, Julho"
console.log(dateEnglish.format('DDDD, MMMM'));
// "Monday, July"import React, { useState, useEffect } from 'react';
import { Text, View } from 'react-native';
import Momento from 'momento-rn';
const LiveClock = () => {
const [time, setTime] = useState(Momento.now());
useEffect(() => {
const interval = setInterval(() => {
setTime(Momento.now());
}, 1000);
return () => clearInterval(interval);
}, []);
return (
<View>
<Text style={{ fontSize: 24, fontWeight: 'bold' }}>
{time.format('HH:mm:ss')}
</Text>
<Text>
{time.locale('en').format('DDDD, MMMM D')}
</Text>
</View>
);
};const BirthdayCountdown = ({ birthDate, name }) => {
const birth = Momento.parse(birthDate);
const now = Momento.now();
const nextBirthday = birth.setYear(now.year);
if (nextBirthday.isBefore(now)) {
nextBirthday.addYears(1);
}
const daysUntil = now.diffInDays(nextBirthday, { absolute: true });
const age = birth.age;
return (
<View>
<Text>{name}</Text>
<Text>Age: {age} years</Text>
<Text>
{daysUntil === 0
? 'Happy Birthday! π'
: `${daysUntil} days until birthday`
}
</Text>
</View>
);
};const formatNotificationTime = (date: Momento): string => {
const now = Momento.now();
const diffInMinutes = now.diffInMinutes(date, { absolute: true });
if (diffInMinutes < 1) {
return 'now';
} else if (diffInMinutes < 60) {
return `${diffInMinutes}m`;
} else if (diffInMinutes < 1440) {
return `${Math.floor(diffInMinutes / 60)}h`;
} else {
return date.format('MM/DD');
}
};
// Usage
console.log(formatNotificationTime(Momento.now().subMinutes(30))); // "30m"
console.log(formatNotificationTime(Momento.now().subHours(2))); // "2h"
console.log(formatNotificationTime(Momento.now().subDays(2))); // "07/13"- en - English
- pt - Portuguese (Brazil/Portugal/Mozambique)
- More locales in development...
// Business days
const addBusinessDays = (date: Momento, days: number): Momento => {
let result = date.clone();
let addedDays = 0;
while (addedDays < days) {
result = result.addDays(1);
if (result.isWeekday()) {
addedDays++;
}
}
return result;
};
// Calculate vacation days
const getVacationDays = (start: Momento, end: Momento, holidays: Momento[] = []): number => {
let totalDays = 0;
let current = start.clone();
while (current.isSameOrBefore(end)) {
const isHoliday = holidays.some(holiday => current.isSameDay(holiday));
if (current.isWeekday() && !isHoliday) {
totalDays++;
}
current = current.addDays(1);
}
return totalDays;
};
// Month calendar
const getMonthCalendar = (date: Momento): Momento[][] => {
const startOfMonth = date.startOfMonth();
const endOfMonth = date.endOfMonth();
const startOfCalendar = startOfMonth.startOfWeek();
const endOfCalendar = endOfMonth.endOfWeek();
const weeks: Momento[][] = [];
let current = startOfCalendar;
while (current.isSameOrBefore(endOfCalendar)) {
const week: Momento[] = [];
for (let i = 0; i < 7; i++) {
week.push(current.clone());
current = current.addDays(1);
}
weeks.push(week);
}
return weeks;
};Momento.now(timezone?)Momento.today(timezone?)Momento.tomorrow(timezone?)Momento.yesterday(timezone?)Momento.create(year?, month?, day?, hour?, minute?, second?, timezone?)Momento.parse(dateString, timezone?)Momento.createFromTimestamp(timestamp, timezone?)Momento.createFromTimestampMs(timestamp, timezone?)
year,month,day,hour,minute,second,milliseconddayOfWeek,dayOfYear,weekOfYear,quarter,age
addYears(),addMonths(),addWeeks(),addDays(),addHours(),addMinutes(),addSeconds()subYears(),subMonths(),subWeeks(),subDays(),subHours(),subMinutes(),subSeconds()setYear(),setMonth(),setDay(),setHour(),setMinute(),setSecond()
startOfYear(),endOfYear()startOfMonth(),endOfMonth()startOfWeek(),endOfWeek()startOfDay(),endOfDay()startOfHour(),endOfHour()startOfMinute(),endOfMinute()
equals(),isBefore(),isAfter(),isSameOrBefore(),isSameOrAfter()isBetween(),isToday(),isTomorrow(),isYesterday()isFuture(),isPast(),isWeekday(),isWeekend(),isLeapYear()isSameYear(),isSameMonth(),isSameDay(),isSameHour(),isSameMinute()
diff(),diffInYears(),diffInMonths(),diffInWeeks(),diffInDays()diffInHours(),diffInMinutes(),diffInSeconds(),diffForHumans()
format(),toDateString(),toTimeString(),toDateTimeString()toISOString(),toJSON(),toString()
clone(),copy(),locale(),timezone()getDate(),getTimestamp(),getTimestampMs()Momento.min(),Momento.max(),Momento.isValidDate()
// Set default timezone
Momento.setTimezone('America/Sao_Paulo');
// Get current timezone
const tz = Momento.getTimezone();// Configure week start (0 = Sunday, 1 = Monday)
Momento.setWeekStartsAt(1); // Monday
Momento.setWeekEndsAt(0); // Sunday# Run tests
npm test
# Tests with coverage
npm run test:coverage
# Tests in watch mode
npm run test:watchContributions are very welcome! To contribute:
- Fork the project
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
# Clone the repository
git clone https://github.com/arnaldotomo/momento-rn.git
cd momento-rn
# Install dependencies
npm install
# Run tests
npm test
# Build the library
npm run build- More localizations (es, fr, de, it)
- Alternative calendar support
- Plugin system for extensions
- Integration with react-native-date-picker
- More advanced periods and intervals support
- Humanized duration formatting
- Natural language date parsing
- Carbon (PHP) - The main inspiration for this library
- Moment.js - For popularizing fluent date manipulation in JS
- Day.js - For the lightweight and modern approach
- Date-fns - For well-thought utility functions
MIT Β© Arnaldo Tomo
Made with β€οΈ in Mozambique by Arnaldo Tomo
"Because dealing with dates shouldn't be complicated" - Arnaldo Tomo
- π§ Email: [email protected]
- π Issues: GitHub Issues
- π¬ Discussions: GitHub Discussions
If this project helped you, consider leaving a β on GitHub. It motivates me to keep developing!