getHistoricalWeather method

Future<HourlyWeatherData> getHistoricalWeather({
  1. required double latitude,
  2. required double longitude,
  3. required DateTime startDate,
  4. required DateTime endDate,
})

Fetch historical weather data for a specific date range.

Returns HourlyWeatherData with hourly data between startDate and endDate. Available from 1940 onwards. Maximum range is 1 year.

Important: ERA5 archive data has a delay of about 5-7 days. The end date must be at least 7 days before today.

Implementation

Future<HourlyWeatherData> getHistoricalWeather({
  required double latitude,
  required double longitude,
  required DateTime startDate,
  required DateTime endDate,
}) async {
  // ERA5 data has approximately 5-7 days delay.
  final now = DateTime.now();
  final maxEndDate = now.subtract(const Duration(days: 7));

  // Validate date range.

  if (endDate.isBefore(startDate)) {
    throw ArgumentError('endDate must be after startDate');
  }

  if (endDate.isAfter(maxEndDate)) {
    throw ArgumentError(
      'ERA5 archive data has a 5-7 day delay. '
      'End date must be at least 7 days before today. '
      'Latest available date: ${maxEndDate.year}-${maxEndDate.month.toString().padLeft(2, '0')}-${maxEndDate.day.toString().padLeft(2, '0')}',
    );
  }

  final daysDiff = endDate.difference(startDate).inDays;
  if (daysDiff > 365) {
    throw ArgumentError('Date range cannot exceed 365 days');
  }

  if (startDate.year < 1940) {
    throw ArgumentError('Historical data only available from 1940 onwards');
  }

  final startDateStr =
      '${startDate.year}-${startDate.month.toString().padLeft(2, '0')}-${startDate.day.toString().padLeft(2, '0')}';
  final endDateStr =
      '${endDate.year}-${endDate.month.toString().padLeft(2, '0')}-${endDate.day.toString().padLeft(2, '0')}';

  final uri = Uri.parse(_archiveUrl).replace(
    queryParameters: {
      'latitude': latitude.toString(),
      'longitude': longitude.toString(),
      'start_date': startDateStr,
      'end_date': endDateStr,
      'hourly': [
        'temperature_2m',
        'relative_humidity_2m',
        'wind_speed_10m',
        'precipitation',
      ].join(','),
      'timezone': 'Australia/Sydney',
    },
  );

  try {
    final response = await http.get(uri);

    if (response.statusCode == 200) {
      final json = jsonDecode(response.body) as Map<String, dynamic>;
      return HourlyWeatherData.fromJson(json);
    } else {
      throw Exception(
        'Failed to load historical weather data: ${response.statusCode}',
      );
    }
  } catch (e) {
    throw Exception('Failed to fetch historical weather: $e');
  }
}