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

feat(uuid-plus): parse uuid v7 date time #2

Merged
merged 2 commits into from
Mar 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions lib/src/v7.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:typed_data';

import 'package:fixnum/fixnum.dart';
import 'package:uuid_plus/src/data.dart';
import 'package:uuid_plus/src/uuid_value.dart';

import 'parsing.dart';

Expand All @@ -10,6 +11,35 @@ class UuidV7 {

const UuidV7({this.goptions});

/// Extracts the [DateTime] encoded by the [uuid] v7.
///
/// Set [utc] to `true` if the returned [DateTime] should be without
/// a timezone or `false` if the [DateTime] should have local timezone.
static DateTime parseDateTime(
String uuid, {
bool utc = false,
}) {
final unformattedUuid = uuid.replaceAll('-', '');
final bytes = UuidParsing.parse(unformattedUuid, noDashes: true);
final value = UuidValue.fromList(bytes);
if (!value.isV7) {
throw ArgumentError('DateTime supported only for uuid v7');
}

// Extract the 48-bit timestamp from the first 6 bytes.
var timestampMillis = Int64(0);
for (var i = 0; i < 6; i++) {
// Using Int64 due to how JS binary shift works,
// it truncates the number to be uint32 which is not enough in this case.
timestampMillis = (timestampMillis << 8) | bytes[i];
}

return DateTime.fromMillisecondsSinceEpoch(
timestampMillis.toInt(),
isUtc: utc,
);
}

/// Generates a time-based version 7 UUID
///
/// By default it generates a string based on current time in Unix Epoch.
Expand Down
65 changes: 65 additions & 0 deletions test/v7_date_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import 'package:test/test.dart';
import 'package:uuid_plus/uuid_plus.dart';

void main() {
group('UuidV7 date parsing', () {
test('returns correct utc datetime for uuid v7', () {
// Given
final sourceDateTime = DateTime.utc(2025, 2, 3, 15, 34);
final config = V7Options(sourceDateTime.millisecondsSinceEpoch, null);
final uuid = const Uuid().v7(config: config);

// When
final dateTime = UuidV7.parseDateTime(uuid, utc: true);

// Then
expect(dateTime, sourceDateTime);
});

test('returns correct local datetime for uuid v7', () {
// Given
final sourceDateTime = DateTime.utc(2025, 2, 3, 15, 34);
final config = V7Options(sourceDateTime.millisecondsSinceEpoch, null);
final uuid = const Uuid().v7(config: config);

// When
final dateTime = UuidV7.parseDateTime(uuid, utc: false);

// Then
expect(dateTime, sourceDateTime.toLocal());
});

test('throws exception for invalid source', () {
// Given
const uuid = '123oneTwo';

// When
void parse() => UuidV7.parseDateTime(uuid);

// Then
expect(parse, throwsA(isA<FormatException>()));
});

test('throws exception for empty source', () {
// Given
const uuid = '';

// When
void parse() => UuidV7.parseDateTime(uuid);

// Then
expect(parse, throwsA(isA<FormatException>()));
});

test('throws exception for uuid v4', () {
// Given
final uuid = const Uuid().v4();

// When
void parse() => UuidV7.parseDateTime(uuid);

// Then
expect(parse, throwsA(isA<ArgumentError>()));
});
});
}