Timezone Implementation Guide
Timezone Implementation Guide
Overview
This document explains the timezone-aware timestamp handling implementation in the FlowFrog approval system. The system automatically detects user timezones and displays timestamps in the user's local timezone while maintaining UTC storage on the server.
Architecture
Server-Side (Backend)
Database Storage:
- All timestamps are stored as
timestamp with time zonein PostgreSQL - Server uses UTC timezone for all operations
- Python backend uses
datetime.now(UTC)for timestamp creation
API Response:
- Timestamps are returned in ISO 8601 format (e.g.,
2024-01-15T10:30:00Z) - UTC timezone is preserved in API responses
Client-Side (Frontend)
Timezone Detection:
- Automatically detects user's browser timezone using
Intl.DateTimeFormat().resolvedOptions().timeZone - Falls back to UTC if detection fails
- Timezone is detected once on application load
Display Formatting:
- All timestamps are converted to user's local timezone for display
- Multiple formatting options available for different use cases
- Consistent timezone handling across all components
Implementation Details
Core Utilities (frontend/src/utils/timezone.js)
The timezone utilities provide several formatting functions:
formatDate(dateString, options)
Formats date only (e.g., "Jan 15, 2024")
formatDate('2024-01-15T10:30:00Z') // "Jan 15, 2024"formatDateTime(dateString, options)
Formats date and time (e.g., "Jan 15, 2024, 10:30 AM")
formatDateTime('2024-01-15T10:30:00Z') // "Jan 15, 2024, 10:30 AM"formatRelativeTime(dateString)
Shows relative time (e.g., "2 hours ago", "3 days ago")
formatRelativeTime('2024-01-15T10:30:00Z') // "2 hours ago"formatSmartDate(dateString)
Smart formatting that shows relative time for recent dates and full date for older ones
formatSmartDate('2024-01-15T10:30:00Z') // "Today at 10:30 AM"
formatSmartDate('2024-01-10T10:30:00Z') // "5 days ago"formatWithTimezone(dateString, showTimezone)
Formats with timezone indicator (e.g., "Jan 15, 2024, 10:30 AM (EST)")
formatWithTimezone('2024-01-15T10:30:00Z') // "Jan 15, 2024, 10:30 AM (EST)"React Hooks (frontend/src/hooks/useTimezone.js)
useTimezone()
Provides timezone-aware formatting functions and current timezone info:
const { timezone, formatSmartDate, formatWithTimezone } = useTimezone();useTimezoneInfo()
Provides detailed timezone information for display:
const { timezoneInfo } = useTimezoneInfo();
// Returns: { full: "America/New_York", abbreviation: "EST", display: "EST (America/New_York)" }Components
TimezoneIndicator
Displays current user timezone in the UI:
<TimezoneIndicator showIcon={true} showFullName={false} />Usage Examples
Basic Usage in Components
import { formatSmartDate, formatWithTimezone } from '../../utils/timezone';
// In your component
<div>Created: {formatSmartDate(approval.created_at)}</div>
<div>Approved: {formatWithTimezone(approval.approved_at)}</div>Using React Hooks
import { useTimezone } from '../../hooks/useTimezone';
function MyComponent() {
const { formatSmartDate, timezone } = useTimezone();
return (
<div>
<p>Your timezone: {timezone}</p>
<p>Created: {formatSmartDate(data.created_at)}</p>
</div>
);
}Displaying Timezone Information
import { useTimezoneInfo } from '../../hooks/useTimezone';
import TimezoneIndicator from '../common/TimezoneIndicator';
function Header() {
const { timezoneInfo } = useTimezoneInfo();
return (
<header>
<TimezoneIndicator showFullName={true} />
<span>Current timezone: {timezoneInfo.display}</span>
</header>
);
}Updated Components
The following components have been updated to use timezone-aware formatting:
Dashboard Components
PendingApprovalsSection.jsx- Shows submission times in user timezoneMySubmissionsSection.jsx- Shows submission and completion timesNavigation.jsx- Includes timezone indicator in the header
Approval Components
ApprovalUI.jsx- Shows creation and action timestampsApprovalUI2.jsx- Shows submission and approval timesApprovalUI3.jsx- Shows workflow timestampsapprovals/[id]/page.js- Shows detailed approval timestampsapprovals/page.js- Shows approval list timestamps
Benefits
For Users
- Consistent Experience: All timestamps displayed in user's local timezone
- Clear Understanding: No confusion about server vs local time
- Automatic Detection: No manual timezone configuration required
- Smart Formatting: Recent events show relative time, older events show full date
For Developers
- Centralized Logic: All timezone handling in one place
- Easy to Use: Simple import and function calls
- Consistent API: Same functions work across all components
- Fallback Handling: Graceful degradation if timezone detection fails
For System
- Data Integrity: Server always stores UTC timestamps
- Scalability: Works for users in any timezone
- Maintainability: Single source of truth for timezone logic
Testing
Manual Testing
- Change your browser/system timezone
- Refresh the application
- Verify timestamps display in the new timezone
- Check that timezone indicator updates correctly
Automated Testing
// Test timezone detection
const timezone = getUserTimezone();
expect(timezone).toBeDefined();
// Test formatting functions
const formatted = formatSmartDate('2024-01-15T10:30:00Z');
expect(formatted).toContain('2024');Troubleshooting
Common Issues
Timezone not detected:
- Check browser support for
Intl.DateTimeFormat - Verify system timezone is set correctly
- Check for JavaScript errors in console
Timestamps showing wrong time:
- Verify server is returning UTC timestamps
- Check that timezone detection is working
- Ensure formatting functions are being used correctly
Inconsistent display:
- Make sure all components use the timezone utilities
- Check for hardcoded
new Date().toLocaleString()calls - Verify timezone indicator is showing correct timezone
Debug Mode
Enable debug logging by adding to browser console:
localStorage.setItem('timezone_debug', 'true');This will log timezone detection and formatting operations.
Future Enhancements
Planned Features
- User Preferences: Allow users to override detected timezone
- Time Range Selection: Date/time pickers with timezone awareness
- Export Options: Export data with timezone information
- Notifications: Timezone-aware notification scheduling
Performance Optimizations
- Caching: Cache timezone detection results
- Lazy Loading: Load timezone utilities only when needed
- Bundle Optimization: Tree-shake unused formatting functions
Migration Guide
For Existing Components
If you have components using old timestamp formatting:
Before:
<span>{new Date(data.created_at).toLocaleDateString()}</span>
<span>{new Date(data.approved_at).toLocaleString()}</span>After:
import { formatSmartDate, formatWithTimezone } from '../../utils/timezone';
<span>{formatSmartDate(data.created_at)}</span>
<span>{formatWithTimezone(data.approved_at)}</span>For New Components
Always use the timezone utilities for any timestamp display:
import { formatSmartDate } from '../../utils/timezone';
// For recent events (shows relative time)
<span>{formatSmartDate(timestamp)}</span>
// For detailed views (shows full date/time with timezone)
<span>{formatWithTimezone(timestamp)}</span>
// For date-only display
<span>{formatDate(timestamp)}</span>Conclusion
The timezone implementation provides a robust, user-friendly solution for handling timestamps across different timezones. It ensures users always see times in their local timezone while maintaining data integrity on the server side.
For questions or issues, refer to the troubleshooting section or contact the development team.