Last active
July 8, 2025 20:45
-
-
Save krisrandall/1a667b961a843fa9bfb0b79c3e4a81a1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import 'package:flutter/material.dart'; | |
| import 'package:timezone/data/latest.dart' as tz; | |
| import 'package:timezone/timezone.dart' as tz; | |
| void main() { | |
| // Initialize the timezone database | |
| tz.initializeTimeZones(); | |
| runApp(MyApp()); | |
| } | |
| class MyApp extends StatelessWidget { | |
| @override | |
| Widget build(BuildContext context) { | |
| return MaterialApp( | |
| title: 'Timezone Dropdown', | |
| theme: ThemeData( | |
| primarySwatch: Colors.blue, | |
| ), | |
| home: TimezoneDropdownPage(), | |
| ); | |
| } | |
| } | |
| class TimezoneDropdownPage extends StatefulWidget { | |
| @override | |
| _TimezoneDropdownPageState createState() => _TimezoneDropdownPageState(); | |
| } | |
| class _TimezoneDropdownPageState extends State<TimezoneDropdownPage> { | |
| String? selectedTimezone; | |
| List<String> timezones = []; | |
| @override | |
| void initState() { | |
| super.initState(); | |
| _loadTimezones(); | |
| } | |
| void _loadTimezones() { | |
| // Get all timezone locations from the database | |
| final locations = tz.timeZoneDatabase.locations; | |
| // Convert to list and sort by city name (last part after '/') | |
| timezones = locations.keys.toList(); | |
| timezones.sort((a, b) { | |
| final cityA = a.split('/').last; | |
| final cityB = b.split('/').last; | |
| return cityA.compareTo(cityB); | |
| }); | |
| // Set a default selection | |
| if (timezones.isNotEmpty) { | |
| selectedTimezone = timezones.first; | |
| } | |
| } | |
| String _getCurrentTimeInTimezone(String timezoneName) { | |
| try { | |
| final location = tz.getLocation(timezoneName); | |
| final now = tz.TZDateTime.now(location); | |
| return now.toString().split('.')[0]; // Remove milliseconds | |
| } catch (e) { | |
| return 'Error loading time'; | |
| } | |
| } | |
| String _getGMTOffset(String timezoneName) { | |
| try { | |
| final location = tz.getLocation(timezoneName); | |
| // Check if timezone has DST by looking at the zones directly | |
| final hasMultipleZones = location.zones.length > 1; | |
| final hasDstZone = location.zones.any((zone) => zone.isDst); | |
| String formatOffset(int hours) { | |
| if (hours == 0) return 'GMT'; | |
| return hours > 0 ? 'GMT+$hours' : 'GMT$hours'; | |
| } | |
| if (!hasMultipleZones && !hasDstZone) { | |
| // No daylight saving time - just use the single zone | |
| final singleZone = location.zones.first; | |
| final offset = (singleZone.offset / (1000 * 60 * 60)).round(); | |
| return formatOffset(offset); | |
| } else { | |
| // Has daylight saving time - show both standard and DST offsets | |
| final standardZone = location.zones.firstWhere((z) => !z.isDst, orElse: () => location.zones.first); | |
| final dstZone = location.zones.firstWhere((z) => z.isDst, orElse: () => location.zones.last); | |
| final standardOffset = (standardZone.offset / (1000 * 60 * 60)).round(); | |
| final dstOffset = (dstZone.offset / (1000 * 60 * 60)).round(); | |
| return '${formatOffset(standardOffset)}, ${formatOffset(dstOffset)}'; | |
| } | |
| } catch (e) { | |
| return 'GMT?'; | |
| } | |
| } | |
| String _formatTimezoneName(String timezoneName) { | |
| final parts = timezoneName.split('/'); | |
| if (parts.length >= 2) { | |
| final region = parts[0]; | |
| final cityParts = parts.sublist(1); | |
| final city = cityParts.join('/'); // Handle cases like America/Indiana/Indianapolis | |
| return '$city, $region'; | |
| } | |
| return timezoneName; | |
| } | |
| String _getDaylightSavingsDetails(String timezoneName) { | |
| try { | |
| final location = tz.getLocation(timezoneName); | |
| final now = tz.TZDateTime.now(location); | |
| // Check if timezone has any DST by looking at zones | |
| final hasMultipleZones = location.zones.length > 1; | |
| final hasDstZone = location.zones.any((zone) => zone.isDst); | |
| if (!hasMultipleZones && !hasDstZone) { | |
| return 'None'; | |
| } | |
| // Find DST transitions for the current year | |
| final startOfYear = tz.TZDateTime(location, now.year, 1, 1).millisecondsSinceEpoch; | |
| final endOfYear = tz.TZDateTime(location, now.year + 1, 1, 1).millisecondsSinceEpoch; | |
| List<String> transitions = []; | |
| for (int i = 0; i < location.transitionAt.length; i++) { | |
| final transitionTime = location.transitionAt[i]; | |
| // Only look at transitions within the current year | |
| if (transitionTime >= startOfYear && transitionTime < endOfYear) { | |
| final zoneIndex = location.transitionZone[i]; | |
| final timeZone = location.zones[zoneIndex]; | |
| final transitionDate = DateTime.fromMillisecondsSinceEpoch(transitionTime); | |
| final monthNames = ['', 'January', 'February', 'March', 'April', 'May', 'June', | |
| 'July', 'August', 'September', 'October', 'November', 'December']; | |
| if (timeZone.isDst) { | |
| transitions.add('DST starts: ${monthNames[transitionDate.month]} ${transitionDate.day}'); | |
| } else { | |
| transitions.add('DST ends: ${monthNames[transitionDate.month]} ${transitionDate.day}'); | |
| } | |
| } | |
| } | |
| if (transitions.isEmpty) { | |
| // No transitions this year, but timezone has DST capability | |
| return 'DST supported (no transitions this year)'; | |
| } | |
| // Calculate offset change if we have transitions | |
| String offsetInfo = ''; | |
| if (location.zones.length >= 2) { | |
| final standardZone = location.zones.firstWhere((z) => !z.isDst, orElse: () => location.zones.first); | |
| final dstZone = location.zones.firstWhere((z) => z.isDst, orElse: () => location.zones.last); | |
| final standardOffset = (standardZone.offset / (1000 * 60 * 60)).round(); | |
| final dstOffset = (dstZone.offset / (1000 * 60 * 60)).round(); | |
| final offsetChange = dstOffset - standardOffset; | |
| if (offsetChange != 0) { | |
| offsetInfo = ', Offset change: ${offsetChange > 0 ? '+' : ''}${offsetChange}h'; | |
| } | |
| } | |
| return transitions.join(', ') + offsetInfo; | |
| } catch (e) { | |
| return 'Error loading DST details'; | |
| } | |
| } | |
| @override | |
| Widget build(BuildContext context) { | |
| return Scaffold( | |
| appBar: AppBar( | |
| title: Text('Timezone Selector'), | |
| ), | |
| body: Padding( | |
| padding: EdgeInsets.all(16.0), | |
| child: Column( | |
| crossAxisAlignment: CrossAxisAlignment.start, | |
| children: [ | |
| Text( | |
| 'Select a Timezone:', | |
| style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), | |
| ), | |
| SizedBox(height: 10), | |
| Container( | |
| width: double.infinity, | |
| padding: EdgeInsets.symmetric(horizontal: 12), | |
| decoration: BoxDecoration( | |
| border: Border.all(color: Colors.grey), | |
| borderRadius: BorderRadius.circular(4), | |
| ), | |
| child: DropdownButtonHideUnderline( | |
| child: DropdownButton<String>( | |
| value: selectedTimezone, | |
| isExpanded: true, | |
| items: timezones.map((String timezone) { | |
| return DropdownMenuItem<String>( | |
| value: timezone, | |
| child: Text('${_formatTimezoneName(timezone)} (${_getGMTOffset(timezone)})'), | |
| ); | |
| }).toList(), | |
| onChanged: (String? newValue) { | |
| setState(() { | |
| selectedTimezone = newValue; | |
| }); | |
| }, | |
| ), | |
| ), | |
| ), | |
| SizedBox(height: 20), | |
| if (selectedTimezone != null) ...[ | |
| Text( | |
| 'Selected Timezone:', | |
| style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), | |
| ), | |
| SizedBox(height: 5), | |
| Text( | |
| selectedTimezone!, | |
| style: TextStyle(fontSize: 16, color: Colors.blue), | |
| ), | |
| SizedBox(height: 15), | |
| Text( | |
| 'Daylight Savings Details:', | |
| style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), | |
| ), | |
| SizedBox(height: 5), | |
| Text( | |
| _getDaylightSavingsDetails(selectedTimezone!), | |
| style: TextStyle(fontSize: 16, color: Colors.orange), | |
| ), | |
| SizedBox(height: 15), | |
| Text( | |
| 'Current Time:', | |
| style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), | |
| ), | |
| SizedBox(height: 5), | |
| Text( | |
| _getCurrentTimeInTimezone(selectedTimezone!), | |
| style: TextStyle(fontSize: 16, color: Colors.green), | |
| ), | |
| ], | |
| SizedBox(height: 20), | |
| Text( | |
| 'Total Timezones: ${timezones.length}', | |
| style: TextStyle(fontSize: 14, color: Colors.grey[600]), | |
| ), | |
| ], | |
| ), | |
| ), | |
| ); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment