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
25 changes: 25 additions & 0 deletions lib/services/database_mutation_functions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ 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/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 +98,13 @@ class DataBaseMutationFunctions {
return await gqlAuthQuery(query, variables: variables);
}
} else if (result.data != null && result.isConcrete) {
// coverage:ignore-start
traverseAndConvertDates(
result.data ?? <String, dynamic>{},
convertUTCToLocal,
splitDateTimeLocal,
);
// coverage:ignore-end
return result;
}
return noData;
Expand All @@ -117,6 +125,11 @@ class DataBaseMutationFunctions {
String mutation, {
Map<String, dynamic>? variables,
}) async {
// coverage:ignore-start
if (variables != null) {
traverseAndConvertDates(variables, convertLocalToUTC, splitDateTimeUTC);
}
// coverage:ignore-end
final MutationOptions options = MutationOptions(
document: gql(mutation),
variables: variables ?? <String, dynamic>{},
Expand Down Expand Up @@ -157,6 +170,11 @@ class DataBaseMutationFunctions {
Map<String, dynamic>? variables,
bool reCall = true,
}) async {
// coverage:ignore-start
if (variables != null) {
traverseAndConvertDates(variables, convertLocalToUTC, splitDateTimeUTC);
}
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved
// coverage:ignore-end
final MutationOptions options = MutationOptions(
document: gql(mutation),
variables: variables ?? <String, dynamic>{},
Expand Down Expand Up @@ -209,6 +227,13 @@ class DataBaseMutationFunctions {
result.exception!,
);
} else if (result.data != null && result.isConcrete) {
// coverage:ignore-start
traverseAndConvertDates(
result.data ?? <String, dynamic>{},
convertUTCToLocal,
splitDateTimeLocal,
);
// coverage:ignore-end
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved
return result;
}
return noData;
Expand Down
138 changes: 138 additions & 0 deletions lib/utils/time_conversion.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import 'package:intl/intl.dart';

/// Combines the given date and time strings into a single string.
///
/// **params**:
/// * `date`: The date string in a valid date format (e.g., 'YYYY-MM-DD').
/// * `time`: The time string in a valid time format (e.g., 'HH:MM:SS').
///
/// **returns**:
/// * `String`: A string that combines the `date` and `time`, separated by a space.
String combineDateTime(String date, String time) {
return '$date $time';
}

/// Splits the given UTC date and time string into separate date and time strings.
///
/// **params**:
/// * `dateTimeStr`: The UTC date and time string in a valid format.
///
/// **returns**:
/// * `Map<String, String>`: A map containing the separate date and time strings.
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),
};
}

/// Splits the given local date and time string into separate date and time strings.
///
/// **params**:
/// * `dateTimeStr`: The local date and time string in a valid format.
///
/// **returns**:
/// * `Map<String, String>`: A map containing the separate date and time strings.
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),
};
}

/// Converts the given UTC time to local time.
///
/// **params**:
/// * `utcTime`: The UTC time string in a valid format.
///
/// **returns**:
/// * `String`: The converted local time string.
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);
}

/// Converts the given local time to UTC time.
///
/// **params**:
/// * `localTime`: The local time string in a valid format.
///
/// **returns**:
/// * `String`: The converted UTC time string.
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);
}

/// Traverses a nested map and converts date and time fields to the desired format.
///
/// **params**:
/// * `obj`: The nested map to traverse and convert.
/// * `convertFn`: A function that converts a combined date and time string to the desired format.
/// * `splitFn`: A function that splits a converted date and time string into separate date and time strings.
///
/// **returns**:
/// None
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 (final 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'] ?? ''] = splitDateTime['date'] ?? '';
obj[field['timeField'] ?? ''] = 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 (final item in value) {
if (item is Map<String, dynamic>) {
traverseAndConvertDates(item, convertFn, splitFn);
}
}
}
});
}

/// Contains information about the date and time fields used for conversion.
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 @@ -225,12 +225,10 @@ class CreateEventViewModel extends BaseModel {
'organizationId': _currentOrg.id,
'startDate': DateFormat('yyyy-MM-dd').format(eventStartDate),
'endDate': DateFormat('yyyy-MM-dd').format(eventEndDate),
'startTime': isAllDay
? null
: '${DateFormat('HH:mm:ss').format(startTime)}Z',
'endTime': isAllDay
? null
: '${DateFormat('HH:mm:ss').format(endTime)}Z',
'startTime':
isAllDay ? null : DateFormat('HH:mm:ss').format(startTime),
'endTime':
isAllDay ? null : DateFormat('HH:mm:ss').format(endTime),
},
if (isRecurring)
'recurrenceRuleData': {
Expand Down
2 changes: 1 addition & 1 deletion pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ packages:
source: hosted
version: "0.4.1"
clock:
dependency: transitive
dependency: "direct main"
description:
name: clock
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies:

auto_size_text: ^3.0.0
cached_network_image: ^3.4.1
clock: ^1.1.1
pranshugupta54 marked this conversation as resolved.
Show resolved Hide resolved
connectivity_plus: ^5.0.2
contained_tab_bar_view: ^0.8.0

Expand Down
7 changes: 5 additions & 2 deletions test/service_tests/event_service_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ void main() {
testSetupLocator();
registerServices();
});

group('Test EventService', () {
test('Test editEvent method', () async {
final dataBaseMutationFunctions = locator<DataBaseMutationFunctions>();
Expand Down Expand Up @@ -118,7 +119,7 @@ void main() {
(realInvocation) async => QueryResult(
options: QueryOptions(document: gql(query)),
data: {
'cretedEvent': {
'createdEvent': {
'_id': 'eventId',
'title': 'Test task',
'description': 'Test description',
Expand All @@ -137,12 +138,13 @@ void main() {
when(
dataBaseMutationFunctions.gqlAuthMutation(
EventQueries().registerForEvent(),
variables: {'eventId': 'eventId'},
),
).thenAnswer(
(realInvocation) async => QueryResult(
options: QueryOptions(document: gql(query)),
data: {
'register for an event': {
'registerForEvent': {
'_id': 'eventId',
},
},
Expand Down Expand Up @@ -250,6 +252,7 @@ void main() {
final model = EventService();
expect(model.eventStream, isA<Stream<List<Event>>>());
});

test('Test createVolunteerGroup method', () async {
final dataBaseMutationFunctions = locator<DataBaseMutationFunctions>();
const query = '';
Expand Down
Loading
Loading