| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | ##################################################################### | 
| 2 |  |  |  |  |  |  | ## ABSTRACT: Convert various epoch times to Time::Moment times. | 
| 3 |  |  |  |  |  |  | ##################################################################### | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | package Time::Moment::Epoch; | 
| 7 |  |  |  |  |  |  | our $VERSION = '1.003002'; # VERSION | 
| 8 |  |  |  |  |  |  |  | 
| 9 | 3 |  |  | 3 |  | 17577 | use v5.10; | 
|  | 3 |  |  |  |  | 13 |  | 
| 10 | 3 |  |  | 3 |  | 23 | use strict; | 
|  | 3 |  |  |  |  | 7 |  | 
|  | 3 |  |  |  |  | 74 |  | 
| 11 | 3 |  |  | 3 |  | 18 | use warnings; | 
|  | 3 |  |  |  |  | 11 |  | 
|  | 3 |  |  |  |  | 109 |  | 
| 12 | 3 |  |  | 3 |  | 1697 | use parent qw(Exporter); | 
|  | 3 |  |  |  |  | 1088 |  | 
|  | 3 |  |  |  |  | 17 |  | 
| 13 | 3 |  |  | 3 |  | 1792 | use Hash::MostUtils qw(hashmap); | 
|  | 3 |  |  |  |  | 17098 |  | 
|  | 3 |  |  |  |  | 226 |  | 
| 14 | 3 |  |  | 3 |  | 3636 | use Math::BigInt try => 'GMP'; | 
|  | 3 |  |  |  |  | 84484 |  | 
|  | 3 |  |  |  |  | 18 |  | 
| 15 | 3 |  |  | 3 |  | 87106 | use Math::BigFloat; | 
|  | 3 |  |  |  |  | 93047 |  | 
|  | 3 |  |  |  |  | 24 |  | 
| 16 | 3 |  |  | 3 |  | 2590 | use Scalar::Util qw(looks_like_number); | 
|  | 3 |  |  |  |  | 9 |  | 
|  | 3 |  |  |  |  | 212 |  | 
| 17 | 3 |  |  | 3 |  | 1924 | use Time::Moment; | 
|  | 3 |  |  |  |  | 4424 |  | 
|  | 3 |  |  |  |  | 6482 |  | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | my $SECONDS_PER_DAY = 24 * 60 * 60; | 
| 20 |  |  |  |  |  |  | my $NANOSECONDS_PER_DAY = $SECONDS_PER_DAY * 1e9; | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | # Time::Moment can represent all epoch integers from -62,135,596,800 | 
| 23 |  |  |  |  |  |  | # to 253,402,300,799; this range suffices to measure times to | 
| 24 |  |  |  |  |  |  | # nanosecond precision for any instant that is within | 
| 25 |  |  |  |  |  |  | # 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z. | 
| 26 |  |  |  |  |  |  | my $MAX_SECONDS = 253_402_300_799; | 
| 27 |  |  |  |  |  |  | my $MIN_SECONDS = -62_135_596_800; | 
| 28 |  |  |  |  |  |  |  | 
| 29 |  |  |  |  |  |  | # Here are a few more constants from moment.h that we need. | 
| 30 |  |  |  |  |  |  | my $MAX_UNIT_DAYS = 3652425; | 
| 31 |  |  |  |  |  |  | my $MIN_UNIT_DAYS = -3652425; | 
| 32 |  |  |  |  |  |  | my $MAX_UNIT_MONTHS = 120000; | 
| 33 |  |  |  |  |  |  | my $MIN_UNIT_MONTHS = -120000; | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | our @conversions = qw( | 
| 36 |  |  |  |  |  |  | apfs | 
| 37 |  |  |  |  |  |  | chrome | 
| 38 |  |  |  |  |  |  | cocoa | 
| 39 |  |  |  |  |  |  | dos | 
| 40 |  |  |  |  |  |  | google_calendar | 
| 41 |  |  |  |  |  |  | icq | 
| 42 |  |  |  |  |  |  | java | 
| 43 |  |  |  |  |  |  | mozilla | 
| 44 |  |  |  |  |  |  | ole | 
| 45 |  |  |  |  |  |  | symbian | 
| 46 |  |  |  |  |  |  | unix | 
| 47 |  |  |  |  |  |  | uuid_v1 | 
| 48 |  |  |  |  |  |  | windows_date | 
| 49 |  |  |  |  |  |  | windows_file | 
| 50 |  |  |  |  |  |  | windows_system | 
| 51 |  |  |  |  |  |  | ); | 
| 52 |  |  |  |  |  |  | our @to_conversions = map {"to_$_"} @conversions; | 
| 53 |  |  |  |  |  |  | our @EXPORT_OK = (@conversions,	@to_conversions, | 
| 54 |  |  |  |  |  |  | qw(@conversions @to_conversions)); | 
| 55 |  |  |  |  |  |  | our %EXPORT_TAGS = (all => [@EXPORT_OK]); | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | # APFS time is in nanoseconds since the Unix epoch. | 
| 59 |  |  |  |  |  |  | sub apfs { | 
| 60 | 1 |  |  | 1 | 1 | 197 | my $num = shift; | 
| 61 | 1 |  |  |  |  | 4 | _epoch2time($num, 1_000_000_000); | 
| 62 |  |  |  |  |  |  | } | 
| 63 |  |  |  |  |  |  | sub to_apfs { | 
| 64 | 1 |  |  | 1 | 0 | 1771 | my $tm = shift; | 
| 65 | 1 |  |  |  |  | 6 | _time2epoch($tm, 1_000_000_000); | 
| 66 |  |  |  |  |  |  | } | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | # Chrome time is the number of microseconds since 1601-01-01, which is | 
| 71 |  |  |  |  |  |  | # 11,644,473,600 seconds before the Unix epoch. | 
| 72 |  |  |  |  |  |  | # | 
| 73 |  |  |  |  |  |  | sub chrome { | 
| 74 | 3 |  |  | 3 | 1 | 4678 | my $num = shift; | 
| 75 | 3 |  |  |  |  | 14 | _epoch2time($num, 1_000_000, -11_644_473_600); | 
| 76 |  |  |  |  |  |  | } | 
| 77 |  |  |  |  |  |  | sub to_chrome { | 
| 78 | 1 |  |  | 1 | 0 | 1013 | my $tm = shift; | 
| 79 | 1 |  |  |  |  | 9 | _time2epoch($tm, 1_000_000, -11_644_473_600); | 
| 80 |  |  |  |  |  |  | } | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | # Cocoa time is the number of seconds since 2001-01-01, which | 
| 84 |  |  |  |  |  |  | # is 978,307,200 seconds after the Unix epoch. | 
| 85 |  |  |  |  |  |  | sub cocoa { | 
| 86 | 2 |  |  | 2 | 1 | 4375 | my $num = shift; | 
| 87 | 2 |  |  |  |  | 9 | _epoch2time($num, 1, 978_307_200); | 
| 88 |  |  |  |  |  |  | } | 
| 89 |  |  |  |  |  |  | sub to_cocoa { | 
| 90 | 1 |  |  | 1 | 0 | 1001 | my $tm = shift; | 
| 91 | 1 |  |  |  |  | 6 | _time2epoch($tm, 1, 978_307_200); | 
| 92 |  |  |  |  |  |  | } | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | # DOS time uses bit fields to store dates between 1980-01-01 and | 
| 96 |  |  |  |  |  |  | # 2107-12-31 (it fails outside that range). | 
| 97 |  |  |  |  |  |  | sub dos { | 
| 98 | 1 |  |  | 1 | 1 | 2821 | my $num = shift; | 
| 99 |  |  |  |  |  |  |  | 
| 100 | 1 |  |  |  |  | 5 | my $year   = ($num >> 25) & 0b1111111; | 
| 101 |  |  |  |  |  |  |  | 
| 102 | 1 |  |  |  |  | 5 | my $month  = ($num >> 21) &    0b1111; | 
| 103 | 1 | 50 | 33 |  |  | 11 | return if $month < 1 or $month > 12; | 
| 104 |  |  |  |  |  |  |  | 
| 105 | 1 |  |  |  |  | 3 | my $day    = ($num >> 16) &   0b11111; | 
| 106 | 1 | 50 | 33 |  |  | 9 | return if $day < 1 or $day > 31; | 
| 107 |  |  |  |  |  |  |  | 
| 108 | 1 |  |  |  |  | 4 | my $hour   = ($num >> 11) &   0b11111; | 
| 109 | 1 | 50 | 33 |  |  | 9 | return if $hour < 0 or $hour > 23; | 
| 110 |  |  |  |  |  |  |  | 
| 111 | 1 |  |  |  |  | 4 | my $minute = ($num >>  5) &  0b111111; | 
| 112 | 1 | 50 | 33 |  |  | 10 | return if $minute < 0 or $minute > 60; | 
| 113 |  |  |  |  |  |  |  | 
| 114 | 1 |  |  |  |  | 4 | my $second = ($num      ) &   0b11111; | 
| 115 | 1 | 50 | 33 |  |  | 10 | return if $second < 0 or $second > 60; | 
| 116 |  |  |  |  |  |  |  | 
| 117 | 1 |  |  |  |  | 15 | Time::Moment->new( | 
| 118 |  |  |  |  |  |  | year   => 1980 + $year, | 
| 119 |  |  |  |  |  |  | month  => $month, | 
| 120 |  |  |  |  |  |  | day    => $day, | 
| 121 |  |  |  |  |  |  | hour   => $hour, | 
| 122 |  |  |  |  |  |  | minute => $minute, | 
| 123 |  |  |  |  |  |  | second => 2 * $second, | 
| 124 |  |  |  |  |  |  | ); | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | } | 
| 127 |  |  |  |  |  |  | sub to_dos { | 
| 128 | 1 |  |  | 1 | 0 | 932 | my $tm = shift; | 
| 129 |  |  |  |  |  |  |  | 
| 130 | 1 | 50 |  |  |  | 11 | if (ref $tm ne 'Time::Moment') { | 
| 131 | 1 |  |  |  |  | 10 | $tm = Time::Moment->from_string($tm); | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  |  | 
| 134 | 1 |  |  |  |  | 23 | ($tm->year - 1980  << 25) + | 
| 135 |  |  |  |  |  |  | ($tm->month        << 21) + | 
| 136 |  |  |  |  |  |  | ($tm->day_of_month << 16) + | 
| 137 |  |  |  |  |  |  | ($tm->hour         << 11) + | 
| 138 |  |  |  |  |  |  | ($tm->minute       <<  5) + | 
| 139 |  |  |  |  |  |  | ($tm->second / 2); | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | } | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | # Google Calendar time seems to count 32-day months from the day | 
| 145 |  |  |  |  |  |  | # before the Unix epoch. @noppers worked out how to do this. | 
| 146 |  |  |  |  |  |  | sub google_calendar { | 
| 147 | 1 |  |  | 1 | 1 | 863 | my $n = shift; | 
| 148 |  |  |  |  |  |  |  | 
| 149 | 1 | 50 |  |  |  | 8 | return unless looks_like_number $n; | 
| 150 |  |  |  |  |  |  |  | 
| 151 | 1 |  |  |  |  | 7 | my $b = Math::BigInt->new($n); | 
| 152 | 1 |  |  |  |  | 165 | my($total_days, $seconds) = $b->bdiv($SECONDS_PER_DAY); | 
| 153 | 1 |  |  |  |  | 438 | my($months, $days) = $total_days->bdiv(32); | 
| 154 |  |  |  |  |  |  |  | 
| 155 | 1 | 50 | 33 |  |  | 351 | return if $months < $MIN_UNIT_MONTHS | 
| 156 |  |  |  |  |  |  | or $months > $MAX_UNIT_MONTHS; | 
| 157 |  |  |  |  |  |  |  | 
| 158 | 1 |  |  |  |  | 392 | Time::Moment | 
| 159 |  |  |  |  |  |  | ->from_epoch(-$SECONDS_PER_DAY) | 
| 160 |  |  |  |  |  |  | ->plus_days($days) | 
| 161 |  |  |  |  |  |  | ->plus_months($months) | 
| 162 |  |  |  |  |  |  | ->plus_seconds($seconds); | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  | sub to_google_calendar { | 
| 165 | 1 |  |  | 1 | 0 | 1030 | my $tm = shift; | 
| 166 |  |  |  |  |  |  |  | 
| 167 | 1 | 50 |  |  |  | 7 | if (ref $tm ne 'Time::Moment') { | 
| 168 | 1 |  |  |  |  | 9 | $tm = Time::Moment->from_string($tm); | 
| 169 |  |  |  |  |  |  | } | 
| 170 |  |  |  |  |  |  |  | 
| 171 | 1 |  |  |  |  | 17 | ((((($tm->year - 1970 )*12 | 
| 172 |  |  |  |  |  |  | + ($tm->month -   1))*32 | 
| 173 |  |  |  |  |  |  | +  $tm->day_of_month)*24 | 
| 174 |  |  |  |  |  |  | +  $tm->hour        )*60 | 
| 175 |  |  |  |  |  |  | +  $tm->minute      )*60 | 
| 176 |  |  |  |  |  |  | +  $tm->second; | 
| 177 |  |  |  |  |  |  | } | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  | #  ICQ time is the number of days since 1899-12-30, which is | 
| 181 |  |  |  |  |  |  | #  2,209,161,600 seconds before the Unix epoch. Days can have a | 
| 182 |  |  |  |  |  |  | #  fractional part. | 
| 183 |  |  |  |  |  |  | sub icq { | 
| 184 | 6 |  | 50 | 6 | 1 | 3628 | my $days = shift // return; | 
| 185 |  |  |  |  |  |  |  | 
| 186 | 6 | 50 |  |  |  | 29 | return unless looks_like_number $days; | 
| 187 |  |  |  |  |  |  |  | 
| 188 | 6 |  |  |  |  | 34 | my $t = Time::Moment->from_epoch(-2_209_161_600); | 
| 189 |  |  |  |  |  |  |  | 
| 190 | 6 |  |  |  |  | 19 | my $intdays = int($days); | 
| 191 |  |  |  |  |  |  |  | 
| 192 | 6 | 50 | 33 |  |  | 35 | return if $intdays < $MIN_UNIT_DAYS | 
| 193 |  |  |  |  |  |  | or $intdays > $MAX_UNIT_DAYS; | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | # Want the fractional part of the day in nanoseconds. | 
| 196 | 6 |  |  |  |  | 19 | my $fracday = int(($days - $intdays) * $NANOSECONDS_PER_DAY); | 
| 197 |  |  |  |  |  |  |  | 
| 198 | 6 |  |  |  |  | 48 | return $t->plus_days($intdays)->plus_nanoseconds($fracday); | 
| 199 |  |  |  |  |  |  | } | 
| 200 |  |  |  |  |  |  | sub to_icq { | 
| 201 | 3 |  |  | 3 | 0 | 1493 | my $tm = shift; | 
| 202 |  |  |  |  |  |  |  | 
| 203 | 3 | 50 |  |  |  | 29 | if (ref $tm ne 'Time::Moment') { | 
| 204 | 3 |  |  |  |  | 23 | $tm = Time::Moment->from_string($tm); | 
| 205 |  |  |  |  |  |  | } | 
| 206 |  |  |  |  |  |  |  | 
| 207 | 3 |  |  |  |  | 15 | my $t2 = Time::Moment->from_epoch(-2_209_161_600); | 
| 208 |  |  |  |  |  |  |  | 
| 209 | 3 |  |  |  |  | 19 | $t2->delta_nanoseconds($tm) / $NANOSECONDS_PER_DAY; | 
| 210 |  |  |  |  |  |  | } | 
| 211 |  |  |  |  |  |  |  | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | # Java time is in milliseconds since the Unix epoch. | 
| 214 |  |  |  |  |  |  | sub java { | 
| 215 | 2 |  |  | 2 | 1 | 1612 | my $num = shift; | 
| 216 | 2 |  |  |  |  | 9 | _epoch2time($num, 1000); | 
| 217 |  |  |  |  |  |  | } | 
| 218 |  |  |  |  |  |  | sub to_java { | 
| 219 | 1 |  |  | 1 | 0 | 857 | my $tm = shift; | 
| 220 | 1 |  |  |  |  | 11 | _time2epoch($tm, 1000); | 
| 221 |  |  |  |  |  |  | } | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | # Mozilla time is in microseconds since the Unix epoch. | 
| 225 |  |  |  |  |  |  | sub mozilla { | 
| 226 | 1 |  |  | 1 | 1 | 2247 | my $num = shift; | 
| 227 | 1 |  |  |  |  | 6 | _epoch2time($num, 1_000_000); | 
| 228 |  |  |  |  |  |  | } | 
| 229 |  |  |  |  |  |  | sub to_mozilla { | 
| 230 | 1 |  |  | 1 | 0 | 876 | my $tm = shift; | 
| 231 | 1 |  |  |  |  | 5 | _time2epoch($tm, 1_000_000); | 
| 232 |  |  |  |  |  |  | } | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  |  | 
| 235 |  |  |  |  |  |  | #  OLE time is the number of days since 1899-12-30, which is | 
| 236 |  |  |  |  |  |  | #  2,209,161,600 seconds before the Unix epoch. | 
| 237 |  |  |  |  |  |  | sub ole { | 
| 238 | 2 |  | 50 | 2 | 1 | 2843 | my $bytes = shift // return; | 
| 239 |  |  |  |  |  |  |  | 
| 240 | 2 | 50 |  |  |  | 10 | my $d_days = unpack('d', $bytes) or return; | 
| 241 |  |  |  |  |  |  |  | 
| 242 | 2 | 50 |  |  |  | 18 | return if $d_days eq '-nan'; | 
| 243 |  |  |  |  |  |  |  | 
| 244 | 2 |  |  |  |  | 7 | return icq $d_days; | 
| 245 |  |  |  |  |  |  | } | 
| 246 |  |  |  |  |  |  | sub to_ole { | 
| 247 | 1 |  | 50 | 1 | 0 | 928 | my $t = shift // return; | 
| 248 |  |  |  |  |  |  |  | 
| 249 | 1 |  |  |  |  | 4 | my $icq = to_icq($t); | 
| 250 |  |  |  |  |  |  |  | 
| 251 | 1 | 50 |  |  |  | 7 | my $epoch = pack('d', $icq) or return; | 
| 252 |  |  |  |  |  |  |  | 
| 253 | 1 |  |  |  |  | 4 | return $epoch; | 
| 254 |  |  |  |  |  |  | } | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  |  | 
| 257 |  |  |  |  |  |  | # Symbian time is the number of microseconds since the year 0, which | 
| 258 |  |  |  |  |  |  | # is 62,167,219,200 seconds before the Unix epoch. | 
| 259 |  |  |  |  |  |  | sub symbian { | 
| 260 | 1 |  |  | 1 | 1 | 727 | my $num = shift; | 
| 261 | 1 |  |  |  |  | 5 | _epoch2time($num, 1_000_000, -62_167_219_200); | 
| 262 |  |  |  |  |  |  | } | 
| 263 |  |  |  |  |  |  | sub to_symbian { | 
| 264 | 1 |  |  | 1 | 0 | 893 | my $tm = shift; | 
| 265 | 1 |  |  |  |  | 4 | _time2epoch($tm, 1_000_000, -62_167_219_200); | 
| 266 |  |  |  |  |  |  | } | 
| 267 |  |  |  |  |  |  |  | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | # Unix time is the number of seconds since 1970-01-01. | 
| 270 |  |  |  |  |  |  | sub unix { | 
| 271 | 2 |  |  | 2 | 1 | 3526 | my $num = shift; | 
| 272 | 2 |  |  |  |  | 7 | _epoch2time($num); | 
| 273 |  |  |  |  |  |  | } | 
| 274 |  |  |  |  |  |  | sub to_unix { | 
| 275 | 2 |  |  | 2 | 0 | 1951 | my $tm = shift; | 
| 276 | 2 |  |  |  |  | 5 | _time2epoch($tm); | 
| 277 |  |  |  |  |  |  | } | 
| 278 |  |  |  |  |  |  |  | 
| 279 |  |  |  |  |  |  |  | 
| 280 |  |  |  |  |  |  | # UUID version 1 time (RFC 4122) is the number of hectonanoseconds | 
| 281 |  |  |  |  |  |  | # (100 ns) since 1582-10-15, which is 12,219,292,800 seconds before | 
| 282 |  |  |  |  |  |  | # the Unix epoch. | 
| 283 |  |  |  |  |  |  | sub uuid_v1 { | 
| 284 | 2 |  |  | 2 | 1 | 1912 | my $num = shift; | 
| 285 | 2 |  |  |  |  | 6 | _epoch2time($num, 10_000_000, -12_219_292_800); | 
| 286 |  |  |  |  |  |  | } | 
| 287 |  |  |  |  |  |  | sub to_uuid_v1 { | 
| 288 | 2 |  |  | 2 | 0 | 2307 | my $tm = shift; | 
| 289 | 2 |  |  |  |  | 7 | _time2epoch($tm, 10_000_000, -12_219_292_800); | 
| 290 |  |  |  |  |  |  | } | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | # Windows date time (e.g., .NET) is the number of hectonanoseconds | 
| 294 |  |  |  |  |  |  | # (100 ns) since 0001-01-01, which is 62,135,596,800 seconds before | 
| 295 |  |  |  |  |  |  | # the Unix epoch. | 
| 296 |  |  |  |  |  |  | sub windows_date { | 
| 297 | 2 |  |  | 2 | 1 | 2329 | my $num = shift; | 
| 298 | 2 |  |  |  |  | 6 | _epoch2time($num, 10_000_000, -62_135_596_800); | 
| 299 |  |  |  |  |  |  | } | 
| 300 |  |  |  |  |  |  | sub to_windows_date { | 
| 301 | 1 |  |  | 1 | 0 | 575 | my $tm = shift; | 
| 302 | 1 |  |  |  |  | 4 | _time2epoch($tm, 10_000_000, -62_135_596_800); | 
| 303 |  |  |  |  |  |  | } | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | # Windows file time (e.g., NTFS) is the number of hectonanoseconds | 
| 307 |  |  |  |  |  |  | # (100 ns) since 1601-01-01, which is 11,644,473,600 seconds before | 
| 308 |  |  |  |  |  |  | # the Unix epoch. | 
| 309 |  |  |  |  |  |  | sub windows_file { | 
| 310 | 2 |  |  | 2 | 1 | 2381 | my $num = shift; | 
| 311 | 2 |  |  |  |  | 7 | _epoch2time($num, 10_000_000, -11_644_473_600); | 
| 312 |  |  |  |  |  |  | } | 
| 313 |  |  |  |  |  |  | sub to_windows_file { | 
| 314 | 1 |  |  | 1 | 0 | 654 | my $tm = shift; | 
| 315 | 1 |  |  |  |  | 4 | _time2epoch($tm, 10_000_000, -11_644_473_600); | 
| 316 |  |  |  |  |  |  | } | 
| 317 |  |  |  |  |  |  |  | 
| 318 |  |  |  |  |  |  |  | 
| 319 |  |  |  |  |  |  | sub windows_system { | 
| 320 | 1 |  |  | 1 | 1 | 1959 | my $num = shift; | 
| 321 |  |  |  |  |  |  |  | 
| 322 | 1 | 50 |  |  |  | 6 | if ($num =~ /^[0-9a-fA-F]{32}$/) { | 
| 323 | 1 |  |  |  |  | 4 | $num = "0x$num"; | 
| 324 |  |  |  |  |  |  | } | 
| 325 |  |  |  |  |  |  |  | 
| 326 | 1 |  |  |  |  | 8 | my $bigint = Math::BigInt->new($num); | 
| 327 | 1 | 50 |  |  |  | 462 | return if $bigint eq 'NaN'; | 
| 328 |  |  |  |  |  |  |  | 
| 329 | 1 |  |  |  |  | 37 | my $hex = substr $bigint->as_hex, 2; | 
| 330 |  |  |  |  |  |  |  | 
| 331 | 1 | 50 |  |  |  | 377 | return if length $hex > 32; | 
| 332 | 1 | 50 |  |  |  | 4 | return if length $hex < 0; | 
| 333 | 1 |  |  |  |  | 3 | $hex = "0$hex" while length $hex < 32; | 
| 334 |  |  |  |  |  |  |  | 
| 335 | 1 |  |  |  |  | 9 | my @bytes = ($hex =~ /../g); | 
| 336 | 1 |  |  |  |  | 5 | my @keys = qw(year month day_of_week day hour minute second milliseconds); | 
| 337 | 1 |  |  | 8 |  | 8 | my @values = hashmap {hex "$b$a"} @bytes; | 
|  | 8 |  |  |  |  | 300 |  | 
| 338 |  |  |  |  |  |  |  | 
| 339 | 1 |  |  |  |  | 17 | my %wst; | 
| 340 | 1 |  |  |  |  | 6 | @wst{@keys} = @values; | 
| 341 |  |  |  |  |  |  |  | 
| 342 |  |  |  |  |  |  | return unless | 
| 343 |  |  |  |  |  |  | $wst{year}         >= 1601 and $wst{year}         <= 30827 and | 
| 344 |  |  |  |  |  |  | $wst{month}        >=    1 and $wst{month}        <=    12 and | 
| 345 |  |  |  |  |  |  | $wst{day_of_week}  >=    0 and $wst{day_of_week}  <=     6 and | 
| 346 |  |  |  |  |  |  | $wst{day}          >=    1 and $wst{day}          <=    31 and | 
| 347 |  |  |  |  |  |  | $wst{hour}         >=    0 and $wst{hour}         <=    23 and | 
| 348 |  |  |  |  |  |  | $wst{minute}       >=    0 and $wst{minute}       <=    59 and | 
| 349 |  |  |  |  |  |  | $wst{second}       >=    0 and $wst{second}       <=    59 and | 
| 350 | 1 | 50 | 33 |  |  | 53 | $wst{milliseconds} >=    0 and $wst{milliseconds} <=   999; | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 351 |  |  |  |  |  |  |  | 
| 352 |  |  |  |  |  |  | return Time::Moment->new( | 
| 353 |  |  |  |  |  |  | year	   => $wst{year}, | 
| 354 |  |  |  |  |  |  | month	   => $wst{month}, | 
| 355 |  |  |  |  |  |  | day		   => $wst{day}, | 
| 356 |  |  |  |  |  |  | hour	   => $wst{hour}, | 
| 357 |  |  |  |  |  |  | minute	   => $wst{minute}, | 
| 358 |  |  |  |  |  |  | second	   => $wst{second}, | 
| 359 | 1 |  |  |  |  | 16 | nanosecond => $wst{milliseconds} * 1e6); | 
| 360 |  |  |  |  |  |  | } | 
| 361 |  |  |  |  |  |  |  | 
| 362 |  |  |  |  |  |  | sub to_windows_system { | 
| 363 | 1 |  |  | 1 | 0 | 593 | my $tm = shift; | 
| 364 | 1 |  |  |  |  | 8 | $tm = Time::Moment->from_string($tm); | 
| 365 |  |  |  |  |  |  |  | 
| 366 |  |  |  |  |  |  | return unless | 
| 367 | 1 | 50 | 33 |  |  | 56 | $tm->year         >= 1601 and $tm->year         <= 30827 and | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 368 |  |  |  |  |  |  | $tm->month        >=    1 and $tm->month        <=    12 and | 
| 369 |  |  |  |  |  |  | $tm->day_of_week  >=    1 and $tm->day_of_week  <=     7 and | 
| 370 |  |  |  |  |  |  | $tm->day_of_month >=    1 and $tm->day_of_month <=    31 and | 
| 371 |  |  |  |  |  |  | $tm->hour         >=    0 and $tm->hour         <=    23 and | 
| 372 |  |  |  |  |  |  | $tm->minute       >=    0 and $tm->minute       <=    59 and | 
| 373 |  |  |  |  |  |  | $tm->second       >=    0 and $tm->second       <=    59 and | 
| 374 |  |  |  |  |  |  | $tm->millisecond  >=    0 and $tm->millisecond  <=   999; | 
| 375 |  |  |  |  |  |  |  | 
| 376 | 1 |  |  |  |  | 14 | my $hex = sprintf "%04x%04x%04x%04x%04x%04x%04x%04x", | 
| 377 |  |  |  |  |  |  | $tm->year, | 
| 378 |  |  |  |  |  |  | $tm->month, | 
| 379 |  |  |  |  |  |  | $tm->day_of_week % 7, | 
| 380 |  |  |  |  |  |  | $tm->day_of_month, | 
| 381 |  |  |  |  |  |  | $tm->hour, | 
| 382 |  |  |  |  |  |  | $tm->minute, | 
| 383 |  |  |  |  |  |  | $tm->second, | 
| 384 |  |  |  |  |  |  | $tm->millisecond; | 
| 385 |  |  |  |  |  |  |  | 
| 386 |  |  |  |  |  |  | # Change endian-ness. | 
| 387 | 1 |  |  | 8 |  | 13 | join '', hashmap {"$b$a"} ($hex =~ /../g); | 
|  | 8 |  |  |  |  | 299 |  | 
| 388 |  |  |  |  |  |  | } | 
| 389 |  |  |  |  |  |  |  | 
| 390 |  |  |  |  |  |  | sub _epoch2time { | 
| 391 | 18 |  | 50 | 18 |  | 63 | my $num = shift // return; | 
| 392 | 18 |  | 100 |  |  | 56 | my $q = shift // 1; | 
| 393 | 18 |  | 100 |  |  | 61 | my $s = shift // 0; | 
| 394 |  |  |  |  |  |  |  | 
| 395 | 18 | 100 |  |  |  | 110 | return unless looks_like_number $num; | 
| 396 |  |  |  |  |  |  |  | 
| 397 | 17 |  |  |  |  | 77 | my($z, $m) = Math::BigInt->new($num)->bdiv($q); | 
| 398 | 17 |  |  |  |  | 6775 | my $seconds = $z + $s; | 
| 399 |  |  |  |  |  |  |  | 
| 400 | 17 | 50 | 33 |  |  | 4213 | return if $seconds < $MIN_SECONDS or $seconds > $MAX_SECONDS; | 
| 401 |  |  |  |  |  |  |  | 
| 402 | 17 |  |  |  |  | 4964 | my $nanoseconds = ($m * 1e9)->bdiv($q); | 
| 403 |  |  |  |  |  |  |  | 
| 404 | 17 |  |  |  |  | 7547 | Time::Moment->from_epoch($seconds, $nanoseconds); | 
| 405 |  |  |  |  |  |  | } | 
| 406 |  |  |  |  |  |  |  | 
| 407 |  |  |  |  |  |  | sub _time2epoch { | 
| 408 | 12 |  | 50 | 12 |  | 45 | my $t = shift // return; | 
| 409 | 12 |  | 100 |  |  | 39 | my $m = shift // 1; | 
| 410 | 12 |  | 100 |  |  | 42 | my $s = shift // 0; | 
| 411 |  |  |  |  |  |  |  | 
| 412 | 12 | 50 |  |  |  | 55 | if (ref $t ne 'Time::Moment') { | 
| 413 | 12 |  |  |  |  | 97 | $t = Time::Moment->from_string($t); | 
| 414 |  |  |  |  |  |  | } | 
| 415 |  |  |  |  |  |  |  | 
| 416 | 12 |  |  |  |  | 83 | my $bf = Math::BigFloat->new($t->nanosecond)->bdiv(1e9); | 
| 417 | 12 |  |  |  |  | 8948 | int $m*($t->epoch + $bf - $s); | 
| 418 |  |  |  |  |  |  | } | 
| 419 |  |  |  |  |  |  |  | 
| 420 |  |  |  |  |  |  | 1; | 
| 421 |  |  |  |  |  |  |  | 
| 422 |  |  |  |  |  |  | __END__ |