| 112 | } |
| 113 | |
| 114 | bool SimpleDateFormatTokenizer::Tokenize( |
| 115 | DateTimeFormatContext* dt_ctx, CastDirection cast_mode, bool accept_time_toks, |
| 116 | bool accept_time_toks_only) { |
| 117 | DCHECK(dt_ctx != NULL); |
| 118 | DCHECK(dt_ctx->fmt != NULL); |
| 119 | DCHECK(dt_ctx->fmt_len > 0); |
| 120 | DCHECK(dt_ctx->toks.size() == 0); |
| 121 | const char* str_begin = dt_ctx->fmt; |
| 122 | const char* str_end = str_begin + dt_ctx->fmt_len; |
| 123 | const char* str = str_begin; |
| 124 | // Parse the tokens from the format string |
| 125 | while (str < str_end) { |
| 126 | if (isdigit(*str)) return false; |
| 127 | |
| 128 | // If time tokens are accepted, track T|Z as separators. |
| 129 | if (*str == 'T' || *str == 'Z') { |
| 130 | if (!accept_time_toks) return false; |
| 131 | dt_ctx->toks.push_back(DateTimeFormatToken(SEPARATOR, str - str_begin, 1, str)); |
| 132 | ++str; |
| 133 | continue; |
| 134 | } |
| 135 | |
| 136 | // A non-alphanumerical char could be the first char of a timezone-offset token. |
| 137 | // If it is not the beginning of a time-zone offset token, track it as a separator. |
| 138 | if (!isalpha(*str)) { |
| 139 | if (dt_ctx->has_time_toks && IsValidTZOffset(str, str_end)) { |
| 140 | // TZ offset must come at the end of the format. |
| 141 | dt_ctx->toks.push_back(DateTimeFormatToken(TZ_OFFSET, str - str_begin, |
| 142 | str_end - str, str)); |
| 143 | break; |
| 144 | } else { |
| 145 | dt_ctx->toks.push_back(DateTimeFormatToken(SEPARATOR, str - str_begin, 1, str)); |
| 146 | ++str; |
| 147 | continue; |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | // Not a separator, verify that the previous token is either a separator or has |
| 152 | // length >1, i.e., it is not a variable length token. |
| 153 | if (!dt_ctx->toks.empty()) { |
| 154 | const DateTimeFormatToken& prev = dt_ctx->toks.back(); |
| 155 | if (UNLIKELY(prev.type != SEPARATOR && prev.len == 1)) return false; |
| 156 | } |
| 157 | DateTimeFormatTokenType tok_type = UNKNOWN; |
| 158 | switch (*str) { |
| 159 | case 'y': tok_type = YEAR; break; |
| 160 | case 'M': tok_type = MONTH_IN_YEAR; break; |
| 161 | case 'd': tok_type = DAY_IN_MONTH; break; |
| 162 | case 'H': tok_type = HOUR_IN_DAY; break; |
| 163 | case 'm': tok_type = MINUTE_IN_HOUR; break; |
| 164 | case 's': tok_type = SECOND_IN_MINUTE; break; |
| 165 | case 'S': tok_type = FRACTION; break; |
| 166 | // Error on aA-zZ reserved characters that are not used yet. |
| 167 | default: return false; |
| 168 | } |
| 169 | dt_ctx->has_date_toks |= tok_type < HOUR_IN_DAY; |
| 170 | dt_ctx->has_time_toks |= tok_type >= HOUR_IN_DAY; |
| 171 | if (!accept_time_toks && dt_ctx->has_time_toks) return false; |
nothing calls this directly
no test coverage detected