24 */ 25 26 package java.nio.file.attribute; 27 28 import java.util.Calendar; 29 import java.util.GregorianCalendar; 30 import java.util.Date; 31 import java.util.Formatter; 32 import java.util.Locale; 33 import java.util.TimeZone; 34 import java.util.concurrent.TimeUnit; 35 36 /** 37 * Represents the value of a file's time stamp attribute. For example, it may 38 * represent the time that the file was last modified, accessed, or created. 39 * 40 * <p> Instances of this class are immutable. 41 * 42 * @since 1.7 43 * @see BasicFileAttributes 44 * @see Attributes#setLastModifiedTime 45 */ 46 47 public final class FileTime implements Comparable<FileTime> { 48 private final long value; 49 private final TimeUnit unit; 50 private String valueAsString; // created lazily 51 52 private FileTime(long value, TimeUnit unit) { 53 if (unit == null) 54 throw new NullPointerException(); 55 this.value = value; 56 this.unit = unit; 57 } 58 59 /** 60 * Returns a {@code FileTime} representing a value at the given unit of 61 * granularity. 62 * 63 * @param value 64 * the value since the epoch (1970-01-01T00:00:00Z); can be 65 * negative 66 * @param unit 67 * the unit of granularity to interpret the value 68 * 69 * @return a {@code FileTime} representing the given value 70 */ 71 public static FileTime from(long value, TimeUnit unit) { 126 * the object to compare with 127 * 128 * @return {@code true} if, and only if, the given object is a {@code 129 * FileTime} that represents the same time 130 */ 131 @Override 132 public boolean equals(Object obj) { 133 return (obj instanceof FileTime) ? compareTo((FileTime)obj) == 0 : false; 134 } 135 136 /** 137 * Computes a hash code for this file time. 138 * 139 * <p> The hash code is based upon the value represented, and satisfies the 140 * general contract of the {@link Object#hashCode} method. 141 * 142 * @return the hash-code value 143 */ 144 @Override 145 public int hashCode() { 146 // hash value for fixed granularity to satisfy contract with equals 147 long ms = toMillis(); 148 return (int)(ms ^ (ms >>> 32)); 149 } 150 151 /** 152 * Compares the value of two {@code FileTime} objects for order. 153 * 154 * @param other 155 * the other {@code FileTime} to be compared 156 * 157 * @return {@code 0} if this {@code FileTime} is equal to {@code other}, a 158 * value less than 0 if this {@code FileTime} represents a time 159 * that is before {@code other}, and a value greater than 0 if this 160 * {@code FileTime} represents a time that is after {@code other} 161 */ 162 @Override 163 public int compareTo(FileTime other) { 164 // same granularity 165 if (unit == other.unit) 166 return (value < other.value) ? -1 : (value == other.value ? 0 : 1); 167 168 // compare in days 169 long thisValueInDays = unit.toDays(value); 170 long otherValueInDays = other.unit.toDays(other.value); 171 if (thisValueInDays != otherValueInDays) 172 return (thisValueInDays < otherValueInDays) ? -1 : 1; 173 174 // compare remainder in nanoseconds 175 long thisRemainder = remainderInNanos(thisValueInDays); 176 long otherRemainder = other.remainderInNanos(otherValueInDays); 177 return (thisRemainder < otherRemainder) ? -1 : 178 (thisRemainder == otherRemainder) ? 0 : 1; 179 } 180 181 private long remainderInNanos(long days) { 182 // constants for conversion 183 final long C0 = 1L; 184 final long C1 = C0 * 24L; 185 final long C2 = C1 * 60L; 186 final long C3 = C2 * 60L; 187 final long C4 = C3 * 1000L; 188 final long C5 = C4 * 1000L; 189 final long C6 = C5 * 1000L; 190 191 long scale; 192 switch (unit) { 193 case DAYS : scale = C0; break; 194 case HOURS : scale = C1; break; 195 case MINUTES : scale = C2; break; 196 case SECONDS : scale = C3; break; 197 case MILLISECONDS : scale = C4; break; 198 case MICROSECONDS : scale = C5; break; 199 case NANOSECONDS : scale = C6; break; 200 default: 201 throw new AssertionError("Unit not handled"); 202 } 203 long rem = value - (days * scale); 204 return unit.toNanos(rem); 205 } 206 207 /** 208 * Returns the string representation of this {@code FileTime}. The string 209 * is returned in the <a 210 * href="http://www.w3.org/TR/NOTE-datetime">ISO 8601</a> format: 211 * <pre> 212 * YYYY-MM-DDThh:mm:ss[.s+]Z 213 * </pre> 214 * where "{@code [.s+]}" represents a dot followed by one of more digits 215 * for the decimal fraction of a second. It is only present when the decimal 216 * fraction of a second is not zero. For example, {@code 217 * FileTime.fromMillis(1234567890000L).toString()} yields {@code 218 * "2009-02-13T23:31:30Z"}, and {@code FileTime.fromMillis(1234567890123L).toString()} 219 * yields {@code "2009-02-13T23:31:30.123Z"}. 220 * 221 * <p> A {@code FileTime} is primarly intended to represent the value of a 222 * file's time stamp. Where used to represent <i>extreme values</i>, where 223 * the year is less than "{@code 0001}" or greater than "{@code 9999}" then 224 * the year may be expanded to more than four digits and may be 225 * negative-signed. If more than four digits then leading zeros are not 226 * present. The year before "{@code 0001}" is "{@code -0001}". 227 * 228 * @return the string representation of this file time 229 */ 230 @Override 231 public String toString() { 232 String v = valueAsString; 233 if (v == null) { 234 // overflow saturates to Long.MIN_VALUE or Long.MAX_VALUE so this 235 // limits the range: 236 // [-292275056-05-16T16:47:04.192Z,292278994-08-17T07:12:55.807Z] 237 long ms = toMillis(); 238 239 // nothing to do when seconds/minutes/hours/days 240 String fractionAsString = ""; 241 if (unit.compareTo(TimeUnit.SECONDS) < 0) { 242 // constants for conversion 243 final long C0 = 1L; 244 final long C1 = C0 * 1000L; 245 final long C2 = C1 * 1000L; 246 final long C3 = C2 * 1000L; 247 248 long scale; 249 int width; 250 switch (unit) { 251 case MILLISECONDS : scale = C1; width = 3; break; 252 case MICROSECONDS : scale = C2; width = 6; break; 253 case NANOSECONDS : scale = C3; width = 9; break; 254 default: 255 throw new AssertionError("Unit not handled"); 256 } 257 long fraction = value % scale; 258 if (fraction != 0L) { 259 // fraction must be positive 260 if (fraction < 0L) { 261 fraction += scale; 262 if (ms != Long.MIN_VALUE) ms--; 263 } 264 265 // convert to String, adding leading zeros as required and 266 // stripping any trailing zeros 267 String s = Long.toString(fraction); 268 int len = s.length(); 269 width -= len; 270 StringBuilder sb = new StringBuilder("."); 271 while (width-- > 0) { 272 sb.append('0'); 273 } 274 if (s.charAt(len-1) == '0') { 275 // drop trailing zeros 276 len--; 277 while (s.charAt(len-1) == '0') 278 len--; 279 sb.append(s.substring(0, len)); 280 } else { 281 sb.append(s); 282 } 283 fractionAsString = sb.toString(); 284 } 285 } 286 287 // create calendar to use with formatter. 288 GregorianCalendar cal = 289 new GregorianCalendar(TimeZone.getTimeZone("UTC"), Locale.ROOT); 290 if (value < 0L) 291 cal.setGregorianChange(new Date(Long.MIN_VALUE)); 292 cal.setTimeInMillis(ms); 293 294 // years are negative before common era 295 String sign = (cal.get(Calendar.ERA) == GregorianCalendar.BC) ? "-" : ""; 296 297 // [-]YYYY-MM-DDThh:mm:ss[.s]Z 298 v = new Formatter(Locale.ROOT) 299 .format("%s%tFT%tR:%tS%sZ", sign, cal, cal, cal, fractionAsString) 300 .toString(); 301 valueAsString = v; 302 } 303 return v; 304 } 305 } | 24 */ 25 26 package java.nio.file.attribute; 27 28 import java.util.Calendar; 29 import java.util.GregorianCalendar; 30 import java.util.Date; 31 import java.util.Formatter; 32 import java.util.Locale; 33 import java.util.TimeZone; 34 import java.util.concurrent.TimeUnit; 35 36 /** 37 * Represents the value of a file's time stamp attribute. For example, it may 38 * represent the time that the file was last modified, accessed, or created. 39 * 40 * <p> Instances of this class are immutable. 41 * 42 * @since 1.7 43 * @see BasicFileAttributes 44 */ 45 46 public final class FileTime 47 implements Comparable<FileTime> 48 { 49 /** 50 * The value since the epoch; can be negative. 51 */ 52 private final long value; 53 54 /** 55 * The unit of granularity to interpret the value. 56 */ 57 private final TimeUnit unit; 58 59 /** 60 * The value return by toString (created lazily) 61 */ 62 private String valueAsString; 63 64 /** 65 * The value in days and excess nanos (created lazily) 66 */ 67 private DaysAndNanos daysAndNanos; 68 69 /** 70 * Returns a DaysAndNanos object representing the value. 71 */ 72 private DaysAndNanos asDaysAndNanos() { 73 if (daysAndNanos == null) 74 daysAndNanos = new DaysAndNanos(value, unit); 75 return daysAndNanos; 76 } 77 78 /** 79 * Initializes a new instance of this class. 80 */ 81 private FileTime(long value, TimeUnit unit) { 82 if (unit == null) 83 throw new NullPointerException(); 84 this.value = value; 85 this.unit = unit; 86 } 87 88 /** 89 * Returns a {@code FileTime} representing a value at the given unit of 90 * granularity. 91 * 92 * @param value 93 * the value since the epoch (1970-01-01T00:00:00Z); can be 94 * negative 95 * @param unit 96 * the unit of granularity to interpret the value 97 * 98 * @return a {@code FileTime} representing the given value 99 */ 100 public static FileTime from(long value, TimeUnit unit) { 155 * the object to compare with 156 * 157 * @return {@code true} if, and only if, the given object is a {@code 158 * FileTime} that represents the same time 159 */ 160 @Override 161 public boolean equals(Object obj) { 162 return (obj instanceof FileTime) ? compareTo((FileTime)obj) == 0 : false; 163 } 164 165 /** 166 * Computes a hash code for this file time. 167 * 168 * <p> The hash code is based upon the value represented, and satisfies the 169 * general contract of the {@link Object#hashCode} method. 170 * 171 * @return the hash-code value 172 */ 173 @Override 174 public int hashCode() { 175 // hashcode of days/nanos representation to satisfy contract with equals 176 return asDaysAndNanos().hashCode(); 177 } 178 179 /** 180 * Compares the value of two {@code FileTime} objects for order. 181 * 182 * @param other 183 * the other {@code FileTime} to be compared 184 * 185 * @return {@code 0} if this {@code FileTime} is equal to {@code other}, a 186 * value less than 0 if this {@code FileTime} represents a time 187 * that is before {@code other}, and a value greater than 0 if this 188 * {@code FileTime} represents a time that is after {@code other} 189 */ 190 @Override 191 public int compareTo(FileTime other) { 192 // same granularity 193 if (unit == other.unit) { 194 return (value < other.value) ? -1 : (value == other.value ? 0 : 1); 195 } else { 196 // compare using days/nanos representation when unit differs 197 return asDaysAndNanos().compareTo(other.asDaysAndNanos()); 198 } 199 } 200 201 /** 202 * Returns the string representation of this {@code FileTime}. The string 203 * is returned in the <a 204 * href="http://www.w3.org/TR/NOTE-datetime">ISO 8601</a> format: 205 * <pre> 206 * YYYY-MM-DDThh:mm:ss[.s+]Z 207 * </pre> 208 * where "{@code [.s+]}" represents a dot followed by one of more digits 209 * for the decimal fraction of a second. It is only present when the decimal 210 * fraction of a second is not zero. For example, {@code 211 * FileTime.fromMillis(1234567890000L).toString()} yields {@code 212 * "2009-02-13T23:31:30Z"}, and {@code FileTime.fromMillis(1234567890123L).toString()} 213 * yields {@code "2009-02-13T23:31:30.123Z"}. 214 * 215 * <p> A {@code FileTime} is primarly intended to represent the value of a 216 * file's time stamp. Where used to represent <i>extreme values</i>, where 217 * the year is less than "{@code 0001}" or greater than "{@code 9999}" then 218 * the year may be expanded to more than four digits and may be 219 * negative-signed. If more than four digits then leading zeros are not 220 * present. The year before "{@code 0001}" is "{@code -0001}". 221 * 222 * @return the string representation of this file time 223 */ 224 @Override 225 public String toString() { 226 String v = valueAsString; 227 if (v == null) { 228 // overflow saturates to Long.MIN_VALUE or Long.MAX_VALUE so this 229 // limits the range: 230 // [-292275056-05-16T16:47:04.192Z,292278994-08-17T07:12:55.807Z] 231 long ms = toMillis(); 232 233 // nothing to do when seconds/minutes/hours/days 234 String fractionAsString = ""; 235 if (unit.compareTo(TimeUnit.SECONDS) < 0) { 236 long fraction = asDaysAndNanos().fractionOfSecondInNanos(); 237 if (fraction != 0L) { 238 // fraction must be positive 239 if (fraction < 0L) { 240 final long MAX_FRACTION_PLUS_1 = 1000L * 1000L * 1000L; 241 fraction += MAX_FRACTION_PLUS_1; 242 if (ms != Long.MIN_VALUE) ms--; 243 } 244 245 // convert to String, adding leading zeros as required and 246 // stripping any trailing zeros 247 String s = Long.toString(fraction); 248 int len = s.length(); 249 int width = 9 - len; 250 StringBuilder sb = new StringBuilder("."); 251 while (width-- > 0) { 252 sb.append('0'); 253 } 254 if (s.charAt(len-1) == '0') { 255 // drop trailing zeros 256 len--; 257 while (s.charAt(len-1) == '0') 258 len--; 259 sb.append(s.substring(0, len)); 260 } else { 261 sb.append(s); 262 } 263 fractionAsString = sb.toString(); 264 } 265 } 266 267 // create calendar to use with formatter. 268 GregorianCalendar cal = 269 new GregorianCalendar(TimeZone.getTimeZone("UTC"), Locale.ROOT); 270 if (value < 0L) 271 cal.setGregorianChange(new Date(Long.MIN_VALUE)); 272 cal.setTimeInMillis(ms); 273 274 // years are negative before common era 275 String sign = (cal.get(Calendar.ERA) == GregorianCalendar.BC) ? "-" : ""; 276 277 // [-]YYYY-MM-DDThh:mm:ss[.s]Z 278 v = new Formatter(Locale.ROOT) 279 .format("%s%tFT%tR:%tS%sZ", sign, cal, cal, cal, fractionAsString) 280 .toString(); 281 valueAsString = v; 282 } 283 return v; 284 } 285 286 /** 287 * Represents a FileTime's value as two longs: the number of days since 288 * the epoch, and the excess (in nanoseconds). This is used for comparing 289 * values with different units of granularity. 290 */ 291 private static class DaysAndNanos implements Comparable<DaysAndNanos> { 292 // constants for conversion 293 private static final long C0 = 1L; 294 private static final long C1 = C0 * 24L; 295 private static final long C2 = C1 * 60L; 296 private static final long C3 = C2 * 60L; 297 private static final long C4 = C3 * 1000L; 298 private static final long C5 = C4 * 1000L; 299 private static final long C6 = C5 * 1000L; 300 301 /** 302 * The value (in days) since the epoch; can be negative. 303 */ 304 private final long days; 305 306 /** 307 * The excess (in nanoseconds); can be negative if days <= 0. 308 */ 309 private final long excessNanos; 310 311 /** 312 * Initializes a new instance of this class. 313 */ 314 DaysAndNanos(long value, TimeUnit unit) { 315 long scale; 316 switch (unit) { 317 case DAYS : scale = C0; break; 318 case HOURS : scale = C1; break; 319 case MINUTES : scale = C2; break; 320 case SECONDS : scale = C3; break; 321 case MILLISECONDS : scale = C4; break; 322 case MICROSECONDS : scale = C5; break; 323 case NANOSECONDS : scale = C6; break; 324 default : throw new AssertionError("Unit not handled"); 325 } 326 this.days = unit.toDays(value); 327 this.excessNanos = unit.toNanos(value - (this.days * scale)); 328 } 329 330 /** 331 * Returns the fraction of a second, in nanoseconds. 332 */ 333 long fractionOfSecondInNanos() { 334 return excessNanos % (1000L * 1000L * 1000L); 335 } 336 337 @Override 338 public boolean equals(Object obj) { 339 return (obj instanceof DaysAndNanos) ? 340 compareTo((DaysAndNanos)obj) == 0 : false; 341 } 342 343 @Override 344 public int hashCode() { 345 return (int)(days ^ (days >>> 32) ^ 346 excessNanos ^ (excessNanos >>> 32)); 347 } 348 349 @Override 350 public int compareTo(DaysAndNanos other) { 351 if (this.days != other.days) 352 return (this.days < other.days) ? -1 : 1; 353 return (this.excessNanos < other.excessNanos) ? -1 : 354 (this.excessNanos == other.excessNanos) ? 0 : 1; 355 } 356 } 357 } |