Skip to content

Commit 8b8874a

Browse files
authored
Clamp functions added to Duration and DateTime #53
2 parents 6ec373d + 042f682 commit 8b8874a

File tree

2 files changed

+195
-2
lines changed

2 files changed

+195
-2
lines changed

lib/src/extensions.dart

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,41 @@ extension DateTimeTimeExtension on DateTime {
224224

225225
/// Returns the last day of this year
226226
DateTime get lastDayOfYear => isUtc ? DateTime.utc(year, 12, 31) : DateTime(year, 12, 31);
227+
228+
/// Returns this [DateTime] clamped to be in the range [min]-[max].
229+
///
230+
/// The comparison is done using [compareTo].
231+
///
232+
/// The arguments [min] and [max] must form a valid range where
233+
/// `min.compareTo(max) <= 0`.
234+
///
235+
/// Example:
236+
/// ```dart
237+
/// var result = DateTime(2022, DateTime.october, 15).clamp(
238+
/// min: DateTime(2022, DateTime.september, 1),
239+
/// max: DateTime(2022, DateTime.september, 30),
240+
/// ); // DateTime(2022, DateTime.september, 30);
241+
/// result = DateTime(2022, DateTime.august, 21).clamp(
242+
/// min: DateTime(2022, DateTime.september, 15),
243+
/// max: DateTime(2022, DateTime.september, 30),
244+
/// ); // DateTime(2022, DateTime.september, 15);
245+
/// result = DateTime(2022, DateTime.september, 1).clamp(
246+
/// min: DateTime(2022, DateTime.august, 1),
247+
/// max: DateTime(2022, DateTime.september, 30),
248+
/// ); // DateTime(2022, DateTime.september, 1);
249+
/// ```
250+
DateTime clamp({DateTime? min, DateTime? max}) {
251+
assert(
252+
((min != null) && (max != null)) ? min.compareTo(max).isNegative : true,
253+
'DateTime min has to be before max\n(min: $min - max: $max)',
254+
);
255+
if ((min != null) && compareTo(min).isNegative) {
256+
return min;
257+
} else if ((max != null) && max.compareTo(this).isNegative) {
258+
return max;
259+
}
260+
return this;
261+
}
227262
}
228263

229264
extension DurationTimeExtension on Duration {
@@ -241,4 +276,39 @@ extension DurationTimeExtension on Duration {
241276

242277
/// Returns a Future.delayed from this
243278
Future<void> get delay => Future.delayed(this);
279+
280+
/// Returns this [Duration] clamped to be in the range [min]-[max].
281+
///
282+
/// The comparison is done using [compareTo].
283+
///
284+
/// The arguments [min] and [max] must form a valid range where
285+
/// `min.compareTo(max) <= 0`.
286+
///
287+
/// Example:
288+
/// ```dart
289+
/// var result = Duration(days: 10, hours: 12).clamp(
290+
/// min: Duration(days: 5),
291+
/// max: Duration(days: 10),
292+
/// ); // Duration(days: 10)
293+
/// result = Duration(hours: 18).clamp(
294+
/// min: Duration(days: 5),
295+
/// max: Duration(days: 10),
296+
/// ); // Duration(days: 5)
297+
/// result = Duration(days: 0).clamp(
298+
/// min: Duration(days: -5),
299+
/// max: Duration(days: 5),
300+
/// ); // Duration(days: 0)
301+
/// ```
302+
Duration clamp({Duration? min, Duration? max}) {
303+
assert(
304+
((min != null) && (max != null)) ? min.compareTo(max).isNegative : true,
305+
'Duration min has to be shorter than max\n(min: $min - max: $max)',
306+
);
307+
if ((min != null) && compareTo(min).isNegative) {
308+
return min;
309+
} else if ((max != null) && max.compareTo(this).isNegative) {
310+
return max;
311+
}
312+
return this;
313+
}
244314
}

test/time_test.dart

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ void main() {
547547
expect(initial.firstDayOfMonth, expected);
548548
});
549549

550-
group('last day of month', (){
550+
group('last day of month', () {
551551
test('last day of month', () {
552552
final initial = DateTime(2022, 5, 20);
553553
final expected = DateTime(2022, 5, 31);
@@ -564,7 +564,6 @@ void main() {
564564
final expected = DateTime(2020, 2, 29);
565565
expect(initial.lastDayOfMonth, expected);
566566
});
567-
568567
});
569568

570569
test('first day of year', () {
@@ -580,6 +579,68 @@ void main() {
580579
});
581580
});
582581
});
582+
583+
group('clamp', () {
584+
group('returns max when before it', () {
585+
final it = DateTime(2022, DateTime.october, 15);
586+
final min = DateTime(2022, DateTime.september, 1);
587+
final max = DateTime(2022, DateTime.september, 30);
588+
589+
test('when it has a value for min', () {
590+
expect(it.clamp(min: min, max: max), equals(max));
591+
});
592+
593+
test('when it does not have a value for min', () {
594+
expect(it.clamp(max: max), equals(max));
595+
});
596+
});
597+
598+
group('returns min when after it', () {
599+
final it = DateTime(2022, DateTime.august, 21);
600+
final min = DateTime(2022, DateTime.september, 15);
601+
final max = DateTime(2022, DateTime.september, 30);
602+
603+
test('when it has a value for max', () {
604+
expect(it.clamp(min: min, max: max), equals(min));
605+
});
606+
607+
test('when it does not have a value for max', () {
608+
expect(it.clamp(min: min), equals(min));
609+
});
610+
});
611+
612+
group('returns it', () {
613+
final it = DateTime(2022, DateTime.september, 1);
614+
final min = DateTime(2022, DateTime.august, 1);
615+
final max = DateTime(2022, DateTime.september, 30);
616+
617+
test('when both min and max are null', () {
618+
expect(it.clamp(), equals(it));
619+
});
620+
621+
test('when is longer than min and max is null', () {
622+
expect(it.clamp(min: min), equals(it));
623+
});
624+
625+
test('when is shorter than max and min is null', () {
626+
expect(it.clamp(max: max), equals(it));
627+
});
628+
629+
test('when is longer than min and shorter than max', () {
630+
expect(it.clamp(min: min, max: max), equals(it));
631+
});
632+
});
633+
634+
test('asserts that min should be before max', () {
635+
final it = DateTime(2022, DateTime.september, 1);
636+
final min = DateTime(2022, DateTime.september, 30);
637+
final max = DateTime(2022, DateTime.august, 1);
638+
expect(
639+
() => it.clamp(min: min, max: max),
640+
throwsA(isA<AssertionError>()),
641+
);
642+
});
643+
});
583644
});
584645

585646
group('Duration', () {
@@ -615,5 +676,67 @@ void main() {
615676
final extraTime = after.millisecondsSinceEpoch - before.add(timeToWait).millisecondsSinceEpoch;
616677
expect(extraTime >= 0, true);
617678
});
679+
680+
group('clamp', () {
681+
group('returns max when shorter than it', () {
682+
final it = Duration(days: 10, hours: 12);
683+
final min = Duration(days: 5);
684+
final max = Duration(days: 10);
685+
686+
test('when it has a value for min', () {
687+
expect(it.clamp(min: min, max: max), equals(max));
688+
});
689+
690+
test('when it does not have a value for min', () {
691+
expect(it.clamp(max: max), equals(max));
692+
});
693+
});
694+
695+
group('returns min when longer than it', () {
696+
final it = Duration(hours: 18);
697+
final min = Duration(days: 5);
698+
final max = Duration(days: 10);
699+
700+
test('when it has a value for max', () {
701+
expect(it.clamp(min: min, max: max), equals(min));
702+
});
703+
704+
test('when it does not have a value for max', () {
705+
expect(it.clamp(min: min), equals(min));
706+
});
707+
});
708+
709+
group('returns it', () {
710+
final it = Duration(days: 0);
711+
final min = Duration(days: -5);
712+
final max = Duration(days: 5);
713+
714+
test('when both min and max are null', () {
715+
expect(it.clamp(), equals(it));
716+
});
717+
718+
test('when is longer than min and max is null', () {
719+
expect(it.clamp(min: min), equals(it));
720+
});
721+
722+
test('when is shorter than max and min is null', () {
723+
expect(it.clamp(max: max), equals(it));
724+
});
725+
726+
test('when is longer than min and shorter than max', () {
727+
expect(it.clamp(min: min, max: max), equals(it));
728+
});
729+
});
730+
731+
test('asserts that min should be shorter than max', () {
732+
final it = Duration(days: -0);
733+
final min = Duration(days: 5);
734+
final max = Duration(days: -5);
735+
expect(
736+
() => it.clamp(min: min, max: max),
737+
throwsA(isA<AssertionError>()),
738+
);
739+
});
740+
});
618741
});
619742
}

0 commit comments

Comments
 (0)