strbuf: let strbuf_addftime handle %z and %Z itself
There is no portable way to pass timezone information to strftime. Add parameters for timezone offset and name to strbuf_addftime and let it handle the timezone-related format specifiers %z and %Z internally. Callers can opt out for %Z by passing NULL as timezone name. %z is always handled internally -- this helps on Windows, where strftime would expand it to a timezone name (same as %Z), in violation of POSIX. Modifiers are not handled, e.g. %Ez is still passed to strftime. Use an empty string as timezone name in show_date (the only current caller) for now because we only have the timezone offset in non-local mode. POSIX allows %Z to resolve to an empty string in case of missing information. Helped-by: Ulrich Mueller <ulm@gentoo.org> Helped-by: Jeff King <peff@peff.net> Signed-off-by: Rene Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									b06d364310
								
							
						
					
					
						commit
						c3fbf81a85
					
				|  | @ -764,7 +764,8 @@ timezone value. | ||||||
| 1970).  As with `--raw`, this is always in UTC and therefore `-local` | 1970).  As with `--raw`, this is always in UTC and therefore `-local` | ||||||
| has no effect. | has no effect. | ||||||
| + | + | ||||||
| `--date=format:...` feeds the format `...` to your system `strftime`. | `--date=format:...` feeds the format `...` to your system `strftime`, | ||||||
|  | except for %z and %Z, which are handled internally. | ||||||
| Use `--date=format:%c` to show the date in your system locale's | Use `--date=format:%c` to show the date in your system locale's | ||||||
| preferred format.  See the `strftime` manual for a complete list of | preferred format.  See the `strftime` manual for a complete list of | ||||||
| format placeholders. When using `-local`, the correct syntax is | format placeholders. When using `-local`, the correct syntax is | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								date.c
								
								
								
								
							
							
						
						
									
										2
									
								
								date.c
								
								
								
								
							|  | @ -233,7 +233,7 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode) | ||||||
| 			month_names[tm->tm_mon], tm->tm_year + 1900, | 			month_names[tm->tm_mon], tm->tm_year + 1900, | ||||||
| 			tm->tm_hour, tm->tm_min, tm->tm_sec, tz); | 			tm->tm_hour, tm->tm_min, tm->tm_sec, tz); | ||||||
| 	else if (mode->type == DATE_STRFTIME) | 	else if (mode->type == DATE_STRFTIME) | ||||||
| 		strbuf_addftime(&timebuf, mode->strftime_fmt, tm); | 		strbuf_addftime(&timebuf, mode->strftime_fmt, tm, tz, ""); | ||||||
| 	else | 	else | ||||||
| 		strbuf_addf(&timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d", | 		strbuf_addf(&timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d", | ||||||
| 				weekday_names[tm->tm_wday], | 				weekday_names[tm->tm_wday], | ||||||
|  |  | ||||||
							
								
								
									
										41
									
								
								strbuf.c
								
								
								
								
							
							
						
						
									
										41
									
								
								strbuf.c
								
								
								
								
							|  | @ -785,14 +785,48 @@ char *xstrfmt(const char *fmt, ...) | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm) | void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm, | ||||||
|  | 		     int tz_offset, const char *tz_name) | ||||||
| { | { | ||||||
|  | 	struct strbuf munged_fmt = STRBUF_INIT; | ||||||
| 	size_t hint = 128; | 	size_t hint = 128; | ||||||
| 	size_t len; | 	size_t len; | ||||||
|  |  | ||||||
| 	if (!*fmt) | 	if (!*fmt) | ||||||
| 		return; | 		return; | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * There is no portable way to pass timezone information to | ||||||
|  | 	 * strftime, so we handle %z and %Z here. | ||||||
|  | 	 */ | ||||||
|  | 	for (;;) { | ||||||
|  | 		const char *percent = strchrnul(fmt, '%'); | ||||||
|  | 		strbuf_add(&munged_fmt, fmt, percent - fmt); | ||||||
|  | 		if (!*percent) | ||||||
|  | 			break; | ||||||
|  | 		fmt = percent + 1; | ||||||
|  | 		switch (*fmt) { | ||||||
|  | 		case '%': | ||||||
|  | 			strbuf_addstr(&munged_fmt, "%%"); | ||||||
|  | 			fmt++; | ||||||
|  | 			break; | ||||||
|  | 		case 'z': | ||||||
|  | 			strbuf_addf(&munged_fmt, "%+05d", tz_offset); | ||||||
|  | 			fmt++; | ||||||
|  | 			break; | ||||||
|  | 		case 'Z': | ||||||
|  | 			if (tz_name) { | ||||||
|  | 				strbuf_addstr(&munged_fmt, tz_name); | ||||||
|  | 				fmt++; | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 			/* FALLTHROUGH */ | ||||||
|  | 		default: | ||||||
|  | 			strbuf_addch(&munged_fmt, '%'); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	fmt = munged_fmt.buf; | ||||||
|  |  | ||||||
| 	strbuf_grow(sb, hint); | 	strbuf_grow(sb, hint); | ||||||
| 	len = strftime(sb->buf + sb->len, sb->alloc - sb->len, fmt, tm); | 	len = strftime(sb->buf + sb->len, sb->alloc - sb->len, fmt, tm); | ||||||
|  |  | ||||||
|  | @ -804,17 +838,16 @@ void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm) | ||||||
| 		 * output contains at least one character, and then drop the extra | 		 * output contains at least one character, and then drop the extra | ||||||
| 		 * character before returning. | 		 * character before returning. | ||||||
| 		 */ | 		 */ | ||||||
| 		struct strbuf munged_fmt = STRBUF_INIT; | 		strbuf_addch(&munged_fmt, ' '); | ||||||
| 		strbuf_addf(&munged_fmt, "%s ", fmt); |  | ||||||
| 		while (!len) { | 		while (!len) { | ||||||
| 			hint *= 2; | 			hint *= 2; | ||||||
| 			strbuf_grow(sb, hint); | 			strbuf_grow(sb, hint); | ||||||
| 			len = strftime(sb->buf + sb->len, sb->alloc - sb->len, | 			len = strftime(sb->buf + sb->len, sb->alloc - sb->len, | ||||||
| 				       munged_fmt.buf, tm); | 				       munged_fmt.buf, tm); | ||||||
| 		} | 		} | ||||||
| 		strbuf_release(&munged_fmt); |  | ||||||
| 		len--; /* drop munged space */ | 		len--; /* drop munged space */ | ||||||
| 	} | 	} | ||||||
|  | 	strbuf_release(&munged_fmt); | ||||||
| 	strbuf_setlen(sb, sb->len + len); | 	strbuf_setlen(sb, sb->len + len); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
							
								
								
									
										8
									
								
								strbuf.h
								
								
								
								
							
							
						
						
									
										8
									
								
								strbuf.h
								
								
								
								
							|  | @ -340,8 +340,14 @@ extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap); | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Add the time specified by `tm`, as formatted by `strftime`. |  * Add the time specified by `tm`, as formatted by `strftime`. | ||||||
|  |  * `tz_name` is used to expand %Z internally unless it's NULL. | ||||||
|  |  * `tz_offset` is in decimal hhmm format, e.g. -600 means six hours west | ||||||
|  |  * of Greenwich, and it's used to expand %z internally.  However, tokens | ||||||
|  |  * with modifiers (e.g. %Ez) are passed to `strftime`. | ||||||
|  */ |  */ | ||||||
| extern void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm); | extern void strbuf_addftime(struct strbuf *sb, const char *fmt, | ||||||
|  | 			    const struct tm *tm, int tz_offset, | ||||||
|  | 			    const char *tz_name); | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Read a given size of data from a FILE* pointer to the buffer. |  * Read a given size of data from a FILE* pointer to the buffer. | ||||||
|  |  | ||||||
|  | @ -51,6 +51,12 @@ check_show iso-local "$TIME" '2016-06-15 14:13:20 +0000' | ||||||
| check_show raw-local "$TIME" '1466000000 +0000' | check_show raw-local "$TIME" '1466000000 +0000' | ||||||
| check_show unix-local "$TIME" '1466000000' | check_show unix-local "$TIME" '1466000000' | ||||||
|  |  | ||||||
|  | check_show 'format:%z' "$TIME" '+0200' | ||||||
|  | check_show 'format-local:%z' "$TIME" '+0000' | ||||||
|  | check_show 'format:%Z' "$TIME" '' | ||||||
|  | check_show 'format:%%z' "$TIME" '%z' | ||||||
|  | check_show 'format-local:%%z' "$TIME" '%z' | ||||||
|  |  | ||||||
| # arbitrary time absurdly far in the future | # arbitrary time absurdly far in the future | ||||||
| FUTURE="5758122296 -0400" | FUTURE="5758122296 -0400" | ||||||
| check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" LONG_IS_64BIT | check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" LONG_IS_64BIT | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 René Scharfe
						René Scharfe