You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
366 lines
12 KiB
Dart
366 lines
12 KiB
Dart
|
21 hours ago
|
import 'dart:developer' as dev;
|
||
|
|
import 'package:flutter_test/flutter_test.dart';
|
||
|
|
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_calendar_model.dart';
|
||
|
|
|
||
|
|
/// Comprehensive test to analyze date conversion issues between Gregorian and Hijri calendars
|
||
|
|
/// This test simulates the onDobChange method logic and tests 200 years of dates
|
||
|
|
void main() {
|
||
|
|
group('Date Conversion Analysis - 200 Years', () {
|
||
|
|
// Test results storage
|
||
|
|
final List<DateConversionResult> results = [];
|
||
|
|
final List<String> errors = [];
|
||
|
|
|
||
|
|
test('Test Gregorian to Hijri conversion for 200 years', () {
|
||
|
|
print('\n========================================');
|
||
|
|
print('GREGORIAN TO HIJRI CONVERSION TEST');
|
||
|
|
print('Testing dates from 1925 to 2125 (200 years)');
|
||
|
|
print('========================================\n');
|
||
|
|
|
||
|
|
int totalDates = 0;
|
||
|
|
int successfulConversions = 0;
|
||
|
|
int failedConversions = 0;
|
||
|
|
|
||
|
|
// Test dates from 1925 to 2125 (200 years)
|
||
|
|
for (int year = 1925; year <= 2125; year++) {
|
||
|
|
// Test all 12 months
|
||
|
|
for (int month = 1; month <= 12; month++) {
|
||
|
|
// Test key days in each month (1st, 15th, last day)
|
||
|
|
final daysToTest = [1, 15, _getLastDayOfMonth(year, month)];
|
||
|
|
|
||
|
|
for (int day in daysToTest) {
|
||
|
|
totalDates++;
|
||
|
|
|
||
|
|
try {
|
||
|
|
// Create Gregorian date
|
||
|
|
final gregorianDate = DateTime(year, month, day);
|
||
|
|
final isoString = gregorianDate.toIso8601String();
|
||
|
|
|
||
|
|
// Test parsing (this is where the error occurs)
|
||
|
|
final parsedDate = DateTime.parse(isoString);
|
||
|
|
|
||
|
|
// Test Hijri conversion
|
||
|
|
final hijriDate = HijriGregConverter.gregorianToHijri(parsedDate);
|
||
|
|
|
||
|
|
// Test creating DateTime from Hijri components
|
||
|
|
final hijriDateTime = DateTime(hijriDate.year, hijriDate.month, hijriDate.day);
|
||
|
|
|
||
|
|
// Test formatting methods
|
||
|
|
final apiFormat = _formatDateForApi(isoString);
|
||
|
|
final displayFormat = _formatDateToDisplay(isoString);
|
||
|
|
final hijriDisplayFormat = _formatHijriDateToDisplay(hijriDateTime.toIso8601String());
|
||
|
|
|
||
|
|
// Store successful result
|
||
|
|
results.add(DateConversionResult(
|
||
|
|
gregorianDate: gregorianDate,
|
||
|
|
isoString: isoString,
|
||
|
|
hijriYear: hijriDate.year,
|
||
|
|
hijriMonth: hijriDate.month,
|
||
|
|
hijriDay: hijriDate.day,
|
||
|
|
apiFormat: apiFormat,
|
||
|
|
displayFormat: displayFormat,
|
||
|
|
hijriDisplayFormat: hijriDisplayFormat,
|
||
|
|
success: true,
|
||
|
|
));
|
||
|
|
|
||
|
|
successfulConversions++;
|
||
|
|
|
||
|
|
} catch (e) {
|
||
|
|
failedConversions++;
|
||
|
|
final errorMsg = 'ERROR on $year-${month.toString().padLeft(2, '0')}-${day.toString().padLeft(2, '0')}: $e';
|
||
|
|
errors.add(errorMsg);
|
||
|
|
print(errorMsg);
|
||
|
|
|
||
|
|
results.add(DateConversionResult(
|
||
|
|
gregorianDate: DateTime(year, month, day),
|
||
|
|
isoString: 'FAILED',
|
||
|
|
hijriYear: 0,
|
||
|
|
hijriMonth: 0,
|
||
|
|
hijriDay: 0,
|
||
|
|
apiFormat: 'FAILED',
|
||
|
|
displayFormat: 'FAILED',
|
||
|
|
hijriDisplayFormat: 'FAILED',
|
||
|
|
success: false,
|
||
|
|
errorMessage: e.toString(),
|
||
|
|
));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Print progress every 10 years
|
||
|
|
if (year % 10 == 0) {
|
||
|
|
print('Processed up to year $year... (Success: $successfulConversions, Failed: $failedConversions)');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Print summary
|
||
|
|
print('\n========================================');
|
||
|
|
print('TEST SUMMARY');
|
||
|
|
print('========================================');
|
||
|
|
print('Total dates tested: $totalDates');
|
||
|
|
print('Successful conversions: $successfulConversions');
|
||
|
|
print('Failed conversions: $failedConversions');
|
||
|
|
print('Success rate: ${(successfulConversions / totalDates * 100).toStringAsFixed(2)}%');
|
||
|
|
print('========================================\n');
|
||
|
|
|
||
|
|
// Print all errors
|
||
|
|
if (errors.isNotEmpty) {
|
||
|
|
print('\n========================================');
|
||
|
|
print('ALL ERRORS (${errors.length} total):');
|
||
|
|
print('========================================');
|
||
|
|
for (var error in errors) {
|
||
|
|
print(error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Print sample successful conversions
|
||
|
|
if (results.where((r) => r.success).isNotEmpty) {
|
||
|
|
print('\n========================================');
|
||
|
|
print('SAMPLE SUCCESSFUL CONVERSIONS (First 5):');
|
||
|
|
print('========================================');
|
||
|
|
int count = 0;
|
||
|
|
for (var result in results.where((r) => r.success)) {
|
||
|
|
if (count >= 5) break;
|
||
|
|
print('Gregorian: ${result.gregorianDate}');
|
||
|
|
print(' ISO: ${result.isoString}');
|
||
|
|
print(' Hijri: ${result.hijriYear}-${result.hijriMonth}-${result.hijriDay}');
|
||
|
|
print(' API Format: ${result.apiFormat}');
|
||
|
|
print(' Display Format: ${result.displayFormat}');
|
||
|
|
print(' Hijri Display: ${result.hijriDisplayFormat}');
|
||
|
|
print('---');
|
||
|
|
count++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Test should fail if there are any errors
|
||
|
|
expect(failedConversions, 0, reason: 'Found $failedConversions failed conversions');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('Test specific edge cases', () {
|
||
|
|
print('\n========================================');
|
||
|
|
print('EDGE CASES TEST');
|
||
|
|
print('========================================\n');
|
||
|
|
|
||
|
|
final edgeCases = [
|
||
|
|
DateTime(1900, 1, 1), // Very old date
|
||
|
|
DateTime(1970, 1, 1), // Unix epoch
|
||
|
|
DateTime(2000, 1, 1), // Y2K
|
||
|
|
DateTime(2000, 2, 29), // Leap year
|
||
|
|
DateTime(2001, 2, 28), // Non-leap year
|
||
|
|
DateTime(2024, 12, 31), // Recent date
|
||
|
|
DateTime(2025, 1, 1), // Near future
|
||
|
|
DateTime(2100, 12, 31), // Far future
|
||
|
|
DateTime(2124, 2, 29), // Leap year in far future
|
||
|
|
DateTime(2125, 12, 31), // End of test range
|
||
|
|
];
|
||
|
|
|
||
|
|
for (var testDate in edgeCases) {
|
||
|
|
try {
|
||
|
|
final isoString = testDate.toIso8601String();
|
||
|
|
final parsedDate = DateTime.parse(isoString);
|
||
|
|
final hijriDate = HijriGregConverter.gregorianToHijri(parsedDate);
|
||
|
|
|
||
|
|
print('✓ ${testDate.toString().split(' ')[0]} -> Hijri: ${hijriDate.year}-${hijriDate.month}-${hijriDate.day}');
|
||
|
|
} catch (e) {
|
||
|
|
print('✗ ${testDate.toString().split(' ')[0]} -> ERROR: $e');
|
||
|
|
fail('Edge case failed: $testDate - $e');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test('Test DateRangCalenderModel with Umm al-Qura offset', () {
|
||
|
|
print('\n========================================');
|
||
|
|
print('UMM AL-QURA OFFSET TEST');
|
||
|
|
print('========================================\n');
|
||
|
|
|
||
|
|
// We can't fully test DateRangCalenderModel without full context,
|
||
|
|
// but we can test the conversion logic
|
||
|
|
|
||
|
|
final testDates = [
|
||
|
|
DateTime(2024, 1, 1),
|
||
|
|
DateTime(2024, 6, 15),
|
||
|
|
DateTime(2024, 12, 31),
|
||
|
|
DateTime(2025, 1, 1),
|
||
|
|
];
|
||
|
|
|
||
|
|
for (var testDate in testDates) {
|
||
|
|
try {
|
||
|
|
// Simulate the Umm al-Qura offset (-1 day)
|
||
|
|
final adjustedDate = testDate.subtract(const Duration(days: 1));
|
||
|
|
final hijriDate = HijriGregConverter.gregorianToHijri(adjustedDate);
|
||
|
|
|
||
|
|
print('Original: ${testDate.toString().split(' ')[0]}');
|
||
|
|
print(' Adjusted (-1 day): ${adjustedDate.toString().split(' ')[0]}');
|
||
|
|
print(' Hijri: ${hijriDate.year}-${hijriDate.month}-${hijriDate.day}');
|
||
|
|
print('---');
|
||
|
|
} catch (e) {
|
||
|
|
print('ERROR on ${testDate.toString().split(' ')[0]}: $e');
|
||
|
|
fail('Umm al-Qura offset test failed: $testDate - $e');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test('Test problematic date formats', () {
|
||
|
|
print('\n========================================');
|
||
|
|
print('PROBLEMATIC DATE FORMATS TEST');
|
||
|
|
print('========================================\n');
|
||
|
|
|
||
|
|
final problematicFormats = [
|
||
|
|
'2024-01-01T00:00:00.000',
|
||
|
|
'2024-01-01T00:00:00.000Z',
|
||
|
|
'2024-01-01T00:00:00',
|
||
|
|
'2024-01-01',
|
||
|
|
'2024-1-1',
|
||
|
|
'2024-01-1',
|
||
|
|
'2024-1-01',
|
||
|
|
];
|
||
|
|
|
||
|
|
for (var format in problematicFormats) {
|
||
|
|
try {
|
||
|
|
final parsed = DateTime.parse(format);
|
||
|
|
print('✓ Format "$format" parsed successfully: ${parsed.toString()}');
|
||
|
|
} catch (e) {
|
||
|
|
print('✗ Format "$format" FAILED to parse: $e');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// Helper function to get last day of month
|
||
|
|
int _getLastDayOfMonth(int year, int month) {
|
||
|
|
if (month == 12) {
|
||
|
|
return DateTime(year + 1, 1, 1).subtract(const Duration(days: 1)).day;
|
||
|
|
}
|
||
|
|
return DateTime(year, month + 1, 1).subtract(const Duration(days: 1)).day;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Replicate Utils methods for testing
|
||
|
|
String _formatDateForApi(String isoDateString) {
|
||
|
|
try {
|
||
|
|
final dateTime = DateTime.parse(isoDateString);
|
||
|
|
final year = dateTime.year.toString();
|
||
|
|
final month = dateTime.month.toString().padLeft(2, '0');
|
||
|
|
final day = dateTime.day.toString().padLeft(2, '0');
|
||
|
|
return '$day/$month/$year';
|
||
|
|
} catch (e) {
|
||
|
|
return 'ERROR: $e';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
String _formatDateToDisplay(String isoDateString) {
|
||
|
|
try {
|
||
|
|
final dateTime = DateTime.parse(isoDateString);
|
||
|
|
final day = dateTime.day.toString().padLeft(2, '0');
|
||
|
|
final year = dateTime.year.toString();
|
||
|
|
const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||
|
|
final month = monthNames[dateTime.month - 1];
|
||
|
|
return '$day $month, $year';
|
||
|
|
} catch (e) {
|
||
|
|
return 'ERROR: $e';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
String _formatHijriDateToDisplay(String hijriDateString) {
|
||
|
|
try {
|
||
|
|
final datePart = hijriDateString.split("T").first;
|
||
|
|
final parts = datePart.split('-');
|
||
|
|
if (parts.length != 3) return 'ERROR: Invalid format';
|
||
|
|
|
||
|
|
final day = parts[2].padLeft(2, '0');
|
||
|
|
final year = parts[0];
|
||
|
|
|
||
|
|
const hijriMonthNames = [
|
||
|
|
'Muharram',
|
||
|
|
'Safar',
|
||
|
|
'Rabi I',
|
||
|
|
'Rabi II',
|
||
|
|
'Jumada I',
|
||
|
|
'Jumada II',
|
||
|
|
'Rajab',
|
||
|
|
'Sha\'ban',
|
||
|
|
'Ramadan',
|
||
|
|
'Shawwal',
|
||
|
|
'Dhu al-Qi\'dah',
|
||
|
|
'Dhu al-Hijjah'
|
||
|
|
];
|
||
|
|
final monthIndex = int.tryParse(parts[1]) ?? 1;
|
||
|
|
final month = hijriMonthNames[monthIndex - 1];
|
||
|
|
|
||
|
|
return '$day $month, $year';
|
||
|
|
} catch (e) {
|
||
|
|
return 'ERROR: $e';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Stub for HijriGregConverter (since we're testing the logic)
|
||
|
|
class HijriGregConverter {
|
||
|
|
static HijriGregDate gregorianToHijri(DateTime gregorianDate) {
|
||
|
|
// This is a simplified conversion for testing
|
||
|
|
// In reality, this would use the actual Hijri calendar library
|
||
|
|
|
||
|
|
// Basic astronomical calculation (not exact Umm al-Qura)
|
||
|
|
final julianDay = _gregorianToJulianDay(gregorianDate);
|
||
|
|
final hijriDate = _julianDayToHijri(julianDay);
|
||
|
|
|
||
|
|
return hijriDate;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int _gregorianToJulianDay(DateTime date) {
|
||
|
|
int a = (14 - date.month) ~/ 12;
|
||
|
|
int y = date.year + 4800 - a;
|
||
|
|
int m = date.month + 12 * a - 3;
|
||
|
|
|
||
|
|
return date.day + (153 * m + 2) ~/ 5 + 365 * y + y ~/ 4 - y ~/ 100 + y ~/ 400 - 32045;
|
||
|
|
}
|
||
|
|
|
||
|
|
static HijriGregDate _julianDayToHijri(int julianDay) {
|
||
|
|
// Basic conversion (simplified for testing)
|
||
|
|
int l = julianDay - 1948440 + 10632;
|
||
|
|
int n = (l - 1) ~/ 10631;
|
||
|
|
l = l - 10631 * n + 354;
|
||
|
|
int j = ((10985 - l) ~/ 5316) * ((50 * l) ~/ 17719) + (l ~/ 5670) * ((43 * l) ~/ 15238);
|
||
|
|
l = l - ((30 - j) ~/ 15) * ((17719 * j) ~/ 50) - (j ~/ 16) * ((15238 * j) ~/ 43) + 29;
|
||
|
|
int month = (24 * l) ~/ 709;
|
||
|
|
int day = l - (709 * month) ~/ 24;
|
||
|
|
int year = 30 * n + j - 30;
|
||
|
|
|
||
|
|
return HijriGregDate(year: year, month: month, day: day);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class HijriGregDate {
|
||
|
|
final int year;
|
||
|
|
final int month;
|
||
|
|
final int day;
|
||
|
|
|
||
|
|
HijriGregDate({required this.year, required this.month, required this.day});
|
||
|
|
}
|
||
|
|
|
||
|
|
// Data class to store test results
|
||
|
|
class DateConversionResult {
|
||
|
|
final DateTime gregorianDate;
|
||
|
|
final String isoString;
|
||
|
|
final int hijriYear;
|
||
|
|
final int hijriMonth;
|
||
|
|
final int hijriDay;
|
||
|
|
final String apiFormat;
|
||
|
|
final String displayFormat;
|
||
|
|
final String hijriDisplayFormat;
|
||
|
|
final bool success;
|
||
|
|
final String? errorMessage;
|
||
|
|
|
||
|
|
DateConversionResult({
|
||
|
|
required this.gregorianDate,
|
||
|
|
required this.isoString,
|
||
|
|
required this.hijriYear,
|
||
|
|
required this.hijriMonth,
|
||
|
|
required this.hijriDay,
|
||
|
|
required this.apiFormat,
|
||
|
|
required this.displayFormat,
|
||
|
|
required this.hijriDisplayFormat,
|
||
|
|
required this.success,
|
||
|
|
this.errorMessage,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|