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