View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.util;
15  
16  import java.time.Instant;
17  import java.time.OffsetDateTime;
18  import java.time.ZoneId;
19  import java.time.format.DateTimeFormatter;
20  import java.util.Locale;
21  import java.util.concurrent.atomic.AtomicReference;
22  
23  /**
24   * A CAS implementation of DateTimeFormatter (previously SimpleDateFormat) which
25   * caches results for the duration of a millisecond.
26   * 
27   * @author Ceki Gülcü
28   * @since 0.9.29
29   */
30  public class CachingDateFormatter {
31  
32      final DateTimeFormatter dtf;
33      final ZoneId zoneId;
34      final AtomicReference<CacheTuple> atomicReference;
35  
36      static class CacheTuple {
37          final long lastTimestamp;
38          final String cachedStr;
39  
40          public CacheTuple(long lastTimestamp, String cachedStr) {
41              super();
42              this.lastTimestamp = lastTimestamp;
43              this.cachedStr = cachedStr;
44          }
45      }
46  
47      public CachingDateFormatter(String pattern) {
48          this(pattern, null);
49      }
50  
51      public CachingDateFormatter(String pattern, ZoneId aZoneId) {
52          this(pattern, aZoneId, null);
53      }
54  
55      public CachingDateFormatter(String pattern, ZoneId aZoneId, Locale aLocale) {
56          if (aZoneId == null) {
57              this.zoneId = ZoneId.systemDefault();
58          } else {
59              this.zoneId = aZoneId;
60          }
61          Locale locale = aLocale != null ? aLocale : Locale.getDefault();
62  
63          dtf = DateTimeFormatter.ofPattern(pattern).withZone(this.zoneId).withLocale(locale);
64          CacheTuple cacheTuple = new CacheTuple(-1, null);
65          this.atomicReference = new AtomicReference<>(cacheTuple);
66      }
67  
68      public final String format(long now) {
69          CacheTuple localCacheTuple = atomicReference.get();
70          CacheTuple oldCacheTuple = localCacheTuple;
71  
72          if (now != localCacheTuple.lastTimestamp) {
73              Instant instant = Instant.ofEpochMilli(now);
74              String result = dtf.format(instant);
75              localCacheTuple = new CacheTuple(now, result);
76              // allow a single thread to update the cache reference
77              atomicReference.compareAndSet(oldCacheTuple, localCacheTuple);
78          }
79          return localCacheTuple.cachedStr;
80      }
81  
82  }