diff --git a/cdrc/cdrc.go b/cdrc/cdrc.go index 209ca1360..fa997822e 100644 --- a/cdrc/cdrc.go +++ b/cdrc/cdrc.go @@ -133,7 +133,7 @@ func (self *Cdrc) recordToStoredCdr(record []string) (*utils.StoredCdr, error) { return nil, fmt.Errorf("Cannot parse answer time field with value: %s, err: %s", fieldVal, err.Error()) } case utils.USAGE: - if storedCdr.Usage, err = utils.ParseDurationWithNanosecs(fieldVal); err != nil { + if storedCdr.Usage, err = utils.ParseDurationWithSecs(fieldVal); err != nil { return nil, fmt.Errorf("Cannot parse duration field with value: %s, err: %s", fieldVal, err.Error()) } default: // Extra fields will not match predefined so they all show up here diff --git a/cdrc/cdrc_test.go b/cdrc/cdrc_test.go index 37b923477..faae9cd25 100644 --- a/cdrc/cdrc_test.go +++ b/cdrc/cdrc_test.go @@ -44,7 +44,7 @@ func TestRecordForkCdr(t *testing.T) { t.Error("Failed to corectly detect missing fields from record") } cdrRow = []string{"ignored", "ignored", utils.VOICE, "acc1", "prepaid", "*out", "cgrates.org", "call", "1001", "1001", "+4986517174963", - "2013-02-03 19:50:00", "2013-02-03 19:54:00", "62000000000", "supplier1", "172.16.1.1"} + "2013-02-03 19:50:00", "2013-02-03 19:54:00", "62", "supplier1", "172.16.1.1"} rtCdr, err := cdrc.recordToStoredCdr(cdrRow) if err != nil { t.Error("Failed to parse CDR in rated cdr", err) diff --git a/cdre/csv_test.go b/cdre/csv_test.go index 257198cf2..14c4732f8 100644 --- a/cdre/csv_test.go +++ b/cdre/csv_test.go @@ -47,7 +47,7 @@ func TestCsvCdrWriter(t *testing.T) { if err := cdre.writeCsv(csvWriter); err != nil { t.Error("Unexpected error: ", err) } - expected := `dbafe9c8614c785a65aabd116dd3959c3c56f7f6,default,*voice,dsafdsaf,rated,*out,cgrates.org,call,1001,1001,1002,2013-11-07T08:42:25Z,2013-11-07T08:42:26Z,10000000000,1.0100` + expected := `dbafe9c8614c785a65aabd116dd3959c3c56f7f6,default,*voice,dsafdsaf,rated,*out,cgrates.org,call,1001,1001,1002,2013-11-07T08:42:25Z,2013-11-07T08:42:26Z,10,1.0100` result := strings.TrimSpace(writer.String()) if result != expected { t.Errorf("Expected: \n%s received: \n%s.", expected, result) @@ -75,7 +75,7 @@ func TestAlternativeFieldSeparator(t *testing.T) { if err := cdre.writeCsv(csvWriter); err != nil { t.Error("Unexpected error: ", err) } - expected := `dbafe9c8614c785a65aabd116dd3959c3c56f7f6|default|*voice|dsafdsaf|rated|*out|cgrates.org|call|1001|1001|1002|2013-11-07T08:42:25Z|2013-11-07T08:42:26Z|10000000000|1.0100` + expected := `dbafe9c8614c785a65aabd116dd3959c3c56f7f6|default|*voice|dsafdsaf|rated|*out|cgrates.org|call|1001|1001|1002|2013-11-07T08:42:25Z|2013-11-07T08:42:26Z|10|1.0100` result := strings.TrimSpace(writer.String()) if result != expected { t.Errorf("Expected: \n%s received: \n%s.", expected, result) diff --git a/utils/cgrcdr.go b/utils/cgrcdr.go index 0f424d560..85b9852d9 100644 --- a/utils/cgrcdr.go +++ b/utils/cgrcdr.go @@ -69,7 +69,7 @@ func (cgrCdr CgrCdr) AsStoredCdr() *StoredCdr { storCdr.Destination = cgrCdr[DESTINATION] storCdr.SetupTime, _ = ParseTimeDetectLayout(cgrCdr[SETUP_TIME]) // Not interested to process errors, should do them if necessary in a previous step storCdr.AnswerTime, _ = ParseTimeDetectLayout(cgrCdr[ANSWER_TIME]) - storCdr.Usage, _ = ParseDurationWithNanosecs(cgrCdr[USAGE]) + storCdr.Usage, _ = ParseDurationWithSecs(cgrCdr[USAGE]) storCdr.ExtraFields = cgrCdr.getExtraFields() storCdr.Cost = -1 return storCdr diff --git a/utils/cgrcdr_test.go b/utils/cgrcdr_test.go index e50e6f31d..178a4262e 100644 --- a/utils/cgrcdr_test.go +++ b/utils/cgrcdr_test.go @@ -34,7 +34,7 @@ func TestCgrCdrInterfaces(t *testing.T) { func TestCgrCdrAsStoredCdr(t *testing.T) { cgrCdr := CgrCdr{TOR: VOICE, ACCID: "dsafdsaf", CDRHOST: "192.168.1.1", CDRSOURCE: "internal_test", REQTYPE: "rated", DIRECTION: "*out", TENANT: "cgrates.org", CATEGORY: "call", - ACCOUNT: "1001", SUBJECT: "1001", DESTINATION: "1002", SETUP_TIME: "2013-11-07T08:42:20Z", ANSWER_TIME: "2013-11-07T08:42:26Z", USAGE: "10000000000", + ACCOUNT: "1001", SUBJECT: "1001", DESTINATION: "1002", SETUP_TIME: "2013-11-07T08:42:20Z", ANSWER_TIME: "2013-11-07T08:42:26Z", USAGE: "10", "field_extr1": "val_extr1", "fieldextr2": "valextr2"} setupTime, _ := ParseTimeDetectLayout(cgrCdr["setup_time"]) expctRtCdr := &StoredCdr{CgrId: Sha1(cgrCdr["accid"], setupTime.String()), TOR: VOICE, AccId: cgrCdr["accid"], CdrHost: cgrCdr["cdrhost"], CdrSource: cgrCdr["cdrsource"], ReqType: cgrCdr["reqtype"], diff --git a/utils/coreutils.go b/utils/coreutils.go index 65a90e673..64ca6ad23 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -208,19 +208,14 @@ func CopyHour(src, dest time.Time) time.Time { return time.Date(dest.Year(), dest.Month(), dest.Day(), src.Hour(), src.Minute(), src.Second(), src.Nanosecond(), src.Location()) } -// Parses duration, considers s as time unit if not provided +// Parses duration, considers s as time unit if not provided, seconds as float to specify subunits func ParseDurationWithSecs(durStr string) (time.Duration, error) { - if _, err := strconv.Atoi(durStr); err == nil { // No suffix, default to seconds - durStr += "s" + if durSecs, err := strconv.ParseFloat(durStr, 64); err == nil { // Seconds format considered + durNanosecs := int(durSecs * 1000000000) + return time.Duration(durNanosecs), nil + } else { + return time.ParseDuration(durStr) } - return time.ParseDuration(durStr) -} - -func ParseDurationWithNanosecs(durStr string) (time.Duration, error) { - if _, err := strconv.Atoi(durStr); err == nil { // No suffix, default to seconds - durStr += "ns" - } - return time.ParseDuration(durStr) } func AccountKey(tenant, account, direction string) string { diff --git a/utils/storedcdr.go b/utils/storedcdr.go index 03beba85e..6b9e43844 100644 --- a/utils/storedcdr.go +++ b/utils/storedcdr.go @@ -77,14 +77,8 @@ func (storedCdr *StoredCdr) FormatUsage(layout string) string { return strconv.FormatFloat(Round(storedCdr.Usage.Seconds(), 0, ROUNDING_MIDDLE), 'f', -1, 64) } switch layout { - case HOURS: - return strconv.FormatFloat(Round(storedCdr.Usage.Seconds(), 0, ROUNDING_MIDDLE), 'f', -1, 64) - case MINUTES: - return strconv.FormatFloat(Round(storedCdr.Usage.Seconds(), 0, ROUNDING_MIDDLE), 'f', -1, 64) - case SECONDS: - return strconv.FormatFloat(Round(storedCdr.Usage.Seconds(), 0, ROUNDING_MIDDLE), 'f', -1, 64) default: - return strconv.FormatInt(storedCdr.Usage.Nanoseconds(), 10) + return strconv.FormatFloat(float64(storedCdr.Usage.Nanoseconds())/1000000000, 'f', -1, 64) } } @@ -122,10 +116,7 @@ func (storedCdr *StoredCdr) FieldAsString(rsrFld *RSRField) string { case ANSWER_TIME: return rsrFld.ParseValue(storedCdr.AnswerTime.String()) case USAGE: - //if IsSliceMember([]string{DATA, SMS}, storedCdr.TOR) { - // return strconv.FormatFloat(Round(storedCdr.Usage.Seconds(), 0, ROUNDING_MIDDLE), 'f', -1, 64) - //} - return rsrFld.ParseValue(strconv.FormatInt(storedCdr.Usage.Nanoseconds(), 10)) + return strconv.FormatFloat(Round(storedCdr.Usage.Seconds(), 0, ROUNDING_MIDDLE), 'f', -1, 64) case MEDI_RUNID: return rsrFld.ParseValue(storedCdr.MediationRunId) case RATED_ACCOUNT: @@ -182,7 +173,7 @@ func (storedCdr *StoredCdr) AsHttpForm() url.Values { v.Set(DESTINATION, storedCdr.Destination) v.Set(SETUP_TIME, storedCdr.SetupTime.String()) v.Set(ANSWER_TIME, storedCdr.AnswerTime.String()) - v.Set(USAGE, strconv.FormatInt(storedCdr.Usage.Nanoseconds(), 10)) + v.Set(USAGE, storedCdr.FormatUsage(SECONDS)) return v } @@ -301,7 +292,7 @@ func (storedCdr *StoredCdr) ForkCdr(runId string, reqTypeFld, directionFld, tena durStr := storedCdr.FieldAsString(durationFld) if primaryMandatory && len(durStr) == 0 { return nil, errors.New(fmt.Sprintf("%s:%s:%s", ERR_MANDATORY_IE_MISSING, USAGE, durationFld.Id)) - } else if frkStorCdr.Usage, err = ParseDurationWithNanosecs(durStr); err != nil { + } else if frkStorCdr.Usage, err = ParseDurationWithSecs(durStr); err != nil { return nil, err } frkStorCdr.ExtraFields = make(map[string]string, len(extraFlds)) diff --git a/utils/storedcdr_test.go b/utils/storedcdr_test.go index 8e33a591e..9bf45c31a 100644 --- a/utils/storedcdr_test.go +++ b/utils/storedcdr_test.go @@ -48,7 +48,7 @@ func TestFieldAsString(t *testing.T) { cdr.FieldAsString(&RSRField{Id: DESTINATION}) != cdr.Destination || cdr.FieldAsString(&RSRField{Id: SETUP_TIME}) != cdr.SetupTime.String() || cdr.FieldAsString(&RSRField{Id: ANSWER_TIME}) != cdr.AnswerTime.String() || - cdr.FieldAsString(&RSRField{Id: USAGE}) != "10000000000" || + cdr.FieldAsString(&RSRField{Id: USAGE}) != "10" || cdr.FieldAsString(&RSRField{Id: MEDI_RUNID}) != cdr.MediationRunId || cdr.FieldAsString(&RSRField{Id: COST}) != "1.01" || cdr.FieldAsString(&RSRField{Id: RATED_ACCOUNT}) != "dan" || @@ -71,7 +71,7 @@ func TestFieldAsString(t *testing.T) { cdr.FieldAsString(&RSRField{Id: DESTINATION}) != cdr.Destination, cdr.FieldAsString(&RSRField{Id: SETUP_TIME}) != cdr.SetupTime.String(), cdr.FieldAsString(&RSRField{Id: ANSWER_TIME}) != cdr.AnswerTime.String(), - cdr.FieldAsString(&RSRField{Id: USAGE}) != "10000000000", + cdr.FieldAsString(&RSRField{Id: USAGE}) != "10", cdr.FieldAsString(&RSRField{Id: MEDI_RUNID}) != cdr.MediationRunId, cdr.FieldAsString(&RSRField{Id: RATED_ACCOUNT}) != "dan", cdr.FieldAsString(&RSRField{Id: RATED_SUBJECT}) != "dans", @@ -225,7 +225,7 @@ func TestFormatUsage(t *testing.T) { if cdr.FormatUsage(SECONDS) != "10" { t.Error("Wrong usage format: ", cdr.FormatUsage(SECONDS)) } - if cdr.FormatUsage("default") != "10000000000" { + if cdr.FormatUsage("default") != "10" { t.Error("Wrong usage format: ", cdr.FormatUsage("default")) } cdr = StoredCdr{TOR: DATA, Usage: time.Duration(1640113000000000)} @@ -280,8 +280,8 @@ func TestStoredCdrAsHttpForm(t *testing.T) { if cdrForm.Get(ANSWER_TIME) != "2013-11-07 08:42:26 +0000 UTC" { t.Errorf("Expected: %s, received: %s", "2013-11-07 08:42:26 +0000 UTC", cdrForm.Get(ANSWER_TIME)) } - if cdrForm.Get(USAGE) != "10000000000" { - t.Errorf("Expected: %s, received: %s", "10000000000", cdrForm.Get(USAGE)) + if cdrForm.Get(USAGE) != "10" { + t.Errorf("Expected: %s, received: %s", "10", cdrForm.Get(USAGE)) } if cdrForm.Get("field_extr1") != "val_extr1" { t.Errorf("Expected: %s, received: %s", "val_extr1", cdrForm.Get("field_extr1")) diff --git a/utils/utils_test.go b/utils/utils_test.go index e904b9633..c223cefe6 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -391,30 +391,6 @@ func TestParseDurationWithSecs(t *testing.T) { } } -func TestParseDurationWithNanosecs(t *testing.T) { - durStr := "2" - durExpected := time.Duration(2) * time.Nanosecond - if parsed, err := ParseDurationWithNanosecs(durStr); err != nil { - t.Error(err) - } else if parsed != durExpected { - t.Error("Parsed different than expected") - } - durStr = "2s" - durExpected = time.Duration(2) * time.Second - if parsed, err := ParseDurationWithNanosecs(durStr); err != nil { - t.Error(err) - } else if parsed != durExpected { - t.Error("Parsed different than expected") - } - durStr = "2ms" - durExpected = time.Duration(2) * time.Millisecond - if parsed, err := ParseDurationWithNanosecs(durStr); err != nil { - t.Error(err) - } else if parsed != durExpected { - t.Error("Parsed different than expected") - } -} - func TestMinDuration(t *testing.T) { d1, _ := time.ParseDuration("1m") d2, _ := time.ParseDuration("59s")