1 module hunt.time.format.ZoneTextPrinterParser; 2 3 import hunt.time.Instant; 4 import hunt.time.LocalDate; 5 import hunt.time.LocalDateTime; 6 import hunt.time.LocalTime; 7 8 import hunt.time.format.DateTimeParseContext; 9 import hunt.time.format.DateTimePrinterParser; 10 import hunt.time.format.DateTimePrintContext; 11 import hunt.time.format.PrefixTree; 12 import hunt.time.format.TextStyle; 13 import hunt.time.format.ZoneIdPrinterParser; 14 import hunt.time.temporal.ChronoField; 15 import hunt.time.temporal.TemporalAccessor; 16 import hunt.time.temporal.TemporalField; 17 import hunt.time.temporal.TemporalQueries; 18 import hunt.time.util.Common; 19 import hunt.time.ZoneId; 20 import hunt.time.ZoneOffset; 21 import hunt.time.zone.ZoneRulesProvider; 22 23 import hunt.Exceptions; 24 import hunt.collection.HashMap; 25 import hunt.collection.HashSet; 26 import hunt.collection.Map; 27 import hunt.collection.Set; 28 import hunt.Integer; 29 import hunt.util.StringBuilder; 30 import hunt.util.Locale; 31 32 //----------------------------------------------------------------------- 33 /** 34 * Prints or parses a zone ID. 35 */ 36 static final class ZoneTextPrinterParser : ZoneIdPrinterParser 37 { 38 39 /** The text style to output. */ 40 private TextStyle textStyle; 41 42 /** The preferred zoneid map */ 43 private Set!(string) preferredZones; 44 45 /** Display _in generic time-zone format. True _in case of pattern letter 'v' */ 46 private bool isGeneric; 47 48 this(TextStyle textStyle, Set!(ZoneId) preferredZones, bool isGeneric) 49 { 50 cachedTree = new HashMap!(Locale, MapEntry!(Integer, PrefixTree))(); 51 cachedTreeCI = new HashMap!(Locale, MapEntry!(Integer, PrefixTree))(); 52 super(TemporalQueries.zone(), "ZoneText(" ~ textStyle.toString ~ ")"); 53 this.textStyle = textStyle; 54 this.isGeneric = isGeneric; 55 if (preferredZones !is null && preferredZones.size() != 0) 56 { 57 this.preferredZones = new HashSet!(string)(); 58 foreach (ZoneId id; preferredZones) 59 { 60 this.preferredZones.add(id.getId()); 61 } 62 } 63 } 64 65 private enum int STD = 0; 66 private enum int DST = 1; 67 private enum int GENERIC = 2; 68 // __gshared Map!(string, Map!(Locale, string[])) cache; 69 70 // shared static this() 71 // { 72 // cache = new HashMap!(string, Map!(Locale, string[]))(); 73 mixin(MakeGlobalVar!(Map!(string, Map!(Locale, string[])))("cache",`new HashMap!(string, Map!(Locale, string[]))()`)); 74 // } 75 76 private string getDisplayName(string id, int type, Locale locale) 77 { 78 if (textStyle == TextStyle.NARROW) 79 { 80 return null; 81 } 82 string[] names; 83 Map!(Locale, string[]) _ref = cache.get(id); 84 Map!(Locale, string[]) perLocale = null; 85 if (_ref is null || (perLocale = _ref) is null 86 || (names = perLocale.get(locale)) is null) 87 { 88 // names = TimeZoneNameUtility.retrieveDisplayNames(id, locale); 89 // if (names is null) { 90 // return null; 91 // } 92 // auto tmp = names[0 .. 7]; 93 // names = tmp; 94 // names[5] = 95 // TimeZoneNameUtility.retrieveGenericDisplayName(id, TimeZone.LONG, locale); 96 // if (names[5] is null) { 97 // names[5] = names[0]; // use the id 98 // } 99 // names[6] = 100 // TimeZoneNameUtility.retrieveGenericDisplayName(id, TimeZone.SHORT, locale); 101 // if (names[6] is null) { 102 // names[6] = names[0]; 103 // } 104 // if (perLocale is null) { 105 // perLocale = new HashMap!(Locale, string[])(); 106 // } 107 // perLocale.put(locale, names); 108 // cache.put(id, perLocale); 109 implementationMissing(); 110 111 } 112 switch (type) 113 { 114 case STD: 115 return names[textStyle.zoneNameStyleIndex() + 1]; 116 case DST: 117 return names[textStyle.zoneNameStyleIndex() + 3]; 118 default: 119 break; 120 } 121 return names[textStyle.zoneNameStyleIndex() + 5]; 122 } 123 124 override public bool format(DateTimePrintContext context, StringBuilder buf) 125 { 126 ZoneId zone = context.getValue(TemporalQueries.zoneId()); 127 if (zone is null) 128 { 129 return false; 130 } 131 string zname = zone.getId(); 132 if (!(cast(ZoneOffset)(zone) !is null)) 133 { 134 TemporalAccessor dt = context.getTemporal(); 135 int type = GENERIC; 136 if (!isGeneric) 137 { 138 if (dt.isSupported(ChronoField.INSTANT_SECONDS)) 139 { 140 type = zone.getRules().isDaylightSavings(Instant.from(dt)) ? DST : STD; 141 } 142 else if (dt.isSupported(ChronoField.EPOCH_DAY) 143 && dt.isSupported(ChronoField.NANO_OF_DAY)) 144 { 145 LocalDate date = LocalDate.ofEpochDay( 146 dt.getLong(ChronoField.EPOCH_DAY)); 147 LocalTime time = LocalTime.ofNanoOfDay( 148 dt.getLong(ChronoField.NANO_OF_DAY)); 149 LocalDateTime ldt = date.atTime_s(time); 150 if (zone.getRules().getTransition(ldt) is null) 151 { 152 type = zone.getRules().isDaylightSavings(ldt.atZone(zone) 153 .toInstant()) ? DST : STD; 154 } 155 } 156 } 157 string name = getDisplayName(zname, type, context.getLocale()); 158 if (name !is null) 159 { 160 zname = name; 161 } 162 } 163 buf.append(zname); 164 return true; 165 } 166 167 // cache per instance for now 168 private Map!(Locale, MapEntry!(Integer, PrefixTree)) cachedTree; 169 private Map!(Locale, MapEntry!(Integer, PrefixTree)) cachedTreeCI; 170 171 override protected PrefixTree getTree(DateTimeParseContext context) 172 { 173 if (textStyle == TextStyle.NARROW) 174 { 175 return super.getTree(context); 176 } 177 Locale locale = context.getLocale(); 178 bool isCaseSensitive = context.isCaseSensitive(); 179 Set!(string) regionIds = ZoneRulesProvider.getAvailableZoneIds(); 180 int regionIdsSize = regionIds.size(); 181 182 Map!(Locale, MapEntry!(Integer, PrefixTree)) cached = isCaseSensitive ? cachedTree 183 : cachedTreeCI; 184 185 MapEntry!(Integer, PrefixTree) entry = null; 186 PrefixTree tree = null; 187 string[][] zoneStrings = null; 188 if ((entry = cached.get(locale)) is null 189 || (entry.getKey() != regionIdsSize || (tree = entry.getValue() /* .get() */ ) is null)) 190 { 191 tree = PrefixTree.newTree(context); 192 // zoneStrings = TimeZoneNameUtility.getZoneStrings(locale); 193 // foreach(string[] names ; zoneStrings) { 194 // string zid = names[0]; 195 // if (!regionIds.contains(zid)) { 196 // continue; 197 // } 198 // tree.add(zid, zid); // don't convert zid -> metazone 199 // zid = ZoneName.toZid(zid, locale); 200 // int i = textStyle == TextStyle.FULL ? 1 : 2; 201 // for (; i < names.length; i += 2) { 202 // tree.add(names[i], zid); 203 // } 204 // } 205 206 // // if we have a set of preferred zones, need a copy and 207 // // add the preferred zones again to overwrite 208 // if (preferredZones !is null) { 209 // foreach(string[] names ; zoneStrings) { 210 // string zid = names[0]; 211 // if (!preferredZones.contains(zid) || !regionIds.contains(zid)) { 212 // continue; 213 // } 214 // int i = textStyle == TextStyle.FULL ? 1 : 2; 215 // for (; i < names.length; i += 2) { 216 // tree.add(names[i], zid); 217 // } 218 // } 219 // } 220 // cached.put(locale, new SimpleImmutableEntry!(Integer, PrefixTree)(new Integer(regionIdsSize), tree)); 221 222 223 } 224 225 implementationMissing(false); 226 return tree; 227 } 228 }