File Coverage

lib/Twitter/ID.pm
Criterion Covered Total %
statement 48 48 100.0
branch 28 28 100.0
condition 9 10 90.0
subroutine 9 9 100.0
pod 5 5 100.0
total 99 100 99.0


line stmt bran cond sub pod time code
1 4     4   237851 use v5.26;
  4         21  
2 4     4   17 use warnings;
  4         5  
  4         164  
3              
4             package Twitter::ID;
5             # ABSTRACT: Parse the date from a Twitter Snowflake ID
6             $Twitter::ID::VERSION = '1.00';
7              
8 4     4   18 use Carp qw(croak);
  4         5  
  4         2155  
9              
10              
11             my $TW_EPOCH = 1288834974657;
12             my $WORKER_BITS = 10;
13             my $SEQUENCE_BITS = 12;
14              
15             my $TIMESTAMP_SHIFT = $WORKER_BITS + $SEQUENCE_BITS;
16             my $WORKER_MASK = (-1 ^ (-1 << $WORKER_BITS)) << $SEQUENCE_BITS;
17             my $SEQUENCE_MASK = -1 ^ (-1 << $SEQUENCE_BITS);
18             my $MAX_WORKER = 1 << $WORKER_BITS;
19             my $MAX_SEQUENCE = 1 << $SEQUENCE_BITS;
20              
21             my $LAST_PRE_SNOWFLAKE_ID = 29700859247;
22              
23              
24             sub new {
25 18     18 1 7772 my ($class, $id) = @_;
26            
27 18 100       42 if (ref $id eq 'HASH') {
28 8         13 my $self = bless \(my $o = 0), $class;
29 8         23 $self->_set( $id->{timestamp}, $id->{worker}, $id->{sequence} );
30 2         6 return $self;
31             }
32            
33 10 100       19 if (! $id) {
34 2         11 return bless \(my $o = 0), $class;
35             }
36            
37 8         29 bless \$id, $class;
38             }
39              
40              
41             sub _set {
42 11     11   20 my ($self, $timestamp, $worker, $sequence) = @_;
43 11   66     21 $timestamp //= $TW_EPOCH;
44 11   100     25 $worker //= 0;
45 11   100     23 $sequence //= 0;
46            
47 11 100       29 croak "Twitter timestamps before $TW_EPOCH unsupported" if $timestamp < $TW_EPOCH;
48 9 100 100     37 croak "Twitter ID components must be positive" if $worker < 0 || $sequence < 0;
49 7 100       30 croak "Worker ID $worker too large (max $MAX_WORKER)" if $worker >= $MAX_WORKER;
50 6 100       28 croak "Sequence number $sequence too large (max $MAX_SEQUENCE)" if $sequence >= $MAX_SEQUENCE;
51            
52 5         10 $$self = ($timestamp - $TW_EPOCH) << $TIMESTAMP_SHIFT
53             | $worker << $SEQUENCE_BITS
54             | $sequence;
55             }
56              
57              
58             sub timestamp {
59 11     11 1 2118 my ($self, $timestamp) = @_;
60            
61 11 100       20 if (defined $timestamp) {
62 1         3 $self->_set( $timestamp, $self->worker, $self->sequence );
63 1         3 return;
64             }
65            
66 10 100       29 return if $$self <= $LAST_PRE_SNOWFLAKE_ID;
67            
68 6         16 return ($$self >> $TIMESTAMP_SHIFT) + $TW_EPOCH;
69             }
70              
71              
72             sub worker {
73 8     8 1 267 my ($self, $worker) = @_;
74            
75 8 100       22 if (defined $worker) {
76 1         2 $self->_set( $self->timestamp, $worker, $self->sequence );
77 1         3 return;
78             }
79            
80 7 100       26 return if $$self <= $LAST_PRE_SNOWFLAKE_ID;
81            
82 3         9 return ($$self & $WORKER_MASK) >> $SEQUENCE_BITS;
83             }
84              
85              
86             sub sequence {
87 8     8 1 263 my ($self, $sequence) = @_;
88            
89 8 100       18 if (defined $sequence) {
90 1         2 $self->_set( $self->timestamp, $self->worker, $sequence );
91 1         3 return;
92             }
93            
94 7 100       19 return if $$self <= $LAST_PRE_SNOWFLAKE_ID;
95            
96 3         13 return $$self & $SEQUENCE_MASK;
97             }
98              
99              
100             sub epoch {
101 4     4 1 12 my ($self, $epoch) = @_;
102            
103 4 100       13 croak "epoch() is read-only" if defined $epoch;
104            
105 3         4 my $timestamp = $self->timestamp;
106 3 100       6 return unless defined $timestamp;
107 2         8 return $timestamp / 1000;
108             }
109              
110              
111             1;
112              
113             __END__