- Added the `isLeapYear` extension to the `DateTime` and `int` objects.
- Added the `FixedSizeList` class.
This commit is contained in:
2025-05-16 11:15:08 +02:00
parent bfdba4603b
commit 363fb20665
9 changed files with 188 additions and 5 deletions
+5
View File
@@ -1,3 +1,8 @@
## 1.4.2
- Added the `isLeapYear` extension to the `DateTime` and `int` objects.
- Added the `FixedSizeList` class. See the readme and examples for details.
## 1.4.1 ## 1.4.1
- Added a `List` equality extension, `equals`. - Added a `List` equality extension, `equals`.
+12
View File
@@ -258,6 +258,18 @@ The following operations are now available on a `DateTime` object:
final DateTime tomorrow = DateTime(0).tomorrow; // 2024-10-08 00:00:00.000 final DateTime tomorrow = DateTime(0).tomorrow; // 2024-10-08 00:00:00.000
``` ```
#### Leap Years
- `isLeapYear`: returns a `bool` corresponding to whether a given year is a leap
year. This can also be used directly on an `int`.
```dart
print(DateTime(2024).isLeapYear); // true
print(DateTime(2025).isLeapYear); // false
print(2024.isLeapYear); // true
print(2025.isLeapYear); // false
```
### JWT Parsing ### JWT Parsing
These extensions enhance the `String` class with JWT-specific functionalities, These extensions enhance the `String` class with JWT-specific functionalities,
+40 -2
View File
@@ -19,6 +19,11 @@ void main() {
print("Yesterday: $yesterday"); print("Yesterday: $yesterday");
print("Tomorrow: $tomorrow"); print("Tomorrow: $tomorrow");
print(DateTime(2024).isLeapYear); // true
print(DateTime(2025).isLeapYear); // false
print(2024.isLeapYear); // true
print(2025.isLeapYear); // false
// * Strings // * Strings
const String? nullString = null; const String? nullString = null;
const String emptyString = " "; const String emptyString = " ";
@@ -70,12 +75,45 @@ void main() {
// * Dynamic debug printing // * Dynamic debug printing
// Debug print the `Person` object before returning the name // Debug print the `Person` object before returning the name
final String alice = const Person(id: 0, name: "Alice").printValue<Person>().name; final String alice =
const Person(id: 0, name: "Alice").printValue<Person>().name;
print(alice); // Output: 'Alice' print(alice); // Output: 'Alice'
// Debug print the `Person` object with a label before returning the name // Debug print the `Person` object with a label before returning the name
final String bob = const Person(id: 1, name: "Bob").printValue<Person>("Person").name; final String bob =
const Person(id: 1, name: "Bob").printValue<Person>("Person").name;
print(bob); // Output: 'Bob' print(bob); // Output: 'Bob'
// * Fixed-size lists
// Create a FixedSizeList with a capacity of 3 strings.
final recentLogs = FixedSizeList(3);
// Output: Initial recentLogs: []
print("Initial recentLogs: ${recentLogs.items}");
// Add some log messages.
recentLogs.add("Request received at 10:00 AM");
print("recentLogs after first add: ${recentLogs.items}");
// Output: recentLogs after first add: [Request received at 10:00 AM]
recentLogs.add("Processing request...");
print("recentLogs after second add: ${recentLogs.items}");
// Output: recentLogs after second add: [Request received at 10:00 AM, Processing request...]
recentLogs.add("Request completed at 10:05 AM");
print("recentLogs after third add: ${recentLogs.items}");
// Output: recentLogs after third add: [Request received at 10:00 AM, Processing request..., Request completed at 10:05 AM]
// Add one more log message, which will cause the oldest one to be removed.
recentLogs.add("Sending response...");
print("recentLogs after fourth add: ${recentLogs.items}");
// Output: recentLogs after fourth add: [Processing request..., Request completed at 10:05 AM, Sending response...]
// Try to modify the list through the 'items' getter (will throw an error).
try {
// This will cause an UnsupportedError because 'items' returns an unmodifiable list.
recentLogs.items.add("This will fail");
} catch (e) {
print("Error trying to modify items: $e");
}
} }
class Person { class Person {
+1
View File
@@ -1,5 +1,6 @@
library arcane_helper_utils; library arcane_helper_utils;
export "package:arcane_helper_utils/src/classes/fixed_size_list.dart";
export "package:arcane_helper_utils/src/extensions/date_time.dart"; export "package:arcane_helper_utils/src/extensions/date_time.dart";
export "package:arcane_helper_utils/src/extensions/dynamic.dart"; export "package:arcane_helper_utils/src/extensions/dynamic.dart";
export "package:arcane_helper_utils/src/extensions/jwt.dart"; export "package:arcane_helper_utils/src/extensions/jwt.dart";
+29
View File
@@ -0,0 +1,29 @@
/// Represents a `List` with a fixed capacity.
///
/// When a new element is added and the list reaches its maximum capacity,
/// the oldest element in the list is automatically removed to make space.
class FixedSizeList {
final int _capacity;
final List<String> _list;
/// Creates a [FixedSizeList] with the specified [capacity].
///
/// The initial list will be empty.
FixedSizeList(this._capacity) : _list = <String>[];
/// Adds a new [element] to the list.
///
/// If the list is already at its maximum [capacity], the oldest element
/// will be removed before the new [element] is added.
void add(String element) {
_list.add(element);
if (_list.length > _capacity) {
_list.removeAt(0);
}
}
/// Returns an unmodifiable view of the current items in the list.
///
/// This prevents external modification of the internal list.
List<String> get items => List.unmodifiable(_list);
}
+14 -2
View File
@@ -1,5 +1,3 @@
import "package:week_number/iso.dart";
/// An extension on `DateTime` to get the start and end of various time periods. /// An extension on `DateTime` to get the start and end of various time periods.
extension StartAndEndOfPeriod on DateTime { extension StartAndEndOfPeriod on DateTime {
/// Returns a `DateTime` object representing the start of the hour. /// Returns a `DateTime` object representing the start of the hour.
@@ -141,3 +139,17 @@ extension YesterdayAndTomorrow on DateTime {
DateTime get tomorrow => DateTime get tomorrow =>
DateTime.now().add(const Duration(days: 1)).startOfDay; DateTime.now().add(const Duration(days: 1)).startOfDay;
} }
extension IsLeapYear on DateTime {
/// Returns `true` if the year is a leap year, otherwise returns `false`.
bool get isLeapYear => year.isLeapYear;
}
extension IsIntLeapYear on int {
/// Returns `true` if the given value would be considered a leap year,
/// otherwise returns `false`.
bool get isLeapYear =>
!this.isNegative &&
this > 0 &&
((this * 1073750999) & 3221352463) <= 126976;
}
+1 -1
View File
@@ -1,7 +1,7 @@
name: arcane_helper_utils name: arcane_helper_utils
description: Provides a variety of helpful utilities and extensions for Flutter description: Provides a variety of helpful utilities and extensions for Flutter
and Dart. and Dart.
version: 1.4.1 version: 1.4.2
repository: https://github.com/hanskokx/arcane_helper_utils repository: https://github.com/hanskokx/arcane_helper_utils
issue_tracker: https://github.com/hanskokx/arcane_helper_utils/issues issue_tracker: https://github.com/hanskokx/arcane_helper_utils/issues
+75
View File
@@ -0,0 +1,75 @@
import "package:arcane_helper_utils/arcane_helper_utils.dart";
import "package:test/test.dart";
void main() {
group("FixedSizeList", () {
test("initial list is empty", () {
final list = FixedSizeList(5);
expect(list.items, isEmpty);
});
test("adding elements below capacity", () {
final list = FixedSizeList(3);
list.add("one");
expect(list.items, ["one"]);
list.add("two");
expect(list.items, ["one", "two"]);
});
test("adding elements up to capacity", () {
final list = FixedSizeList(2);
list.add("a");
list.add("b");
expect(list.items, ["a", "b"]);
});
test("adding elements beyond capacity removes the oldest", () {
final list = FixedSizeList(2);
list.add("first");
list.add("second");
expect(list.items, ["first", "second"]);
list.add("third");
expect(list.items, ["second", "third"]);
list.add("fourth");
expect(list.items, ["third", "fourth"]);
});
test("capacity of zero results in an empty list that stays empty", () {
final list = FixedSizeList(0);
list.add("anything");
expect(list.items, isEmpty);
list.add("something else");
expect(list.items, isEmpty);
});
test("items getter returns an unmodifiable list", () {
final list = FixedSizeList(2);
list.add("alpha");
list.add("beta");
final items = list.items;
expect(() => items.add("gamma"), throwsUnsupportedError);
// Ensure original list is not modified
expect(list.items, ["alpha", "beta"]);
});
test("adding multiple elements beyond capacity", () {
final list = FixedSizeList(1);
list.add("one");
list.add("two");
list.add("three");
list.add("four");
expect(list.items, ["four"]);
});
test("adding the same element multiple times", () {
final list = FixedSizeList(3);
list.add("same");
list.add("same");
list.add("same");
expect(list.items, ["same", "same", "same"]);
list.add("different");
expect(list.items, ["same", "same", "different"]);
});
});
}
+11
View File
@@ -96,5 +96,16 @@ void main() {
DateTime(now.year, now.month, now.day).add(const Duration(days: 1)); DateTime(now.year, now.month, now.day).add(const Duration(days: 1));
expect(DateTime.now().tomorrow, expected); expect(DateTime.now().tomorrow, expected);
}); });
test("leap year calculations work as expected", () {
expect(DateTime(0).isLeapYear, false);
expect(DateTime(2024).isLeapYear, true);
expect(DateTime(2025).isLeapYear, false);
expect((-1).isLeapYear, false);
expect(0.isLeapYear, false);
expect(2024.isLeapYear, true);
expect(2025.isLeapYear, false);
});
}); });
} }