The timezones that we store in commits as part of the identity information are encoded in "[+-]HHMM", for example "-0700" for UTC-7. Internally we typically pass around this timezone either as string or as a parsed integer (-700). Some sites want to convert between this format and minutes or vice versa, and that conversion is performed ad-hoc. We're about to introduce another site though that wants to have access to this logic, and having it cluttered across our codebase is a bit awkward. Introduce two new helpers `tz_to_minutes()` and `minutes_to_tz()` that perform the conversion for us and convert call sites to use them. Note that we used to perform a dance in `gm_time_t()` where we first convert `tz` into a positive value, then calculate the minutes, and finally turn the minutes into a negative value again. This dance is performed because it is implementation-defined in C89 whether the division on negative values truncates towards zero or not [1]: If either operand is negative, whether the result of the / operator is the largest integer less than the algebraic quotient or the smallest integer greater than the algebraic quotient is implementation-defined, as is the sign of the result of the % operator. So under C89, `-130 / 100` could legitimately result in -1 or -2, and `-130 % 100` could result in either -30 or 70. For us though, the result that we want is the first one (-1 and -30), which is called truncation toward zero. This part of the C language has changed in C99, where this edge case is now well-defined to always truncate towards zero [2]: When integers are divided, the result of the / operator is the algebraic quotient with any fractional part discarded.90) If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a. 90) This is often called ''truncation toward zero''. So in theory it's unlikely that we still need this logic. In practice though it feels safer to just retain it as we don't require a fully C99-compliant compiler in Git. [1]: https://port70.net/~nsz/c/c89/c89-draft.html#3.3.5 [2]: https://port70.net/~nsz/c/c99/n1256.html#6.5.5p6 Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
83 lines
2.4 KiB
C
83 lines
2.4 KiB
C
#ifndef DATE_H
|
|
#define DATE_H
|
|
|
|
/**
|
|
* The date mode type. This has DATE_NORMAL at an explicit "= 0" to
|
|
* accommodate a memset([...], 0, [...]) initialization when "struct
|
|
* date_mode" is used as an embedded struct member, as in the case of
|
|
* e.g. "struct pretty_print_context" and "struct rev_info".
|
|
*/
|
|
enum date_mode_type {
|
|
DATE_NORMAL = 0,
|
|
DATE_HUMAN,
|
|
DATE_RELATIVE,
|
|
DATE_SHORT,
|
|
DATE_ISO8601,
|
|
DATE_ISO8601_STRICT,
|
|
DATE_RFC2822,
|
|
DATE_STRFTIME,
|
|
DATE_RAW,
|
|
DATE_UNIX
|
|
};
|
|
|
|
struct date_mode {
|
|
enum date_mode_type type;
|
|
int local;
|
|
const char *strftime_fmt;
|
|
};
|
|
|
|
#define DATE_MODE_INIT { \
|
|
.type = DATE_NORMAL, \
|
|
}
|
|
|
|
/**
|
|
* Convenience helper for passing a constant type, like:
|
|
*
|
|
* show_date(t, tz, DATE_MODE(NORMAL));
|
|
*/
|
|
#define DATE_MODE(t) date_mode_from_type(DATE_##t)
|
|
struct date_mode date_mode_from_type(enum date_mode_type type);
|
|
|
|
/**
|
|
* Format <'time', 'timezone'> into static memory according to 'mode'
|
|
* and return it. The mode is an initialized "struct date_mode"
|
|
* (usually from the DATE_MODE() macro).
|
|
*/
|
|
const char *show_date(timestamp_t time, int timezone, struct date_mode mode);
|
|
|
|
/**
|
|
* Parse a date format for later use with show_date().
|
|
*
|
|
* When the "date_mode_type" is DATE_STRFTIME the "strftime_fmt"
|
|
* member of "struct date_mode" will be a malloc()'d format string to
|
|
* be used with strbuf_addftime(), in which case you'll need to call
|
|
* date_mode_release() later.
|
|
*/
|
|
void parse_date_format(const char *format, struct date_mode *mode);
|
|
|
|
/**
|
|
* Release a "struct date_mode", currently only required if
|
|
* parse_date_format() has parsed a "DATE_STRFTIME" format.
|
|
*/
|
|
void date_mode_release(struct date_mode *mode);
|
|
|
|
void show_date_relative(timestamp_t time, struct strbuf *timebuf);
|
|
int parse_date(const char *date, struct strbuf *out);
|
|
int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset);
|
|
int parse_expiry_date(const char *date, timestamp_t *timestamp);
|
|
void datestamp(struct strbuf *out);
|
|
#define approxidate(s) approxidate_careful((s), NULL)
|
|
timestamp_t approxidate_careful(const char *, int *);
|
|
int date_overflows(timestamp_t date);
|
|
time_t tm_to_time_t(const struct tm *tm);
|
|
|
|
/**
|
|
* Convert between the "[+-]HHMM" timezone format and minutes. This format is
|
|
* used for example as part of commit headers and reflogs. For example, the
|
|
* timezone -0100 is converted to -60 minutes.
|
|
*/
|
|
int tz_to_minutes(int tz);
|
|
int minutes_to_tz(int minutes);
|
|
|
|
#endif
|