Index: include/asterisk/cdr.h
===================================================================
--- include/asterisk/cdr.h	(revision 238312)
+++ include/asterisk/cdr.h	(working copy)
@@ -93,6 +93,8 @@
 	long int duration;				
 	/*! Total time call is up, in seconds */
 	long int billsec;				
+	/*! Total time call is up, accurate to milliseconds */
+	double hrbilltime;				
 	/*! What happened to the call */
 	long int disposition;
 	/*! What flags to use */
Index: main/cdr.c
===================================================================
--- main/cdr.c	(revision 238312)
+++ main/cdr.c	(working copy)
@@ -282,6 +282,8 @@
 		snprintf(workspace, workspacelen, "%ld", cdr->duration ? cdr->duration : (long)ast_tvdiff_ms(ast_tvnow(), cdr->start) / 1000);
 	else if (!strcasecmp(name, "billsec"))
 		snprintf(workspace, workspacelen, "%ld", cdr->billsec || cdr->answer.tv_sec == 0 ? cdr->billsec : (long)ast_tvdiff_ms(ast_tvnow(), cdr->answer) / 1000);
+	else if (!strcasecmp(name, "hrbilltime")) 
+		snprintf(workspace, workspacelen, "%.4f", cdr->hrbilltime ? cdr->hrbilltime : 0.0);
 	else if (!strcasecmp(name, "disposition")) {
 		if (raw) {
 			snprintf(workspace, workspacelen, "%ld", cdr->disposition);
@@ -319,7 +321,7 @@
 static const char * const cdr_readonly_vars[] = { "clid", "src", "dst", "dcontext", "channel", "dstchannel",
 						  "lastapp", "lastdata", "start", "answer", "end", "duration",
 						  "billsec", "disposition", "amaflags", "accountcode", "uniqueid", "linkedid",
-						  "userfield", "sequence", NULL };
+						  "userfield", "sequence", "hrbilltime", NULL };
 /*! Set a CDR channel variable 
 	\note You can't set the CDR variables that belong to the actual CDR record, like "billsec".
 */
@@ -617,6 +619,13 @@
 				from->end = ast_tv(0,0); /* we actively "steal" these values */
 				to->duration = to->end.tv_sec - to->start.tv_sec;  /* don't forget to update the duration, billsec, when we set end */
 				to->billsec = ast_tvzero(to->answer) ? 0 : to->end.tv_sec - to->answer.tv_sec;
+				
+				if (ast_tvzero(to->answer)) {
+					to->hrbilltime = (double) (to->end.tv_sec - to->answer.tv_sec) +
+										(double) ((to->end.tv_usec - to->answer.tv_usec)/1000000.0);
+				} else {
+					to->hrbilltime = 0.0;
+				}
 			}
 			/* else, nothing to do */
 		} else {
@@ -624,6 +633,12 @@
 			from->end = ast_tv(0,0); /* we actively "steal" these values */
 			to->duration = to->end.tv_sec - to->start.tv_sec;
 			to->billsec = ast_tvzero(to->answer) ? 0 : to->end.tv_sec - to->answer.tv_sec;
+			if (ast_tvzero(to->answer)) {
+				to->hrbilltime = (double) (to->end.tv_sec - to->answer.tv_sec) +
+									(double) ((to->end.tv_usec - to->answer.tv_usec)/1000000.0);
+			} else {
+				to->hrbilltime = 0.0;
+			}
 		}
 	}
 	if (to->disposition < from->disposition) {
@@ -938,6 +953,9 @@
 			}
 		} else {
 			cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec;
+			cdr->hrbilltime = (double) (cdr->end.tv_sec - cdr->answer.tv_sec) +
+									(double) ((cdr->end.tv_usec - cdr->answer.tv_usec)/1000000.0);
+
 			if (ast_test_flag(&ast_options, AST_OPT_FLAG_INITIATED_SECONDS))
 				cdr->billsec += cdr->end.tv_usec > cdr->answer.tv_usec ? 1 : 0;
 		}
@@ -1172,6 +1190,7 @@
 			memset(&cdr->end, 0, sizeof(cdr->end));
 			memset(&cdr->answer, 0, sizeof(cdr->answer));
 			cdr->billsec = 0;
+			cdr->hrbilltime = 0;
 			cdr->duration = 0;
 			ast_cdr_start(cdr);
 			cdr->disposition = AST_CDR_NOANSWER;
@@ -1198,6 +1217,7 @@
 	memset(&cdr->end, 0, sizeof(cdr->end));
 	memset(&cdr->answer, 0, sizeof(cdr->answer));
 	cdr->billsec = 0;
+	cdr->hrbilltime = 0;
 	cdr->duration = 0;
 	ast_cdr_start(cdr);
 	cdr->disposition = AST_CDR_NULL;
Index: cdr/cdr_csv.c
===================================================================
--- cdr/cdr_csv.c	(revision 238312)
+++ cdr/cdr_csv.c	(working copy)
@@ -82,6 +82,7 @@
   "amaflags",       	DOCUMENTATION, BILL, IGNORE etc, specified on a per channel basis like accountcode.
   "uniqueid",           unique call identifier
   "userfield"		user field set via SetCDRUserField
+  "hrbilltime"		the duration down to the millisecond
 ----------------------------------------------------------*/
 
 static char *name = "csv";
@@ -178,6 +179,24 @@
 	return 0;
 }
 
+static int append_double(char *buf, double s, size_t bufsize)
+{
+	char tmp[32];
+	int pos = strlen(buf);
+
+	snprintf(tmp, sizeof(tmp), "%.4f", s);
+
+	if (pos + strlen(tmp) > bufsize - 3)
+		return -1;
+
+	strncat(buf, tmp, bufsize - strlen(buf) - 1);
+	pos = strlen(buf);
+	buf[pos++] = ',';
+	buf[pos++] = '\0';
+
+	return 0;
+}
+
 static int append_date(char *buf, struct timeval when, size_t bufsize)
 {
 	char tmp[80] = "";
@@ -229,6 +248,8 @@
 	append_int(buf, cdr->duration, bufsize);
 	/* Billable seconds */
 	append_int(buf, cdr->billsec, bufsize);
+	/* Billable seconds to the millisecond */
+	append_double(buf, cdr->hrbilltime, bufsize);
 	/* Disposition */
 	append_string(buf, ast_cdr_disp2str(cdr->disposition), bufsize);
 	/* AMA Flags */
