Skip to content

Instantly share code, notes, and snippets.

@krisrandall
Last active July 8, 2025 20:45
Show Gist options
  • Select an option

  • Save krisrandall/1a667b961a843fa9bfb0b79c3e4a81a1 to your computer and use it in GitHub Desktop.

Select an option

Save krisrandall/1a667b961a843fa9bfb0b79c3e4a81a1 to your computer and use it in GitHub Desktop.
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