Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Timezone in App #2581

Merged
merged 11 commits into from
Oct 14, 2024
12 changes: 12 additions & 0 deletions lib/services/database_mutation_functions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import 'package:talawa/locator.dart';
import 'package:talawa/models/organization/org_info.dart';
import 'package:talawa/utils/post_queries.dart';
import 'package:talawa/utils/queries.dart';
import 'package:talawa/view_model/connectivity_view_model.dart';
import 'package:talawa/utils/time_conversion.dart';

/// DataBaseMutationFunctions class provides different services that are under the context of graphQL mutations and queries.
///
Expand Down Expand Up @@ -97,6 +99,8 @@ class DataBaseMutationFunctions {
return await gqlAuthQuery(query, variables: variables);
}
} else if (result.data != null && result.isConcrete) {
traverseAndConvertDates(result.data as Map<String, dynamic>,
convertUTCToLocal, splitDateTimeLocal);
return result;
}
return noData;
Expand All @@ -117,6 +121,9 @@ class DataBaseMutationFunctions {
String mutation, {
Map<String, dynamic>? variables,
}) async {
if (variables != null) {
traverseAndConvertDates(variables, convertLocalToUTC, splitDateTimeUTC);
}
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved
final MutationOptions options = MutationOptions(
document: gql(mutation),
variables: variables ?? <String, dynamic>{},
Expand Down Expand Up @@ -157,6 +164,9 @@ class DataBaseMutationFunctions {
Map<String, dynamic>? variables,
bool reCall = true,
}) async {
if (variables != null) {
traverseAndConvertDates(variables, convertLocalToUTC, splitDateTimeUTC);
}
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved
final MutationOptions options = MutationOptions(
document: gql(mutation),
variables: variables ?? <String, dynamic>{},
Expand Down Expand Up @@ -209,6 +219,8 @@ class DataBaseMutationFunctions {
result.exception!,
);
} else if (result.data != null && result.isConcrete) {
traverseAndConvertDates(result.data as Map<String, dynamic>,
convertUTCToLocal, splitDateTimeLocal);
return result;
}
return noData;
Expand Down
8 changes: 4 additions & 4 deletions lib/services/event_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,17 @@ class EventService extends BaseFeedManager<Event> {
final String currentOrgID = _currentOrg.id!;
// mutation to fetch the events
final String mutation = EventQueries().fetchOrgEvents(currentOrgID);
final result = await _dbFunctions.gqlAuthMutation(mutation);
final result = await _dbFunctions.gqlAuthQuery(mutation);
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved

if (result.data == null) {
throw Exception('unable to fetch data');
}

print(result.data!["eventsByOrganizationConnection"]);
final List<Map<String, dynamic>> eventsJson = result
.data!["eventsByOrganizationConnection"] as List<Map<String, dynamic>>;
final eventsJson = result
.data!["eventsByOrganizationConnection"] as List<dynamic>;
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved
eventsJson.forEach((eventJsonData) {
final Event event = Event.fromJson(eventJsonData);
final Event event = Event.fromJson(eventJsonData as Map<String,dynamic>);
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved
event.isRegistered = event.attendees?.any(
(attendee) => attendee.id == _userConfig.currentUser.id,
) ??
Expand Down
93 changes: 93 additions & 0 deletions lib/utils/time_conversion.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import 'package:intl/intl.dart';

String combineDateTime(String date, String time) {
return '$date $time';
}

Map<String, String> splitDateTimeUTC(String dateTimeStr) {
final DateTime dateTime = DateTime.parse(dateTimeStr);
return {
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved
'date': DateFormat('yyyy-MM-dd').format(dateTime),
'time': DateFormat('HH:mm:ss.SSS\'Z\'').format(dateTime),
};
}
Map<String, String> splitDateTimeLocal(String dateTimeStr) {
final DateTime dateTime = DateTime.parse(dateTimeStr);
return {
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved
'date': DateFormat('yyyy-MM-dd').format(dateTime),
'time': DateFormat('HH:mm').format(dateTime),
};
}

String convertUTCToLocal(String utcTime) {
final DateTime dateTime = DateTime.parse(utcTime).toLocal();
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved
return DateFormat('yyyy-MM-ddTHH:mm:ss.SSS').format(dateTime);
}

String convertLocalToUTC(String localTime) {
final DateTime dateTime = DateTime.parse(localTime).toUtc();
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved
return DateFormat('yyyy-MM-ddTHH:mm:ss.SSS\'Z\'').format(dateTime);
}

void traverseAndConvertDates(
Map<String, dynamic> obj,
String Function(String) convertFn,
Map<String, String> Function(String) splitFn,
) {
obj.forEach((key, value) {

final pairedFields =
dateTimeFields['pairedFields']?.cast<Map<String, String>>();
if (pairedFields != null) {
for (var field in pairedFields) {
if (key == field['dateField'] && obj.containsKey(field['timeField'])) {
final combinedDateTime = combineDateTime(
obj[field['dateField']] as String,
obj[field['timeField']] as String,
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved
);

final convertedDateTime = convertFn(combinedDateTime);

final splitDateTime = splitFn(convertedDateTime ?? '');

obj[field['dateField'] as String] = splitDateTime['date'] ?? '';
obj[field['timeField'] as String] = splitDateTime['time'] ?? '';
}
}
}

if (dateTimeFields['directFields']?.cast<String>()?.contains(key) ??
false) {
obj[key] = convertFn(value as String);
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved
}

if (value is Map<String, dynamic>) {
traverseAndConvertDates(value, convertFn, splitFn);
} else if (value is List) {
for (var item in value) {
if (item is Map<String, dynamic>) {
traverseAndConvertDates(item, convertFn, splitFn);
}
}
}
});
}
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved

const dateTimeFields = {
'directFields': [
'createdAt',
'birthDate',
'updatedAt',
'recurrenceStartDate',
'recurrenceEndDate',
'pluginCreatedBy',
'dueDate',
'completionDate',
'startCursor',
'endCursor',
],
'pairedFields': [
{'dateField': 'startDate', 'timeField': 'startTime'},
{'dateField': 'endDate', 'timeField': 'endTime'},
],
};
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,10 @@ class CreateEventViewModel extends BaseModel {
'endDate': DateFormat('yyyy-MM-dd').format(eventEndDate),
'startTime': isAllDay
? null
: '${DateFormat('HH:mm:ss').format(startTime)}Z',
: '${DateFormat('HH:mm:ss').format(startTime)}',
'endTime': isAllDay
? null
: '${DateFormat('HH:mm:ss').format(endTime)}Z',
: '${DateFormat('HH:mm:ss').format(endTime)}',
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved
},
if (isRecurring)
'recurrenceRuleData': {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:talawa/utils/time_conversion.dart';
import '../../../helpers/test_helpers.dart';
import 'package:clock/clock.dart';

void main() {
group('Time Conversion Utils', () {
setUp(() {
registerServices();
});

tearDown(() {
unregisterServices();
});

test('combineDateTime combines date and time correctly', () {
expect(combineDateTime('2023-05-01', '14:30:00'), '2023-05-01 14:30:00');
});

test('splitDateTimeUTC splits UTC datetime correctly', () {
final result = splitDateTimeUTC('2023-05-01T14:30:00.000Z');
expect(result['date'], '2023-05-01');
expect(result['time'], '14:30:00.000Z');
});

test('splitDateTimeLocal splits local datetime correctly', () {
final result = splitDateTimeLocal('2023-05-01T14:30:00.000');
expect(result['date'], '2023-05-01');
expect(result['time'], '14:30');
});

test('convertUTCToLocal converts UTC to local time', () {
final utcTime = '2023-05-01T14:30:00.000Z';
final localTime = convertUTCToLocal(utcTime);
expect(localTime, isNot(equals(utcTime)));
expect(
localTime, matches(r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}$'));
});

test('convertLocalToUTC converts local to UTC time', () {
final localTime = '2023-05-01T14:30:00.000';
final utcTime = convertLocalToUTC(localTime);
expect(utcTime, isNot(equals(localTime)));
expect(
utcTime, matches(r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$'));
});
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved

group('traverseAndConvertDates', () {
test('converts direct fields', () {
final testObj = {
'createdAt': '2023-05-01T14:30:00.000Z',
'name': 'Test',
};
traverseAndConvertDates(testObj, convertUTCToLocal, splitDateTimeLocal);
expect(testObj['createdAt'], isNot(equals('2023-05-01T14:30:00.000Z')));
expect(testObj['createdAt'],
matches(r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}$'));
});
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved

test('converts paired fields', () {
final testObj = {
'startDate': '2023-05-01',
'startTime': '14:30:00',
'name': 'Test',
};
traverseAndConvertDates(testObj, convertUTCToLocal, splitDateTimeLocal);
expect(testObj['startDate'], '2023-05-01');
expect(testObj['startTime'], matches(r'^\d{2}:\d{2}$'));
});
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved

test('converts nested objects', () {
final testObj = {
'user': {
'createdAt': '2023-05-01T14:30:00.000Z',
'name': 'Test User',
},
};
traverseAndConvertDates(testObj, convertUTCToLocal, splitDateTimeLocal);
expect(testObj['user']?['createdAt'],
isNot(equals('2023-05-01T14:30:00.000Z')));
expect(testObj['user']?['createdAt'],
matches(r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}$'));
});

test('converts objects in lists', () {
withClock(Clock.fixed(DateTime.utc(2023, 5, 1, 12, 0)), () {
final testObj = {
'items': [
{'createdAt': '2023-05-01T14:30:00.000Z'},
{'createdAt': '2023-05-02T15:45:00.000Z'},
],
};
traverseAndConvertDates(
testObj, convertUTCToLocal, splitDateTimeLocal);
expect(testObj['items']?[0]['createdAt'],
isNot(equals('2023-05-01T14:30:00.000Z')));
expect(testObj['items']?[0]['createdAt'],
matches(r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}$'));
expect(testObj['items']?[1]['createdAt'],
isNot(equals('2023-05-02T15:45:00.000Z')));
expect(testObj['items']?[1]['createdAt'],
matches(r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}$'));
});
});
});
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved
});
}
Loading