xiaohongshu
xiaohongshu
Handling currency conversion, formatting, and timezone management for global applications to support users worldwide with local currency and time display.
Full skill instructions
Current Level: Intermediate
Domain: Internationalization / Backend
Currency and timezone handling is essential for global applications. Effective currency and timezone management ensures users see prices and times in their local format, supports multi-currency payments, and complies with local regulations.
| Benefit | Description |
|---|---|
| Global Applications | Support users worldwide |
| User Expectations | Local currency and time |
| Business Requirements | Multi-currency payments |
| Compliance | Local regulations |
Definition: The primary time standard by which the world regulates clocks and time.
Why Use UTC:
| City | Offset | UTC Time Example |
|---|---|---|
| New York | UTC-5 | 14:00 = 19:00 |
| London | UTC+0 | 14:00 = 14:00 |
| Bangkok | UTC+7 | 14:00 = 21:00 |
| Tokyo | UTC+9 | 14:00 = 23:00 |
Definition: Clocks set forward in spring and back in autumn.
Impact:
Format: Area/Location
| Timezone | Description |
|---|---|
America/New_York | Eastern Time |
Europe/London | UK Time |
Asia/Bangkok | Indochina Time |
Asia/Tokyo | Japan Time |
-- Good: Store in UTC
CREATE TABLE events (
id SERIAL PRIMARY KEY,
event_name VARCHAR(255),
event_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
-- Insert with explicit UTC
INSERT INTO events (event_name, event_time)
VALUES ('Meeting', '2024-01-15 14:00:00+00'::timestamp);
| Database | Type | Description |
|---|---|---|
| PostgreSQL | TIMESTAMP WITH TIME ZONE | Stores with timezone info |
| MySQL | TIMESTAMP | Stores in UTC (recommended) |
| SQL Server | DATETIMEOFFSET | Stores with timezone offset |
-- Convert to UTC
SELECT event_time AT TIME ZONE 'UTC' FROM events;
-- Convert to user timezone
SELECT event_time AT TIME ZONE 'America/New_York' FROM events;
-- Extract timezone info
SELECT
event_time,
EXTRACT(TIMEZONE FROM event_time) AS timezone_offset
FROM events;
-- Store in UTC
CREATE TABLE events (
id INT AUTO_INCREMENT PRIMARY KEY,
event_name VARCHAR(255),
event_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- Convert to UTC on insert
INSERT INTO events (event_name, event_time)
VALUES ('Meeting', CONVERT_TZ('2024-01-15 14:00:00', 'Asia/Bangkok', 'UTC'));
// Date object uses browser timezone
const now = new Date();
console.log(now.toString()); // Local time
console.log(now.toISOString()); // UTC time
// Format date for user's timezone
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'America/New_York',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
timeZoneName: 'short'
});
console.log(formatter.format(new Date()));
// Output: January 15, 2024 at 9:00 AM EST
| Library | Description |
|---|---|
| date-fns-tz | Timezone support for date-fns |
| Luxon | Modern date/time library |
| Day.js | Lightweight alternative to Moment.js |
| Moment.js | Deprecated, use alternatives |
import { format, utcToZonedTime, zonedTimeToUtc } from 'date-fns-tz';
import { utcToZonedTime } from 'date-fns-tz/utcToZonedTime';
// Convert UTC to user timezone
const utcDate = new Date('2024-01-15 14:00:00Z');
const userDate = utcToZonedTime(utcDate, 'America/New_York');
console.log(format(userDate, 'yyyy-MM-dd HH:mm:ss zzz'));
// Convert user timezone to UTC
const userDate = new Date('2024-01-15 09:00:00');
const utcDate = zonedTimeToUtc(userDate, 'America/New_York');
console.log(utcDate.toISOString());
import { DateTime } from 'luxon';
// Create in UTC
const utcDate = DateTime.utc(2024, 1, 15, 14, 0, 0);
console.log(utcDate.toString()); // 2024-01-15T14:00:00.000Z
// Convert to user timezone
const userDate = utcDate.setZone('America/New_York');
console.log(userDate.toString()); // 2024-01-15T09:00:00.000-05:00
// Format for display
console.log(userDate.toFormat('yyyy-MM-dd HH:mm:ss zzz')); // 2024-01-15 09:00:00 EST
from datetime import datetime
import pytz
# Create timezone-aware datetime
utc_now = datetime.utcnow()
utc_tz = pytz.UTC.localize(utc_now)
# Convert to user timezone
user_tz = pytz.timezone('America/New_York')
user_now = utc_tz.astimezone(user_tz)
print(f"UTC: {utc_now}")
print(f"User: {user_now}")
from datetime import datetime
from dateutil import tz
# Parse with timezone
user_date = datetime.strptime('2024-01-15 09:00:00', '%Y-%m-%d %H:%M:%S')
user_date = user_date.replace(tzinfo=tz.gettz('America/New_York'))
# Convert to UTC
utc_date = user_date.astimezone(tz.UTC)
print(f"User: {user_date}")
print(f"UTC: {utc_date}")
import { utcToZonedTime, format } from 'date-fns-tz';
// Convert UTC to user timezone
function formatForUser(utcDate, userTimezone) {
const userDate = utcToZonedTime(utcDate, userTimezone);
return format(userDate, 'yyyy-MM-dd HH:mm:ss zzz');
}
// Usage
const utcDate = new Date('2024-01-15 14:00:00Z');
const userTimezone = 'America/New_York';
console.log(formatForUser(utcDate, userTimezone));
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
// Create in UTC
ZonedDateTime utcDate = ZonedDateTime.of(2024, 1, 15, 14, 0, 0, ZoneId.of("UTC"));
// Convert to user timezone
ZonedDateTime userDate = utcDate.withZoneSameInstant(ZoneId.of("America/New_York"));
// Format
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss zzz");
System.out.println(userDate.format(formatter));
// Get user's timezone
const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
console.log(userTimezone); // e.g., "America/New_York"
// Accept from client
app.get('/api/timezone', (req, res) => {
const userTimezone = req.headers['x-user-timezone'] || req.query.timezone;
// Store in user profile
updateUserTimezone(req.user.id, userTimezone);
res.json({ timezone: userTimezone });
});
// Use IP to guess timezone
const axios = require('axios');
const geoip = require('geoip-lite');
async function getTimezoneFromIP(ip) {
const geo = geoip.lookup(ip);
if (geo && geo.timezone) {
return geo.timezone;
}
return 'UTC'; // Fallback
}
┌─────────────────────────────────────────────────────────────────┐
│ Timezone Selection │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Select your timezone: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ (●) UTC │ │
│ │ ( ) America/New_York │ │
│ │ ( ) Europe/London │ │
│ │ ( ) Asia/Bangkok │ │
│ │ ( ) Asia/Tokyo │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ [Save Settings] │
│ │
└─────────────────────────────────────────────────────────────────┘
// Convert UTC to user timezone for display
function formatDateTime(utcDate, userTimezone) {
return utcToZonedTime(utcDate, userTimezone)
.toFormat('yyyy-MM-dd HH:mm:ss zzz');
}
// Usage
const eventDate = new Date('2024-01-15 14:00:00Z');
const userTimezone = getUserTimezone(userId);
console.log(formatDateTime(eventDate, userTimezone));
// Convert user input to UTC before storing
function parseUserDateTime(userDate, userTimezone) {
const parsed = new Date(userDate);
const userTz = tz.getTimezone(userTimezone);
const utcDate = tz.utc(parsed);
return utcDate.toJSDate();
}
// Usage
const userInput = '2024-01-15 09:00:00';
const userTimezone = 'America/New_York';
const utcDate = parseUserDateTime(userInput, userTimezone);
// Store utcDate in database
import { utcToZonedTime, format } from 'date-fns-tz';
// Handle DST transitions
function formatWithDST(utcDate, userTimezone) {
const userDate = utcToZonedTime(utcDate, userTimezone);
const isDST = userDate.isDST;
const formatted = format(userDate, 'yyyy-MM-dd HH:mm:ss zzz');
return `${formatted} ${isDST ? '(DST)' : ''}`;
}
// Example: Spring forward (2:30 AM doesn't exist)
const springDate = new Date('2024-03-10 07:00:00Z'); // 2:00 AM EST
console.log(formatWithDST(springDate, 'America/New_York'));
// Output: 2024-03-10 03:00:00 EDT (DST)
// Handle ambiguous times (fall back)
function handleAmbiguousTime(utcDate, userTimezone) {
const userDate = utcToZonedTime(utcDate, userTimezone);
if (userDate.isAmbiguous) {
// Choose the earlier occurrence
return userDate.setZone('UTC');
}
return userDate;
}
// Example: Fall back (1:30 AM occurs twice)
const fallDate = new Date('2024-11-03 06:00:00Z'); // 1:00 AM EST
console.log(handleAmbiguousTime(fallDate, 'America/New_York'));
// Handle non-existent times (spring forward)
function handleNonExistentTime(utcDate, userTimezone) {
const userDate = utcToZonedTime(utcDate, userTimezone);
if (!userDate.isValid) {
// Adjust to next valid time
return userDate.plus({ hours: 1 });
}
return userDate;
}
// Example: Spring forward (2:30 AM doesn't exist)
const springDate = new Date('2024-03-10 07:30:00Z'); // 2:30 AM EST
console.log(handleNonExistentTime(springDate, 'America/New_York'));
// Output: 3:30 AM EDT
| Code | Name | Symbol | Decimal Places |
|---|---|---|---|
| USD | US Dollar | $ | 2 |
| EUR | Euro | € | 2 |
| GBP | British Pound | £ | 2 |
| JPY | Japanese Yen | ¥ | 0 |
| THB | Thai Baht | ฿ | 2 |
| Code | Symbol | Position |
|---|---|---|
| USD | $ | Prefix or suffix |
| EUR | € | Prefix or suffix |
| GBP | £ | Prefix |
| JPY | ¥ | Prefix |
| THB | ฿ | Prefix |
| Currency | Decimal Places | Example |
|---|---|---|
| USD | 2 | 99.99 |
| EUR | 2 | 99.99 |
| JPY | 0 | 100 |
| THB | 2 | 99.99 |
| BHD | 3 | 99.999 |
-- Good: Store amount and currency separately
CREATE TABLE prices (
id SERIAL PRIMARY KEY,
product_id INTEGER,
amount DECIMAL(10,2) NOT NULL,
currency VARCHAR(3) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
INSERT INTO prices (product_id, amount, currency)
VALUES (1, 99.99, 'USD');
-- Bad: FLOAT (precision loss)
CREATE TABLE prices (
id SERIAL PRIMARY KEY,
amount FLOAT NOT NULL -- Precision issues!
);
-- Good: DECIMAL (exact precision)
CREATE TABLE prices (
id SERIAL PRIMARY KEY,
amount DECIMAL(10,2) NOT NULL -- Exact precision!
);
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
quantity INTEGER NOT NULL,
unit_price DECIMAL(10,2) NOT NULL,
currency VARCHAR(3) NOT NULL,
total_amount DECIMAL(10,2) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
// Format currency for user's locale
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2
});
console.log(formatter.format(99.99)); // $99.99
// Different locale
const formatterEUR = new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR'
});
console.log(formatterEUR.format(99.99)); // 99,99 €
| Locale | Format | Example |
|---|---|---|
| en-US | 1,000.00 | $1,000.00 |
| de-DE | 1.000,00 | 1.000,00 € |
| fr-FR | 1 000,00 | 1 000,00 € |
| th-TH | 1,000.00 | ฿1,000.00 |
// Symbol position varies by locale
const formatterUS = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
console.log(formatterUS.format(100)); // $100.00
const formatterEU = new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR'
});
console.log(formatterEU.format(100)); // 100,00 €
// Exchange rate cache
const exchangeRates = {
USD: 1.0,
EUR: 0.85,
GBP: 0.75,
THB: 35.0
};
function convertCurrency(amount, fromCurrency, toCurrency) {
const rate = exchangeRates[toCurrency] / exchangeRates[fromCurrency];
return amount * rate;
}
function formatCurrency(amount, currency, locale) {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency: currency
}).format(amount);
}
// Usage
const priceUSD = 99.99;
const userCurrency = 'THB';
const userLocale = 'th-TH';
const priceTHB = convertCurrency(priceUSD, 'USD', userCurrency);
console.log(formatCurrency(priceTHB, userCurrency, userLocale)); // ฿3,499.65
-- Store in base currency (USD)
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
price_usd DECIMAL(10,2) NOT NULL
);
-- Convert on display
SELECT
name,
price_usd,
price_usd * 35.0 AS price_thb
FROM products;
// Fetch exchange rates
async function getExchangeRates(baseCurrency) {
const response = await fetch(`https://api.exchangerate.host/latest?base=${baseCurrency}`);
const rates = await response.json();
return rates;
}
// Usage
const rates = await getExchangeRates('USD');
console.log(rates.rates.THB); // 35.0
// Simple in-memory cache
const exchangeRateCache = {
rates: null,
timestamp: null,
ttl: 3600000 // 1 hour
};
async function getCachedExchangeRates(baseCurrency) {
const now = Date.now();
if (exchangeRateCache.timestamp &&
now - exchangeRateCache.timestamp < exchangeRateCache.ttl) {
return exchangeRateCache.rates;
}
const rates = await getExchangeRates(baseCurrency);
exchangeRateCache.rates = rates;
exchangeRateCache.timestamp = now;
return rates;
}
const BASE_CURRENCY = 'USD';
function convertToBase(amount, fromCurrency) {
const rate = exchangeRates[BASE_CURRENCY] / exchangeRates[fromCurrency];
return amount * rate;
}
function convertFromBase(amount, toCurrency) {
const rate = exchangeRates[toCurrency] / exchangeRates[BASE_CURRENCY];
return amount * rate;
}
// Tooltip with original currency
function formatPriceWithOriginal(amount, fromCurrency, toCurrency, locale) {
const converted = convertCurrency(amount, fromCurrency, toCurrency);
const formatted = formatCurrency(converted, toCurrency, locale);
const original = formatCurrency(amount, fromCurrency, locale);
return {
formatted,
original,
tooltip: `Original: ${original} (${fromCurrency})`
};
}
// Currency selector
function setCurrency(currency) {
localStorage.setItem('userCurrency', currency);
// Refresh page or update prices
}
function getUserCurrency() {
return localStorage.getItem('userCurrency') || 'USD';
}
// Usage
<select onchange="setCurrency(this.value)">
<option value="USD">USD ($)</option>
<option value="EUR">EUR (€)</option>
<option value="THB">THB (฿)</option>
</select>
// Stripe example
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
async function createPayment(amount, currency) {
const paymentIntent = await stripe.paymentIntents.create({
amount: Math.round(amount * 100), // Convert to cents
currency: currency.toLowerCase(),
payment_method_types: ['card'],
});
return paymentIntent;
}
// Usage
createPayment(99.99, 'USD'); // Charge $99.99 USD
createPayment(3499.65, 'THB'); // Charge ฿3,499.65 THB
| Processor | Currencies Supported |
|---|---|
| Stripe | 135+ currencies |
| PayPal | 100+ currencies |
| Braintree | 130+ currencies |
| Square | 135+ currencies |
// Use DECIMAL for calculations
function calculateTotal(items) {
return items.reduce((total, item) => {
// Use precise arithmetic
return total + (item.price * item.quantity);
}, 0);
}
// Banker's rounding (round to nearest even)
function roundBankers(amount) {
return Math.round(amount * 2) / 2;
}
// Standard rounding
function roundStandard(amount) {
return Math.round(amount);
}
// Currency-specific rounding
function roundCurrency(amount, currency) {
const decimals = currency === 'JPY' ? 0 : 2;
const multiplier = Math.pow(10, decimals);
return Math.round(amount * multiplier) / multiplier;
}
| Library | Description |
|---|---|
| Intl API | Built-in browser/Node.js |
| dinero.js | Currency calculations |
| currency.js | Currency formatting |
| accounting.js | Precise financial calculations |
| Library | Description |
|---|---|
| Babel | Internationalization |
| forex-python | Currency conversion |
| py-moneyed | Currency handling |
| Library | Description |
|---|---|
| Joda-Money | Currency calculations |
| JSR 354 | Java currency API |
| ICU4J | Internationalization |
// Bad: Float precision loss
const price = 0.1 + 0.2; // 0.30000000000000004
// Good: Use decimal library
const { Dinero } = require('dinero.js');
const price = Dinero({ amount: 100, currency: 'USD' })
.add(Dinero({ amount: 200, currency: 'USD' }));
-- Bad: No timezone info
CREATE TABLE events (
event_time TIMESTAMP NOT NULL -- Ambiguous!
);
-- Good: Store in UTC
CREATE TABLE events (
event_time TIMESTAMP WITH TIME ZONE NOT NULL -- Explicit UTC
);
// Bad: Server timezone assumption
const eventDate = new Date('2024-01-15 14:00:00');
// Good: Convert to user timezone
const userTimezone = getUserTimezone(userId);
const eventDate = utcToZonedTime(new Date('2024-01-15 14:00:00Z'), userTimezone);
// Bad: Hardcoded symbol
const formatted = `$${price}`;
// Good: Use Intl API
const formatted = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(price);
import { utcToZonedTime, format } from 'date-fns-tz';
function formatDateTime(utcDate, userTimezone, userLocale) {
const userDate = utcToZonedTime(utcDate, userTimezone);
return format(userDate, 'PPP', {
locale: userLocale
});
}
// Usage
const utcDate = new Date('2024-01-15 14:00:00Z');
const userTimezone = 'America/New_York';
const userLocale = 'en-US';
console.log(formatDateTime(utcDate, userTimezone, userLocale));
// Output: January 15, 2024
import React, { useState, useEffect } from 'react';
function CurrencyDisplay({ amount, currency, locale }) {
const [formatted, setFormatted] = useState('');
useEffect(() => {
const formatter = new Intl.NumberFormat(locale, {
style: 'currency',
currency: currency
});
setFormatted(formatter.format(amount));
}, [amount, currency, locale]);
return <span>{formatted}</span>;
}
// Usage
<CurrencyDisplay amount={99.99} currency="USD" locale="en-US" />
import React, { useState, useEffect } from 'react';
function PriceDisplay({ priceUSD, userCurrency, userLocale }) {
const [localPrice, setLocalPrice] = useState(null);
useEffect(() => {
async function fetchAndConvert() {
const rates = await getCachedExchangeRates('USD');
const converted = priceUSD * rates.rates[userCurrency];
const formatted = new Intl.NumberFormat(userLocale, {
style: 'currency',
currency: userCurrency
}).format(converted);
setLocalPrice(formatted);
}
fetchAndConvert();
}, [priceUSD, userCurrency, userLocale]);
return (
<div>
<span title={`$${priceUSD.toFixed(2)} USD`}>
{localPrice}
</span>
</div>
);
}
---
## Quick Start
### Currency Formatting
```javascript
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
})
formatter.format(1234.56) // "$1,234.56"
const date = new Date('2024-01-15T10:00:00Z')
// Convert to user timezone
const userTimezone = 'America/New_York'
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: userTimezone,
dateStyle: 'full',
timeStyle: 'long'
})
formatter.format(date) // "Monday, January 15, 2024 at 5:00:00 AM EST"
// ❌ Bad - Store in user currency
const price = { amount: 100, currency: 'USD' } // User changes currency!
// ✅ Good - Store in base currency
const price = { amount: 100, currency: 'USD' } // Base currency
// Convert on display
const displayPrice = convertCurrency(price, userCurrency)
// ❌ Bad - Store local time
const createdAt = new Date('2024-01-15 10:00:00') // Which timezone?
// ✅ Good - Store UTC
const createdAt = new Date('2024-01-15T10:00:00Z') // UTC
// Convert on display
const displayTime = convertToUserTimezone(createdAt, userTimezone)
25-internationalization/i18n-setup/) - Internationalization25-internationalization/localization/) - Content localization30-ecommerce/payment-gateways/) - Multi-currency paymentsxiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience