Commit 13710f1e86d9048b88361092a94b4648aeb864fd

nulltoken 2010-12-10T16:30:06

Added timezone offset parsing and outputting.

diff --git a/src/commit.c b/src/commit.c
index d39fc54..d8a72a9 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -159,6 +159,7 @@ int commit_parse_buffer(git_commit *commit, void *data, size_t len, unsigned int
 		return error;
 
 	commit->commit_time = commit->committer->time;
+	commit->commit_timezone_offset = commit->committer->timezone_offset;
 
 	/* parse commit message */
 	while (buffer <= buffer_end && *buffer == '\n')
@@ -249,6 +250,19 @@ time_t git_commit_time(git_commit *commit)
 	return commit->commit_time;
 }
 
+int git_commit_timezone_offset(git_commit *commit)
+{
+	assert(commit);
+
+	if (commit->commit_timezone_offset)
+		return commit->commit_timezone_offset;
+
+	if (!commit->object.in_memory)
+		git_commit__parse_full(commit);
+
+	return commit->commit_timezone_offset;
+}
+
 unsigned int git_commit_parentcount(git_commit *commit)
 {
 	assert(commit);
@@ -269,24 +283,24 @@ void git_commit_set_tree(git_commit *commit, git_tree *tree)
 	commit->tree = tree;
 }
 
-void git_commit_set_author(git_commit *commit, const char *name, const char *email, time_t time)
+void git_commit_set_author(git_commit *commit, const char *name, const char *email, time_t time, int offset)
 {
 	assert(commit && name && email);
 	commit->object.modified = 1;
 	CHECK_FULL_PARSE();
 
 	git_person__free(commit->author);
-	commit->author = git_person__new(name, email, time);
+	commit->author = git_person__new(name, email, time, offset);
 }
 
-void git_commit_set_committer(git_commit *commit, const char *name, const char *email, time_t time)
+void git_commit_set_committer(git_commit *commit, const char *name, const char *email, time_t time, int offset)
 {
 	assert(commit && name && email);
 	commit->object.modified = 1;
 	CHECK_FULL_PARSE();
 
 	git_person__free(commit->committer);
-	commit->committer = git_person__new(name, email, time);
+	commit->committer = git_person__new(name, email, time, offset);
 	commit->commit_time = time;
 }
 
diff --git a/src/commit.h b/src/commit.h
index eca566d..8c76c3b 100644
--- a/src/commit.h
+++ b/src/commit.h
@@ -12,6 +12,8 @@ struct git_commit {
 	git_object object;
 
 	time_t commit_time;
+	int commit_timezone_offset;
+
 	git_vector parents;
 
 	git_tree *tree;
diff --git a/src/git2/commit.h b/src/git2/commit.h
index 7286797..54b0ed8 100644
--- a/src/git2/commit.h
+++ b/src/git2/commit.h
@@ -93,6 +93,13 @@ GIT_EXTERN(const char *) git_commit_message(git_commit *commit);
 GIT_EXTERN(time_t) git_commit_time(git_commit *commit);
 
 /**
+ * Get the commit timezone offset (i.e. committer's preferred timezone) of a commit.
+ * @param commit a previously loaded commit.
+ * @return positive or negative timezone offset, in minutes from UTC
+ */
+GIT_EXTERN(int) git_commit_timezone_offset(git_commit *commit);
+
+/**
  * Get the committer of a commit.
  * @param commit a previously loaded commit.
  * @return the committer of a commit
@@ -150,8 +157,9 @@ GIT_EXTERN(void) git_commit_set_message(git_commit *commit, const char *message)
  * @param name name of the new committer
  * @param email email of the new committer
  * @param time time when the committer committed the commit
+ * @param offset committer positive or negative timezone offset, in minutes from UTC
  */
-GIT_EXTERN(void) git_commit_set_committer(git_commit *commit, const char *name, const char *email, time_t time);
+GIT_EXTERN(void) git_commit_set_committer(git_commit *commit, const char *name, const char *email, time_t time, int offset);
 
 /**
  * Set the author of a commit
@@ -159,8 +167,9 @@ GIT_EXTERN(void) git_commit_set_committer(git_commit *commit, const char *name, 
  * @param name name of the new author
  * @param email email of the new author
  * @param time time when the author created the commit
+ * @param offset author positive or negative timezone offset, in minutes from UTC
  */
-GIT_EXTERN(void) git_commit_set_author(git_commit *commit, const char *name, const char *email, time_t time);
+GIT_EXTERN(void) git_commit_set_author(git_commit *commit, const char *name, const char *email, time_t time, int offset);
 
 /**
  * Set the tree which is pointed to by a commit
diff --git a/src/git2/common.h b/src/git2/common.h
index b819f05..9734074 100644
--- a/src/git2/common.h
+++ b/src/git2/common.h
@@ -143,6 +143,7 @@ typedef struct git_person git_person;
 const char *git_person_name(git_person *person);
 const char *git_person_email(git_person *person);
 time_t git_person_time(git_person *person);
+int git_person_timezone_offset(git_person *person);
 
 /** @} */
 GIT_END_DECL
diff --git a/src/git2/tag.h b/src/git2/tag.h
index 39b45ac..d78235e 100644
--- a/src/git2/tag.h
+++ b/src/git2/tag.h
@@ -125,8 +125,9 @@ GIT_EXTERN(void) git_tag_set_name(git_tag *tag, const char *name);
  * @param name the name of the new tagger
  * @param email the email of the new tagger
  * @param time the time when the tag was created
+ * @param offset tagger positive or negative timezone offset, in minutes from UTC
  */
-GIT_EXTERN(void) git_tag_set_tagger(git_tag *tag, const char *name, const char *email, time_t time);
+GIT_EXTERN(void) git_tag_set_tagger(git_tag *tag, const char *name, const char *email, time_t time, int offset);
 
 /**
  * Set the message of a tag
diff --git a/src/person.c b/src/person.c
index e93e1b8..8372d6e 100644
--- a/src/person.c
+++ b/src/person.c
@@ -38,7 +38,7 @@ void git_person__free(git_person *person)
 	free(person);
 }
 
-git_person *git_person__new(const char *name, const char *email, time_t time)
+git_person *git_person__new(const char *name, const char *email, time_t time, int offset)
 {
 	git_person *p;
 
@@ -48,6 +48,7 @@ git_person *git_person__new(const char *name, const char *email, time_t time)
 	p->name = git__strdup(name);
 	p->email = git__strdup(email);
 	p->time = time;
+	p->timezone_offset = offset;
 
 	if (p->name == NULL || p->email == NULL)
 		goto cleanup;
@@ -74,6 +75,51 @@ time_t git_person_time(git_person *person)
 	return person->time;
 }
 
+int git_person_timezone_offset(git_person *person)
+{
+	return person->timezone_offset;
+}
+
+int git_person__parse_timezone_offset(const char *buffer, int *offset_out)
+{
+	int offset, dec_offset;
+	int mins, hours;
+
+	const char* offset_start;
+	char* offset_end;
+
+	offset_start = buffer + 1;
+
+	if (offset_start[0] != '-' && offset_start[0] != '+')
+		return GIT_EOBJCORRUPTED;
+
+	dec_offset = strtol(offset_start + 1, &offset_end, 10);
+
+	if (offset_end - offset_start != 5)
+		return GIT_EOBJCORRUPTED;
+
+	hours = dec_offset / 100;
+	mins = dec_offset % 100;
+
+	if (hours > 14)			// see http://www.worldtimezone.com/faq.html 
+		return GIT_EOBJCORRUPTED;
+
+	if (mins > 59) 
+		return GIT_EOBJCORRUPTED;
+
+	offset = (hours * 60) + mins;
+
+	if (offset_start[0] == '-')
+	{
+		offset *= -1;
+	}
+	
+	*offset_out = offset;
+
+	return GIT_SUCCESS;
+}
+
+
 int git_person__parse(git_person *person, char **buffer_out,
 		const char *buffer_end, const char *header)
 {
@@ -82,6 +128,7 @@ int git_person__parse(git_person *person, char **buffer_out,
 	int name_length, email_length;
 	char *buffer = *buffer_out;
 	char *line_end, *name_end, *email_end;
+	int offset = 0;
 
 	memset(person, 0x0, sizeof(git_person));
 
@@ -128,13 +175,30 @@ int git_person__parse(git_person *person, char **buffer_out,
 	if (person->time == 0)
 		return GIT_EOBJCORRUPTED;
 
+	if (git_person__parse_timezone_offset(buffer, &offset) < GIT_SUCCESS)
+		return GIT_EOBJCORRUPTED;
+	
+	person->timezone_offset = offset;
+
 	*buffer_out = (line_end + 1);
 	return GIT_SUCCESS;
 }
 
 int git_person__write(git_odb_source *src, const char *header, const git_person *person)
 {
-	return git__source_printf(src, "%s %s <%s> %u\n", header, person->name, person->email, person->time);
+	char *sign;
+	int offset, hours, mins;
+
+	offset = person->timezone_offset;
+	sign = (person->timezone_offset < 0) ? "-" : "+";
+	
+	if (offset < 0)
+		offset = -offset;
+
+	hours = offset / 60;
+	mins = offset % 60;
+
+	return git__source_printf(src, "%s %s <%s> %u %s%02d%02d\n", header, person->name, person->email, person->time, sign, hours, mins);
 }
 
 
diff --git a/src/person.h b/src/person.h
index 7a9fcb7..d7ccbc9 100644
--- a/src/person.h
+++ b/src/person.h
@@ -10,10 +10,11 @@ struct git_person {
 	char *name; /**< Full name */
 	char *email; /**< Email address */
 	time_t time; /**< Time when this person committed the change */
+	int timezone_offset; /**< Time zone offset in minutes. Can be either positive or negative. */
 };
 
 void git_person__free(git_person *person);
-git_person *git_person__new(const char *name, const char *email, time_t time);
+git_person *git_person__new(const char *name, const char *email, time_t time, int offset);
 int git_person__parse(git_person *person, char **buffer_out, const char *buffer_end, const char *header);
 int git_person__write(git_odb_source *src, const char *header, const git_person *person);
 
diff --git a/src/tag.c b/src/tag.c
index b5b8b9f..7485daa 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -97,13 +97,13 @@ const git_person *git_tag_tagger(git_tag *t)
 	return t->tagger;
 }
 
-void git_tag_set_tagger(git_tag *tag, const char *name, const char *email, time_t time)
+void git_tag_set_tagger(git_tag *tag, const char *name, const char *email, time_t time, int offset)
 {
 	assert(tag && name && email);
 	tag->object.modified = 1;
 
 	git_person__free(tag->tagger);
-	tag->tagger = git_person__new(name, email, time);
+	tag->tagger = git_person__new(name, email, time, offset);
 }
 
 const char *git_tag_message(git_tag *t)
diff --git a/tests/t0403-write.c b/tests/t0403-write.c
index 8b29b05..889c9e1 100644
--- a/tests/t0403-write.c
+++ b/tests/t0403-write.c
@@ -42,8 +42,8 @@ BEGIN_TEST(writenew_test)
 	git_commit_add_parent(commit, parent);
 
 	/* Set other attributes */
-	git_commit_set_committer(commit, COMMITTER_NAME, COMMITTER_EMAIL, 123456789);
-	git_commit_set_author(commit, COMMITTER_NAME, COMMITTER_EMAIL, 987654321);
+	git_commit_set_committer(commit, COMMITTER_NAME, COMMITTER_EMAIL, 123456789, 60);
+	git_commit_set_author(commit, COMMITTER_NAME, COMMITTER_EMAIL, 987654321, 90);
 	git_commit_set_message(commit, COMMIT_MESSAGE);
 
 	/* Check attributes were set correctly */
@@ -52,13 +52,16 @@ BEGIN_TEST(writenew_test)
 	must_be_true(strcmp(author->name, COMMITTER_NAME) == 0);
 	must_be_true(strcmp(author->email, COMMITTER_EMAIL) == 0);
 	must_be_true(author->time == 987654321);
+	must_be_true(author->time_offset == 90);
 
 	committer = git_commit_committer(commit);
 	must_be_true(committer != NULL);
 	must_be_true(strcmp(committer->name, COMMITTER_NAME) == 0);
 	must_be_true(strcmp(committer->email, COMMITTER_EMAIL) == 0);
 	must_be_true(committer->time == 123456789);
+	must_be_true(committer->time_offset == 60);
 	must_be_true(git_commit_time(commit) == 123456789);
+	must_be_true(git_commit_time_offset(commit) == 60);
 
 	must_be_true(strcmp(git_commit_message(commit), COMMIT_MESSAGE) == 0);