diff --git a/apier/v1/apier.go b/apier/v1/apier.go index a40eb161b..3f47f614c 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -205,7 +205,7 @@ func (self *ApierV1) LoadDestination(attrs AttrLoadDestination, reply *string) e if len(attrs.TPid) == 0 { return utils.NewErrMandatoryIeMissing("TPid") } - dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid) + dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid, self.Config.DefaultTimezone) if loaded, err := dbReader.LoadDestinationsFiltered(attrs.DestinationId); err != nil { return utils.NewErrServerError(err) } else if !loaded { @@ -229,7 +229,7 @@ func (self *ApierV1) LoadDerivedChargers(attrs utils.TPDerivedChargers, reply *s if len(attrs.TPid) == 0 { return utils.NewErrMandatoryIeMissing("TPid") } - dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid) + dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid, self.Config.DefaultTimezone) dc := engine.APItoModelDerivedCharger(&attrs) if err := dbReader.LoadDerivedChargersFiltered(&dc[0], true); err != nil { return utils.NewErrServerError(err) @@ -256,7 +256,7 @@ func (self *ApierV1) LoadRatingPlan(attrs AttrLoadRatingPlan, reply *string) err if len(attrs.TPid) == 0 { return utils.NewErrMandatoryIeMissing("TPid") } - dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid) + dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid, self.Config.DefaultTimezone) if loaded, err := dbReader.LoadRatingPlansFiltered(attrs.RatingPlanId); err != nil { return utils.NewErrServerError(err) } else if !loaded { @@ -282,7 +282,7 @@ func (self *ApierV1) LoadRatingProfile(attrs utils.TPRatingProfile, reply *strin if len(attrs.TPid) == 0 { return utils.NewErrMandatoryIeMissing("TPid") } - dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid) + dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid, self.Config.DefaultTimezone) rp := engine.APItoModelRatingProfile(&attrs) if err := dbReader.LoadRatingProfilesFiltered(&rp[0]); err != nil { return utils.NewErrServerError(err) @@ -309,7 +309,7 @@ func (self *ApierV1) LoadSharedGroup(attrs AttrLoadSharedGroup, reply *string) e if len(attrs.TPid) == 0 { return utils.NewErrMandatoryIeMissing("TPid") } - dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid) + dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid, self.Config.DefaultTimezone) if err := dbReader.LoadSharedGroupsFiltered(attrs.SharedGroupId, true); err != nil { return utils.NewErrServerError(err) } @@ -335,7 +335,7 @@ func (self *ApierV1) LoadCdrStats(attrs AttrLoadCdrStats, reply *string) error { if len(attrs.TPid) == 0 { return utils.NewErrMandatoryIeMissing("TPid") } - dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid) + dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid, self.Config.DefaultTimezone) if err := dbReader.LoadCdrStatsFiltered(attrs.CdrStatsId, true); err != nil { return utils.NewErrServerError(err) } @@ -355,7 +355,7 @@ func (self *ApierV1) LoadTariffPlanFromStorDb(attrs AttrLoadTpFromStorDb, reply if len(attrs.TPid) == 0 { return utils.NewErrMandatoryIeMissing("TPid") } - dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid) + dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid, self.Config.DefaultTimezone) if err := dbReader.LoadAll(); err != nil { return utils.NewErrServerError(err) } @@ -706,7 +706,7 @@ func (self *ApierV1) AddTriggeredAction(attr AttrAddActionTrigger, reply *string if attr.BalanceDirection == "" { attr.BalanceDirection = engine.OUTBOUND } - balExpiryTime, err := utils.ParseTimeDetectLayout(attr.BalanceExpiryTime) + balExpiryTime, err := utils.ParseTimeDetectLayout(attr.BalanceExpiryTime, self.Config.DefaultTimezone) if err != nil { return utils.NewErrServerError(err) } @@ -822,7 +822,7 @@ func (self *ApierV1) LoadAccountActions(attrs utils.TPAccountActions, reply *str if len(attrs.TPid) == 0 { return utils.NewErrMandatoryIeMissing("TPid") } - dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid) + dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid, self.Config.DefaultTimezone) if _, err := engine.Guardian.Guard(func() (interface{}, error) { aas := engine.APItoModelAccountAction(&attrs) if err := dbReader.LoadAccountActionsFiltered(aas); err != nil { @@ -1024,7 +1024,7 @@ func (self *ApierV1) LoadTariffPlanFromFolder(attrs utils.AttrLoadTpFromFolder, path.Join(attrs.FolderPath, utils.ACCOUNT_ACTIONS_CSV), path.Join(attrs.FolderPath, utils.DERIVED_CHARGERS_CSV), path.Join(attrs.FolderPath, utils.CDR_STATS_CSV), - path.Join(attrs.FolderPath, utils.USERS_CSV)), "") + path.Join(attrs.FolderPath, utils.USERS_CSV)), "", self.Config.DefaultTimezone) if err := loader.LoadAll(); err != nil { return utils.NewErrServerError(err) } diff --git a/apier/v1/auth.go b/apier/v1/auth.go index d0ef5e8b3..3c14dfaa6 100644 --- a/apier/v1/auth.go +++ b/apier/v1/auth.go @@ -57,7 +57,7 @@ func (self *ApierV1) GetMaxUsage(usageRecord engine.UsageRecord, maxUsage *float if usageRecord.Usage == "" { usageRecord.Usage = strconv.FormatFloat(self.Config.MaxCallDuration.Seconds(), 'f', -1, 64) } - storedCdr, err := usageRecord.AsStoredCdr() + storedCdr, err := usageRecord.AsStoredCdr(self.Config.DefaultTimezone) if err != nil { return utils.NewErrServerError(err) } diff --git a/apier/v1/cdre.go b/apier/v1/cdre.go index e5e93356f..96490fdda 100644 --- a/apier/v1/cdre.go +++ b/apier/v1/cdre.go @@ -164,7 +164,7 @@ func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.E if attr.MaskLength != nil { maskLen = *attr.MaskLength } - cdrsFltr, err := attr.AsCdrsFilter() + cdrsFltr, err := attr.AsCdrsFilter(self.Config.DefaultTimezone) if err != nil { return utils.NewErrServerError(err) } @@ -176,7 +176,7 @@ func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.E return nil } cdrexp, err := cdre.NewCdrExporter(cdrs, self.CdrDb, exportTemplate, cdrFormat, fieldSep, exportId, dataUsageMultiplyFactor, smsUsageMultiplyFactor, genericUsageMultiplyFactor, - costMultiplyFactor, costShiftDigits, roundingDecimals, self.Config.RoundingDecimals, maskDestId, maskLen, self.Config.HttpSkipTlsVerify) + costMultiplyFactor, costShiftDigits, roundingDecimals, self.Config.RoundingDecimals, maskDestId, maskLen, self.Config.HttpSkipTlsVerify, self.Config.DefaultTimezone) if err != nil { return utils.NewErrServerError(err) } diff --git a/apier/v1/cdrs.go b/apier/v1/cdrs.go index 44cd50771..909766a0a 100644 --- a/apier/v1/cdrs.go +++ b/apier/v1/cdrs.go @@ -43,7 +43,7 @@ func (apier *ApierV1) GetCallCostLog(attrs utils.AttrGetCallCost, reply *engine. // Retrieves CDRs based on the filters func (apier *ApierV1) GetCdrs(attrs utils.AttrGetCdrs, reply *[]*engine.ExternalCdr) error { - cdrsFltr, err := attrs.AsCdrsFilter() + cdrsFltr, err := attrs.AsCdrsFilter(apier.Config.DefaultTimezone) if err != nil { return utils.NewErrServerError(err) } diff --git a/apier/v1/cdrsv1.go b/apier/v1/cdrsv1.go index afa46d850..9e7f70cf4 100644 --- a/apier/v1/cdrsv1.go +++ b/apier/v1/cdrsv1.go @@ -53,12 +53,12 @@ func (self *CdrsV1) RateCdrs(attrs utils.AttrRateCdrs, reply *string) error { var tStart, tEnd time.Time var err error if len(attrs.TimeStart) != 0 { - if tStart, err = utils.ParseTimeDetectLayout(attrs.TimeStart); err != nil { + if tStart, err = utils.ParseTimeDetectLayout(attrs.TimeStart, self.CdrSrv.Timezone()); err != nil { return err } } if len(attrs.TimeEnd) != 0 { - if tEnd, err = utils.ParseTimeDetectLayout(attrs.TimeEnd); err != nil { + if tEnd, err = utils.ParseTimeDetectLayout(attrs.TimeEnd, self.CdrSrv.Timezone()); err != nil { return err } } diff --git a/apier/v1/debit.go b/apier/v1/debit.go index 9daf87fd1..01f4d1cd0 100644 --- a/apier/v1/debit.go +++ b/apier/v1/debit.go @@ -54,7 +54,7 @@ func (self *ApierV1) DebitUsage(usageRecord engine.UsageRecord, reply *string) e if usageRecord.AnswerTime == "" { usageRecord.AnswerTime = utils.META_NOW } - cd, err := usageRecord.AsCallDescriptor() + cd, err := usageRecord.AsCallDescriptor(self.Config.DefaultTimezone) if err != nil { return utils.NewErrServerError(err) } diff --git a/apier/v1/lcr.go b/apier/v1/lcr.go index 2b8966ac1..15ff407f1 100644 --- a/apier/v1/lcr.go +++ b/apier/v1/lcr.go @@ -27,7 +27,7 @@ import ( // Computes the LCR for a specific request emulating a call func (self *ApierV1) GetLcr(lcrReq engine.LcrRequest, lcrReply *engine.LcrReply) error { - cd, err := lcrReq.AsCallDescriptor() + cd, err := lcrReq.AsCallDescriptor(self.Config.DefaultTimezone) if err != nil { return err } @@ -60,7 +60,7 @@ func (self *ApierV1) GetLcr(lcrReq engine.LcrRequest, lcrReply *engine.LcrReply) // Computes the LCR for a specific request emulating a call, returns a comma separated list of suppliers func (self *ApierV1) GetLcrSuppliers(lcrReq engine.LcrRequest, suppliers *string) (err error) { - cd, err := lcrReq.AsCallDescriptor() + cd, err := lcrReq.AsCallDescriptor(self.Config.DefaultTimezone) if err != nil { return err } diff --git a/apier/v2/apier.go b/apier/v2/apier.go index 80e05bf06..e56846f22 100644 --- a/apier/v2/apier.go +++ b/apier/v2/apier.go @@ -41,7 +41,7 @@ func (self *ApierV2) LoadRatingProfile(attrs AttrLoadRatingProfile, reply *strin tpRpf := &utils.TPRatingProfile{TPid: attrs.TPid} tpRpf.SetRatingProfilesId(attrs.RatingProfileId) rpf := engine.APItoModelRatingProfile(tpRpf) - dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid) + dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid, self.Config.DefaultTimezone) if err := dbReader.LoadRatingProfilesFiltered(&rpf[0]); err != nil { return utils.NewErrServerError(err) } @@ -67,7 +67,7 @@ func (self *ApierV2) LoadAccountActions(attrs AttrLoadAccountActions, reply *str if len(attrs.TPid) == 0 { return utils.NewErrMandatoryIeMissing("TPid") } - dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid) + dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid, self.Config.DefaultTimezone) tpAa := &utils.TPAccountActions{TPid: attrs.TPid} tpAa.SetAccountActionsId(attrs.AccountActionsId) aa := engine.APItoModelAccountAction(tpAa) @@ -105,7 +105,7 @@ func (self *ApierV2) LoadDerivedChargers(attrs AttrLoadDerivedChargers, reply *s tpDc := &utils.TPDerivedChargers{TPid: attrs.TPid} tpDc.SetDerivedChargersId(attrs.DerivedChargersId) dc := engine.APItoModelDerivedCharger(tpDc) - dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid) + dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid, self.Config.DefaultTimezone) if err := dbReader.LoadDerivedChargersFiltered(&dc[0], true); err != nil { return utils.NewErrServerError(err) } diff --git a/apier/v2/cdre.go b/apier/v2/cdre.go index 9db571018..52ee87a6e 100644 --- a/apier/v2/cdre.go +++ b/apier/v2/cdre.go @@ -102,7 +102,7 @@ func (self *ApierV2) ExportCdrsToFile(attr utils.AttrExportCdrsToFile, reply *ut if attr.MaskLength != nil { maskLen = *attr.MaskLength } - cdrsFltr, err := attr.RpcCdrsFilter.AsCdrsFilter() + cdrsFltr, err := attr.RpcCdrsFilter.AsCdrsFilter(self.Config.DefaultTimezone) if err != nil { return utils.NewErrServerError(err) } @@ -114,7 +114,7 @@ func (self *ApierV2) ExportCdrsToFile(attr utils.AttrExportCdrsToFile, reply *ut return nil } cdrexp, err := cdre.NewCdrExporter(cdrs, self.CdrDb, exportTemplate, cdrFormat, fieldSep, exportId, dataUsageMultiplyFactor, smsUsageMultiplyFactor, genericUsageMultiplyFactor, - costMultiplyFactor, costShiftDigits, roundingDecimals, self.Config.RoundingDecimals, maskDestId, maskLen, self.Config.HttpSkipTlsVerify) + costMultiplyFactor, costShiftDigits, roundingDecimals, self.Config.RoundingDecimals, maskDestId, maskLen, self.Config.HttpSkipTlsVerify, self.Config.DefaultTimezone) if err != nil { return utils.NewErrServerError(err) } diff --git a/apier/v2/cdrs.go b/apier/v2/cdrs.go index 1ca28036a..b6fa0f5c8 100644 --- a/apier/v2/cdrs.go +++ b/apier/v2/cdrs.go @@ -27,7 +27,7 @@ import ( // Retrieves CDRs based on the filters func (apier *ApierV2) GetCdrs(attrs utils.RpcCdrsFilter, reply *[]*engine.ExternalCdr) error { - cdrsFltr, err := attrs.AsCdrsFilter() + cdrsFltr, err := attrs.AsCdrsFilter(apier.Config.DefaultTimezone) if err != nil { return utils.NewErrServerError(err) } @@ -44,7 +44,7 @@ func (apier *ApierV2) GetCdrs(attrs utils.RpcCdrsFilter, reply *[]*engine.Extern } func (apier *ApierV2) CountCdrs(attrs utils.RpcCdrsFilter, reply *int64) error { - cdrsFltr, err := attrs.AsCdrsFilter() + cdrsFltr, err := attrs.AsCdrsFilter(apier.Config.DefaultTimezone) if err != nil { return utils.NewErrServerError(err) } diff --git a/cdrc/cdrc.go b/cdrc/cdrc.go index a99976f42..e02291c9f 100644 --- a/cdrc/cdrc.go +++ b/cdrc/cdrc.go @@ -42,7 +42,7 @@ const ( ) // Populates the -func populateStoredCdrField(cdr *engine.StoredCdr, fieldId, fieldVal string) error { +func populateStoredCdrField(cdr *engine.StoredCdr, fieldId, fieldVal, timezone string) error { var err error switch fieldId { case utils.TOR: @@ -64,7 +64,7 @@ func populateStoredCdrField(cdr *engine.StoredCdr, fieldId, fieldVal string) err case utils.DESTINATION: cdr.Destination += fieldVal case utils.SETUP_TIME: - if cdr.SetupTime, err = utils.ParseTimeDetectLayout(fieldVal); err != nil { + if cdr.SetupTime, err = utils.ParseTimeDetectLayout(fieldVal, timezone); err != nil { return fmt.Errorf("Cannot parse answer time field with value: %s, err: %s", fieldVal, err.Error()) } case utils.PDD: @@ -72,7 +72,7 @@ func populateStoredCdrField(cdr *engine.StoredCdr, fieldId, fieldVal string) err return fmt.Errorf("Cannot parse answer time field with value: %s, err: %s", fieldVal, err.Error()) } case utils.ANSWER_TIME: - if cdr.AnswerTime, err = utils.ParseTimeDetectLayout(fieldVal); err != nil { + if cdr.AnswerTime, err = utils.ParseTimeDetectLayout(fieldVal, timezone); err != nil { return fmt.Errorf("Cannot parse answer time field with value: %s, err: %s", fieldVal, err.Error()) } case utils.USAGE: @@ -101,14 +101,15 @@ Common parameters within configs processed: Parameters specific per config instance: * duMultiplyFactor, cdrSourceId, cdrFilter, cdrFields */ -func NewCdrc(cdrcCfgs map[string]*config.CdrcConfig, httpSkipTlsCheck bool, cdrs engine.Connector, exitChan chan struct{}) (*Cdrc, error) { +func NewCdrc(cdrcCfgs map[string]*config.CdrcConfig, httpSkipTlsCheck bool, cdrs engine.Connector, exitChan chan struct{}, dfltTimezone string) (*Cdrc, error) { var cdrcCfg *config.CdrcConfig for _, cdrcCfg = range cdrcCfgs { // Take the first config out, does not matter which one break } cdrc := &Cdrc{cdrFormat: cdrcCfg.CdrFormat, cdrInDir: cdrcCfg.CdrInDir, cdrOutDir: cdrcCfg.CdrOutDir, runDelay: cdrcCfg.RunDelay, csvSep: cdrcCfg.FieldSeparator, - httpSkipTlsCheck: httpSkipTlsCheck, cdrcCfgs: cdrcCfgs, dfltCdrcCfg: cdrcCfg, cdrs: cdrs, exitChan: exitChan, maxOpenFiles: make(chan struct{}, cdrcCfg.MaxOpenFiles), + httpSkipTlsCheck: httpSkipTlsCheck, cdrcCfgs: cdrcCfgs, dfltCdrcCfg: cdrcCfg, timezone: utils.FirstNonEmpty(cdrcCfg.Timezone, dfltTimezone), cdrs: cdrs, + exitChan: exitChan, maxOpenFiles: make(chan struct{}, cdrcCfg.MaxOpenFiles), } var processFile struct{} for i := 0; i < cdrcCfg.MaxOpenFiles; i++ { @@ -157,6 +158,7 @@ type Cdrc struct { httpSkipTlsCheck bool cdrcCfgs map[string]*config.CdrcConfig // All cdrc config profiles attached to this CDRC (key will be profile instance name) dfltCdrcCfg *config.CdrcConfig + timezone string cdrs engine.Connector httpClient *http.Client exitChan chan struct{} @@ -250,10 +252,10 @@ func (self *Cdrc) processFile(filePath string) error { case CSV, FS_CSV, utils.KAM_FLATSTORE, utils.OSIPS_FLATSTORE: csvReader := csv.NewReader(bufio.NewReader(file)) csvReader.Comma = self.csvSep - recordsProcessor = NewCsvRecordsProcessor(csvReader, self.cdrFormat, fn, self.failedCallsPrefix, + recordsProcessor = NewCsvRecordsProcessor(csvReader, self.cdrFormat, self.timezone, fn, self.failedCallsPrefix, self.cdrSourceIds, self.duMultiplyFactors, self.cdrFilters, self.cdrFields, self.httpSkipTlsCheck, self.partialRecordsCache) case utils.FWV: - recordsProcessor = NewFwvRecordsProcessor(file, self.cdrcCfgs, self.dfltCdrcCfg, self.httpClient, self.httpSkipTlsCheck) + recordsProcessor = NewFwvRecordsProcessor(file, self.cdrcCfgs, self.dfltCdrcCfg, self.httpClient, self.httpSkipTlsCheck, self.timezone) default: return fmt.Errorf("Unsupported CDR format: %s", self.cdrFormat) } diff --git a/cdrc/cdrc_local_test.go b/cdrc/cdrc_local_test.go index d8a013b06..8e8566d99 100644 --- a/cdrc/cdrc_local_test.go +++ b/cdrc/cdrc_local_test.go @@ -176,7 +176,7 @@ func TestProcessCdrDir(t *testing.T) { if err := startEngine(); err != nil { t.Fatal(err.Error()) } - cdrc, err := NewCdrc(cdrcCfgs, true, nil, make(chan struct{})) + cdrc, err := NewCdrc(cdrcCfgs, true, nil, make(chan struct{}), "") if err != nil { t.Fatal(err.Error()) } @@ -212,7 +212,7 @@ func TestProcessCdr3Dir(t *testing.T) { if err := startEngine(); err != nil { t.Fatal(err.Error()) } - cdrc, err := NewCdrc(cdrcCfgs, true, nil, make(chan struct{})) + cdrc, err := NewCdrc(cdrcCfgs, true, nil, make(chan struct{}), "") if err != nil { t.Fatal(err.Error()) } diff --git a/cdrc/csv.go b/cdrc/csv.go index 80ef4d636..ff175e997 100644 --- a/cdrc/csv.go +++ b/cdrc/csv.go @@ -33,13 +33,13 @@ import ( "github.com/cgrates/cgrates/utils" ) -func NewPartialFlatstoreRecord(record []string) (*PartialFlatstoreRecord, error) { +func NewPartialFlatstoreRecord(record []string, timezone string) (*PartialFlatstoreRecord, error) { if len(record) < 7 { return nil, errors.New("MISSING_IE") } pr := &PartialFlatstoreRecord{Method: record[0], AccId: record[3] + record[1] + record[2], Values: record} var err error - if pr.Timestamp, err = utils.ParseTimeDetectLayout(record[6]); err != nil { + if pr.Timestamp, err = utils.ParseTimeDetectLayout(record[6], timezone); err != nil { return nil, err } return pr, nil @@ -179,10 +179,10 @@ func (self *PartialRecordsCache) UncachePartial(fileName string, pr *PartialFlat }, fileName) } -func NewCsvRecordsProcessor(csvReader *csv.Reader, cdrFormat, fileName, failedCallsPrefix string, +func NewCsvRecordsProcessor(csvReader *csv.Reader, cdrFormat, timezone, fileName, failedCallsPrefix string, cdrSourceIds []string, duMultiplyFactors []float64, cdrFilters []utils.RSRFields, cdrFields [][]*config.CfgCdrField, httpSkipTlsCheck bool, partialRecordsCache *PartialRecordsCache) *CsvRecordsProcessor { - return &CsvRecordsProcessor{csvReader: csvReader, cdrFormat: cdrFormat, fileName: fileName, + return &CsvRecordsProcessor{csvReader: csvReader, cdrFormat: cdrFormat, timezone: timezone, fileName: fileName, failedCallsPrefix: failedCallsPrefix, cdrSourceIds: cdrSourceIds, duMultiplyFactors: duMultiplyFactors, cdrFilters: cdrFilters, cdrFields: cdrFields, httpSkipTlsCheck: httpSkipTlsCheck, partialRecordsCache: partialRecordsCache} @@ -192,6 +192,7 @@ func NewCsvRecordsProcessor(csvReader *csv.Reader, cdrFormat, fileName, failedCa type CsvRecordsProcessor struct { csvReader *csv.Reader cdrFormat string + timezone string // Timezone for CDRs which are not clearly specifying it fileName string failedCallsPrefix string cdrSourceIds []string // Should be in sync with cdrFields on indexes @@ -224,7 +225,7 @@ func (self *CsvRecordsProcessor) processPartialRecord(record []string) ([]string record = append(record, "0") // Append duration 0 for failed calls flatstore CDR and do not process it further return record, nil } - pr, err := NewPartialFlatstoreRecord(record) + pr, err := NewPartialFlatstoreRecord(record, self.timezone) if err != nil { return nil, err } @@ -304,7 +305,7 @@ func (self *CsvRecordsProcessor) recordToStoredCdr(record []string, cfgIdx int) } else { return nil, fmt.Errorf("Unsupported field type: %s", cdrFldCfg.Type) } - if err := populateStoredCdrField(storedCdr, cdrFldCfg.CdrFieldId, fieldVal); err != nil { + if err := populateStoredCdrField(storedCdr, cdrFldCfg.CdrFieldId, fieldVal, self.timezone); err != nil { return nil, err } } @@ -325,7 +326,7 @@ func (self *CsvRecordsProcessor) recordToStoredCdr(record []string, cfgIdx int) if len(fieldVal) == 0 && httpFieldCfg.Mandatory { return nil, fmt.Errorf("MandatoryIeMissing: Empty result for http_post field: %s", httpFieldCfg.Tag) } - if err := populateStoredCdrField(storedCdr, httpFieldCfg.CdrFieldId, fieldVal); err != nil { + if err := populateStoredCdrField(storedCdr, httpFieldCfg.CdrFieldId, fieldVal, self.timezone); err != nil { return nil, err } } diff --git a/cdrc/fwv.go b/cdrc/fwv.go index 5f97d106f..4b21ca508 100644 --- a/cdrc/fwv.go +++ b/cdrc/fwv.go @@ -47,8 +47,8 @@ func fwvValue(cdrLine string, indexStart, width int, padding string) string { return rawVal } -func NewFwvRecordsProcessor(file *os.File, cdrcCfgs map[string]*config.CdrcConfig, dfltCfg *config.CdrcConfig, httpClient *http.Client, httpSkipTlsCheck bool) *FwvRecordsProcessor { - return &FwvRecordsProcessor{file: file, cdrcCfgs: cdrcCfgs, dfltCfg: dfltCfg, httpSkipTlsCheck: httpSkipTlsCheck} +func NewFwvRecordsProcessor(file *os.File, cdrcCfgs map[string]*config.CdrcConfig, dfltCfg *config.CdrcConfig, httpClient *http.Client, httpSkipTlsCheck bool, timezone string) *FwvRecordsProcessor { + return &FwvRecordsProcessor{file: file, cdrcCfgs: cdrcCfgs, dfltCfg: dfltCfg, httpSkipTlsCheck: httpSkipTlsCheck, timezone: timezone} } type FwvRecordsProcessor struct { @@ -57,6 +57,7 @@ type FwvRecordsProcessor struct { dfltCfg *config.CdrcConfig // General parameters httpClient *http.Client httpSkipTlsCheck bool + timezone string lineLen int64 // Length of the line in the file offset int64 // Index of the next byte to process trailerOffset int64 // Index where trailer starts, to be used as boundary when reading cdrs @@ -189,7 +190,7 @@ func (self *FwvRecordsProcessor) recordToStoredCdr(record string, cfgKey string) //return nil, fmt.Errorf("Unsupported field type: %s", cdrFldCfg.Type) continue // Don't do anything for unsupported fields } - if err := populateStoredCdrField(storedCdr, cdrFldCfg.CdrFieldId, fieldVal); err != nil { + if err := populateStoredCdrField(storedCdr, cdrFldCfg.CdrFieldId, fieldVal, self.timezone); err != nil { return nil, err } } @@ -212,7 +213,7 @@ func (self *FwvRecordsProcessor) recordToStoredCdr(record string, cfgKey string) if len(fieldVal) == 0 && httpFieldCfg.Mandatory { return nil, fmt.Errorf("MandatoryIeMissing: Empty result for http_post field: %s", httpFieldCfg.Tag) } - if err := populateStoredCdrField(storedCdr, httpFieldCfg.CdrFieldId, fieldVal); err != nil { + if err := populateStoredCdrField(storedCdr, httpFieldCfg.CdrFieldId, fieldVal, self.timezone); err != nil { return nil, err } } diff --git a/cdre/cdrexporter.go b/cdre/cdrexporter.go index 3c971116e..4f44a8e57 100644 --- a/cdre/cdrexporter.go +++ b/cdre/cdrexporter.go @@ -52,7 +52,8 @@ const ( var err error func NewCdrExporter(cdrs []*engine.StoredCdr, cdrDb engine.CdrStorage, exportTpl *config.CdreConfig, cdrFormat string, fieldSeparator rune, exportId string, - dataUsageMultiplyFactor, smsUsageMultiplyFactor, genericUsageMultiplyFactor, costMultiplyFactor float64, costShiftDigits, roundDecimals, cgrPrecision int, maskDestId string, maskLen int, httpSkipTlsCheck bool) (*CdrExporter, error) { + dataUsageMultiplyFactor, smsUsageMultiplyFactor, genericUsageMultiplyFactor, costMultiplyFactor float64, + costShiftDigits, roundDecimals, cgrPrecision int, maskDestId string, maskLen int, httpSkipTlsCheck bool, timezone string) (*CdrExporter, error) { if len(cdrs) == 0 { // Nothing to export return nil, nil } @@ -71,6 +72,7 @@ func NewCdrExporter(cdrs []*engine.StoredCdr, cdrDb engine.CdrStorage, exportTpl cgrPrecision: cgrPrecision, maskDestId: maskDestId, httpSkipTlsCheck: httpSkipTlsCheck, + timezone: timezone, maskLen: maskLen, negativeExports: make(map[string]string), } @@ -95,6 +97,7 @@ type CdrExporter struct { maskDestId string maskLen int httpSkipTlsCheck bool + timezone string header, trailer []string // Header and Trailer fields content [][]string // Rows of cdr fields firstCdrATime, lastCdrATime time.Time @@ -162,7 +165,7 @@ func (cdre *CdrExporter) getDateTimeFieldVal(cdr *engine.StoredCdr, cfgCdrFld *c if len(layout) == 0 { layout = time.RFC3339 } - if dtFld, err := utils.ParseTimeDetectLayout(cdr.FieldAsString(cfgCdrFld.Value[0])); err != nil { // Only one rule makes sense here + if dtFld, err := utils.ParseTimeDetectLayout(cdr.FieldAsString(cfgCdrFld.Value[0]), cdre.timezone); err != nil { // Only one rule makes sense here return "", err } else { return dtFld.Format(layout), nil diff --git a/cdre/cdrexporter_test.go b/cdre/cdrexporter_test.go index d3cf35491..9cf3204dc 100644 --- a/cdre/cdrexporter_test.go +++ b/cdre/cdrexporter_test.go @@ -52,7 +52,7 @@ func TestCdreGetCombimedCdrFieldVal(t *testing.T) { Usage: time.Duration(10) * time.Second, MediationRunId: "RETAIL1", Cost: 5.01}, } cdre, err := NewCdrExporter(cdrs, nil, cfg.CdreProfiles["*default"], cfg.CdreProfiles["*default"].CdrFormat, cfg.CdreProfiles["*default"].FieldSeparator, - "firstexport", 0.0, 0.0, 0.0, 0.0, 0, 4, cfg.RoundingDecimals, "", 0, cfg.HttpSkipTlsVerify) + "firstexport", 0.0, 0.0, 0.0, 0.0, 0, 4, cfg.RoundingDecimals, "", 0, cfg.HttpSkipTlsVerify, "") if err != nil { t.Error("Unexpected error received: ", err) } diff --git a/cdre/csv_test.go b/cdre/csv_test.go index 4fdbabb9f..647f8379a 100644 --- a/cdre/csv_test.go +++ b/cdre/csv_test.go @@ -40,7 +40,7 @@ func TestCsvCdrWriter(t *testing.T) { ExtraFields: map[string]string{"extra1": "val_extra1", "extra2": "val_extra2", "extra3": "val_extra3"}, Cost: 1.01, } cdre, err := NewCdrExporter([]*engine.StoredCdr{storedCdr1}, nil, cfg.CdreProfiles["*default"], utils.CSV, ',', "firstexport", 0.0, 0.0, 0.0, 0.0, 0, 4, - cfg.RoundingDecimals, "", 0, cfg.HttpSkipTlsVerify) + cfg.RoundingDecimals, "", 0, cfg.HttpSkipTlsVerify, "") if err != nil { t.Error("Unexpected error received: ", err) } @@ -67,7 +67,8 @@ func TestAlternativeFieldSeparator(t *testing.T) { Usage: time.Duration(10) * time.Second, MediationRunId: utils.DEFAULT_RUNID, ExtraFields: map[string]string{"extra1": "val_extra1", "extra2": "val_extra2", "extra3": "val_extra3"}, Cost: 1.01, } - cdre, err := NewCdrExporter([]*engine.StoredCdr{storedCdr1}, nil, cfg.CdreProfiles["*default"], utils.CSV, '|', "firstexport", 0.0, 0.0, 0.0, 0.0, 0, 4, cfg.RoundingDecimals, "", 0, cfg.HttpSkipTlsVerify) + cdre, err := NewCdrExporter([]*engine.StoredCdr{storedCdr1}, nil, cfg.CdreProfiles["*default"], utils.CSV, '|', + "firstexport", 0.0, 0.0, 0.0, 0.0, 0, 4, cfg.RoundingDecimals, "", 0, cfg.HttpSkipTlsVerify, "") if err != nil { t.Error("Unexpected error received: ", err) } diff --git a/cdre/fixedwidth_test.go b/cdre/fixedwidth_test.go index 81d5300d2..6c1c39c43 100644 --- a/cdre/fixedwidth_test.go +++ b/cdre/fixedwidth_test.go @@ -127,7 +127,8 @@ func TestWriteCdr(t *testing.T) { Usage: time.Duration(10) * time.Second, MediationRunId: utils.DEFAULT_RUNID, Cost: 2.34567, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, } - cdre, err := NewCdrExporter([]*engine.StoredCdr{cdr}, nil, cdreCfg, utils.CDRE_FIXED_WIDTH, ',', "fwv_1", 0.0, 0.0, 0.0, 0.0, 0, 4, cfg.RoundingDecimals, "", -1, cfg.HttpSkipTlsVerify) + cdre, err := NewCdrExporter([]*engine.StoredCdr{cdr}, nil, cdreCfg, utils.CDRE_FIXED_WIDTH, ',', "fwv_1", 0.0, 0.0, 0.0, 0.0, 0, 4, + cfg.RoundingDecimals, "", -1, cfg.HttpSkipTlsVerify, "") if err != nil { t.Error(err) } @@ -202,7 +203,7 @@ func TestWriteCdrs(t *testing.T) { } cfg, _ := config.NewDefaultCGRConfig() cdre, err := NewCdrExporter([]*engine.StoredCdr{cdr1, cdr2, cdr3, cdr4}, nil, cdreCfg, utils.CDRE_FIXED_WIDTH, ',', - "fwv_1", 0.0, 0.0, 0.0, 0.0, 0, 4, cfg.RoundingDecimals, "", -1, cfg.HttpSkipTlsVerify) + "fwv_1", 0.0, 0.0, 0.0, 0.0, 0, 4, cfg.RoundingDecimals, "", -1, cfg.HttpSkipTlsVerify, "") if err != nil { t.Error(err) } diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go index e2a55c9e5..7289be191 100644 --- a/cmd/cgr-engine/cgr-engine.go +++ b/cmd/cgr-engine/cgr-engine.go @@ -106,7 +106,7 @@ func startCdrc(responder *engine.Responder, cdrsChan chan struct{}, cdrcCfgs map } cdrsConn = &engine.RPCClientConnector{Client: conn} } - cdrc, err := cdrc.NewCdrc(cdrcCfgs, httpSkipTlsCheck, cdrsConn, closeChan) + cdrc, err := cdrc.NewCdrc(cdrcCfgs, httpSkipTlsCheck, cdrsConn, closeChan, cfg.DefaultTimezone) if err != nil { engine.Logger.Crit(fmt.Sprintf("Cdrc config parsing error: %s", err.Error())) exitChan <- true @@ -162,7 +162,7 @@ func startSmFreeSWITCH(responder *engine.Responder, cdrDb engine.CdrStorage, cac } cdrsConn = &engine.RPCClientConnector{Client: client} } - sm := sessionmanager.NewFSSessionManager(cfg.SmFsConfig, raterConn, cdrsConn) + sm := sessionmanager.NewFSSessionManager(cfg.SmFsConfig, raterConn, cdrsConn, cfg.DefaultTimezone) sms = append(sms, sm) smRpc.SMs = append(smRpc.SMs, sm) if err = sm.Connect(); err != nil { @@ -214,7 +214,7 @@ func startSmKamailio(responder *engine.Responder, cdrDb engine.CdrStorage, cache } cdrsConn = &engine.RPCClientConnector{Client: client} } - sm, _ := sessionmanager.NewKamailioSessionManager(cfg.SmKamConfig, raterConn, cdrsConn) + sm, _ := sessionmanager.NewKamailioSessionManager(cfg.SmKamConfig, raterConn, cdrsConn, cfg.DefaultTimezone) sms = append(sms, sm) smRpc.SMs = append(smRpc.SMs, sm) if err = sm.Connect(); err != nil { @@ -266,7 +266,7 @@ func startSmOpenSIPS(responder *engine.Responder, cdrDb engine.CdrStorage, cache } cdrsConn = &engine.RPCClientConnector{Client: client} } - sm, _ := sessionmanager.NewOSipsSessionManager(cfg.SmOsipsConfig, raterConn, cdrsConn) + sm, _ := sessionmanager.NewOSipsSessionManager(cfg.SmOsipsConfig, raterConn, cdrsConn, cfg.DefaultTimezone) sms = append(sms, sm) smRpc.SMs = append(smRpc.SMs, sm) if err := sm.Connect(); err != nil { diff --git a/cmd/cgr-loader/cgr-loader.go b/cmd/cgr-loader/cgr-loader.go index d6681708e..4a7005eb5 100644 --- a/cmd/cgr-loader/cgr-loader.go +++ b/cmd/cgr-loader/cgr-loader.go @@ -72,6 +72,7 @@ var ( cdrstatsAddress = flag.String("cdrstats_address", cgrConfig.RPCGOBListen, "CDRStats service to contact for data reloads, empty to disable automatic data reloads") usersAddress = flag.String("users_address", cgrConfig.RPCGOBListen, "Users service to contact for data reloads, empty to disable automatic data reloads") runId = flag.String("runid", "", "Uniquely identify an import/load, postpended to some automatic fields") + timezone = flag.String("timezone", cgrConfig.DefaultTimezone, `Timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB>`) ) func main() { @@ -158,7 +159,7 @@ func main() { path.Join(*dataPath, utils.CDR_STATS_CSV), path.Join(*dataPath, utils.USERS_CSV)) } - tpReader := engine.NewTpReader(ratingDb, accountDb, loader, *tpid) + tpReader := engine.NewTpReader(ratingDb, accountDb, loader, *tpid, *timezone) err = tpReader.LoadAll() if err != nil { log.Fatal(err) diff --git a/config/cdrcconfig.go b/config/cdrcconfig.go index 6122a238e..d34d6bc9f 100644 --- a/config/cdrcconfig.go +++ b/config/cdrcconfig.go @@ -31,6 +31,7 @@ type CdrcConfig struct { CdrFormat string // The type of CDR file to process FieldSeparator rune // The separator to use when reading csvs DataUsageMultiplyFactor float64 // Conversion factor for data usage + Timezone string // timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB> RunDelay time.Duration // Delay between runs, 0 for inotify driven requests MaxOpenFiles int // Maximum number of files opened simultaneously CdrInDir string // Folder to process CDRs from @@ -68,6 +69,9 @@ func (self *CdrcConfig) loadFromJsonCfg(jsnCfg *CdrcJsonCfg) error { if jsnCfg.Data_usage_multiply_factor != nil { self.DataUsageMultiplyFactor = *jsnCfg.Data_usage_multiply_factor } + if jsnCfg.Timezone != nil { + self.Timezone = *jsnCfg.Timezone + } if jsnCfg.Run_delay != nil { self.RunDelay = time.Duration(*jsnCfg.Run_delay) * time.Second } @@ -122,6 +126,7 @@ func (self *CdrcConfig) Clone() *CdrcConfig { clnCdrc.CdrFormat = self.CdrFormat clnCdrc.FieldSeparator = self.FieldSeparator clnCdrc.DataUsageMultiplyFactor = self.DataUsageMultiplyFactor + clnCdrc.Timezone = self.Timezone clnCdrc.RunDelay = self.RunDelay clnCdrc.MaxOpenFiles = self.MaxOpenFiles clnCdrc.CdrInDir = self.CdrInDir diff --git a/config/config.go b/config/config.go index 1e834085c..944062405 100644 --- a/config/config.go +++ b/config/config.go @@ -185,6 +185,7 @@ type CGRConfig struct { DefaultCategory string // set default type of record DefaultTenant string // set default tenant DefaultSubject string // set default rating subject, useful in case of fallback + DefaultTimezone string // default timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB> Reconnects int // number of recconect attempts in case of connection lost <-1 for infinite | nb> ConnectAttempts int // number of initial connection attempts before giving up RoundingDecimals int // Number of decimals to round end prices at diff --git a/config/config_defaults.go b/config/config_defaults.go index 519b01f47..b8c84761a 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -36,6 +36,7 @@ const CGRATES_CFG_JSON = ` "default_category": "call", // default Type of Record to consider when missing from requests "default_tenant": "cgrates.org", // default Tenant to consider when missing from requests "default_subject": "cgrates", // default rating Subject to consider when missing from requests + "default_timezone": "Local", // default timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB> "connect_attempts": 3, // initial server connect attempts "reconnects": -1, // number of retries in case of connection lost }, @@ -160,6 +161,7 @@ const CGRATES_CFG_JSON = ` "cdrs": "internal", // address where to reach CDR server. "cdr_format": "csv", // CDR file format "field_separator": ",", // separator used in case of csv files + "timezone": "", // timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB> "run_delay": 0, // sleep interval in seconds between consecutive runs, 0 to use automation via inotify "max_open_files": 1024, // maximum simultaneous files to process, 0 for unlimited "data_usage_multiply_factor": 1024, // conversion factor for data usage diff --git a/config/config_json_test.go b/config/config_json_test.go index b32e74e1c..c753b49c6 100644 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -46,6 +46,7 @@ func TestDfGeneralJsonCfg(t *testing.T) { Default_category: utils.StringPointer("call"), Default_tenant: utils.StringPointer("cgrates.org"), Default_subject: utils.StringPointer("cgrates"), + Default_timezone: utils.StringPointer("Local"), Connect_attempts: utils.IntPointer(3), Reconnects: utils.IntPointer(-1)} if gCfg, err := dfCgrJsonCfg.GeneralJsonCfg(); err != nil { @@ -294,6 +295,7 @@ func TestDfCdrcJsonCfg(t *testing.T) { Cdrs: utils.StringPointer("internal"), Cdr_format: utils.StringPointer("csv"), Field_separator: utils.StringPointer(","), + Timezone: utils.StringPointer(""), Run_delay: utils.IntPointer(0), Max_open_files: utils.IntPointer(1024), Data_usage_multiply_factor: utils.Float64Pointer(1024.0), diff --git a/config/libconfig_json.go b/config/libconfig_json.go index 1445f7235..a3ae0494e 100644 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -28,6 +28,7 @@ type GeneralJsonCfg struct { Default_category *string Default_tenant *string Default_subject *string + Default_timezone *string Reconnects *int Connect_attempts *int } @@ -135,6 +136,7 @@ type CdrcJsonCfg struct { Cdrs *string Cdr_format *string Field_separator *string + Timezone *string Run_delay *int Data_usage_multiply_factor *float64 Cdr_in_dir *string diff --git a/engine/cdrs.go b/engine/cdrs.go index 39f3b1c9c..82229f34c 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -41,12 +41,12 @@ type CallCostLog struct { // Handler for generic cgr cdr http func cgrCdrHandler(w http.ResponseWriter, r *http.Request) { - cgrCdr, err := NewCgrCdrFromHttpReq(r) + cgrCdr, err := NewCgrCdrFromHttpReq(r, cdrServer.cgrCfg.DefaultTimezone) if err != nil { Logger.Err(fmt.Sprintf(" Could not create CDR entry: %s", err.Error())) return } - if err := cdrServer.processCdr(cgrCdr.AsStoredCdr()); err != nil { + if err := cdrServer.processCdr(cgrCdr.AsStoredCdr(cdrServer.cgrCfg.DefaultTimezone)); err != nil { Logger.Err(fmt.Sprintf(" Errors when storing CDR entry: %s", err.Error())) } } @@ -59,7 +59,7 @@ func fsCdrHandler(w http.ResponseWriter, r *http.Request) { Logger.Err(fmt.Sprintf(" Could not create CDR entry: %s", err.Error())) return } - if err := cdrServer.processCdr(fsCdr.AsStoredCdr()); err != nil { + if err := cdrServer.processCdr(fsCdr.AsStoredCdr(cdrServer.Timezone())); err != nil { Logger.Err(fmt.Sprintf(" Errors when storing CDR entry: %s", err.Error())) } } @@ -76,6 +76,10 @@ type CdrServer struct { guard *GuardianLock } +func (self *CdrServer) Timezone() string { + return self.cgrCfg.DefaultTimezone +} + func (self *CdrServer) RegisterHanlersToServer(server *Server) { cdrServer = self // Share the server object for handlers server.RegisterHttpFunc("/cdr_http", cgrCdrHandler) @@ -92,7 +96,7 @@ func (self *CdrServer) ProcessExternalCdr(cdr *ExternalCdr) error { if cdr.Subject == "" { // Use account information as rating subject if missing cdr.Subject = cdr.Account } - storedCdr, err := NewStoredCdrFromExternalCdr(cdr) + storedCdr, err := NewStoredCdrFromExternalCdr(cdr, self.cgrCfg.DefaultTimezone) if err != nil { return err } @@ -246,7 +250,7 @@ func (self *CdrServer) deriveCdrs(storedCdr *StoredCdr) ([]*StoredCdr, error) { dcSupplFld, _ := utils.NewRSRField(dc.SupplierField) dcDCausseld, _ := utils.NewRSRField(dc.DisconnectCauseField) forkedCdr, err := storedCdr.ForkCdr(dc.RunId, dcReqTypeFld, dcDirFld, dcTenantFld, dcCategoryFld, dcAcntFld, dcSubjFld, dcDstFld, - dcSTimeFld, dcPddFld, dcATimeFld, dcDurFld, dcSupplFld, dcDCausseld, []*utils.RSRField{}, true) + dcSTimeFld, dcPddFld, dcATimeFld, dcDurFld, dcSupplFld, dcDCausseld, []*utils.RSRField{}, true, self.cgrCfg.DefaultTimezone) if err != nil { Logger.Err(fmt.Sprintf("Could not fork CGR with cgrid %s, run: %s, error: %s", storedCdr.CgrId, dc.RunId, err.Error())) continue // do not add it to the forked CDR list diff --git a/engine/cdrs_local_test.go b/engine/cdrs_local_test.go index cadfb84ba..7e18e2830 100644 --- a/engine/cdrs_local_test.go +++ b/engine/cdrs_local_test.go @@ -115,8 +115,8 @@ func TestCdrsHttpCdrReplication(t *testing.T) { } else if len(rcvedCdrs) != 1 { t.Error("Unexpected number of CDRs returned: ", len(rcvedCdrs)) } else { - rcvSetupTime, _ := utils.ParseTimeDetectLayout(rcvedCdrs[0].SetupTime) - rcvAnswerTime, _ := utils.ParseTimeDetectLayout(rcvedCdrs[0].AnswerTime) + rcvSetupTime, _ := utils.ParseTimeDetectLayout(rcvedCdrs[0].SetupTime, "") + rcvAnswerTime, _ := utils.ParseTimeDetectLayout(rcvedCdrs[0].AnswerTime, "") //rcvUsage, _ := utils.ParseDurationWithSecs(rcvedCdrs[0].Usage) if rcvedCdrs[0].CgrId != testCdr1.CgrId || rcvedCdrs[0].TOR != testCdr1.TOR || diff --git a/engine/cgrcdr.go b/engine/cgrcdr.go index d3a33de13..db33e4020 100644 --- a/engine/cgrcdr.go +++ b/engine/cgrcdr.go @@ -24,7 +24,7 @@ import ( "strconv" ) -func NewCgrCdrFromHttpReq(req *http.Request) (CgrCdr, error) { +func NewCgrCdrFromHttpReq(req *http.Request, timezone string) (CgrCdr, error) { if req.Form == nil { if err := req.ParseForm(); err != nil { return nil, err @@ -40,11 +40,11 @@ func NewCgrCdrFromHttpReq(req *http.Request) (CgrCdr, error) { type CgrCdr map[string]string -func (cgrCdr CgrCdr) getCgrId() string { +func (cgrCdr CgrCdr) getCgrId(timezone string) string { if cgrId, hasIt := cgrCdr[utils.CGRID]; hasIt { return cgrId } - setupTime, _ := utils.ParseTimeDetectLayout(cgrCdr[utils.SETUP_TIME]) + setupTime, _ := utils.ParseTimeDetectLayout(cgrCdr[utils.SETUP_TIME], timezone) return utils.Sha1(cgrCdr[utils.ACCID], setupTime.UTC().String()) } @@ -58,9 +58,9 @@ func (cgrCdr CgrCdr) getExtraFields() map[string]string { return extraFields } -func (cgrCdr CgrCdr) AsStoredCdr() *StoredCdr { +func (cgrCdr CgrCdr) AsStoredCdr(timezone string) *StoredCdr { storCdr := new(StoredCdr) - storCdr.CgrId = cgrCdr.getCgrId() + storCdr.CgrId = cgrCdr.getCgrId(timezone) storCdr.TOR = cgrCdr[utils.TOR] storCdr.AccId = cgrCdr[utils.ACCID] storCdr.CdrHost = cgrCdr[utils.CDRHOST] @@ -72,9 +72,9 @@ func (cgrCdr CgrCdr) AsStoredCdr() *StoredCdr { storCdr.Account = cgrCdr[utils.ACCOUNT] storCdr.Subject = cgrCdr[utils.SUBJECT] storCdr.Destination = cgrCdr[utils.DESTINATION] - storCdr.SetupTime, _ = utils.ParseTimeDetectLayout(cgrCdr[utils.SETUP_TIME]) // Not interested to process errors, should do them if necessary in a previous step + storCdr.SetupTime, _ = utils.ParseTimeDetectLayout(cgrCdr[utils.SETUP_TIME], timezone) // Not interested to process errors, should do them if necessary in a previous step storCdr.Pdd, _ = utils.ParseDurationWithSecs(cgrCdr[utils.PDD]) - storCdr.AnswerTime, _ = utils.ParseTimeDetectLayout(cgrCdr[utils.ANSWER_TIME]) + storCdr.AnswerTime, _ = utils.ParseTimeDetectLayout(cgrCdr[utils.ANSWER_TIME], timezone) storCdr.Usage, _ = utils.ParseDurationWithSecs(cgrCdr[utils.USAGE]) storCdr.Supplier = cgrCdr[utils.SUPPLIER] storCdr.DisconnectCause = cgrCdr[utils.DISCONNECT_CAUSE] diff --git a/engine/cgrcdr_test.go b/engine/cgrcdr_test.go index 53397d155..681288f60 100644 --- a/engine/cgrcdr_test.go +++ b/engine/cgrcdr_test.go @@ -39,14 +39,14 @@ func TestCgrCdrAsStoredCdr(t *testing.T) { utils.TENANT: "cgrates.org", utils.CATEGORY: "call", utils.ACCOUNT: "1001", utils.SUBJECT: "1001", utils.DESTINATION: "1002", utils.SETUP_TIME: "2013-11-07T08:42:20Z", utils.ANSWER_TIME: "2013-11-07T08:42:26Z", utils.USAGE: "10", utils.SUPPLIER: "SUPPL1", "field_extr1": "val_extr1", "fieldextr2": "valextr2"} - setupTime, _ := utils.ParseTimeDetectLayout(cgrCdr["setup_time"]) + setupTime, _ := utils.ParseTimeDetectLayout(cgrCdr["setup_time"], "") expctRtCdr := &StoredCdr{CgrId: utils.Sha1(cgrCdr["accid"], setupTime.String()), TOR: utils.VOICE, AccId: cgrCdr["accid"], CdrHost: cgrCdr["cdrhost"], CdrSource: cgrCdr["cdrsource"], ReqType: cgrCdr["reqtype"], Direction: cgrCdr[utils.DIRECTION], Tenant: cgrCdr["tenant"], Category: cgrCdr[utils.CATEGORY], Account: cgrCdr["account"], Subject: cgrCdr["subject"], Destination: cgrCdr["destination"], SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), Usage: time.Duration(10) * time.Second, Supplier: "SUPPL1", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: -1} - if storedCdr := cgrCdr.AsStoredCdr(); !reflect.DeepEqual(expctRtCdr, storedCdr) { + if storedCdr := cgrCdr.AsStoredCdr(""); !reflect.DeepEqual(expctRtCdr, storedCdr) { t.Errorf("Expecting %v, received: %v", expctRtCdr, storedCdr) } } @@ -80,7 +80,7 @@ func TestReplicatedCgrCdrAsStoredCdr(t *testing.T) { Cost: 0.12, Rated: true, } - if storedCdr := cgrCdr.AsStoredCdr(); !reflect.DeepEqual(expctRtCdr, storedCdr) { + if storedCdr := cgrCdr.AsStoredCdr(""); !reflect.DeepEqual(expctRtCdr, storedCdr) { t.Errorf("Expecting %v, received: %v", expctRtCdr, storedCdr) } } diff --git a/engine/event.go b/engine/event.go index 58d34007a..521e4fc3f 100644 --- a/engine/event.go +++ b/engine/event.go @@ -25,7 +25,7 @@ import ( type Event interface { GetName() string - GetCgrId() string + GetCgrId(timezone string) string GetUUID() string GetSessionIds() []string // Returns identifiers needed to control a session (eg disconnect) GetDirection(string) string @@ -36,8 +36,8 @@ type Event interface { GetCategory(string) string GetTenant(string) string GetReqType(string) string - GetSetupTime(string) (time.Time, error) - GetAnswerTime(string) (time.Time, error) + GetSetupTime(string, string) (time.Time, error) + GetAnswerTime(string, string) (time.Time, error) GetEndTime() (time.Time, error) GetDuration(string) (time.Duration, error) GetPdd(string) (time.Duration, error) @@ -46,9 +46,9 @@ type Event interface { GetOriginatorIP(string) string GetExtraFields() map[string]string MissingParameter() bool - ParseEventValue(*utils.RSRField) string + ParseEventValue(*utils.RSRField, string) string PassesFieldFilter(*utils.RSRField) (bool, string) - AsStoredCdr() *StoredCdr + AsStoredCdr(timezone string) *StoredCdr String() string AsEvent(string) Event ComputeLcr() bool diff --git a/engine/fscdr.go b/engine/fscdr.go index 7fe798c3c..7952220c5 100644 --- a/engine/fscdr.go +++ b/engine/fscdr.go @@ -76,8 +76,8 @@ type FSCdr struct { body map[string]interface{} // keeps the loaded body for extra field search } -func (fsCdr FSCdr) getCgrId() string { - setupTime, _ := utils.ParseTimeDetectLayout(fsCdr.vars[FS_SETUP_TIME]) +func (fsCdr FSCdr) getCgrId(timezone string) string { + setupTime, _ := utils.ParseTimeDetectLayout(fsCdr.vars[FS_SETUP_TIME], timezone) return utils.Sha1(fsCdr.vars[FS_UUID], setupTime.UTC().String()) } @@ -124,10 +124,10 @@ func (fsCdr FSCdr) searchExtraField(field string, body map[string]interface{}) ( return } -func (fsCdr FSCdr) AsStoredCdr() *StoredCdr { +func (fsCdr FSCdr) AsStoredCdr(timezone string) *StoredCdr { storCdr := new(StoredCdr) - storCdr.CgrId = fsCdr.getCgrId() + storCdr.CgrId = fsCdr.getCgrId(timezone) storCdr.TOR = utils.VOICE storCdr.AccId = fsCdr.vars[FS_UUID] storCdr.CdrHost = fsCdr.vars[FS_IP] @@ -139,11 +139,11 @@ func (fsCdr FSCdr) AsStoredCdr() *StoredCdr { storCdr.Account = utils.FirstNonEmpty(fsCdr.vars[FS_ACCOUNT], fsCdr.vars[FS_USERNAME]) storCdr.Subject = utils.FirstNonEmpty(fsCdr.vars[FS_SUBJECT], fsCdr.vars[FS_USERNAME]) storCdr.Destination = utils.FirstNonEmpty(fsCdr.vars[FS_DESTINATION], fsCdr.vars[FS_CALL_DEST_NR], fsCdr.vars[FS_SIP_REQUSER]) - storCdr.SetupTime, _ = utils.ParseTimeDetectLayout(fsCdr.vars[FS_SETUP_TIME]) // Not interested to process errors, should do them if necessary in a previous step + storCdr.SetupTime, _ = utils.ParseTimeDetectLayout(fsCdr.vars[FS_SETUP_TIME], timezone) // Not interested to process errors, should do them if necessary in a previous step pddStr := utils.FirstNonEmpty(fsCdr.vars[FS_PROGRESS_MEDIAMSEC], fsCdr.vars[FS_PROGRESSMS]) pddStr = pddStr + "ms" storCdr.Pdd, _ = time.ParseDuration(pddStr) - storCdr.AnswerTime, _ = utils.ParseTimeDetectLayout(fsCdr.vars[FS_ANSWER_TIME]) + storCdr.AnswerTime, _ = utils.ParseTimeDetectLayout(fsCdr.vars[FS_ANSWER_TIME], timezone) storCdr.Usage, _ = utils.ParseDurationWithSecs(fsCdr.vars[FS_DURATION]) storCdr.Supplier = fsCdr.vars[utils.CGR_SUPPLIER] storCdr.DisconnectCause = utils.FirstNonEmpty(fsCdr.vars[utils.CGR_DISCONNECT_CAUSE], fsCdr.vars["hangup_cause"]) diff --git a/engine/fscdr_test.go b/engine/fscdr_test.go index 15d6832ac..521d7b5f0 100644 --- a/engine/fscdr_test.go +++ b/engine/fscdr_test.go @@ -53,13 +53,13 @@ func TestCDRFields(t *testing.T) { if err != nil { t.Errorf("Error loading cdr: %v", err) } - setupTime, _ := utils.ParseTimeDetectLayout("1436280728") - answerTime, _ := utils.ParseTimeDetectLayout("1436280728") + setupTime, _ := utils.ParseTimeDetectLayout("1436280728", "") + answerTime, _ := utils.ParseTimeDetectLayout("1436280728", "") expctStoredCdr := &StoredCdr{CgrId: "164b0422fdc6a5117031b427439482c6a4f90e41", TOR: utils.VOICE, AccId: "e3133bf7-dcde-4daf-9663-9a79ffcef5ad", CdrHost: "127.0.0.1", CdrSource: "freeswitch_json", Direction: utils.OUT, Category: "call", ReqType: utils.META_PREPAID, Tenant: "cgrates.org", Account: "1001", Subject: "1001", Destination: "1003", SetupTime: setupTime, Pdd: time.Duration(28) * time.Millisecond, AnswerTime: answerTime, Usage: time.Duration(66) * time.Second, Supplier: "supplier1", DisconnectCause: "NORMAL_CLEARING", ExtraFields: map[string]string{"sip_user_agent": "PJSUA v2.3 Linux-3.2.0.4/x86_64/glibc-2.13"}, Cost: -1} - if storedCdr := fsCdr.AsStoredCdr(); !reflect.DeepEqual(expctStoredCdr, storedCdr) { + if storedCdr := fsCdr.AsStoredCdr(""); !reflect.DeepEqual(expctStoredCdr, storedCdr) { t.Errorf("Expecting: %v, received: %v", expctStoredCdr, storedCdr) } } diff --git a/engine/lcr.go b/engine/lcr.go index b2df016e3..904e57dc5 100644 --- a/engine/lcr.go +++ b/engine/lcr.go @@ -60,7 +60,7 @@ type LcrRequest struct { *utils.Paginator } -func (self *LcrRequest) AsCallDescriptor() (*CallDescriptor, error) { +func (self *LcrRequest) AsCallDescriptor(timezone string) (*CallDescriptor, error) { if len(self.Account) == 0 || len(self.Destination) == 0 { return nil, utils.ErrMandatoryIeMissing } @@ -81,7 +81,7 @@ func (self *LcrRequest) AsCallDescriptor() (*CallDescriptor, error) { var err error if len(self.SetupTime) == 0 { timeStart = time.Now() - } else if timeStart, err = utils.ParseTimeDetectLayout(self.SetupTime); err != nil { + } else if timeStart, err = utils.ParseTimeDetectLayout(self.SetupTime, timezone); err != nil { return nil, err } var callDur time.Duration diff --git a/engine/lcr_test.go b/engine/lcr_test.go index 79f036087..906a6c045 100644 --- a/engine/lcr_test.go +++ b/engine/lcr_test.go @@ -216,7 +216,7 @@ func TestLcrRequestAsCallDescriptor(t *testing.T) { sTime := time.Date(2015, 04, 06, 17, 40, 0, 0, time.UTC) callDur := time.Duration(1) * time.Minute lcrReq := &LcrRequest{Account: "2001", SetupTime: sTime.String()} - if _, err := lcrReq.AsCallDescriptor(); err == nil || err != utils.ErrMandatoryIeMissing { + if _, err := lcrReq.AsCallDescriptor(""); err == nil || err != utils.ErrMandatoryIeMissing { t.Error("Unexpected error received: %v", err) } lcrReq = &LcrRequest{Account: "2001", Destination: "2002", SetupTime: sTime.String()} @@ -230,7 +230,7 @@ func TestLcrRequestAsCallDescriptor(t *testing.T) { TimeStart: sTime, TimeEnd: sTime.Add(callDur), } - if cd, err := lcrReq.AsCallDescriptor(); err != nil { + if cd, err := lcrReq.AsCallDescriptor(""); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCd, cd) { t.Errorf("Expected: %+v, received: %+v", eCd, cd) diff --git a/engine/libtest.go b/engine/libtest.go index 6511eabd3..661360fed 100644 --- a/engine/libtest.go +++ b/engine/libtest.go @@ -90,7 +90,7 @@ func StopStartEngine(cfgPath string, waitEngine int) (*exec.Cmd, error) { return StartEngine(cfgPath, waitEngine) } -func LoadTariffPlanFromFolder(tpPath string, ratingDb RatingStorage, accountingDb AccountingStorage) error { +func LoadTariffPlanFromFolder(tpPath, timezone string, ratingDb RatingStorage, accountingDb AccountingStorage) error { loader := NewTpReader(ratingDb, accountingDb, NewFileCSVStorage(utils.CSV_SEP, path.Join(tpPath, utils.DESTINATIONS_CSV), path.Join(tpPath, utils.TIMINGS_CSV), @@ -106,7 +106,7 @@ func LoadTariffPlanFromFolder(tpPath string, ratingDb RatingStorage, accountingD path.Join(tpPath, utils.ACCOUNT_ACTIONS_CSV), path.Join(tpPath, utils.DERIVED_CHARGERS_CSV), path.Join(tpPath, utils.CDR_STATS_CSV), - path.Join(tpPath, utils.USERS_CSV)), "") + path.Join(tpPath, utils.USERS_CSV)), "", timezone) if err := loader.LoadAll(); err != nil { return utils.NewErrServerError(err) } diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 957537cdc..2a3206ec5 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -223,7 +223,7 @@ var csvr *TpReader func init() { csvr = NewTpReader(ratingStorage, accountingStorage, NewStringCSVStorage(',', destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, - sharedGroups, lcrs, actions, actionTimings, actionTriggers, accountActions, derivedCharges, cdrStats, users), "") + sharedGroups, lcrs, actions, actionTimings, actionTriggers, accountActions, derivedCharges, cdrStats, users), "", "") if err := csvr.LoadDestinations(); err != nil { log.Print("error in LoadDestinations:", err) } diff --git a/engine/loader_local_test.go b/engine/loader_local_test.go index 393832d0e..3b7ca25ca 100644 --- a/engine/loader_local_test.go +++ b/engine/loader_local_test.go @@ -131,7 +131,7 @@ func TestLoadFromCSV(t *testing.T) { path.Join(*dataDir, "tariffplans", *tpCsvScenario, utils.ACCOUNT_ACTIONS_CSV), path.Join(*dataDir, "tariffplans", *tpCsvScenario, utils.DERIVED_CHARGERS_CSV), path.Join(*dataDir, "tariffplans", *tpCsvScenario, utils.CDR_STATS_CSV), - path.Join(*dataDir, "tariffplans", *tpCsvScenario, utils.USERS_CSV)), "") + path.Join(*dataDir, "tariffplans", *tpCsvScenario, utils.USERS_CSV)), "", "") if err = loader.LoadDestinations(); err != nil { t.Error("Failed loading destinations: ", err.Error()) @@ -201,7 +201,7 @@ func TestLoadFromStorDb(t *testing.T) { if !*testLocal { return } - loader := NewTpReader(ratingDbStor, accountDbStor, storDb, utils.TEST_SQL) + loader := NewTpReader(ratingDbStor, accountDbStor, storDb, utils.TEST_SQL, "") if err := loader.LoadDestinations(); err != nil { t.Error("Failed loading destinations: ", err.Error()) } @@ -244,7 +244,7 @@ func TestLoadIndividualProfiles(t *testing.T) { if !*testLocal { return } - loader := NewTpReader(ratingDbApier, accountDbApier, storDb, utils.TEST_SQL) + loader := NewTpReader(ratingDbApier, accountDbApier, storDb, utils.TEST_SQL, "") // Load ratingPlans. This will also set destination keys if ratingPlans, err := storDb.GetTpRatingPlans(utils.TEST_SQL, "", nil); err != nil { t.Fatal("Could not retrieve rating plans") diff --git a/engine/model_helpers.go b/engine/model_helpers.go index c7ef2c0d6..9bde56ac9 100644 --- a/engine/model_helpers.go +++ b/engine/model_helpers.go @@ -532,7 +532,7 @@ func (tps TpCdrStats) GetCdrStats() (map[string][]*utils.TPCdrStat, error) { return css, nil } -func UpdateCdrStats(cs *CdrStats, triggers ActionTriggerPriotityList, tpCs *utils.TPCdrStat) { +func UpdateCdrStats(cs *CdrStats, triggers ActionTriggerPriotityList, tpCs *utils.TPCdrStat, timezone string) { if tpCs.QueueLength != "" && tpCs.QueueLength != "0" { if qi, err := strconv.Atoi(tpCs.QueueLength); err == nil { cs.QueueLength = qi @@ -560,7 +560,7 @@ func UpdateCdrStats(cs *CdrStats, triggers ActionTriggerPriotityList, tpCs *util if tpCs.SetupInterval != "" { times := strings.Split(tpCs.SetupInterval, utils.INFIELD_SEP) if len(times) > 0 { - if sTime, err := utils.ParseTimeDetectLayout(times[0]); err == nil { + if sTime, err := utils.ParseTimeDetectLayout(times[0], timezone); err == nil { if len(cs.SetupInterval) < 1 { cs.SetupInterval = append(cs.SetupInterval, sTime) } else { @@ -571,7 +571,7 @@ func UpdateCdrStats(cs *CdrStats, triggers ActionTriggerPriotityList, tpCs *util } } if len(times) > 1 { - if eTime, err := utils.ParseTimeDetectLayout(times[1]); err == nil { + if eTime, err := utils.ParseTimeDetectLayout(times[1], timezone); err == nil { if len(cs.SetupInterval) < 2 { cs.SetupInterval = append(cs.SetupInterval, eTime) } else { diff --git a/engine/rawcdr.go b/engine/rawcdr.go index 518b34f56..472ecd175 100644 --- a/engine/rawcdr.go +++ b/engine/rawcdr.go @@ -20,5 +20,5 @@ package engine // RawCDR is the original CDR received from external sources (eg: FreeSWITCH) type RawCdr interface { - AsStoredCdr() *StoredCdr // Convert the inbound Cdr into internally used one, CgrCdr + AsStoredCdr(string) *StoredCdr // Convert the inbound Cdr into internally used one, CgrCdr } diff --git a/engine/responder.go b/engine/responder.go index 839e1c466..15f9c9ecf 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -50,6 +50,7 @@ type Responder struct { ExitChan chan bool CdrSrv *CdrServer Stats StatsInterface + Timezone string cnt int64 } @@ -208,7 +209,7 @@ func (rs *Responder) GetDerivedMaxSessionTime(ev *StoredCdr, reply *float64) err if !matchingAllFilters { // Do not process the derived charger further if not all filters were matched continue } - startTime, err := ev.GetSetupTime(utils.META_DEFAULT) + startTime, err := ev.GetSetupTime(utils.META_DEFAULT, rs.Timezone) if err != nil { return err } @@ -272,7 +273,7 @@ func (rs *Responder) GetSessionRuns(ev *StoredCdr, sRuns *[]*SessionRun) error { if !utils.IsSliceMember([]string{utils.META_PREPAID, utils.PREPAID}, ev.GetReqType(dc.ReqTypeField)) { continue // We only consider prepaid sessions } - startTime, err := ev.GetAnswerTime(dc.AnswerTimeField) + startTime, err := ev.GetAnswerTime(dc.AnswerTimeField, rs.Timezone) if err != nil { return errors.New("Error parsing answer event start time") } diff --git a/engine/storage_mysql_local_test.go b/engine/storage_mysql_local_test.go index ccce9eded..4f36bf03f 100644 --- a/engine/storage_mysql_local_test.go +++ b/engine/storage_mysql_local_test.go @@ -496,7 +496,7 @@ func TestMySQLSetCdr(t *testing.T) { utils.ANSWER_TIME: "2013-11-07T08:42:26Z", utils.USAGE: "15s", utils.PDD: "7s", utils.SUPPLIER: "SUPPL1", "field_extr1": "val_extr1", "fieldextr2": "valextr2", "cdrsource": utils.TEST_SQL} for _, cdr := range []*CgrCdr{cgrCdr1, cgrCdr2, cgrCdr3, cgrCdr4, cgrCdr5} { - if err := mysqlDb.SetCdr(cdr.AsStoredCdr()); err != nil { + if err := mysqlDb.SetCdr(cdr.AsStoredCdr("")); err != nil { t.Error(err.Error()) } } @@ -890,15 +890,15 @@ func TestMySQLRemStoredCdrs(t *testing.T) { } else if len(storedCdrs) != 7 { t.Error("Unexpected number of StoredCdrs returned: ", storedCdrs) } - tm, _ := utils.ParseTimeDetectLayout("2013-11-08T08:42:20Z") + tm, _ := utils.ParseTimeDetectLayout("2013-11-08T08:42:20Z", "") cgrIdA1 := utils.Sha1("aaa1", tm.String()) - tm, _ = utils.ParseTimeDetectLayout("2013-11-08T08:42:22Z") + tm, _ = utils.ParseTimeDetectLayout("2013-11-08T08:42:22Z", "") cgrIdA2 := utils.Sha1("aaa2", tm.String()) - tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:24Z") + tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:24Z", "") cgrIdA3 := utils.Sha1("aaa3", tm.String()) - tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:21Z") + tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:21Z", "") cgrIdA4 := utils.Sha1("aaa4", tm.String()) - tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:25Z") + tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:25Z", "") cgrIdA5 := utils.Sha1("aaa5", tm.String()) cgrIdB2 := utils.Sha1("bbb2", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()) cgrIdB3 := utils.Sha1("bbb3", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()) diff --git a/engine/storage_psql_local_test.go b/engine/storage_psql_local_test.go index 4683a920f..1926bd74c 100644 --- a/engine/storage_psql_local_test.go +++ b/engine/storage_psql_local_test.go @@ -491,7 +491,7 @@ func TestPSQLSetCdr(t *testing.T) { utils.ANSWER_TIME: "2013-11-07T08:42:26Z", utils.USAGE: "15s", utils.PDD: "7s", utils.SUPPLIER: "SUPPL1", "field_extr1": "val_extr1", "fieldextr2": "valextr2", "cdrsource": utils.TEST_SQL} for _, cdr := range []*CgrCdr{cgrCdr1, cgrCdr2, cgrCdr3, cgrCdr4, cgrCdr5} { - if err := psqlDb.SetCdr(cdr.AsStoredCdr()); err != nil { + if err := psqlDb.SetCdr(cdr.AsStoredCdr("")); err != nil { t.Error(err.Error()) } } @@ -885,15 +885,15 @@ func TestPSQLRemStoredCdrs(t *testing.T) { } else if len(storedCdrs) != 7 { t.Error("Unexpected number of StoredCdrs returned: ", storedCdrs) } - tm, _ := utils.ParseTimeDetectLayout("2013-11-08T08:42:20Z") + tm, _ := utils.ParseTimeDetectLayout("2013-11-08T08:42:20Z", "") cgrIdA1 := utils.Sha1("aaa1", tm.String()) - tm, _ = utils.ParseTimeDetectLayout("2013-11-08T08:42:22Z") + tm, _ = utils.ParseTimeDetectLayout("2013-11-08T08:42:22Z", "") cgrIdA2 := utils.Sha1("aaa2", tm.String()) - tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:24Z") + tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:24Z", "") cgrIdA3 := utils.Sha1("aaa3", tm.String()) - tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:21Z") + tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:21Z", "") cgrIdA4 := utils.Sha1("aaa4", tm.String()) - tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:25Z") + tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:25Z", "") cgrIdA5 := utils.Sha1("aaa5", tm.String()) cgrIdB2 := utils.Sha1("bbb2", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()) cgrIdB3 := utils.Sha1("bbb3", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()) diff --git a/engine/storedcdr.go b/engine/storedcdr.go index a7968dd9b..fe72776b6 100644 --- a/engine/storedcdr.go +++ b/engine/storedcdr.go @@ -29,19 +29,19 @@ import ( "github.com/cgrates/cgrates/utils" ) -func NewStoredCdrFromExternalCdr(extCdr *ExternalCdr) (*StoredCdr, error) { +func NewStoredCdrFromExternalCdr(extCdr *ExternalCdr, timezone string) (*StoredCdr, error) { var err error storedCdr := &StoredCdr{CgrId: extCdr.CgrId, OrderId: extCdr.OrderId, TOR: extCdr.TOR, AccId: extCdr.AccId, CdrHost: extCdr.CdrHost, CdrSource: extCdr.CdrSource, ReqType: extCdr.ReqType, Direction: extCdr.Direction, Tenant: extCdr.Tenant, Category: extCdr.Category, Account: extCdr.Account, Subject: extCdr.Subject, Destination: extCdr.Destination, Supplier: extCdr.Supplier, DisconnectCause: extCdr.DisconnectCause, MediationRunId: extCdr.MediationRunId, RatedAccount: extCdr.RatedAccount, RatedSubject: extCdr.RatedSubject, Cost: extCdr.Cost, Rated: extCdr.Rated} - if storedCdr.SetupTime, err = utils.ParseTimeDetectLayout(extCdr.SetupTime); err != nil { + if storedCdr.SetupTime, err = utils.ParseTimeDetectLayout(extCdr.SetupTime, timezone); err != nil { return nil, err } if len(storedCdr.CgrId) == 0 { // Populate CgrId if not present storedCdr.CgrId = utils.Sha1(storedCdr.AccId, storedCdr.SetupTime.String()) } - if storedCdr.AnswerTime, err = utils.ParseTimeDetectLayout(extCdr.AnswerTime); err != nil { + if storedCdr.AnswerTime, err = utils.ParseTimeDetectLayout(extCdr.AnswerTime, timezone); err != nil { return nil, err } if storedCdr.Usage, err = utils.ParseDurationWithSecs(extCdr.Usage); err != nil { @@ -209,7 +209,7 @@ func (storedCdr *StoredCdr) PassesFieldFilter(fieldFilter *utils.RSRField) (bool return false, "" } -func (storedCdr *StoredCdr) AsStoredCdr() *StoredCdr { +func (storedCdr *StoredCdr) AsStoredCdr(timezone string) *StoredCdr { return storedCdr } @@ -259,7 +259,7 @@ func (storedCdr *StoredCdr) AsHttpForm() url.Values { // Used in mediation, primaryMandatory marks whether missing field out of request represents error or can be ignored func (storedCdr *StoredCdr) ForkCdr(runId string, reqTypeFld, directionFld, tenantFld, categFld, accountFld, subjectFld, destFld, setupTimeFld, pddFld, answerTimeFld, durationFld, supplierFld, disconnectCauseFld *utils.RSRField, - extraFlds []*utils.RSRField, primaryMandatory bool) (*StoredCdr, error) { + extraFlds []*utils.RSRField, primaryMandatory bool, timezone string) (*StoredCdr, error) { if reqTypeFld == nil { reqTypeFld, _ = utils.NewRSRField(utils.META_DEFAULT) } @@ -378,13 +378,13 @@ func (storedCdr *StoredCdr) ForkCdr(runId string, reqTypeFld, directionFld, tena sTimeStr := storedCdr.FieldAsString(setupTimeFld) if primaryMandatory && len(sTimeStr) == 0 { return nil, utils.NewErrMandatoryIeMissing(utils.SETUP_TIME, setupTimeFld.Id) - } else if frkStorCdr.SetupTime, err = utils.ParseTimeDetectLayout(sTimeStr); err != nil { + } else if frkStorCdr.SetupTime, err = utils.ParseTimeDetectLayout(sTimeStr, timezone); err != nil { return nil, err } aTimeStr := storedCdr.FieldAsString(answerTimeFld) if primaryMandatory && len(aTimeStr) == 0 { return nil, utils.NewErrMandatoryIeMissing(utils.ANSWER_TIME, answerTimeFld.Id) - } else if frkStorCdr.AnswerTime, err = utils.ParseTimeDetectLayout(aTimeStr); err != nil { + } else if frkStorCdr.AnswerTime, err = utils.ParseTimeDetectLayout(aTimeStr, timezone); err != nil { return nil, err } durStr := storedCdr.FieldAsString(durationFld) @@ -447,7 +447,7 @@ func (storedCdr *StoredCdr) ComputeLcr() bool { func (storedCdr *StoredCdr) GetName() string { return storedCdr.CdrSource } -func (storedCdr *StoredCdr) GetCgrId() string { +func (storedCdr *StoredCdr) GetCgrId(timezone string) string { return storedCdr.CgrId } func (storedCdr *StoredCdr) GetUUID() string { @@ -528,7 +528,7 @@ func (storedCdr *StoredCdr) GetReqType(fieldName string) string { } return storedCdr.FieldAsString(&utils.RSRField{Id: fieldName}) } -func (storedCdr *StoredCdr) GetSetupTime(fieldName string) (time.Time, error) { +func (storedCdr *StoredCdr) GetSetupTime(fieldName, timezone string) (time.Time, error) { if utils.IsSliceMember([]string{utils.SETUP_TIME, utils.META_DEFAULT}, fieldName) { return storedCdr.SetupTime, nil } @@ -538,9 +538,9 @@ func (storedCdr *StoredCdr) GetSetupTime(fieldName string) (time.Time, error) { } else { sTimeVal = storedCdr.FieldAsString(&utils.RSRField{Id: fieldName}) } - return utils.ParseTimeDetectLayout(sTimeVal) + return utils.ParseTimeDetectLayout(sTimeVal, timezone) } -func (storedCdr *StoredCdr) GetAnswerTime(fieldName string) (time.Time, error) { +func (storedCdr *StoredCdr) GetAnswerTime(fieldName, timezone string) (time.Time, error) { if utils.IsSliceMember([]string{utils.ANSWER_TIME, utils.META_DEFAULT}, fieldName) { return storedCdr.AnswerTime, nil } @@ -550,7 +550,7 @@ func (storedCdr *StoredCdr) GetAnswerTime(fieldName string) (time.Time, error) { } else { aTimeVal = storedCdr.FieldAsString(&utils.RSRField{Id: fieldName}) } - return utils.ParseTimeDetectLayout(aTimeVal) + return utils.ParseTimeDetectLayout(aTimeVal, timezone) } func (storedCdr *StoredCdr) GetEndTime() (time.Time, error) { return storedCdr.AnswerTime.Add(storedCdr.Usage), nil @@ -607,7 +607,7 @@ func (storedCdr *StoredCdr) MissingParameter() bool { len(storedCdr.Account) == 0 || len(storedCdr.Destination) == 0 } -func (storedCdr *StoredCdr) ParseEventValue(rsrFld *utils.RSRField) string { +func (storedCdr *StoredCdr) ParseEventValue(rsrFld *utils.RSRField, timezone string) string { return storedCdr.FieldAsString(rsrFld) } func (storedCdr *StoredCdr) String() string { @@ -660,14 +660,14 @@ type UsageRecord struct { ExtraFields map[string]string } -func (self *UsageRecord) AsStoredCdr() (*StoredCdr, error) { +func (self *UsageRecord) AsStoredCdr(timezone string) (*StoredCdr, error) { var err error storedCdr := &StoredCdr{TOR: self.TOR, ReqType: self.ReqType, Direction: self.Direction, Tenant: self.Tenant, Category: self.Category, Account: self.Account, Subject: self.Subject, Destination: self.Destination} - if storedCdr.SetupTime, err = utils.ParseTimeDetectLayout(self.SetupTime); err != nil { + if storedCdr.SetupTime, err = utils.ParseTimeDetectLayout(self.SetupTime, timezone); err != nil { return nil, err } - if storedCdr.AnswerTime, err = utils.ParseTimeDetectLayout(self.AnswerTime); err != nil { + if storedCdr.AnswerTime, err = utils.ParseTimeDetectLayout(self.AnswerTime, timezone); err != nil { return nil, err } if storedCdr.Usage, err = utils.ParseDurationWithSecs(self.Usage); err != nil { @@ -682,7 +682,7 @@ func (self *UsageRecord) AsStoredCdr() (*StoredCdr, error) { return storedCdr, nil } -func (self *UsageRecord) AsCallDescriptor() (*CallDescriptor, error) { +func (self *UsageRecord) AsCallDescriptor(timezone string) (*CallDescriptor, error) { var err error cd := &CallDescriptor{ TOR: self.TOR, @@ -697,7 +697,7 @@ func (self *UsageRecord) AsCallDescriptor() (*CallDescriptor, error) { if len(timeStr) == 0 { // In case of auth, answer time will not be defined, so take it out of setup one timeStr = self.SetupTime } - if cd.TimeStart, err = utils.ParseTimeDetectLayout(timeStr); err != nil { + if cd.TimeStart, err = utils.ParseTimeDetectLayout(timeStr, timezone); err != nil { return nil, err } if usage, err := utils.ParseDurationWithSecs(self.Usage); err != nil { diff --git a/engine/storedcdr_test.go b/engine/storedcdr_test.go index a8afb1e9d..1a23f1953 100644 --- a/engine/storedcdr_test.go +++ b/engine/storedcdr_test.go @@ -45,7 +45,7 @@ func TestNewStoredCdrFromExternalCdr(t *testing.T) { SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID, Usage: time.Duration(10), Pdd: time.Duration(7) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans", Rated: true, } - if storedCdr, err := NewStoredCdrFromExternalCdr(extCdr); err != nil { + if storedCdr, err := NewStoredCdrFromExternalCdr(extCdr, ""); err != nil { t.Error(err) } else if !reflect.DeepEqual(eStorCdr, storedCdr) { t.Errorf("Expected: %+v, received: %+v", eStorCdr, storedCdr) @@ -357,7 +357,7 @@ func TestStoredCdrForkCdr(t *testing.T) { &utils.RSRField{Id: utils.CATEGORY}, &utils.RSRField{Id: utils.ACCOUNT}, &utils.RSRField{Id: utils.SUBJECT}, &utils.RSRField{Id: utils.DESTINATION}, &utils.RSRField{Id: utils.SETUP_TIME}, &utils.RSRField{Id: utils.PDD}, &utils.RSRField{Id: utils.ANSWER_TIME}, &utils.RSRField{Id: utils.USAGE}, &utils.RSRField{Id: utils.SUPPLIER}, &utils.RSRField{Id: utils.DISCONNECT_CAUSE}, - []*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "field_extr2"}}, true) + []*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "field_extr2"}}, true, "") if err != nil { t.Error("Unexpected error received", err) } @@ -391,7 +391,7 @@ func TestStoredCdrForkCdrStaticVals(t *testing.T) { rsrStDCause, _ := utils.NewRSRField("^HANGUP_COMPLETE") rsrPdd, _ := utils.NewRSRField("^3") rtCdrOut2, err := storCdr.ForkCdr("wholesale_run", rsrStPostpaid, rsrStIn, rsrStCgr, rsrStPC, rsrStFA, rsrStFS, &utils.RSRField{Id: "destination"}, - rsrStST, rsrPdd, rsrStAT, rsrStDur, rsrStSuppl, rsrStDCause, []*utils.RSRField{}, true) + rsrStST, rsrPdd, rsrStAT, rsrStDur, rsrStSuppl, rsrStDCause, []*utils.RSRField{}, true, "") if err != nil { t.Error("Unexpected error received", err) } @@ -407,7 +407,7 @@ func TestStoredCdrForkCdrStaticVals(t *testing.T) { _, err = storCdr.ForkCdr("wholesale_run", &utils.RSRField{Id: "dummy_header"}, &utils.RSRField{Id: "direction"}, &utils.RSRField{Id: "tenant"}, &utils.RSRField{Id: "tor"}, &utils.RSRField{Id: "account"}, &utils.RSRField{Id: "subject"}, &utils.RSRField{Id: "destination"}, &utils.RSRField{Id: "setup_time"}, &utils.RSRField{Id: utils.PDD}, &utils.RSRField{Id: "answer_time"}, &utils.RSRField{Id: "duration"}, &utils.RSRField{Id: utils.SUPPLIER}, - &utils.RSRField{Id: utils.DISCONNECT_CAUSE}, []*utils.RSRField{}, true) + &utils.RSRField{Id: utils.DISCONNECT_CAUSE}, []*utils.RSRField{}, true, "") if err == nil { t.Error("Failed to detect missing header") } @@ -429,7 +429,7 @@ func TestStoredCdrForkCdrFromMetaDefaults(t *testing.T) { &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, - []*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "fieldextr2"}}, true) + []*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "fieldextr2"}}, true, "") if err != nil { t.Fatal("Unexpected error received", err) } @@ -439,7 +439,7 @@ func TestStoredCdrForkCdrFromMetaDefaults(t *testing.T) { } // Should also accept nil as defaults if cdrOut, err := storCdr.ForkCdr("wholesale_run", nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - []*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "fieldextr2"}}, true); err != nil { + []*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "fieldextr2"}}, true, ""); err != nil { t.Fatal("Unexpected error received", err) } else if !reflect.DeepEqual(expctCdr, cdrOut) { t.Errorf("Expected: %v, received: %v", expctCdr, cdrOut) @@ -479,7 +479,7 @@ func TestStoredCdrEventFields(t *testing.T) { if res := cdr.GetName(); res != "test" { t.Error("Received: ", res) } - if res := cdr.GetCgrId(); res != utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()) { + if res := cdr.GetCgrId(""); res != utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()) { t.Error("Received: ", res) } if res := cdr.GetUUID(); res != "dsafdsaf" { @@ -509,10 +509,10 @@ func TestStoredCdrEventFields(t *testing.T) { if res := cdr.GetReqType(utils.META_DEFAULT); res != utils.META_RATED { t.Error("Received: ", res) } - if st, _ := cdr.GetSetupTime(utils.META_DEFAULT); st != time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC) { + if st, _ := cdr.GetSetupTime(utils.META_DEFAULT, ""); st != time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC) { t.Error("Received: ", st) } - if at, _ := cdr.GetAnswerTime(utils.META_DEFAULT); at != time.Date(2013, 11, 7, 8, 42, 27, 0, time.UTC) { + if at, _ := cdr.GetAnswerTime(utils.META_DEFAULT, ""); at != time.Date(2013, 11, 7, 8, 42, 27, 0, time.UTC) { t.Error("Received: ", at) } if et, _ := cdr.GetEndTime(); et != time.Date(2013, 11, 7, 8, 42, 37, 0, time.UTC) { @@ -540,7 +540,7 @@ func TesUsageReqAsStoredCdr(t *testing.T) { eStorCdr := &StoredCdr{TOR: utils.VOICE, ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), Usage: time.Duration(10)} - if storedCdr, err := setupReq.AsStoredCdr(); err != nil { + if storedCdr, err := setupReq.AsStoredCdr(""); err != nil { t.Error(err) } else if !reflect.DeepEqual(eStorCdr, storedCdr) { t.Errorf("Expected: %+v, received: %+v", eStorCdr, storedCdr) @@ -555,7 +555,7 @@ func TestUsageReqAsCD(t *testing.T) { eCD := &CallDescriptor{TOR: req.TOR, Direction: req.Direction, Tenant: req.Tenant, Category: req.Category, Account: req.Account, Subject: req.Subject, Destination: req.Destination, TimeStart: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), TimeEnd: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).Add(time.Duration(10))} - if cd, err := req.AsCallDescriptor(); err != nil { + if cd, err := req.AsCallDescriptor(""); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCD, cd) { t.Errorf("Expected: %+v, received: %+v", eCD, cd) diff --git a/engine/tp_reader.go b/engine/tp_reader.go index e6ca07d64..e6d0899f0 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -12,6 +12,7 @@ import ( type TpReader struct { tpid string + timezone string ratingStorage RatingStorage accountingStorage AccountingStorage lr LoadReader @@ -36,9 +37,10 @@ type TpReader struct { users map[string]*UserProfile } -func NewTpReader(rs RatingStorage, as AccountingStorage, lr LoadReader, tpid string) *TpReader { +func NewTpReader(rs RatingStorage, as AccountingStorage, lr LoadReader, tpid, timezone string) *TpReader { tpr := &TpReader{ tpid: tpid, + timezone: timezone, ratingStorage: rs, accountingStorage: as, lr: lr, @@ -456,7 +458,7 @@ func (tpr *TpReader) LoadLCRs() (err error) { } } tag := utils.LCRKey(tpLcr.Direction, tpLcr.Tenant, tpLcr.Category, tpLcr.Account, tpLcr.Subject) - activationTime, _ := utils.ParseTimeDetectLayout(tpLcr.ActivationTime) + activationTime, _ := utils.ParseTimeDetectLayout(tpLcr.ActivationTime, tpr.timezone) lcr, found := tpr.lcrs[tag] if !found { @@ -611,7 +613,7 @@ func (tpr *TpReader) LoadActionTriggers() (err error) { for key, atrsLst := range storAts { atrs := make([]*ActionTrigger, len(atrsLst)) for idx, atr := range atrsLst { - balanceExpirationDate, _ := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate) + balanceExpirationDate, _ := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone) id := atr.Id if id == "" { id = utils.GenUUID() @@ -989,7 +991,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { // only return error if there was something there for the tag return fmt.Errorf("could not get action triggers for cdr stats id %s: %s", cs.Id, triggerTag) } - UpdateCdrStats(cs, triggers, tpStat) + UpdateCdrStats(cs, triggers, tpStat, tpr.timezone) tpr.cdrStats[tag] = cs } } diff --git a/general_tests/acntacts_test.go b/general_tests/acntacts_test.go index de7d646c7..94d5e7cf2 100644 --- a/general_tests/acntacts_test.go +++ b/general_tests/acntacts_test.go @@ -54,7 +54,7 @@ ENABLE_ACNT,*enable_account,,,,,,,,,,,,,10` cdrStats := `` users := `` csvr := engine.NewTpReader(ratingDbAcntActs, acntDbAcntActs, engine.NewStringCSVStorage(',', destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, - sharedGroups, lcrs, actions, actionPlans, actionTriggers, accountActions, derivedCharges, cdrStats, users), "") + sharedGroups, lcrs, actions, actionPlans, actionTriggers, accountActions, derivedCharges, cdrStats, users), "", "") if err := csvr.LoadAll(); err != nil { t.Fatal(err) } diff --git a/general_tests/costs1_test.go b/general_tests/costs1_test.go index f3127ad25..e06d3438c 100644 --- a/general_tests/costs1_test.go +++ b/general_tests/costs1_test.go @@ -54,7 +54,7 @@ RP_SMS1,DR_SMS_1,ALWAYS,10` *out,cgrates.org,data,*any,2012-01-01T00:00:00Z,RP_DATA1,, *out,cgrates.org,sms,*any,2012-01-01T00:00:00Z,RP_SMS1,,` csvr := engine.NewTpReader(ratingDb, acntDb, engine.NewStringCSVStorage(',', dests, timings, rates, destinationRates, ratingPlans, ratingProfiles, - "", "", "", "", "", "", "", "", ""), "") + "", "", "", "", "", "", "", "", ""), "", "") if err := csvr.LoadTimings(); err != nil { t.Fatal(err) @@ -86,8 +86,8 @@ RP_SMS1,DR_SMS_1,ALWAYS,10` } func TestCosts1GetCost1(t *testing.T) { - tStart, _ := utils.ParseTimeDetectLayout("2013-08-07T17:30:00Z") - tEnd, _ := utils.ParseTimeDetectLayout("2013-08-07T17:31:30Z") + tStart, _ := utils.ParseTimeDetectLayout("2013-08-07T17:30:00Z", "") + tEnd, _ := utils.ParseTimeDetectLayout("2013-08-07T17:31:30Z", "") cd := &engine.CallDescriptor{ Direction: "*out", Category: "call", @@ -106,8 +106,8 @@ func TestCosts1GetCost1(t *testing.T) { } func TestCosts1GetCostZeroDuration(t *testing.T) { - tStart, _ := utils.ParseTimeDetectLayout("2013-08-07T17:30:00Z") - tEnd, _ := utils.ParseTimeDetectLayout("2013-08-07T17:30:00Z") + tStart, _ := utils.ParseTimeDetectLayout("2013-08-07T17:30:00Z", "") + tEnd, _ := utils.ParseTimeDetectLayout("2013-08-07T17:30:00Z", "") cd := &engine.CallDescriptor{ Direction: "*out", Category: "call", diff --git a/general_tests/datachrg1_test.go b/general_tests/datachrg1_test.go index c200bf661..ae5564c10 100644 --- a/general_tests/datachrg1_test.go +++ b/general_tests/datachrg1_test.go @@ -45,7 +45,7 @@ DR_DATA_2,*any,RT_DATA_1c,*up,4,0,` RP_DATA1,DR_DATA_2,TM2,10` ratingProfiles := `*out,cgrates.org,data,*any,2012-01-01T00:00:00Z,RP_DATA1,,` csvr := engine.NewTpReader(ratingDb, acntDb, engine.NewStringCSVStorage(',', "", timings, rates, destinationRates, ratingPlans, ratingProfiles, - "", "", "", "", "", "", "", "", ""), "") + "", "", "", "", "", "", "", "", ""), "", "") if err := csvr.LoadTimings(); err != nil { t.Fatal(err) } diff --git a/general_tests/ddazmbl1_test.go b/general_tests/ddazmbl1_test.go index be8a38562..f194dac33 100644 --- a/general_tests/ddazmbl1_test.go +++ b/general_tests/ddazmbl1_test.go @@ -63,7 +63,7 @@ TOPUP10_AT,TOPUP10_AC1,ASAP,10` cdrStats := `` users := `` csvr := engine.NewTpReader(ratingDb, acntDb, engine.NewStringCSVStorage(',', destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, - sharedGroups, lcrs, actions, actionPlans, actionTriggers, accountActions, derivedCharges, cdrStats, users), "") + sharedGroups, lcrs, actions, actionPlans, actionTriggers, accountActions, derivedCharges, cdrStats, users), "", "") if err := csvr.LoadDestinations(); err != nil { t.Fatal(err) } diff --git a/general_tests/ddazmbl2_test.go b/general_tests/ddazmbl2_test.go index 29bd6f177..eb7a1142d 100644 --- a/general_tests/ddazmbl2_test.go +++ b/general_tests/ddazmbl2_test.go @@ -63,7 +63,7 @@ TOPUP10_AT,TOPUP10_AC1,ASAP,10` cdrStats := `` users := `` csvr := engine.NewTpReader(ratingDb2, acntDb2, engine.NewStringCSVStorage(',', destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, - sharedGroups, lcrs, actions, actionPlans, actionTriggers, accountActions, derivedCharges, cdrStats, users), "") + sharedGroups, lcrs, actions, actionPlans, actionTriggers, accountActions, derivedCharges, cdrStats, users), "", "") if err := csvr.LoadDestinations(); err != nil { t.Fatal(err) } diff --git a/general_tests/ddazmbl3_test.go b/general_tests/ddazmbl3_test.go index 512176715..87efbc30e 100644 --- a/general_tests/ddazmbl3_test.go +++ b/general_tests/ddazmbl3_test.go @@ -61,7 +61,7 @@ RP_UK,DR_UK_Mobile_BIG5,ALWAYS,10` cdrStats := `` users := `` csvr := engine.NewTpReader(ratingDb3, acntDb3, engine.NewStringCSVStorage(',', destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, - sharedGroups, lcrs, actions, actionPlans, actionTriggers, accountActions, derivedCharges, cdrStats, users), "") + sharedGroups, lcrs, actions, actionPlans, actionTriggers, accountActions, derivedCharges, cdrStats, users), "", "") if err := csvr.LoadDestinations(); err != nil { t.Fatal(err) } diff --git a/general_tests/fsevcorelate_test.go b/general_tests/fsevcorelate_test.go index a6d64dae7..0928ebc93 100644 --- a/general_tests/fsevcorelate_test.go +++ b/general_tests/fsevcorelate_test.go @@ -222,11 +222,11 @@ func TestEvCorelate(t *testing.T) { cdrEv, err := engine.NewFSCdr(jsonCdr, cfg) if err != nil { t.Errorf("Error loading cdr: %v", err.Error()) - } else if cdrEv.AsStoredCdr().AccId != "86cfd6e2-dbda-45a3-b59d-f683ec368e8b" { - t.Error("Unexpected acntId received", cdrEv.AsStoredCdr().AccId) + } else if cdrEv.AsStoredCdr("").AccId != "86cfd6e2-dbda-45a3-b59d-f683ec368e8b" { + t.Error("Unexpected acntId received", cdrEv.AsStoredCdr("").AccId) } - if answerEv.GetCgrId() != cdrEv.AsStoredCdr().CgrId { - t.Error("CgrIds do not match", answerEv.GetCgrId(), cdrEv.AsStoredCdr().CgrId) + if answerEv.GetCgrId("") != cdrEv.AsStoredCdr("").CgrId { + t.Error("CgrIds do not match", answerEv.GetCgrId(""), cdrEv.AsStoredCdr("").CgrId) } } @@ -550,14 +550,14 @@ func TestEvCdrCorelate(t *testing.T) { } cfg, _ := config.NewDefaultCGRConfig() config.SetCgrConfig(cfg) - evStoredCdr := hangupEv.AsStoredCdr() + evStoredCdr := hangupEv.AsStoredCdr("") cdrEv, err := engine.NewFSCdr(jsonCdr2, cfg) if err != nil { t.Errorf("Error loading cdr: %v", err.Error()) - } else if cdrEv.AsStoredCdr().AccId != "e3133bf7-dcde-4daf-9663-9a79ffcef5ad" { - t.Error("Unexpected acntId received", cdrEv.AsStoredCdr().AccId) + } else if cdrEv.AsStoredCdr("").AccId != "e3133bf7-dcde-4daf-9663-9a79ffcef5ad" { + t.Error("Unexpected acntId received", cdrEv.AsStoredCdr("").AccId) } - jsnStoredCdr := cdrEv.AsStoredCdr() + jsnStoredCdr := cdrEv.AsStoredCdr("") if evStoredCdr.CgrId != jsnStoredCdr.CgrId { t.Errorf("evStoredCdr.CgrId: %s, jsnStoredCdr.CgrId: %s", evStoredCdr.CgrId, jsnStoredCdr.CgrId) } diff --git a/general_tests/smschrg1_test.go b/general_tests/smschrg1_test.go index e391d8b2e..5390b161b 100644 --- a/general_tests/smschrg1_test.go +++ b/general_tests/smschrg1_test.go @@ -41,7 +41,7 @@ func TestSMSLoadCsvTpSmsChrg1(t *testing.T) { ratingPlans := `RP_SMS1,DR_SMS_1,ALWAYS,10` ratingProfiles := `*out,cgrates.org,sms,*any,2012-01-01T00:00:00Z,RP_SMS1,,` csvr := engine.NewTpReader(ratingDb, acntDb, engine.NewStringCSVStorage(',', "", timings, rates, destinationRates, ratingPlans, ratingProfiles, - "", "", "", "", "", "", "", "", ""), "") + "", "", "", "", "", "", "", "", ""), "", "") if err := csvr.LoadTimings(); err != nil { t.Fatal(err) } diff --git a/sessionmanager/fsevent.go b/sessionmanager/fsevent.go index 2e1273aec..36ad6341a 100644 --- a/sessionmanager/fsevent.go +++ b/sessionmanager/fsevent.go @@ -146,8 +146,8 @@ func (fsev FSEvent) GetCategory(fieldName string) string { } return utils.FirstNonEmpty(fsev[fieldName], fsev[Category], config.CgrConfig().DefaultCategory) } -func (fsev FSEvent) GetCgrId() string { - setupTime, _ := fsev.GetSetupTime(utils.META_DEFAULT) +func (fsev FSEvent) GetCgrId(timezone string) string { + setupTime, _ := fsev.GetSetupTime(utils.META_DEFAULT, timezone) return utils.Sha1(fsev[UUID], setupTime.UTC().String()) } func (fsev FSEvent) GetUUID() string { @@ -188,7 +188,7 @@ func (fsev FSEvent) MissingParameter() bool { strings.TrimSpace(fsev.GetTenant(utils.META_DEFAULT)) == "" || strings.TrimSpace(fsev.GetCallDestNr(utils.META_DEFAULT)) == "" } -func (fsev FSEvent) GetSetupTime(fieldName string) (t time.Time, err error) { +func (fsev FSEvent) GetSetupTime(fieldName, timezone string) (t time.Time, err error) { fsSTimeStr, hasKey := fsev[SETUP_TIME] if hasKey && fsSTimeStr != "0" { // Discard the nanoseconds information since MySQL cannot store them in early versions and csv uses default seconds so cgrid will not corelate @@ -198,9 +198,9 @@ func (fsev FSEvent) GetSetupTime(fieldName string) (t time.Time, err error) { if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value sTimeStr = fieldName[len(utils.STATIC_VALUE_PREFIX):] } - return utils.ParseTimeDetectLayout(sTimeStr) + return utils.ParseTimeDetectLayout(sTimeStr, timezone) } -func (fsev FSEvent) GetAnswerTime(fieldName string) (t time.Time, err error) { +func (fsev FSEvent) GetAnswerTime(fieldName, timezone string) (t time.Time, err error) { fsATimeStr, hasKey := fsev[ANSWER_TIME] if hasKey && fsATimeStr != "0" { // Discard the nanoseconds information since MySQL cannot store them in early versions and csv uses default seconds so cgrid will not corelate @@ -210,11 +210,11 @@ func (fsev FSEvent) GetAnswerTime(fieldName string) (t time.Time, err error) { if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value aTimeStr = fieldName[len(utils.STATIC_VALUE_PREFIX):] } - return utils.ParseTimeDetectLayout(aTimeStr) + return utils.ParseTimeDetectLayout(aTimeStr, timezone) } func (fsev FSEvent) GetEndTime() (t time.Time, err error) { - return utils.ParseTimeDetectLayout(fsev[END_TIME]) + return utils.ParseTimeDetectLayout(fsev[END_TIME], config.CgrConfig().DefaultTimezone) } func (fsev FSEvent) GetDuration(fieldName string) (time.Duration, error) { @@ -266,16 +266,16 @@ func (fsev FSEvent) GetOriginatorIP(fieldName string) string { func (fsev FSEvent) GetExtraFields() map[string]string { extraFields := make(map[string]string) for _, fldRule := range config.CgrConfig().SmFsConfig.CdrExtraFields { - extraFields[fldRule.Id] = fsev.ParseEventValue(fldRule) + extraFields[fldRule.Id] = fsev.ParseEventValue(fldRule, config.CgrConfig().DefaultTimezone) } return extraFields } // Used in derived charging and sittuations when we need to run regexp on fields -func (fsev FSEvent) ParseEventValue(rsrFld *utils.RSRField) string { +func (fsev FSEvent) ParseEventValue(rsrFld *utils.RSRField, timezone string) string { switch rsrFld.Id { case utils.CGRID: - return rsrFld.ParseValue(fsev.GetCgrId()) + return rsrFld.ParseValue(fsev.GetCgrId(timezone)) case utils.TOR: return rsrFld.ParseValue(utils.VOICE) case utils.ACCID: @@ -299,10 +299,10 @@ func (fsev FSEvent) ParseEventValue(rsrFld *utils.RSRField) string { case utils.DESTINATION: return rsrFld.ParseValue(fsev.GetDestination("")) case utils.SETUP_TIME: - st, _ := fsev.GetSetupTime("") + st, _ := fsev.GetSetupTime("", timezone) return rsrFld.ParseValue(st.String()) case utils.ANSWER_TIME: - at, _ := fsev.GetAnswerTime("") + at, _ := fsev.GetAnswerTime("", timezone) return rsrFld.ParseValue(at.String()) case utils.USAGE: dur, _ := fsev.GetDuration("") @@ -328,25 +328,25 @@ func (fsev FSEvent) PassesFieldFilter(fieldFilter *utils.RSRField) (bool, string if fieldFilter == nil { return true, "" } - if fieldFilter.IsStatic() && fsev.ParseEventValue(&utils.RSRField{Id: fieldFilter.Id}) == fsev.ParseEventValue(fieldFilter) { - return true, fsev.ParseEventValue(&utils.RSRField{Id: fieldFilter.Id}) + if fieldFilter.IsStatic() && fsev.ParseEventValue(&utils.RSRField{Id: fieldFilter.Id}, config.CgrConfig().DefaultTimezone) == fsev.ParseEventValue(fieldFilter, config.CgrConfig().DefaultTimezone) { + return true, fsev.ParseEventValue(&utils.RSRField{Id: fieldFilter.Id}, config.CgrConfig().DefaultTimezone) } preparedFilter := &utils.RSRField{Id: fieldFilter.Id, RSRules: make([]*utils.ReSearchReplace, len(fieldFilter.RSRules))} // Reset rules so they do not point towards same structures as original fieldFilter for idx := range fieldFilter.RSRules { // Hardcode the template with maximum of 5 groups ordered preparedFilter.RSRules[idx] = &utils.ReSearchReplace{SearchRegexp: fieldFilter.RSRules[idx].SearchRegexp, ReplaceTemplate: utils.FILTER_REGEXP_TPL} } - preparedVal := fsev.ParseEventValue(preparedFilter) - filteredValue := fsev.ParseEventValue(fieldFilter) + preparedVal := fsev.ParseEventValue(preparedFilter, config.CgrConfig().DefaultTimezone) + filteredValue := fsev.ParseEventValue(fieldFilter, config.CgrConfig().DefaultTimezone) if preparedFilter.RegexpMatched() && (len(preparedVal) == 0 || preparedVal == filteredValue) { return true, filteredValue } return false, "" } -func (fsev FSEvent) AsStoredCdr() *engine.StoredCdr { +func (fsev FSEvent) AsStoredCdr(timezone string) *engine.StoredCdr { storCdr := new(engine.StoredCdr) - storCdr.CgrId = fsev.GetCgrId() + storCdr.CgrId = fsev.GetCgrId(timezone) storCdr.TOR = utils.VOICE storCdr.AccId = fsev.GetUUID() storCdr.CdrHost = fsev.GetOriginatorIP(utils.META_DEFAULT) @@ -358,8 +358,8 @@ func (fsev FSEvent) AsStoredCdr() *engine.StoredCdr { storCdr.Account = fsev.GetAccount(utils.META_DEFAULT) storCdr.Subject = fsev.GetSubject(utils.META_DEFAULT) storCdr.Destination = fsev.GetDestination(utils.META_DEFAULT) - storCdr.SetupTime, _ = fsev.GetSetupTime(utils.META_DEFAULT) - storCdr.AnswerTime, _ = fsev.GetAnswerTime(utils.META_DEFAULT) + storCdr.SetupTime, _ = fsev.GetSetupTime(utils.META_DEFAULT, timezone) + storCdr.AnswerTime, _ = fsev.GetAnswerTime(utils.META_DEFAULT, timezone) storCdr.Usage, _ = fsev.GetDuration(utils.META_DEFAULT) storCdr.Pdd, _ = fsev.GetPdd(utils.META_DEFAULT) storCdr.ExtraFields = fsev.GetExtraFields() @@ -389,7 +389,7 @@ func (fsev FSEvent) AsCallDescriptor() (*engine.CallDescriptor, error) { SetupTime: utils.FirstNonEmpty(fsev[SETUP_TIME], fsev[ANSWER_TIME]), Duration: fsev[DURATION], } - return lcrReq.AsCallDescriptor() + return lcrReq.AsCallDescriptor(config.CgrConfig().DefaultTimezone) } // Converts a slice of strings into a FS array string, contains len(array) at first index since FS does not support len(ARRAY::) for now diff --git a/sessionmanager/fsevent_test.go b/sessionmanager/fsevent_test.go index 5a570bee5..94f49c332 100644 --- a/sessionmanager/fsevent_test.go +++ b/sessionmanager/fsevent_test.go @@ -368,8 +368,8 @@ Task-Runtime: 1349437318` // Detects if any of the parsers do not return static values func TestEventParseStatic(t *testing.T) { ev := new(FSEvent).AsEvent("") - setupTime, _ := ev.GetSetupTime("^2013-12-07 08:42:24") - answerTime, _ := ev.GetAnswerTime("^2013-12-07 08:42:24") + setupTime, _ := ev.GetSetupTime("^2013-12-07 08:42:24", "") + answerTime, _ := ev.GetAnswerTime("^2013-12-07 08:42:24", "") dur, _ := ev.GetDuration("^60s") if ev.GetReqType("^test") != "test" || ev.GetDirection("^test") != "test" || @@ -421,8 +421,8 @@ Task-Runtime: 1349437318` cfg, _ := config.NewDefaultCGRConfig() config.SetCgrConfig(cfg) ev := new(FSEvent).AsEvent(body) - setupTime, _ := ev.GetSetupTime("Event-Date-Local") - answerTime, _ := ev.GetAnswerTime("Event-Date-Local") + setupTime, _ := ev.GetSetupTime("Event-Date-Local", "") + answerTime, _ := ev.GetAnswerTime("Event-Date-Local", "") dur, _ := ev.GetDuration("Event-Calling-Line-Number") if ev.GetReqType("FreeSWITCH-Hostname") != "h1.ip-switch.net" || ev.GetDirection("FreeSWITCH-Hostname") != "*out" || @@ -463,12 +463,12 @@ Caller-Channel-Answered-Time Task-Runtime: 1349437318` var nilTime time.Time ev := new(FSEvent).AsEvent(body) - if setupTime, err := ev.GetSetupTime(""); err != nil { + if setupTime, err := ev.GetSetupTime("", ""); err != nil { t.Error("Error when parsing empty setupTime") } else if setupTime != nilTime { t.Error("Expecting nil time, got: ", setupTime) } - if answerTime, err := ev.GetAnswerTime(""); err != nil { + if answerTime, err := ev.GetAnswerTime("", ""); err != nil { t.Error("Error when parsing empty setupTime") } else if answerTime != nilTime { t.Error("Expecting nil time, got: ", answerTime) @@ -479,8 +479,8 @@ func TestParseFsHangup(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() config.SetCgrConfig(cfg) ev := new(FSEvent).AsEvent(hangupEv) - setupTime, _ := ev.GetSetupTime(utils.META_DEFAULT) - answerTime, _ := ev.GetAnswerTime(utils.META_DEFAULT) + setupTime, _ := ev.GetSetupTime(utils.META_DEFAULT, "") + answerTime, _ := ev.GetAnswerTime(utils.META_DEFAULT, "") dur, _ := ev.GetDuration(utils.META_DEFAULT) if ev.GetReqType(utils.META_DEFAULT) != utils.META_PREPAID || ev.GetDirection(utils.META_DEFAULT) != "*out" || @@ -514,66 +514,66 @@ func TestParseEventValue(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() config.SetCgrConfig(cfg) ev := new(FSEvent).AsEvent(hangupEv) - if cgrid := ev.ParseEventValue(&utils.RSRField{Id: utils.CGRID}); cgrid != "164b0422fdc6a5117031b427439482c6a4f90e41" { + if cgrid := ev.ParseEventValue(&utils.RSRField{Id: utils.CGRID}, ""); cgrid != "164b0422fdc6a5117031b427439482c6a4f90e41" { t.Error("Unexpected cgrid parsed", cgrid) } - if tor := ev.ParseEventValue(&utils.RSRField{Id: utils.TOR}); tor != utils.VOICE { + if tor := ev.ParseEventValue(&utils.RSRField{Id: utils.TOR}, ""); tor != utils.VOICE { t.Error("Unexpected tor parsed", tor) } - if accid := ev.ParseEventValue(&utils.RSRField{Id: utils.ACCID}); accid != "e3133bf7-dcde-4daf-9663-9a79ffcef5ad" { + if accid := ev.ParseEventValue(&utils.RSRField{Id: utils.ACCID}, ""); accid != "e3133bf7-dcde-4daf-9663-9a79ffcef5ad" { t.Error("Unexpected result parsed", accid) } - if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.CDRHOST}); parsed != "10.0.3.15" { + if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.CDRHOST}, ""); parsed != "10.0.3.15" { t.Error("Unexpected result parsed", parsed) } - if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.CDRSOURCE}); parsed != "FS_EVENT" { + if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.CDRSOURCE}, ""); parsed != "FS_EVENT" { t.Error("Unexpected result parsed", parsed) } - if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.REQTYPE}); parsed != utils.META_PREPAID { + if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.REQTYPE}, ""); parsed != utils.META_PREPAID { t.Error("Unexpected result parsed", parsed) } - if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.DIRECTION}); parsed != utils.OUT { + if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.DIRECTION}, ""); parsed != utils.OUT { t.Error("Unexpected result parsed", parsed) } - if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.TENANT}); parsed != "cgrates.org" { + if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.TENANT}, ""); parsed != "cgrates.org" { t.Error("Unexpected result parsed", parsed) } - if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.CATEGORY}); parsed != "call" { + if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.CATEGORY}, ""); parsed != "call" { t.Error("Unexpected result parsed", parsed) } - if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.ACCOUNT}); parsed != "1001" { + if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.ACCOUNT}, ""); parsed != "1001" { t.Error("Unexpected result parsed", parsed) } - if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.SUBJECT}); parsed != "1001" { + if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.SUBJECT}, ""); parsed != "1001" { t.Error("Unexpected result parsed", parsed) } - if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.DESTINATION}); parsed != "1003" { + if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.DESTINATION}, ""); parsed != "1003" { t.Error("Unexpected result parsed", parsed) } - sTime, _ := utils.ParseTimeDetectLayout("1436280728471153"[:len("1436280728471153")-6]) // We discard nanoseconds information so we can correlate csv - if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.SETUP_TIME}); parsed != sTime.String() { + sTime, _ := utils.ParseTimeDetectLayout("1436280728471153"[:len("1436280728471153")-6], "") // We discard nanoseconds information so we can correlate csv + if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.SETUP_TIME}, ""); parsed != sTime.String() { t.Errorf("Expecting: %s, parsed: %s", sTime.String(), parsed) } - aTime, _ := utils.ParseTimeDetectLayout("1436280728971147"[:len("1436280728971147")-6]) - if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.ANSWER_TIME}); parsed != aTime.String() { + aTime, _ := utils.ParseTimeDetectLayout("1436280728971147"[:len("1436280728971147")-6], "") + if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.ANSWER_TIME}, ""); parsed != aTime.String() { t.Errorf("Expecting: %s, parsed: %s", aTime.String(), parsed) } - if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.USAGE}); parsed != "66000000000" { + if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.USAGE}, ""); parsed != "66000000000" { t.Error("Unexpected result parsed", parsed) } - if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.PDD}); parsed != "0.028" { + if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.PDD}, ""); parsed != "0.028" { t.Error("Unexpected result parsed", parsed) } - if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.SUPPLIER}); parsed != "supplier1" { + if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.SUPPLIER}, ""); parsed != "supplier1" { t.Error("Unexpected result parsed", parsed) } - if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.MEDI_RUNID}); parsed != utils.DEFAULT_RUNID { + if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.MEDI_RUNID}, ""); parsed != utils.DEFAULT_RUNID { t.Error("Unexpected result parsed", parsed) } - if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.COST}); parsed != "-1" { + if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.COST}, ""); parsed != "-1" { t.Error("Unexpected result parsed", parsed) } - if parsed := ev.ParseEventValue(&utils.RSRField{Id: "Hangup-Cause"}); parsed != "NORMAL_CLEARING" { + if parsed := ev.ParseEventValue(&utils.RSRField{Id: "Hangup-Cause"}, ""); parsed != "NORMAL_CLEARING" { t.Error("Unexpected result parsed", parsed) } } @@ -631,14 +631,14 @@ func TestFsEvAsStoredCdr(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() config.SetCgrConfig(cfg) ev := new(FSEvent).AsEvent(hangupEv) - setupTime, _ := utils.ParseTimeDetectLayout("1436280728") - aTime, _ := utils.ParseTimeDetectLayout("1436280728") + setupTime, _ := utils.ParseTimeDetectLayout("1436280728", "") + aTime, _ := utils.ParseTimeDetectLayout("1436280728", "") eStoredCdr := &engine.StoredCdr{CgrId: "164b0422fdc6a5117031b427439482c6a4f90e41", TOR: utils.VOICE, AccId: "e3133bf7-dcde-4daf-9663-9a79ffcef5ad", CdrHost: "10.0.3.15", CdrSource: "FS_CHANNEL_HANGUP_COMPLETE", ReqType: utils.META_PREPAID, Direction: utils.OUT, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1003", SetupTime: setupTime, AnswerTime: aTime, Usage: time.Duration(66) * time.Second, Pdd: time.Duration(28) * time.Millisecond, Supplier: "supplier1", DisconnectCause: "NORMAL_CLEARING", ExtraFields: make(map[string]string), Cost: -1} - if storedCdr := ev.AsStoredCdr(); !reflect.DeepEqual(eStoredCdr, storedCdr) { + if storedCdr := ev.AsStoredCdr(""); !reflect.DeepEqual(eStoredCdr, storedCdr) { t.Errorf("Expecting: %+v, received: %+v", eStoredCdr, storedCdr) } } diff --git a/sessionmanager/fssessionmanager.go b/sessionmanager/fssessionmanager.go index 321cc9574..1652431be 100644 --- a/sessionmanager/fssessionmanager.go +++ b/sessionmanager/fssessionmanager.go @@ -40,15 +40,17 @@ type FSSessionManager struct { sessions []*Session rater engine.Connector cdrsrv engine.Connector + timezone string } -func NewFSSessionManager(smFsConfig *config.SmFsConfig, rater, cdrs engine.Connector) *FSSessionManager { +func NewFSSessionManager(smFsConfig *config.SmFsConfig, rater, cdrs engine.Connector, timezone string) *FSSessionManager { return &FSSessionManager{ cfg: smFsConfig, conns: make(map[string]*fsock.FSock), senderPools: make(map[string]*fsock.FSockPool), rater: rater, cdrsrv: cdrs, + timezone: timezone, } } @@ -165,6 +167,10 @@ func (sm *FSSessionManager) RemoveSession(uuid string) { } } +func (sm *FSSessionManager) Timezone() string { + return sm.timezone +} + // Sets the call timeout valid of starting of the call func (sm *FSSessionManager) setMaxCallDuration(uuid, connId string, maxDur time.Duration) error { // _, err := fsock.FS.SendApiCmd(fmt.Sprintf("sched_hangup +%d %s\n\n", int(maxDur.Seconds()), uuid)) @@ -179,7 +185,7 @@ func (sm *FSSessionManager) setMaxCallDuration(uuid, connId string, maxDur time. // Queries LCR and sets the cgr_lcr channel variable func (sm *FSSessionManager) setCgrLcr(ev engine.Event, connId string) error { var lcrCost engine.LCRCost - startTime, err := ev.GetSetupTime(utils.META_DEFAULT) + startTime, err := ev.GetSetupTime(utils.META_DEFAULT, sm.timezone) if err != nil { return err } @@ -217,7 +223,7 @@ func (sm *FSSessionManager) onChannelPark(ev engine.Event, connId string) { return } var maxCallDuration float64 // This will be the maximum duration this channel will be allowed to last - if err := sm.rater.GetDerivedMaxSessionTime(ev.AsStoredCdr(), &maxCallDuration); err != nil { + if err := sm.rater.GetDerivedMaxSessionTime(ev.AsStoredCdr(config.CgrConfig().DefaultTimezone), &maxCallDuration); err != nil { engine.Logger.Err(fmt.Sprintf(" Could not get max session time for %s, error: %s", ev.GetUUID(), err.Error())) } if maxCallDuration != -1 { // For calls different than unlimited, set limits @@ -291,7 +297,7 @@ func (sm *FSSessionManager) onChannelHangupComplete(ev engine.Event) { return } if sm.cfg.CreateCdr { - go sm.ProcessCdr(ev.AsStoredCdr()) + go sm.ProcessCdr(ev.AsStoredCdr(config.CgrConfig().DefaultTimezone)) } var s *Session for i := 0; i < 2; i++ { // Protect us against concurrency, wait a couple of seconds for the answer to be populated before we process hangup @@ -397,7 +403,7 @@ func (sm *FSSessionManager) SyncSessions() error { sm.RemoveSession(session.eventStart.GetUUID()) // Unreference it early so we avoid concurrency fsev := session.eventStart.(FSEvent) now := time.Now() - aTime, _ := fsev.GetAnswerTime("") + aTime, _ := fsev.GetAnswerTime("", sm.timezone) dur := now.Sub(aTime) fsev[END_TIME] = now.String() fsev[DURATION] = strconv.FormatFloat(dur.Seconds(), 'f', -1, 64) diff --git a/sessionmanager/kamailiosm.go b/sessionmanager/kamailiosm.go index cc86f9c31..ff970439a 100644 --- a/sessionmanager/kamailiosm.go +++ b/sessionmanager/kamailiosm.go @@ -31,8 +31,8 @@ import ( "github.com/cgrates/kamevapi" ) -func NewKamailioSessionManager(smKamCfg *config.SmKamConfig, rater, cdrsrv engine.Connector) (*KamailioSessionManager, error) { - ksm := &KamailioSessionManager{cfg: smKamCfg, rater: rater, cdrsrv: cdrsrv, conns: make(map[string]*kamevapi.KamEvapi)} +func NewKamailioSessionManager(smKamCfg *config.SmKamConfig, rater, cdrsrv engine.Connector, timezone string) (*KamailioSessionManager, error) { + ksm := &KamailioSessionManager{cfg: smKamCfg, rater: rater, cdrsrv: cdrsrv, timezone: timezone, conns: make(map[string]*kamevapi.KamEvapi)} return ksm, nil } @@ -40,6 +40,7 @@ type KamailioSessionManager struct { cfg *config.SmKamConfig rater engine.Connector cdrsrv engine.Connector + timezone string conns map[string]*kamevapi.KamEvapi sessions []*Session } @@ -63,7 +64,7 @@ func (self *KamailioSessionManager) onCgrAuth(evData []byte, connId string) { } var remainingDuration float64 var errMaxSession error - if errMaxSession = self.rater.GetDerivedMaxSessionTime(kev.AsStoredCdr(), &remainingDuration); errMaxSession != nil { + if errMaxSession = self.rater.GetDerivedMaxSessionTime(kev.AsStoredCdr(self.Timezone()), &remainingDuration); errMaxSession != nil { engine.Logger.Err(fmt.Sprintf(" Could not get max session time, error: %s", errMaxSession.Error())) } var supplStr string @@ -148,7 +149,7 @@ func (self *KamailioSessionManager) onCallEnd(evData []byte, connId string) { if kev.MissingParameter() { engine.Logger.Err(fmt.Sprintf(" Mandatory IE missing out of event: %+v", kev)) } - go self.ProcessCdr(kev.AsStoredCdr()) + go self.ProcessCdr(kev.AsStoredCdr(self.Timezone())) s := self.GetSession(kev.GetUUID()) if s == nil { // Not handled by us return @@ -244,3 +245,7 @@ func (self *KamailioSessionManager) Sessions() []*Session { func (self *KamailioSessionManager) SyncSessions() error { return nil } + +func (self *KamailioSessionManager) Timezone() string { + return self.timezone +} diff --git a/sessionmanager/kamevent.go b/sessionmanager/kamevent.go index 07669b43e..24849a32b 100644 --- a/sessionmanager/kamevent.go +++ b/sessionmanager/kamevent.go @@ -110,8 +110,8 @@ func (kev KamEvent) AsEvent(ignored string) engine.Event { func (kev KamEvent) GetName() string { return kev[EVENT] } -func (kev KamEvent) GetCgrId() string { - setupTime, _ := kev.GetSetupTime(utils.META_DEFAULT) +func (kev KamEvent) GetCgrId(timezone string) string { + setupTime, _ := kev.GetSetupTime(utils.META_DEFAULT, timezone) return utils.Sha1(kev.GetUUID(), setupTime.UTC().String()) } func (kev KamEvent) GetUUID() string { @@ -162,22 +162,22 @@ func (kev KamEvent) GetReqType(fieldName string) string { } return utils.FirstNonEmpty(kev[fieldName], kev[CGR_REQTYPE], config.CgrConfig().DefaultReqType) } -func (kev KamEvent) GetAnswerTime(fieldName string) (time.Time, error) { +func (kev KamEvent) GetAnswerTime(fieldName, timezone string) (time.Time, error) { aTimeStr := utils.FirstNonEmpty(kev[fieldName], kev[CGR_ANSWERTIME]) if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value aTimeStr = fieldName[len(utils.STATIC_VALUE_PREFIX):] } - return utils.ParseTimeDetectLayout(aTimeStr) + return utils.ParseTimeDetectLayout(aTimeStr, timezone) } -func (kev KamEvent) GetSetupTime(fieldName string) (time.Time, error) { +func (kev KamEvent) GetSetupTime(fieldName, timezone string) (time.Time, error) { sTimeStr := utils.FirstNonEmpty(kev[fieldName], kev[CGR_SETUPTIME], kev[CGR_ANSWERTIME]) if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value sTimeStr = fieldName[len(utils.STATIC_VALUE_PREFIX):] } - return utils.ParseTimeDetectLayout(sTimeStr) + return utils.ParseTimeDetectLayout(sTimeStr, timezone) } func (kev KamEvent) GetEndTime() (time.Time, error) { - return utils.ParseTimeDetectLayout(kev[CGR_STOPTIME]) + return utils.ParseTimeDetectLayout(kev[CGR_STOPTIME], config.CgrConfig().DefaultTimezone) } func (kev KamEvent) GetDuration(fieldName string) (time.Duration, error) { durStr := utils.FirstNonEmpty(kev[fieldName], kev[CGR_DURATION]) @@ -232,7 +232,7 @@ func (kev KamEvent) MissingParameter() bool { var nullTime time.Time switch kev.GetName() { case CGR_AUTH_REQUEST: - if setupTime, err := kev.GetSetupTime(utils.META_DEFAULT); err != nil || setupTime == nullTime { + if setupTime, err := kev.GetSetupTime(utils.META_DEFAULT, config.CgrConfig().DefaultTimezone); err != nil || setupTime == nullTime { return true } return len(kev.GetAccount(utils.META_DEFAULT)) == 0 || @@ -243,7 +243,7 @@ func (kev KamEvent) MissingParameter() bool { len(kev.GetDestination(utils.META_DEFAULT)) == 0 || len(kev[KAM_TR_INDEX]) == 0 || len(kev[KAM_TR_LABEL]) == 0 case CGR_CALL_START: - if aTime, err := kev.GetAnswerTime(utils.META_DEFAULT); err != nil || aTime == nullTime { + if aTime, err := kev.GetAnswerTime(utils.META_DEFAULT, config.CgrConfig().DefaultTimezone); err != nil || aTime == nullTime { return true } return len(kev.GetUUID()) == 0 || @@ -262,13 +262,13 @@ func (kev KamEvent) MissingParameter() bool { } // Useful for CDR generation -func (kev KamEvent) ParseEventValue(rsrFld *utils.RSRField) string { - sTime, _ := kev.GetSetupTime(utils.META_DEFAULT) - aTime, _ := kev.GetAnswerTime(utils.META_DEFAULT) +func (kev KamEvent) ParseEventValue(rsrFld *utils.RSRField, timezone string) string { + sTime, _ := kev.GetSetupTime(utils.META_DEFAULT, config.CgrConfig().DefaultTimezone) + aTime, _ := kev.GetAnswerTime(utils.META_DEFAULT, config.CgrConfig().DefaultTimezone) duration, _ := kev.GetDuration(utils.META_DEFAULT) switch rsrFld.Id { case utils.CGRID: - return rsrFld.ParseValue(kev.GetCgrId()) + return rsrFld.ParseValue(kev.GetCgrId(timezone)) case utils.TOR: return rsrFld.ParseValue(utils.VOICE) case utils.ACCID: @@ -315,9 +315,9 @@ func (kev KamEvent) PassesFieldFilter(*utils.RSRField) (bool, string) { return false, "" } -func (kev KamEvent) AsStoredCdr() *engine.StoredCdr { +func (kev KamEvent) AsStoredCdr(timezone string) *engine.StoredCdr { storCdr := new(engine.StoredCdr) - storCdr.CgrId = kev.GetCgrId() + storCdr.CgrId = kev.GetCgrId(timezone) storCdr.TOR = utils.VOICE storCdr.AccId = kev.GetUUID() storCdr.CdrHost = kev.GetOriginatorIP(utils.META_DEFAULT) @@ -329,8 +329,8 @@ func (kev KamEvent) AsStoredCdr() *engine.StoredCdr { storCdr.Account = kev.GetAccount(utils.META_DEFAULT) storCdr.Subject = kev.GetSubject(utils.META_DEFAULT) storCdr.Destination = kev.GetDestination(utils.META_DEFAULT) - storCdr.SetupTime, _ = kev.GetSetupTime(utils.META_DEFAULT) - storCdr.AnswerTime, _ = kev.GetAnswerTime(utils.META_DEFAULT) + storCdr.SetupTime, _ = kev.GetSetupTime(utils.META_DEFAULT, timezone) + storCdr.AnswerTime, _ = kev.GetAnswerTime(utils.META_DEFAULT, timezone) storCdr.Usage, _ = kev.GetDuration(utils.META_DEFAULT) storCdr.Pdd, _ = kev.GetPdd(utils.META_DEFAULT) storCdr.Supplier = kev.GetSupplier(utils.META_DEFAULT) @@ -384,7 +384,7 @@ func (kev KamEvent) AsCallDescriptor() (*engine.CallDescriptor, error) { SetupTime: utils.FirstNonEmpty(kev[CGR_SETUPTIME], kev[CGR_ANSWERTIME]), Duration: kev[CGR_DURATION], } - return lcrReq.AsCallDescriptor() + return lcrReq.AsCallDescriptor(config.CgrConfig().DefaultTimezone) } func (kev KamEvent) ComputeLcr() bool { diff --git a/sessionmanager/osipsevent.go b/sessionmanager/osipsevent.go index 6994c5025..4a40f3d6a 100644 --- a/sessionmanager/osipsevent.go +++ b/sessionmanager/osipsevent.go @@ -76,8 +76,8 @@ func (osipsev *OsipsEvent) GetName() string { return osipsev.osipsEvent.Name } -func (osipsev *OsipsEvent) GetCgrId() string { - setupTime, _ := osipsev.GetSetupTime(utils.META_DEFAULT) +func (osipsev *OsipsEvent) GetCgrId(timezone string) string { + setupTime, _ := osipsev.GetSetupTime(utils.META_DEFAULT, timezone) return utils.Sha1(osipsev.GetUUID(), setupTime.UTC().String()) } @@ -141,25 +141,25 @@ func (osipsev *OsipsEvent) GetReqType(fieldName string) string { } return utils.FirstNonEmpty(osipsev.osipsEvent.AttrValues[fieldName], osipsev.osipsEvent.AttrValues[CGR_REQTYPE], config.CgrConfig().DefaultReqType) } -func (osipsev *OsipsEvent) GetSetupTime(fieldName string) (time.Time, error) { +func (osipsev *OsipsEvent) GetSetupTime(fieldName, timezone string) (time.Time, error) { sTimeStr := utils.FirstNonEmpty(osipsev.osipsEvent.AttrValues[fieldName], osipsev.osipsEvent.AttrValues[OSIPS_SETUP_TIME], osipsev.osipsEvent.AttrValues[OSIPS_EVENT_TIME]) if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value sTimeStr = fieldName[len(utils.STATIC_VALUE_PREFIX):] } - return utils.ParseTimeDetectLayout(sTimeStr) + return utils.ParseTimeDetectLayout(sTimeStr, timezone) } -func (osipsev *OsipsEvent) GetAnswerTime(fieldName string) (time.Time, error) { +func (osipsev *OsipsEvent) GetAnswerTime(fieldName, timezone string) (time.Time, error) { aTimeStr := utils.FirstNonEmpty(osipsev.osipsEvent.AttrValues[fieldName], osipsev.osipsEvent.AttrValues[TIME]) if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value aTimeStr = fieldName[len(utils.STATIC_VALUE_PREFIX):] } else if fieldName == utils.META_DEFAULT { aTimeStr = osipsev.osipsEvent.AttrValues[TIME] } - return utils.ParseTimeDetectLayout(aTimeStr) + return utils.ParseTimeDetectLayout(aTimeStr, timezone) } func (osipsev *OsipsEvent) GetEndTime() (time.Time, error) { var nilTime time.Time - aTime, err := osipsev.GetAnswerTime(utils.META_DEFAULT) + aTime, err := osipsev.GetAnswerTime(utils.META_DEFAULT, config.CgrConfig().DefaultTimezone) if err != nil { return nilTime, err } @@ -217,11 +217,11 @@ func (osipsev *OsipsEvent) MissingParameter() bool { len(osipsev.osipsEvent.AttrValues[TIME]) == 0 } else if osipsev.GetName() == "E_ACC_EVENT" && osipsev.osipsEvent.AttrValues["method"] == "UPDATE" { // Updated event out of start/stop // Data needed when stopping a prepaid loop or building a CDR with start/stop event - setupTime, err := osipsev.GetSetupTime(TIME) + setupTime, err := osipsev.GetSetupTime(TIME, config.CgrConfig().DefaultTimezone) if err != nil || setupTime.Equal(nilTime) { return true } - aTime, err := osipsev.GetAnswerTime(utils.META_DEFAULT) + aTime, err := osipsev.GetAnswerTime(utils.META_DEFAULT, config.CgrConfig().DefaultTimezone) if err != nil || aTime.Equal(nilTime) { return true } @@ -240,7 +240,7 @@ func (osipsev *OsipsEvent) MissingParameter() bool { } return true } -func (osipsev *OsipsEvent) ParseEventValue(*utils.RSRField) string { +func (osipsev *OsipsEvent) ParseEventValue(fld *utils.RSRField, timezone string) string { return "" } func (osipsev *OsipsEvent) PassesFieldFilter(*utils.RSRField) (bool, string) { @@ -262,9 +262,9 @@ func (osipsev *OsipsEvent) DialogId() string { return osipsev.osipsEvent.AttrValues[OSIPS_DIALOG_ID] } -func (osipsEv *OsipsEvent) AsStoredCdr() *engine.StoredCdr { +func (osipsEv *OsipsEvent) AsStoredCdr(timezone string) *engine.StoredCdr { storCdr := new(engine.StoredCdr) - storCdr.CgrId = osipsEv.GetCgrId() + storCdr.CgrId = osipsEv.GetCgrId(timezone) storCdr.TOR = utils.VOICE storCdr.AccId = osipsEv.GetUUID() storCdr.CdrHost = osipsEv.GetOriginatorIP(utils.META_DEFAULT) @@ -276,8 +276,8 @@ func (osipsEv *OsipsEvent) AsStoredCdr() *engine.StoredCdr { storCdr.Account = osipsEv.GetAccount(utils.META_DEFAULT) storCdr.Subject = osipsEv.GetSubject(utils.META_DEFAULT) storCdr.Destination = osipsEv.GetDestination(utils.META_DEFAULT) - storCdr.SetupTime, _ = osipsEv.GetSetupTime(utils.META_DEFAULT) - storCdr.AnswerTime, _ = osipsEv.GetAnswerTime(utils.META_DEFAULT) + storCdr.SetupTime, _ = osipsEv.GetSetupTime(utils.META_DEFAULT, timezone) + storCdr.AnswerTime, _ = osipsEv.GetAnswerTime(utils.META_DEFAULT, timezone) storCdr.Usage, _ = osipsEv.GetDuration(utils.META_DEFAULT) storCdr.Pdd, _ = osipsEv.GetPdd(utils.META_DEFAULT) storCdr.Supplier = osipsEv.GetSupplier(utils.META_DEFAULT) @@ -289,11 +289,11 @@ func (osipsEv *OsipsEvent) AsStoredCdr() *engine.StoredCdr { // Computes duration out of setup time of the callEnd func (osipsEv *OsipsEvent) updateDurationFromEvent(updatedOsipsEv *OsipsEvent) error { - endTime, err := updatedOsipsEv.GetSetupTime(TIME) + endTime, err := updatedOsipsEv.GetSetupTime(TIME, config.CgrConfig().DefaultTimezone) if err != nil { return err } - answerTime, err := osipsEv.GetAnswerTime(utils.META_DEFAULT) + answerTime, err := osipsEv.GetAnswerTime(utils.META_DEFAULT, config.CgrConfig().DefaultTimezone) osipsEv.osipsEvent.AttrValues[OSIPS_DURATION] = endTime.Sub(answerTime).String() osipsEv.osipsEvent.AttrValues["method"] = "UPDATE" // So we can know it is an end event osipsEv.osipsEvent.AttrValues[OSIPS_SIPCODE] = updatedOsipsEv.osipsEvent.AttrValues[OSIPS_SIPCODE] diff --git a/sessionmanager/osipsevent_test.go b/sessionmanager/osipsevent_test.go index 351946317..60eab224c 100644 --- a/sessionmanager/osipsevent_test.go +++ b/sessionmanager/osipsevent_test.go @@ -41,8 +41,8 @@ func TestOsipsEventInterface(t *testing.T) { } func TestOsipsEventParseStatic(t *testing.T) { - setupTime, _ := osipsEv.GetSetupTime("^2013-12-07 08:42:24") - answerTime, _ := osipsEv.GetAnswerTime("^2013-12-07 08:42:24") + setupTime, _ := osipsEv.GetSetupTime("^2013-12-07 08:42:24", "") + answerTime, _ := osipsEv.GetAnswerTime("^2013-12-07 08:42:24", "") dur, _ := osipsEv.GetDuration("^60s") pdd, _ := osipsEv.GetPdd("^10s") if osipsEv.GetReqType("^test") != "test" || @@ -78,15 +78,15 @@ func TestOsipsEventParseStatic(t *testing.T) { func TestOsipsEventGetValues(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() config.SetCgrConfig(cfg) - setupTime, _ := osipsEv.GetSetupTime(utils.META_DEFAULT) - eSetupTime, _ := utils.ParseTimeDetectLayout("1406370492") - answerTime, _ := osipsEv.GetAnswerTime(utils.META_DEFAULT) - eAnswerTime, _ := utils.ParseTimeDetectLayout("1406370499") + setupTime, _ := osipsEv.GetSetupTime(utils.META_DEFAULT, "") + eSetupTime, _ := utils.ParseTimeDetectLayout("1406370492", "") + answerTime, _ := osipsEv.GetAnswerTime(utils.META_DEFAULT, "") + eAnswerTime, _ := utils.ParseTimeDetectLayout("1406370499", "") dur, _ := osipsEv.GetDuration(utils.META_DEFAULT) pdd, _ := osipsEv.GetPdd(utils.META_DEFAULT) endTime, _ := osipsEv.GetEndTime() if osipsEv.GetName() != "E_ACC_CDR" || - osipsEv.GetCgrId() != utils.Sha1("ODVkMDI2Mzc2MDY5N2EzODhjNTAzNTdlODhiZjRlYWQ", setupTime.UTC().String()) || + osipsEv.GetCgrId("") != utils.Sha1("ODVkMDI2Mzc2MDY5N2EzODhjNTAzNTdlODhiZjRlYWQ", setupTime.UTC().String()) || osipsEv.GetUUID() != "ODVkMDI2Mzc2MDY5N2EzODhjNTAzNTdlODhiZjRlYWQ" || osipsEv.GetDirection(utils.META_DEFAULT) != utils.OUT || osipsEv.GetSubject(utils.META_DEFAULT) != "dan" || @@ -105,7 +105,7 @@ func TestOsipsEventGetValues(t *testing.T) { osipsEv.GetDisconnectCause(utils.META_DEFAULT) != "200" || osipsEv.GetOriginatorIP(utils.META_DEFAULT) != "172.16.254.77" { t.Error("GetValues not matching: ", osipsEv.GetName() != "E_ACC_CDR", - osipsEv.GetCgrId() != utils.Sha1("ODVkMDI2Mzc2MDY5N2EzODhjNTAzNTdlODhiZjRlYWQ", setupTime.UTC().String()), + osipsEv.GetCgrId("") != utils.Sha1("ODVkMDI2Mzc2MDY5N2EzODhjNTAzNTdlODhiZjRlYWQ", setupTime.UTC().String()), osipsEv.GetUUID() != "ODVkMDI2Mzc2MDY5N2EzODhjNTAzNTdlODhiZjRlYWQ", osipsEv.GetDirection(utils.META_DEFAULT) != utils.OUT, osipsEv.GetSubject(utils.META_DEFAULT) != "dan", @@ -141,21 +141,21 @@ func TestOsipsEventMissingParameter(t *testing.T) { } func TestOsipsEventAsStoredCdr(t *testing.T) { - setupTime, _ := utils.ParseTimeDetectLayout("1406370492") - answerTime, _ := utils.ParseTimeDetectLayout("1406370499") + setupTime, _ := utils.ParseTimeDetectLayout("1406370492", "") + answerTime, _ := utils.ParseTimeDetectLayout("1406370499", "") eStoredCdr := &engine.StoredCdr{CgrId: utils.Sha1("ODVkMDI2Mzc2MDY5N2EzODhjNTAzNTdlODhiZjRlYWQ", setupTime.UTC().String()), TOR: utils.VOICE, AccId: "ODVkMDI2Mzc2MDY5N2EzODhjNTAzNTdlODhiZjRlYWQ", CdrHost: "172.16.254.77", CdrSource: "OSIPS_E_ACC_CDR", ReqType: utils.META_PREPAID, Direction: utils.OUT, Tenant: "itsyscom.com", Category: "call", Account: "dan", Subject: "dan", Destination: "+4986517174963", SetupTime: setupTime, AnswerTime: answerTime, Usage: time.Duration(20) * time.Second, Pdd: time.Duration(3) * time.Second, Supplier: "supplier3", DisconnectCause: "200", ExtraFields: map[string]string{"extra1": "val1", "extra2": "val2"}, Cost: -1} - if storedCdr := osipsEv.AsStoredCdr(); !reflect.DeepEqual(eStoredCdr, storedCdr) { + if storedCdr := osipsEv.AsStoredCdr(""); !reflect.DeepEqual(eStoredCdr, storedCdr) { t.Errorf("Expecting: %+v, received: %+v", eStoredCdr, storedCdr) } } func TestOsipsAccMissedToStoredCdr(t *testing.T) { - setupTime, _ := utils.ParseTimeDetectLayout("1431182699") + setupTime, _ := utils.ParseTimeDetectLayout("1431182699", "") osipsEv := &OsipsEvent{osipsEvent: &osipsdagram.OsipsEvent{Name: "E_ACC_MISSED_EVENT", AttrValues: map[string]string{"method": "INVITE", "from_tag": "5cb81eaa", "to_tag": "", "callid": "27b1e6679ad0109b5d756e42bb4c9c28@0:0:0:0:0:0:0:0", "sip_code": "404", "sip_reason": "Not Found", "time": "1431182699", "cgr_reqtype": utils.META_PSEUDOPREPAID, @@ -167,7 +167,7 @@ func TestOsipsAccMissedToStoredCdr(t *testing.T) { ReqType: utils.META_PSEUDOPREPAID, Direction: utils.OUT, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Supplier: "supplier1", DisconnectCause: "404", Destination: "1002", SetupTime: setupTime, AnswerTime: setupTime, Usage: time.Duration(0), ExtraFields: map[string]string{"extra1": "val1", "extra2": "val2"}, Cost: -1} - if storedCdr := osipsEv.AsStoredCdr(); !reflect.DeepEqual(eStoredCdr, storedCdr) { + if storedCdr := osipsEv.AsStoredCdr(""); !reflect.DeepEqual(eStoredCdr, storedCdr) { t.Errorf("Expecting: %+v, received: %+v", eStoredCdr, storedCdr) } diff --git a/sessionmanager/osipssm.go b/sessionmanager/osipssm.go index b69804998..ec14daf8b 100644 --- a/sessionmanager/osipssm.go +++ b/sessionmanager/osipssm.go @@ -80,8 +80,8 @@ duration:: */ -func NewOSipsSessionManager(smOsipsCfg *config.SmOsipsConfig, rater, cdrsrv engine.Connector) (*OsipsSessionManager, error) { - osm := &OsipsSessionManager{cfg: smOsipsCfg, rater: rater, cdrsrv: cdrsrv, cdrStartEvents: make(map[string]*OsipsEvent)} +func NewOSipsSessionManager(smOsipsCfg *config.SmOsipsConfig, rater, cdrsrv engine.Connector, timezone string) (*OsipsSessionManager, error) { + osm := &OsipsSessionManager{cfg: smOsipsCfg, rater: rater, cdrsrv: cdrsrv, timezone: timezone, cdrStartEvents: make(map[string]*OsipsEvent)} osm.eventHandlers = map[string][]func(*osipsdagram.OsipsEvent){ "E_OPENSIPS_START": []func(*osipsdagram.OsipsEvent){osm.onOpensipsStart}, // Raised when OpenSIPS starts so we can register our event handlers "E_ACC_CDR": []func(*osipsdagram.OsipsEvent){osm.onCdr}, // Raised if cdr_flag is configured @@ -95,6 +95,7 @@ type OsipsSessionManager struct { cfg *config.SmOsipsConfig rater engine.Connector cdrsrv engine.Connector + timezone string eventHandlers map[string][]func(*osipsdagram.OsipsEvent) evSubscribeStop chan struct{} // Reference towards the channel controlling subscriptions, keep it as reference so we do not need to copy it stopServing chan struct{} // Stop serving datagrams @@ -237,8 +238,8 @@ func (osm *OsipsSessionManager) onOpensipsStart(cdrDagram *osipsdagram.OsipsEven // Triggered by CDR event func (osm *OsipsSessionManager) onCdr(cdrDagram *osipsdagram.OsipsEvent) { osipsEv, _ := NewOsipsEvent(cdrDagram) - if err := osm.ProcessCdr(osipsEv.AsStoredCdr()); err != nil { - engine.Logger.Err(fmt.Sprintf(" Failed processing CDR, cgrid: %s, accid: %s, error: <%s>", osipsEv.GetCgrId(), osipsEv.GetUUID(), err.Error())) + if err := osm.ProcessCdr(osipsEv.AsStoredCdr(osm.timezone)); err != nil { + engine.Logger.Err(fmt.Sprintf(" Failed processing CDR, cgrid: %s, accid: %s, error: <%s>", osipsEv.GetCgrId(osm.timezone), osipsEv.GetUUID(), err.Error())) } } @@ -330,7 +331,7 @@ func (osm *OsipsSessionManager) processCdrStop(osipsEv *OsipsEvent) error { if err := osipsEvStart.updateDurationFromEvent(osipsEv); err != nil { return err } - return osm.ProcessCdr(osipsEvStart.AsStoredCdr()) + return osm.ProcessCdr(osipsEvStart.AsStoredCdr(osm.timezone)) } // Searches and return the session with the specifed uuid @@ -351,3 +352,7 @@ func (osm *OsipsSessionManager) Sessions() []*Session { func (osm *OsipsSessionManager) SyncSessions() error { return nil } + +func (osm *OsipsSessionManager) Timezone() string { + return osm.timezone +} diff --git a/sessionmanager/session.go b/sessionmanager/session.go index a7cb64aae..b040b8f91 100644 --- a/sessionmanager/session.go +++ b/sessionmanager/session.go @@ -58,7 +58,7 @@ func NewSession(ev engine.Event, connId string, sm SessionManager) *Session { sessionManager: sm, connId: connId, } - if err := sm.Rater().GetSessionRuns(ev.AsStoredCdr(), &s.sessionRuns); err != nil || len(s.sessionRuns) == 0 { + if err := sm.Rater().GetSessionRuns(ev.AsStoredCdr(s.sessionManager.Timezone()), &s.sessionRuns); err != nil || len(s.sessionRuns) == 0 { return nil } for runIdx := range s.sessionRuns { @@ -130,7 +130,7 @@ func (s *Session) Close(ev engine.Event) error { lastCC := sr.CallCosts[len(sr.CallCosts)-1] lastCC.Timespans.Decompress() // put credit back - startTime, err := ev.GetAnswerTime(sr.DerivedCharger.AnswerTimeField) + startTime, err := ev.GetAnswerTime(sr.DerivedCharger.AnswerTimeField, s.sessionManager.Timezone()) if err != nil { engine.Logger.Crit("Error parsing prepaid call start time from event") return err @@ -235,7 +235,7 @@ func (s *Session) SaveOperations() { var reply string err := s.sessionManager.CdrSrv().LogCallCost(&engine.CallCostLog{ - CgrId: s.eventStart.GetCgrId(), + CgrId: s.eventStart.GetCgrId(s.sessionManager.Timezone()), Source: utils.SESSION_MANAGER_SOURCE, RunId: sr.DerivedCharger.RunId, CallCost: firstCC, @@ -256,13 +256,13 @@ func (s *Session) SaveOperations() { func (s *Session) AsActiveSessions() []*ActiveSession { var aSessions []*ActiveSession - sTime, _ := s.eventStart.GetSetupTime(utils.META_DEFAULT) - aTime, _ := s.eventStart.GetAnswerTime(utils.META_DEFAULT) + sTime, _ := s.eventStart.GetSetupTime(utils.META_DEFAULT, s.sessionManager.Timezone()) + aTime, _ := s.eventStart.GetAnswerTime(utils.META_DEFAULT, s.sessionManager.Timezone()) usage, _ := s.eventStart.GetDuration(utils.META_DEFAULT) pdd, _ := s.eventStart.GetPdd(utils.META_DEFAULT) for _, sessionRun := range s.sessionRuns { aSession := &ActiveSession{ - CgrId: s.eventStart.GetCgrId(), + CgrId: s.eventStart.GetCgrId(s.sessionManager.Timezone()), TOR: utils.VOICE, AccId: s.eventStart.GetUUID(), CdrHost: s.eventStart.GetOriginatorIP(utils.META_DEFAULT), diff --git a/sessionmanager/sessionmanager.go b/sessionmanager/sessionmanager.go index 181589afa..3efcbfb9d 100644 --- a/sessionmanager/sessionmanager.go +++ b/sessionmanager/sessionmanager.go @@ -36,4 +36,5 @@ type SessionManager interface { Shutdown() error Sessions() []*Session SyncSessions() error + Timezone() string } diff --git a/utils/apitpdata.go b/utils/apitpdata.go index 861242b50..cbadfc052 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -606,7 +606,7 @@ type AttrExpFileCdrs struct { Paginator } -func (self *AttrExpFileCdrs) AsCdrsFilter() (*CdrsFilter, error) { +func (self *AttrExpFileCdrs) AsCdrsFilter(timezone string) (*CdrsFilter, error) { cdrFltr := &CdrsFilter{ CgrIds: self.CgrIds, RunIds: self.MediationRunIds, @@ -627,14 +627,14 @@ func (self *AttrExpFileCdrs) AsCdrsFilter() (*CdrsFilter, error) { Paginator: self.Paginator, } if len(self.TimeStart) != 0 { - if answerTimeStart, err := ParseTimeDetectLayout(self.TimeStart); err != nil { + if answerTimeStart, err := ParseTimeDetectLayout(self.TimeStart, timezone); err != nil { return nil, err } else { cdrFltr.AnswerTimeStart = &answerTimeStart } } if len(self.TimeEnd) != 0 { - if answerTimeEnd, err := ParseTimeDetectLayout(self.TimeEnd); err != nil { + if answerTimeEnd, err := ParseTimeDetectLayout(self.TimeEnd, timezone); err != nil { return nil, err } else { cdrFltr.AnswerTimeEnd = &answerTimeEnd @@ -682,7 +682,7 @@ type AttrGetCdrs struct { Paginator } -func (self *AttrGetCdrs) AsCdrsFilter() (*CdrsFilter, error) { +func (self *AttrGetCdrs) AsCdrsFilter(timezone string) (*CdrsFilter, error) { cdrFltr := &CdrsFilter{ CgrIds: self.CgrIds, RunIds: self.MediationRunIds, @@ -703,14 +703,14 @@ func (self *AttrGetCdrs) AsCdrsFilter() (*CdrsFilter, error) { Paginator: self.Paginator, } if len(self.TimeStart) != 0 { - if answerTimeStart, err := ParseTimeDetectLayout(self.TimeStart); err != nil { + if answerTimeStart, err := ParseTimeDetectLayout(self.TimeStart, timezone); err != nil { return nil, err } else { cdrFltr.AnswerTimeStart = &answerTimeStart } } if len(self.TimeEnd) != 0 { - if answerTimeEnd, err := ParseTimeDetectLayout(self.TimeEnd); err != nil { + if answerTimeEnd, err := ParseTimeDetectLayout(self.TimeEnd, timezone); err != nil { return nil, err } else { cdrFltr.AnswerTimeEnd = &answerTimeEnd @@ -935,7 +935,7 @@ type RpcCdrsFilter struct { Paginator // Add pagination } -func (self *RpcCdrsFilter) AsCdrsFilter() (*CdrsFilter, error) { +func (self *RpcCdrsFilter) AsCdrsFilter(timezone string) (*CdrsFilter, error) { cdrFltr := &CdrsFilter{ CgrIds: self.CgrIds, NotCgrIds: self.NotCgrIds, @@ -985,56 +985,56 @@ func (self *RpcCdrsFilter) AsCdrsFilter() (*CdrsFilter, error) { Paginator: self.Paginator, } if len(self.SetupTimeStart) != 0 { - if sTimeStart, err := ParseTimeDetectLayout(self.SetupTimeStart); err != nil { + if sTimeStart, err := ParseTimeDetectLayout(self.SetupTimeStart, timezone); err != nil { return nil, err } else { cdrFltr.SetupTimeStart = &sTimeStart } } if len(self.SetupTimeEnd) != 0 { - if sTimeEnd, err := ParseTimeDetectLayout(self.SetupTimeEnd); err != nil { + if sTimeEnd, err := ParseTimeDetectLayout(self.SetupTimeEnd, timezone); err != nil { return nil, err } else { cdrFltr.SetupTimeEnd = &sTimeEnd } } if len(self.AnswerTimeStart) != 0 { - if aTimeStart, err := ParseTimeDetectLayout(self.AnswerTimeStart); err != nil { + if aTimeStart, err := ParseTimeDetectLayout(self.AnswerTimeStart, timezone); err != nil { return nil, err } else { cdrFltr.AnswerTimeStart = &aTimeStart } } if len(self.AnswerTimeEnd) != 0 { - if aTimeEnd, err := ParseTimeDetectLayout(self.AnswerTimeEnd); err != nil { + if aTimeEnd, err := ParseTimeDetectLayout(self.AnswerTimeEnd, timezone); err != nil { return nil, err } else { cdrFltr.AnswerTimeEnd = &aTimeEnd } } if len(self.CreatedAtStart) != 0 { - if tStart, err := ParseTimeDetectLayout(self.CreatedAtStart); err != nil { + if tStart, err := ParseTimeDetectLayout(self.CreatedAtStart, timezone); err != nil { return nil, err } else { cdrFltr.CreatedAtStart = &tStart } } if len(self.CreatedAtEnd) != 0 { - if tEnd, err := ParseTimeDetectLayout(self.CreatedAtEnd); err != nil { + if tEnd, err := ParseTimeDetectLayout(self.CreatedAtEnd, timezone); err != nil { return nil, err } else { cdrFltr.CreatedAtEnd = &tEnd } } if len(self.UpdatedAtStart) != 0 { - if tStart, err := ParseTimeDetectLayout(self.UpdatedAtStart); err != nil { + if tStart, err := ParseTimeDetectLayout(self.UpdatedAtStart, timezone); err != nil { return nil, err } else { cdrFltr.UpdatedAtStart = &tStart } } if len(self.UpdatedAtEnd) != 0 { - if tEnd, err := ParseTimeDetectLayout(self.UpdatedAtEnd); err != nil { + if tEnd, err := ParseTimeDetectLayout(self.UpdatedAtEnd, timezone); err != nil { return nil, err } else { cdrFltr.UpdatedAtEnd = &tEnd diff --git a/utils/coreutils.go b/utils/coreutils.go index 983c7193c..fd841ea28 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -128,8 +128,12 @@ func Round(x float64, prec int, method string) float64 { return rounder / pow } -func ParseTimeDetectLayout(tmStr string) (time.Time, error) { +func ParseTimeDetectLayout(tmStr string, timezone string) (time.Time, error) { var nilTime time.Time + loc, err := time.LoadLocation(timezone) + if err != nil { + return nilTime, err + } rfc3339Rule := regexp.MustCompile(`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.+$`) sqlRule := regexp.MustCompile(`^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}$`) gotimeRule := regexp.MustCompile(`^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\.?\d*\s[+,-]\d+\s\w+$`) @@ -144,27 +148,27 @@ func ParseTimeDetectLayout(tmStr string) (time.Time, error) { case gotimeRule.MatchString(tmStr): return time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", tmStr) case sqlRule.MatchString(tmStr): - return time.Parse("2006-01-02 15:04:05", tmStr) + return time.ParseInLocation("2006-01-02 15:04:05", tmStr, loc) case fsTimestamp.MatchString(tmStr): if tmstmp, err := strconv.ParseInt(tmStr+"000", 10, 64); err != nil { return nilTime, err } else { - return time.Unix(0, tmstmp), nil + return time.Unix(0, tmstmp).In(loc), nil } case unixTimestampRule.MatchString(tmStr): if tmstmp, err := strconv.ParseInt(tmStr, 10, 64); err != nil { return nilTime, err } else { - return time.Unix(tmstmp, 0), nil + return time.Unix(tmstmp, 0).In(loc), nil } case tmStr == "0" || len(tmStr) == 0: // Time probably missing from request return nilTime, nil case oneLineTimestampRule.MatchString(tmStr): - return time.Parse("20060102150405", tmStr) + return time.ParseInLocation("20060102150405", tmStr, loc) case oneSpaceTimestampRule.MatchString(tmStr): - return time.Parse("02.01.2006 15:04:05", tmStr) + return time.ParseInLocation("02.01.2006 15:04:05", tmStr, loc) case eamonTimestampRule.MatchString(tmStr): - return time.Parse("02/01/2006 15:04:05", tmStr) + return time.ParseInLocation("02/01/2006 15:04:05", tmStr, loc) case tmStr == "*now": return time.Now(), nil } diff --git a/utils/utils_test.go b/utils/utils_test.go index 8ea8cd6ba..8edfcdbf9 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -133,62 +133,62 @@ func TestRoundByMethodDown2(t *testing.T) { func TestParseTimeDetectLayout(t *testing.T) { tmStr := "2013-12-30T15:00:01Z" expectedTime := time.Date(2013, 12, 30, 15, 0, 1, 0, time.UTC) - tm, err := ParseTimeDetectLayout(tmStr) + tm, err := ParseTimeDetectLayout(tmStr, "") if err != nil { t.Error(err) } else if !tm.Equal(expectedTime) { t.Errorf("Unexpected time parsed: %v, expecting: %v", tm, expectedTime) } - _, err = ParseTimeDetectLayout(tmStr[1:]) + _, err = ParseTimeDetectLayout(tmStr[1:], "") if err == nil { t.Errorf("Expecting error") } sqlTmStr := "2013-12-30 15:00:01" - sqlTm, err := ParseTimeDetectLayout(sqlTmStr) + sqlTm, err := ParseTimeDetectLayout(sqlTmStr, "") if err != nil { t.Error(err) } else if !sqlTm.Equal(expectedTime) { t.Errorf("Unexpected time parsed: %v, expecting: %v", sqlTm, expectedTime) } - _, err = ParseTimeDetectLayout(sqlTmStr[1:]) + _, err = ParseTimeDetectLayout(sqlTmStr[1:], "") if err == nil { t.Errorf("Expecting error") } unixTmStr := "1388415601" - unixTm, err := ParseTimeDetectLayout(unixTmStr) + unixTm, err := ParseTimeDetectLayout(unixTmStr, "") if err != nil { t.Error(err) } else if !unixTm.Equal(expectedTime) { t.Errorf("Unexpected time parsed: %v, expecting: %v", unixTm, expectedTime) } - _, err = ParseTimeDetectLayout(unixTmStr[1:]) + _, err = ParseTimeDetectLayout(unixTmStr[1:], "") if err == nil { t.Errorf("Expecting error") } goTmStr := "2013-12-30 15:00:01 +0000 UTC" - goTm, err := ParseTimeDetectLayout(goTmStr) + goTm, err := ParseTimeDetectLayout(goTmStr, "") if err != nil { t.Error(err) } else if !goTm.Equal(expectedTime) { t.Errorf("Unexpected time parsed: %v, expecting: %v", goTm, expectedTime) } - _, err = ParseTimeDetectLayout(goTmStr[1:]) + _, err = ParseTimeDetectLayout(goTmStr[1:], "") if err == nil { t.Errorf("Expecting error") } goTmStr = "2013-12-30 15:00:01.000000000 +0000 UTC" - goTm, err = ParseTimeDetectLayout(goTmStr) + goTm, err = ParseTimeDetectLayout(goTmStr, "") if err != nil { t.Error(err) } else if !goTm.Equal(expectedTime) { t.Errorf("Unexpected time parsed: %v, expecting: %v", goTm, expectedTime) } - _, err = ParseTimeDetectLayout(goTmStr[1:]) + _, err = ParseTimeDetectLayout(goTmStr[1:], "") if err == nil { t.Errorf("Expecting error") } fsTmstampStr := "1394291049287234" - fsTm, err := ParseTimeDetectLayout(fsTmstampStr) + fsTm, err := ParseTimeDetectLayout(fsTmstampStr, "") expectedTime = time.Date(2014, 3, 8, 15, 4, 9, 287234000, time.UTC) if err != nil { t.Error(err) @@ -196,7 +196,7 @@ func TestParseTimeDetectLayout(t *testing.T) { t.Errorf("Unexpected time parsed: %v, expecting: %v", fsTm, expectedTime) } fsTmstampStr = "0" - fsTm, err = ParseTimeDetectLayout(fsTmstampStr) + fsTm, err = ParseTimeDetectLayout(fsTmstampStr, "") expectedTime = time.Time{} if err != nil { t.Error(err) @@ -204,7 +204,7 @@ func TestParseTimeDetectLayout(t *testing.T) { t.Errorf("Unexpected time parsed: %v, expecting: %v", fsTm, expectedTime) } onelineTmstampStr := "20131023215149" - olTm, err := ParseTimeDetectLayout(onelineTmstampStr) + olTm, err := ParseTimeDetectLayout(onelineTmstampStr, "") expectedTime = time.Date(2013, 10, 23, 21, 51, 49, 0, time.UTC) if err != nil { t.Error(err) @@ -212,20 +212,20 @@ func TestParseTimeDetectLayout(t *testing.T) { t.Errorf("Unexpected time parsed: %v, expecting: %v", olTm, expectedTime) } oneSpaceTmStr := "08.04.2014 22:14:29" - tsTm, err := ParseTimeDetectLayout(oneSpaceTmStr) + tsTm, err := ParseTimeDetectLayout(oneSpaceTmStr, "") expectedTime = time.Date(2014, 4, 8, 22, 14, 29, 0, time.UTC) if err != nil { t.Error(err) } else if !tsTm.Equal(expectedTime) { t.Errorf("Unexpected time parsed: %v, expecting: %v", tsTm, expectedTime) } - if nowTm, err := ParseTimeDetectLayout(META_NOW); err != nil { + if nowTm, err := ParseTimeDetectLayout(META_NOW, ""); err != nil { t.Error(err) } else if time.Now().Sub(nowTm) > time.Duration(1)*time.Millisecond { t.Errorf("Unexpected time parsed: %v", nowTm) } eamonTmStr := "31/05/2015 14:46:00" - eamonTmS, err := ParseTimeDetectLayout(eamonTmStr) + eamonTmS, err := ParseTimeDetectLayout(eamonTmStr, "") expectedTime = time.Date(2015, 5, 31, 14, 46, 0, 0, time.UTC) if err != nil { t.Error(err)