1 module hunt.time.format.LocalizedOffsetIdPrinterParser; 2 3 import hunt.time.format.DateTimeParseContext; 4 import hunt.time.format.DateTimePrinterParser; 5 import hunt.time.format.DateTimePrintContext; 6 import hunt.time.format.TextStyle; 7 import hunt.time.temporal.ChronoField; 8 import hunt.time.temporal.TemporalField; 9 import hunt.util.StringBuilder; 10 11 12 import hunt.Long; 13 import hunt.math.Helper; 14 15 import std.conv; 16 17 18 19 //----------------------------------------------------------------------- 20 /** 21 * Prints or parses an offset ID. 22 */ 23 static final class LocalizedOffsetIdPrinterParser : DateTimePrinterParser 24 { 25 private TextStyle style; 26 27 /** 28 * Constructor. 29 * 30 * @param style the style, not null 31 */ 32 this(TextStyle style) 33 { 34 this.style = style; 35 } 36 37 private static StringBuilder appendHMS(StringBuilder buf, int t) 38 { 39 return buf.append( /* cast(char) */ ((t / 10).to!string ~ '0')).append( /* cast(char) */ ((t % 10) 40 .to!string ~ '0')); 41 } 42 43 override public bool format(DateTimePrintContext context, StringBuilder buf) 44 { 45 Long offsetSecs = context.getValue(ChronoField.OFFSET_SECONDS); 46 if (offsetSecs is null) 47 { 48 return false; 49 } 50 string gmtText = "GMT"; // TODO: get localized version of 'GMT' 51 buf.append(gmtText); 52 int totalSecs = MathHelper.toIntExact(offsetSecs.longValue()); 53 if (totalSecs != 0) 54 { 55 int absHours = MathHelper.abs((totalSecs / 3600) % 100); // anything larger than 99 silently dropped 56 int absMinutes = MathHelper.abs((totalSecs / 60) % 60); 57 int absSeconds = MathHelper.abs(totalSecs % 60); 58 buf.append(totalSecs < 0 ? "-" : "+"); 59 if (style == TextStyle.FULL) 60 { 61 appendHMS(buf, absHours); 62 buf.append(':'); 63 appendHMS(buf, absMinutes); 64 if (absSeconds != 0) 65 { 66 buf.append(':'); 67 appendHMS(buf, absSeconds); 68 } 69 } 70 else 71 { 72 if (absHours >= 10) 73 { 74 buf.append( /* cast(char) */ ((absHours / 10).to!string ~ '0')); 75 } 76 buf.append( /* cast(char) */ ((absHours % 10).to!string ~ '0')); 77 if (absMinutes != 0 || absSeconds != 0) 78 { 79 buf.append(':'); 80 appendHMS(buf, absMinutes); 81 if (absSeconds != 0) 82 { 83 buf.append(':'); 84 appendHMS(buf, absSeconds); 85 } 86 } 87 } 88 } 89 return true; 90 } 91 92 int getDigit(string text, int position) 93 { 94 char c = text[position]; 95 if (c < '0' || c > '9') 96 { 97 return -1; 98 } 99 return c - '0'; 100 } 101 102 override public int parse(DateTimeParseContext context, string text, int position) 103 { 104 int pos = position; 105 int end = cast(int)(text.length); 106 string gmtText = "GMT"; // TODO: get localized version of 'GMT' 107 if (!context.subSequenceEquals(text, pos, gmtText, 0, cast(int)(gmtText.length))) 108 { 109 return ~position; 110 } 111 pos += gmtText.length; 112 // parse normal plus/minus offset 113 int negative = 0; 114 if (pos == end) 115 { 116 return context.setParsedField(ChronoField.OFFSET_SECONDS, 0, position, pos); 117 } 118 char sign = text[pos]; // IOOBE if invalid position 119 if (sign == '+') 120 { 121 negative = 1; 122 } 123 else if (sign == '-') 124 { 125 negative = -1; 126 } 127 else 128 { 129 return context.setParsedField(ChronoField.OFFSET_SECONDS, 0, position, pos); 130 } 131 pos++; 132 int h = 0; 133 int m = 0; 134 int s = 0; 135 if (style == TextStyle.FULL) 136 { 137 int h1 = getDigit(text, pos++); 138 int h2 = getDigit(text, pos++); 139 if (h1 < 0 || h2 < 0 || text[pos++] != ':') 140 { 141 return ~position; 142 } 143 h = h1 * 10 + h2; 144 int m1 = getDigit(text, pos++); 145 int m2 = getDigit(text, pos++); 146 if (m1 < 0 || m2 < 0) 147 { 148 return ~position; 149 } 150 m = m1 * 10 + m2; 151 if (pos + 2 < end && text[pos] == ':') 152 { 153 int s1 = getDigit(text, pos + 1); 154 int s2 = getDigit(text, pos + 2); 155 if (s1 >= 0 && s2 >= 0) 156 { 157 s = s1 * 10 + s2; 158 pos += 3; 159 } 160 } 161 } 162 else 163 { 164 h = getDigit(text, pos++); 165 if (h < 0) 166 { 167 return ~position; 168 } 169 if (pos < end) 170 { 171 int h2 = getDigit(text, pos); 172 if (h2 >= 0) 173 { 174 h = h * 10 + h2; 175 pos++; 176 } 177 if (pos + 2 < end && text[pos] == ':') 178 { 179 if (pos + 2 < end && text[pos] == ':') 180 { 181 int m1 = getDigit(text, pos + 1); 182 int m2 = getDigit(text, pos + 2); 183 if (m1 >= 0 && m2 >= 0) 184 { 185 m = m1 * 10 + m2; 186 pos += 3; 187 if (pos + 2 < end && text[pos] == ':') 188 { 189 int s1 = getDigit(text, pos + 1); 190 int s2 = getDigit(text, pos + 2); 191 if (s1 >= 0 && s2 >= 0) 192 { 193 s = s1 * 10 + s2; 194 pos += 3; 195 } 196 } 197 } 198 } 199 } 200 } 201 } 202 long offsetSecs = negative * (h * 3600L + m * 60L + s); 203 return context.setParsedField(ChronoField.OFFSET_SECONDS, 204 offsetSecs, position, pos); 205 } 206 207 override public string toString() 208 { 209 return "LocalizedOffset(" ~ typeid(style).name ~ ")"; 210 } 211 } 212