|
| 1 | +# Timezone Handling Guide |
| 2 | + |
| 3 | +This guide explains how react-datepicker handles timezones and provides solutions for common timezone-related scenarios. |
| 4 | + |
| 5 | +## How react-datepicker Handles Dates |
| 6 | + |
| 7 | +React-datepicker uses native JavaScript `Date` objects and the [date-fns](https://date-fns.org/) library for date manipulation. This means: |
| 8 | + |
| 9 | +1. **All dates are stored as JavaScript Date objects** - These represent a specific moment in time (internally stored as milliseconds since Unix epoch in UTC). |
| 10 | + |
| 11 | +2. **Display is based on the user's local timezone** - When a Date object is displayed, JavaScript automatically converts it to the user's local timezone. |
| 12 | + |
| 13 | +3. **No built-in timezone conversion** - The datepicker does not include built-in timezone conversion utilities. If you need timezone support, you'll need to handle conversions in your application code. |
| 14 | + |
| 15 | +## Common Questions and Solutions |
| 16 | + |
| 17 | +### 1. Why do dates appear differently across timezones? |
| 18 | + |
| 19 | +JavaScript Date objects represent an absolute moment in time. When displayed, they're converted to the user's local timezone. For example, `2025-01-15T00:00:00Z` (midnight UTC) will display as: |
| 20 | + |
| 21 | +- `Jan 14, 2025 7:00 PM` in New York (UTC-5) |
| 22 | +- `Jan 15, 2025 9:00 AM` in Tokyo (UTC+9) |
| 23 | + |
| 24 | +**Solution**: If you want dates to display consistently regardless of timezone, consider: |
| 25 | + |
| 26 | +- Storing and transmitting dates as date-only strings (e.g., `"2025-01-15"`) without time components |
| 27 | +- Creating dates at noon local time to avoid day boundary issues: `new Date(2025, 0, 15, 12, 0, 0)` |
| 28 | + |
| 29 | +### 2. How do I work with UTC dates? |
| 30 | + |
| 31 | +If your backend stores dates in UTC and you want to display them as UTC: |
| 32 | + |
| 33 | +```jsx |
| 34 | +// Convert UTC string to local Date for display |
| 35 | +const utcDateString = "2025-01-15T10:30:00Z"; |
| 36 | +const date = new Date(utcDateString); |
| 37 | + |
| 38 | +// When sending back to server, convert to UTC ISO string |
| 39 | +const handleChange = (date) => { |
| 40 | + const utcString = date.toISOString(); // Always returns UTC |
| 41 | + sendToServer(utcString); |
| 42 | +}; |
| 43 | + |
| 44 | +<DatePicker selected={date} onChange={handleChange} />; |
| 45 | +``` |
| 46 | + |
| 47 | +### 3. How do I display dates in a specific timezone? |
| 48 | + |
| 49 | +For displaying dates in a specific timezone (not the user's local timezone), you can use libraries like [date-fns-tz](https://github.com/marnusw/date-fns-tz): |
| 50 | + |
| 51 | +```jsx |
| 52 | +import { formatInTimeZone, toZonedTime } from "date-fns-tz"; |
| 53 | + |
| 54 | +// Display a date in a specific timezone |
| 55 | +const timeZone = "America/New_York"; |
| 56 | +const utcDate = new Date(); // Current time in UTC |
| 57 | + |
| 58 | +// Convert to the target timezone for display |
| 59 | +const zonedDate = toZonedTime(utcDate, timeZone); |
| 60 | + |
| 61 | +// Format with timezone |
| 62 | +const formatted = formatInTimeZone(utcDate, timeZone, "yyyy-MM-dd HH:mm:ss zzz"); |
| 63 | +``` |
| 64 | + |
| 65 | +### 4. Why does the selected date change when I submit a form? |
| 66 | + |
| 67 | +This typically happens because: |
| 68 | + |
| 69 | +- The date is created in local time but sent to the server as UTC |
| 70 | +- The server interprets the UTC date differently |
| 71 | + |
| 72 | +**Solution**: Be explicit about how you serialize dates: |
| 73 | + |
| 74 | +```jsx |
| 75 | +const handleChange = (date) => { |
| 76 | + // Option 1: Send as ISO string (includes timezone info) |
| 77 | + const isoString = date.toISOString(); // "2025-01-15T05:00:00.000Z" |
| 78 | + |
| 79 | + // Option 2: Send as date-only string (no timezone ambiguity) |
| 80 | + const dateOnly = date.toISOString().split("T")[0]; // "2025-01-15" |
| 81 | + |
| 82 | + // Option 3: Send as Unix timestamp |
| 83 | + const timestamp = date.getTime(); // 1736920800000 |
| 84 | +}; |
| 85 | +``` |
| 86 | + |
| 87 | +### 5. How do I create a date without timezone issues? |
| 88 | + |
| 89 | +For date-only scenarios (no time component), create dates at noon to avoid day boundary issues: |
| 90 | + |
| 91 | +```jsx |
| 92 | +// Instead of this (can shift days at timezone boundaries): |
| 93 | +const date = new Date("2025-01-15"); // Parsed as UTC midnight |
| 94 | + |
| 95 | +// Do this (noon local time is safe from day shifts): |
| 96 | +const createLocalDate = (year, month, day) => { |
| 97 | + return new Date(year, month - 1, day, 12, 0, 0); |
| 98 | +}; |
| 99 | + |
| 100 | +// Or parse a date string as local time: |
| 101 | +const parseLocalDate = (dateString) => { |
| 102 | + const [year, month, day] = dateString.split("-").map(Number); |
| 103 | + return new Date(year, month - 1, day, 12, 0, 0); |
| 104 | +}; |
| 105 | +``` |
| 106 | + |
| 107 | +### 6. How do I handle date ranges across timezones? |
| 108 | + |
| 109 | +When working with date ranges, ensure both start and end dates are handled consistently: |
| 110 | + |
| 111 | +```jsx |
| 112 | +const [startDate, setStartDate] = useState(null); |
| 113 | +const [endDate, setEndDate] = useState(null); |
| 114 | + |
| 115 | +// Normalize dates to start/end of day in local time |
| 116 | +const normalizeStartDate = (date) => { |
| 117 | + if (!date) return null; |
| 118 | + const d = new Date(date); |
| 119 | + d.setHours(0, 0, 0, 0); |
| 120 | + return d; |
| 121 | +}; |
| 122 | + |
| 123 | +const normalizeEndDate = (date) => { |
| 124 | + if (!date) return null; |
| 125 | + const d = new Date(date); |
| 126 | + d.setHours(23, 59, 59, 999); |
| 127 | + return d; |
| 128 | +}; |
| 129 | + |
| 130 | +<DatePicker |
| 131 | + selectsRange |
| 132 | + startDate={startDate} |
| 133 | + endDate={endDate} |
| 134 | + onChange={(dates) => { |
| 135 | + const [start, end] = dates; |
| 136 | + setStartDate(normalizeStartDate(start)); |
| 137 | + setEndDate(normalizeEndDate(end)); |
| 138 | + }} |
| 139 | +/>; |
| 140 | +``` |
| 141 | + |
| 142 | +## Best Practices |
| 143 | + |
| 144 | +1. **Be consistent**: Choose one approach for handling timezones across your application and stick with it. |
| 145 | + |
| 146 | +2. **Store dates in UTC**: When persisting dates, store them in UTC format (ISO 8601 strings or Unix timestamps). |
| 147 | + |
| 148 | +3. **Convert on the boundaries**: Handle timezone conversion at the edges of your application (when receiving from or sending to APIs). |
| 149 | + |
| 150 | +4. **Use date-fns-tz for complex timezone needs**: If you need to display dates in specific timezones, use a dedicated library like date-fns-tz. |
| 151 | + |
| 152 | +5. **Test across timezones**: Test your application with different system timezones to catch timezone-related bugs. |
| 153 | + |
| 154 | +6. **Document your date format**: Make sure your team knows what format dates are stored and transmitted in. |
| 155 | + |
| 156 | +## Additional Resources |
| 157 | + |
| 158 | +- [date-fns Documentation](https://date-fns.org/docs/Getting-Started) |
| 159 | +- [date-fns-tz for timezone support](https://github.com/marnusw/date-fns-tz) |
| 160 | +- [MDN Date Documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) |
| 161 | +- [Understanding JavaScript Date and Timezones](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#date_time_string_format) |
0 commit comments