Skip to content

Commit ba44107

Browse files
authored
Merge pull request #2 from input-output-hk/feat/uuid-date-parsing
feat(uuid-plus): parse uuid v7 date time
2 parents a0cdcc5 + 119e33a commit ba44107

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

lib/src/v7.dart

+30
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'dart:typed_data';
22

33
import 'package:fixnum/fixnum.dart';
44
import 'package:uuid_plus/src/data.dart';
5+
import 'package:uuid_plus/src/uuid_value.dart';
56

67
import 'parsing.dart';
78

@@ -10,6 +11,35 @@ class UuidV7 {
1011

1112
const UuidV7({this.goptions});
1213

14+
/// Extracts the [DateTime] encoded by the [uuid] v7.
15+
///
16+
/// Set [utc] to `true` if the returned [DateTime] should be without
17+
/// a timezone or `false` if the [DateTime] should have local timezone.
18+
static DateTime parseDateTime(
19+
String uuid, {
20+
bool utc = false,
21+
}) {
22+
final unformattedUuid = uuid.replaceAll('-', '');
23+
final bytes = UuidParsing.parse(unformattedUuid, noDashes: true);
24+
final value = UuidValue.fromList(bytes);
25+
if (!value.isV7) {
26+
throw ArgumentError('DateTime supported only for uuid v7');
27+
}
28+
29+
// Extract the 48-bit timestamp from the first 6 bytes.
30+
var timestampMillis = Int64(0);
31+
for (var i = 0; i < 6; i++) {
32+
// Using Int64 due to how JS binary shift works,
33+
// it truncates the number to be uint32 which is not enough in this case.
34+
timestampMillis = (timestampMillis << 8) | bytes[i];
35+
}
36+
37+
return DateTime.fromMillisecondsSinceEpoch(
38+
timestampMillis.toInt(),
39+
isUtc: utc,
40+
);
41+
}
42+
1343
/// Generates a time-based version 7 UUID
1444
///
1545
/// By default it generates a string based on current time in Unix Epoch.

test/v7_date_test.dart

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import 'package:test/test.dart';
2+
import 'package:uuid_plus/uuid_plus.dart';
3+
4+
void main() {
5+
group('UuidV7 date parsing', () {
6+
test('returns correct utc datetime for uuid v7', () {
7+
// Given
8+
final sourceDateTime = DateTime.utc(2025, 2, 3, 15, 34);
9+
final config = V7Options(sourceDateTime.millisecondsSinceEpoch, null);
10+
final uuid = const Uuid().v7(config: config);
11+
12+
// When
13+
final dateTime = UuidV7.parseDateTime(uuid, utc: true);
14+
15+
// Then
16+
expect(dateTime, sourceDateTime);
17+
});
18+
19+
test('returns correct local datetime for uuid v7', () {
20+
// Given
21+
final sourceDateTime = DateTime.utc(2025, 2, 3, 15, 34);
22+
final config = V7Options(sourceDateTime.millisecondsSinceEpoch, null);
23+
final uuid = const Uuid().v7(config: config);
24+
25+
// When
26+
final dateTime = UuidV7.parseDateTime(uuid, utc: false);
27+
28+
// Then
29+
expect(dateTime, sourceDateTime.toLocal());
30+
});
31+
32+
test('throws exception for invalid source', () {
33+
// Given
34+
const uuid = '123oneTwo';
35+
36+
// When
37+
void parse() => UuidV7.parseDateTime(uuid);
38+
39+
// Then
40+
expect(parse, throwsA(isA<FormatException>()));
41+
});
42+
43+
test('throws exception for empty source', () {
44+
// Given
45+
const uuid = '';
46+
47+
// When
48+
void parse() => UuidV7.parseDateTime(uuid);
49+
50+
// Then
51+
expect(parse, throwsA(isA<FormatException>()));
52+
});
53+
54+
test('throws exception for uuid v4', () {
55+
// Given
56+
final uuid = const Uuid().v4();
57+
58+
// When
59+
void parse() => UuidV7.parseDateTime(uuid);
60+
61+
// Then
62+
expect(parse, throwsA(isA<ArgumentError>()));
63+
});
64+
});
65+
}

0 commit comments

Comments
 (0)