From 34c5b9ffdf4cd65ff8d20a49ae7100e9fc69db91 Mon Sep 17 00:00:00 2001 From: Jama Mohamed Date: Thu, 29 Dec 2022 16:37:43 +0300 Subject: [PATCH] [ISSUE-170] Move the diff implemetation to a new file --- lib/src/display.dart | 102 ++++++++++++++++- lib/src/getter.dart | 2 +- lib/src/manipulator.dart | 156 +++++++++++++++---------- lib/src/parser.dart | 21 ++-- test/src/display_test.dart | 225 +++++++++++++++++++++++++++++++++++++ test/src/parser_test.dart | 4 +- 6 files changed, 436 insertions(+), 74 deletions(-) create mode 100644 test/src/display_test.dart diff --git a/lib/src/display.dart b/lib/src/display.dart index 067276d..222e363 100644 --- a/lib/src/display.dart +++ b/lib/src/display.dart @@ -1,9 +1,16 @@ import 'package:intl/intl.dart'; +import 'getter.dart'; import 'enums/units.dart'; +import 'manipulator.dart'; import 'utils/exception.dart'; class Display { + final Getter getter; + final Manipulator manipulator; + + Display(this.getter, this.manipulator); + String formatToISO8601(DateTime dateTime) => dateTime.toIso8601String(); String format(DateTime dateTime, String pattern, String ordinal) { @@ -20,9 +27,48 @@ class Display { return ''; } - num diff(DateTime firstDateTime, DateTime secondsDateTime, Units units, + num diff(DateTime firstDateTime, DateTime secondDateTime, Units unit, bool asFloat) { - return 0; + final firstDateTimeMicrosecondsSinceEpoch = + getter.microsecondsSinceEpoch(firstDateTime); + final secondDateTimeMicrosecondsSinceEpoch = + getter.microsecondsSinceEpoch(secondDateTime); + final diffMicrosecondsSinceEpoch = firstDateTimeMicrosecondsSinceEpoch - + secondDateTimeMicrosecondsSinceEpoch; + + var diff; + + switch (unit) { + case Units.MICROSECOND: + diff = diffMicrosecondsSinceEpoch; + break; + case Units.MILLISECOND: + diff = diffMicrosecondsSinceEpoch / Duration.microsecondsPerMillisecond; + break; + case Units.SECOND: + diff = diffMicrosecondsSinceEpoch / Duration.microsecondsPerSecond; + break; + case Units.MINUTE: + diff = diffMicrosecondsSinceEpoch / Duration.microsecondsPerMinute; + break; + case Units.HOUR: + diff = diffMicrosecondsSinceEpoch / Duration.microsecondsPerHour; + break; + case Units.DAY: + diff = diffMicrosecondsSinceEpoch / Duration.microsecondsPerDay; + break; + case Units.WEEK: + diff = (diffMicrosecondsSinceEpoch / Duration.microsecondsPerDay) / 7; + break; + case Units.MONTH: + diff = _monthDiff(firstDateTime, secondDateTime); + break; + case Units.YEAR: + diff = _monthDiff(firstDateTime, secondDateTime) / 12; + break; + } + + return asFloat ? _absFloor(diff) : diff; } String _replaceEscapePattern(String input) { @@ -41,9 +87,59 @@ class Display { return pattern; } - // tod understand what this regex pattern does + // todo understand what this regex pattern does Pattern _matchesOrdinalDatePattern() { return RegExp( '''(?[^"'\\s]\\w*)|(?:["][^"]+?["])|(?:['][^']+?['])'''); } + + num _monthDiff(DateTime firstDateTime, DateTime secondDateTime) { + if (getter.date(firstDateTime) < getter.date(secondDateTime)) { + return -(_monthDiff(secondDateTime, firstDateTime)); + } + + final monthDiff = + ((getter.year(secondDateTime) - getter.year(firstDateTime)) * 12) + + (getter.month(secondDateTime) - getter.month(firstDateTime)); + + final thirdDateTime = _addMonths(firstDateTime, monthDiff); + final thirdDateTimeMicrosecondsSinceEpoch = + getMicrosecondsSinceEpoch(thirdDateTime); + + final diffMicrosecondsSinceEpoch = + getMicrosecondsSinceEpoch(secondDateTime) - + thirdDateTimeMicrosecondsSinceEpoch; + + var offset; + + if (diffMicrosecondsSinceEpoch < 0) { + final fifthDateTime = _addMonths(firstDateTime, monthDiff - 1); + offset = diffMicrosecondsSinceEpoch / + (thirdDateTimeMicrosecondsSinceEpoch - + getMicrosecondsSinceEpoch(fifthDateTime)); + } else { + final fifthDateTime = _addMonths(firstDateTime, monthDiff + 1); + offset = diffMicrosecondsSinceEpoch / + (getMicrosecondsSinceEpoch(fifthDateTime) - + thirdDateTimeMicrosecondsSinceEpoch); + } + + return -(monthDiff + offset); + } + + int _absFloor(num number) { + if (number < 0) { + return number.ceil(); + } else { + return number.floor(); + } + } + + DateTime _addMonths(DateTime dateTime, int months) { + return manipulator.add(dateTime, 0, 0, 0, 0, 0, 0, 0, months, 0); + } + + int getMicrosecondsSinceEpoch(DateTime dateTime) { + return getter.microsecondsSinceEpoch(dateTime); + } } diff --git a/lib/src/getter.dart b/lib/src/getter.dart index 05f085a..556a7d9 100644 --- a/lib/src/getter.dart +++ b/lib/src/getter.dart @@ -56,7 +56,7 @@ class Getter { return int.parse(DateFormat('Q').format(dateTime)); } - // todo see if you can use a parse and formatter class + // todo see if you can use a formatter class int dayOfYear(DateTime dateTime) { return int.parse(DateFormat('D').format(dateTime)); } diff --git a/lib/src/manipulator.dart b/lib/src/manipulator.dart index 17eadb3..a309a91 100644 --- a/lib/src/manipulator.dart +++ b/lib/src/manipulator.dart @@ -71,50 +71,61 @@ class Manipulator { switch (unit) { case Units.MICROSECOND: newDateTime = DateTime( - dateTime.year, - dateTime.month, - dateTime.day, - dateTime.hour, - dateTime.minute, - dateTime.second, - dateTime.millisecond, - dateTime.microsecond); + getter.year(dateTime), + getter.month(dateTime), + getter.date(dateTime), + getter.hour(dateTime), + getter.minute(dateTime), + getter.second(dateTime), + getter.millisecond(dateTime), + getter.microsecond(dateTime)); break; case Units.MILLISECOND: newDateTime = DateTime( - dateTime.year, - dateTime.month, - dateTime.day, - dateTime.hour, - dateTime.minute, - dateTime.second, - dateTime.millisecond); + getter.year(dateTime), + getter.month(dateTime), + getter.date(dateTime), + getter.hour(dateTime), + getter.minute(dateTime), + getter.second(dateTime), + getter.millisecond(dateTime)); break; case Units.SECOND: - newDateTime = DateTime(dateTime.year, dateTime.month, dateTime.day, - dateTime.hour, dateTime.minute, dateTime.second); + newDateTime = DateTime( + getter.year(dateTime), + getter.month(dateTime), + getter.date(dateTime), + getter.hour(dateTime), + getter.minute(dateTime), + getter.second(dateTime)); break; case Units.MINUTE: - newDateTime = DateTime(dateTime.year, dateTime.month, dateTime.day, - dateTime.hour, dateTime.minute); + newDateTime = DateTime( + getter.year(dateTime), + getter.month(dateTime), + getter.date(dateTime), + getter.hour(dateTime), + getter.minute(dateTime)); break; case Units.HOUR: - newDateTime = DateTime( - dateTime.year, dateTime.month, dateTime.day, dateTime.hour); + newDateTime = DateTime(getter.year(dateTime), getter.month(dateTime), + getter.date(dateTime), getter.hour(dateTime)); break; case Units.DAY: - newDateTime = DateTime(dateTime.year, dateTime.month, dateTime.day); + newDateTime = DateTime(getter.year(dateTime), getter.month(dateTime), + getter.date(dateTime)); break; case Units.WEEK: var newDate = subtractDuration(dateTime, Duration(days: getter.dayOfWeek(dateTime, startOfWeek) - 1)); - newDateTime = DateTime(newDate.year, newDate.month, newDate.day); + newDateTime = DateTime( + getter.year(newDate), getter.month(newDate), getter.date(newDate)); break; case Units.MONTH: - newDateTime = DateTime(dateTime.year, dateTime.month); + newDateTime = DateTime(getter.year(dateTime), getter.month(dateTime)); break; case Units.YEAR: - newDateTime = DateTime(dateTime.year); + newDateTime = DateTime(getter.year(dateTime)); break; } return newDateTime; @@ -125,41 +136,55 @@ class Manipulator { switch (unit) { case Units.MICROSECOND: newDateTime = DateTime( - dateTime.year, - dateTime.month, - dateTime.day, - dateTime.hour, - dateTime.minute, - dateTime.second, - dateTime.millisecond, - dateTime.microsecond); + getter.year(dateTime), + getter.month(dateTime), + getter.date(dateTime), + getter.hour(dateTime), + getter.minute(dateTime), + getter.second(dateTime), + getter.millisecond(dateTime), + getter.microsecond(dateTime)); break; case Units.MILLISECOND: newDateTime = DateTime( - dateTime.year, - dateTime.month, - dateTime.day, - dateTime.hour, - dateTime.minute, - dateTime.second, - dateTime.millisecond, + getter.year(dateTime), + getter.month(dateTime), + getter.date(dateTime), + getter.hour(dateTime), + getter.minute(dateTime), + getter.second(dateTime), + getter.millisecond(dateTime), 999); break; case Units.SECOND: - newDateTime = DateTime(dateTime.year, dateTime.month, dateTime.day, - dateTime.hour, dateTime.minute, dateTime.second, 999, 999); + newDateTime = DateTime( + getter.year(dateTime), + getter.month(dateTime), + getter.date(dateTime), + getter.hour(dateTime), + getter.minute(dateTime), + getter.second(dateTime), + 999, + 999); break; case Units.MINUTE: - newDateTime = DateTime(dateTime.year, dateTime.month, dateTime.day, - dateTime.hour, dateTime.minute, 59, 999, 999); + newDateTime = DateTime( + getter.year(dateTime), + getter.month(dateTime), + getter.date(dateTime), + getter.hour(dateTime), + getter.minute(dateTime), + 59, + 999, + 999); break; case Units.HOUR: - newDateTime = DateTime(dateTime.year, dateTime.month, dateTime.day, - dateTime.hour, 59, 59, 999, 999); + newDateTime = DateTime(getter.year(dateTime), getter.month(dateTime), + getter.date(dateTime), getter.hour(dateTime), 59, 59, 999, 999); break; case Units.DAY: - newDateTime = DateTime( - dateTime.year, dateTime.month, dateTime.day, 23, 59, 59, 999, 999); + newDateTime = DateTime(getter.year(dateTime), getter.month(dateTime), + getter.date(dateTime), 23, 59, 59, 999, 999); break; case Units.WEEK: var newDate = addDuration( @@ -167,19 +192,21 @@ class Manipulator { Duration( days: DateTime.daysPerWeek - getter.dayOfWeek(dateTime, startOfWeek))); - newDateTime = DateTime( - newDate.year, newDate.month, newDate.day, 23, 59, 59, 999, 999); + newDateTime = DateTime(getter.year(newDate), getter.month(newDate), + getter.date(newDate), 23, 59, 59, 999, 999); break; case Units.MONTH: - var endMonthDate = Getter.daysInMonthArray[dateTime.month]; - if (Query.isLeapYear(dateTime.year) && dateTime.month == 2) { + var endMonthDate = Getter.daysInMonthArray[getter.month(dateTime)]; + if (Query.isLeapYear(getter.year(dateTime)) && + getter.month(dateTime) == 2) { endMonthDate = 29; } - newDateTime = DateTime( - dateTime.year, dateTime.month, endMonthDate, 23, 59, 59, 999, 999); + newDateTime = DateTime(getter.year(dateTime), getter.month(dateTime), + endMonthDate, 23, 59, 59, 999, 999); break; case Units.YEAR: - newDateTime = DateTime(dateTime.year, 12, 31, 23, 59, 59, 999, 999); + newDateTime = + DateTime(getter.year(dateTime), 12, 31, 23, 59, 59, 999, 999); break; } return newDateTime; @@ -191,15 +218,22 @@ class Manipulator { DateTime _addMonths(DateTime dateTime, int months) { final modMonths = months % 12; - var newYear = dateTime.year + ((months - modMonths) ~/ 12); - var newMonth = dateTime.month + modMonths; + var newYear = getter.year(dateTime) + ((months - modMonths) ~/ 12); + var newMonth = getter.month(dateTime) + modMonths; if (newMonth > 12) { newYear++; newMonth -= 12; } - final newDay = - min(dateTime.day, getter.daysInMonth(DateTime(newYear, newMonth))); - return DateTime(newYear, newMonth, newDay, dateTime.hour, dateTime.minute, - dateTime.second, dateTime.millisecond, dateTime.microsecond); + final newDay = min( + getter.date(dateTime), getter.daysInMonth(DateTime(newYear, newMonth))); + return DateTime( + newYear, + newMonth, + newDay, + getter.hour(dateTime), + getter.minute(dateTime), + getter.second(dateTime), + getter.millisecond(dateTime), + getter.microsecond(dateTime)); } } diff --git a/lib/src/parser.dart b/lib/src/parser.dart index 64da3f6..9c7edb3 100644 --- a/lib/src/parser.dart +++ b/lib/src/parser.dart @@ -1,9 +1,14 @@ import 'package:intl/intl.dart'; import 'enums/units.dart'; +import 'getter.dart'; import 'utils/exception.dart'; class Parser { + final Getter getter; + + Parser(this.getter); + DateTime fromString(String input, String? pattern, List ordinals) { if (pattern != null) { if (pattern.trim().isEmpty) { @@ -49,14 +54,14 @@ class Parser { throw JiffyException('The provided datetime map cannot be empty'); } return DateTime( - input[Units.YEAR] ?? DateTime.now().year, - input[Units.MONTH] ?? DateTime.now().month, - input[Units.DAY] ?? DateTime.now().day, - input[Units.HOUR] ?? DateTime.now().hour, - input[Units.MINUTE] ?? DateTime.now().minute, - input[Units.SECOND] ?? DateTime.now().second, - input[Units.MILLISECOND] ?? DateTime.now().millisecond, - input[Units.MICROSECOND] ?? DateTime.now().microsecond); + input[Units.YEAR] ?? getter.year(DateTime.now()), + input[Units.MONTH] ?? getter.month(DateTime.now()), + input[Units.DAY] ?? getter.date(DateTime.now()), + input[Units.HOUR] ?? getter.hour(DateTime.now()), + input[Units.MINUTE] ?? getter.minute(DateTime.now()), + input[Units.SECOND] ?? getter.second(DateTime.now()), + input[Units.MILLISECOND] ?? getter.millisecond(DateTime.now()), + input[Units.MICROSECOND] ?? getter.microsecond(DateTime.now())); } DateTime parseString(String input, String pattern) { diff --git a/test/src/display_test.dart b/test/src/display_test.dart new file mode 100644 index 0000000..232d81d --- /dev/null +++ b/test/src/display_test.dart @@ -0,0 +1,225 @@ +import 'package:jiffy/src/display.dart'; +import 'package:jiffy/src/enums/units.dart'; +import 'package:jiffy/src/getter.dart'; +import 'package:jiffy/src/manipulator.dart'; +import 'package:test/scaffolding.dart'; +import 'package:test/test.dart'; + +void main() { + final getter = Getter(); + final manipulator = Manipulator(getter); + + final underTest = Display(getter, manipulator); + + group('Test diff', () { + for (var testData in diffDateTimeTestData()) { + test('Should successfully get difference between two datetime', () { + // Setup + final asFloat = true; + + // Execute + final actualDifference = underTest.diff(testData['firstDateTime'], + testData['secondDateTime'], testData['unit'], asFloat); + + // Verify + expect(actualDifference, testData['expectedDifference']); + }); + } + + test('Should successfully get difference when float is disabled', () { + // Setup + final firstDateTime = DateTime(2022, 12, 5); + final secondDateTime = DateTime(2022, 12, 8); + final unit = Units.WEEK; + final asFloat = false; + + final expectedDifference = -0.42857142857142855; + + // Execute + final actualDifference = + underTest.diff(firstDateTime, secondDateTime, unit, asFloat); + + // Verify + expect(actualDifference, expectedDifference); + }); + }); +} + +List> diffDateTimeTestData() { + return [ + { + 'firstDateTime': DateTime(1997, 9, 23, 12, 11, 22, 123, 10), + 'secondDateTime': DateTime(1997, 9, 23, 12, 11, 22, 123, 24), + 'unit': Units.MICROSECOND, + 'expectedDifference': -14 + }, + { + 'firstDateTime': DateTime(1997, 9, 23, 12, 11, 22, 123, 24), + 'secondDateTime': DateTime(1997, 9, 23, 12, 11, 22, 123, 10), + 'unit': Units.MICROSECOND, + 'expectedDifference': 14 + }, + { + 'firstDateTime': DateTime(1997, 9, 23, 12, 11, 22, 123, 2), + 'secondDateTime': DateTime(1997, 9, 23, 12, 11, 22, 123, 2), + 'unit': Units.MICROSECOND, + 'expectedDifference': 0 + }, + { + 'firstDateTime': DateTime(1997, 9, 23, 12, 11, 22, 10), + 'secondDateTime': DateTime(1997, 9, 23, 12, 11, 22, 24), + 'unit': Units.MILLISECOND, + 'expectedDifference': -14 + }, + { + 'firstDateTime': DateTime(1997, 9, 23, 12, 11, 22, 24), + 'secondDateTime': DateTime(1997, 9, 23, 12, 11, 22, 10), + 'unit': Units.MILLISECOND, + 'expectedDifference': 14 + }, + { + 'firstDateTime': DateTime(1997, 9, 23, 12, 11, 22, 2), + 'secondDateTime': DateTime(1997, 9, 23, 12, 11, 22, 2), + 'unit': Units.MILLISECOND, + 'expectedDifference': 0 + }, + { + 'firstDateTime': DateTime(1997, 9, 23, 12, 11, 10), + 'secondDateTime': DateTime(1997, 9, 23, 12, 11, 24), + 'unit': Units.SECOND, + 'expectedDifference': -14 + }, + { + 'firstDateTime': DateTime(1997, 9, 23, 12, 11, 24), + 'secondDateTime': DateTime(1997, 9, 23, 12, 11, 10), + 'unit': Units.SECOND, + 'expectedDifference': 14 + }, + { + 'firstDateTime': DateTime(1997, 9, 23, 12, 11, 2), + 'secondDateTime': DateTime(1997, 9, 23, 12, 11, 2), + 'unit': Units.SECOND, + 'expectedDifference': 0 + }, + { + 'firstDateTime': DateTime(1997, 9, 23, 12, 10), + 'secondDateTime': DateTime(1997, 9, 23, 12, 24), + 'unit': Units.MINUTE, + 'expectedDifference': -14 + }, + { + 'firstDateTime': DateTime(1997, 9, 23, 12, 24), + 'secondDateTime': DateTime(1997, 9, 23, 12, 10), + 'unit': Units.MINUTE, + 'expectedDifference': 14 + }, + { + 'firstDateTime': DateTime(1997, 9, 23, 12, 2), + 'secondDateTime': DateTime(1997, 9, 23, 12, 2), + 'unit': Units.MINUTE, + 'expectedDifference': 0 + }, + { + 'firstDateTime': DateTime(1997, 9, 23, 10), + 'secondDateTime': DateTime(1997, 9, 23, 24), + 'unit': Units.HOUR, + 'expectedDifference': -14 + }, + { + 'firstDateTime': DateTime(1997, 9, 23, 24), + 'secondDateTime': DateTime(1997, 9, 23, 10), + 'unit': Units.HOUR, + 'expectedDifference': 14 + }, + { + 'firstDateTime': DateTime(1997, 9, 23, 2), + 'secondDateTime': DateTime(1997, 9, 23, 2), + 'unit': Units.HOUR, + 'expectedDifference': 0 + }, + { + 'firstDateTime': DateTime(1997, 9, 10), + 'secondDateTime': DateTime(1997, 9, 24), + 'unit': Units.DAY, + 'expectedDifference': -14 + }, + { + 'firstDateTime': DateTime(1997, 9, 24), + 'secondDateTime': DateTime(1997, 9, 10), + 'unit': Units.DAY, + 'expectedDifference': 14 + }, + { + 'firstDateTime': DateTime(1997, 9, 2), + 'secondDateTime': DateTime(1997, 9, 2), + 'unit': Units.DAY, + 'expectedDifference': 0 + }, + { + 'firstDateTime': DateTime(2022, 12, 5), + 'secondDateTime': DateTime(2022, 12, 20), + 'unit': Units.WEEK, + 'expectedDifference': -2 + }, + { + 'firstDateTime': DateTime(2022, 12, 22), + 'secondDateTime': DateTime(2022, 12, 6), + 'unit': Units.WEEK, + 'expectedDifference': 2 + }, + { + 'firstDateTime': DateTime(2022, 12, 5), + 'secondDateTime': DateTime(2022, 12, 8), + 'unit': Units.WEEK, + 'expectedDifference': 0 + }, + { + 'firstDateTime': DateTime(2022, 5), + 'secondDateTime': DateTime(2022, 7), + 'unit': Units.MONTH, + 'expectedDifference': -2 + }, + { + 'firstDateTime': DateTime(2022, 7), + 'secondDateTime': DateTime(2022, 5), + 'unit': Units.MONTH, + 'expectedDifference': 2 + }, + { + 'firstDateTime': DateTime(2022, 5), + 'secondDateTime': DateTime(2022, 5), + 'unit': Units.MONTH, + 'expectedDifference': 0 + }, + { + 'firstDateTime': DateTime(2016, 2, 29), + 'secondDateTime': DateTime(2016, 1, 30), + 'unit': Units.MONTH, + 'expectedDifference': 1 + }, + { + 'firstDateTime': DateTime(2016, 2, 29), + 'secondDateTime': DateTime(2016, 1, 31), + 'unit': Units.MONTH, + 'expectedDifference': 1 + }, + { + 'firstDateTime': DateTime(1997), + 'secondDateTime': DateTime(1999), + 'unit': Units.YEAR, + 'expectedDifference': -2 + }, + { + 'firstDateTime': DateTime(1999), + 'secondDateTime': DateTime(1997), + 'unit': Units.YEAR, + 'expectedDifference': 2 + }, + { + 'firstDateTime': DateTime(1997), + 'secondDateTime': DateTime(1997), + 'unit': Units.YEAR, + 'expectedDifference': 0 + }, + ]; +} diff --git a/test/src/parser_test.dart b/test/src/parser_test.dart index 844b545..cd6644c 100644 --- a/test/src/parser_test.dart +++ b/test/src/parser_test.dart @@ -1,10 +1,12 @@ import 'package:jiffy/src/enums/units.dart'; +import 'package:jiffy/src/getter.dart'; import 'package:jiffy/src/parser.dart'; import 'package:jiffy/src/utils/exception.dart'; import 'package:test/test.dart'; void main() { - final underTest = Parser(); + final getter = Getter(); + final underTest = Parser(getter); group('Test parsing datetime from string', () { test('Should successfully parse datetime without a pattern', () {});