diff --git a/utils/dateseries.go b/utils/dateseries.go
new file mode 100644
index 000000000..e98994d9b
--- /dev/null
+++ b/utils/dateseries.go
@@ -0,0 +1,272 @@
+/*
+Rating system designed to be used in VoIP Carriers World
+Copyright (C) 2013 ITsysCOM
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see
+*/
+
+package utils
+
+import (
+ "fmt"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// Defines years days series
+type Years []int
+
+func (ys Years) Sort() {
+ sort.Sort(ys)
+}
+
+func (ys Years) Len() int {
+ return len(ys)
+}
+
+func (ys Years) Swap(i, j int) {
+ ys[i], ys[j] = ys[j], ys[i]
+}
+
+func (ys Years) Less(j, i int) bool {
+ return ys[j] < ys[i]
+}
+
+// Return true if the specified date is inside the series
+func (ys Years) Contains(year int) (result bool) {
+ result = false
+ for _, yss := range ys {
+ if yss == year {
+ result = true
+ break
+ }
+ }
+ return
+}
+
+// Parse Years elements from string separated by sep.
+func (ys *Years) Parse(input, sep string) {
+ switch input {
+ case "*any", "":
+ *ys = []int{}
+ default:
+ elements := strings.Split(input, sep)
+ for _, yss := range elements {
+ if year, err := strconv.Atoi(yss); err == nil {
+ *ys = append(*ys, year)
+ }
+ }
+ }
+}
+
+func (ys Years) Serialize(sep string) string {
+ if len(ys) == 0 {
+ return "*any"
+ }
+ var yStr string
+ for idx, yr := range ys {
+ if idx != 0 {
+ yStr = fmt.Sprintf("%s%s%d", yStr, sep, yr)
+ } else {
+ yStr = strconv.Itoa(yr)
+ }
+ }
+ return yStr
+}
+
+// Defines months series
+type Months []time.Month
+
+func (m Months) Sort() {
+ sort.Sort(m)
+}
+
+func (m Months) Len() int {
+ return len(m)
+}
+
+func (m Months) Swap(i, j int) {
+ m[i], m[j] = m[j], m[i]
+}
+
+func (m Months) Less(j, i int) bool {
+ return m[j] < m[i]
+}
+
+// Return true if the specified date is inside the series
+func (m Months) Contains(month time.Month) (result bool) {
+ for _, ms := range m {
+ if ms == month {
+ result = true
+ break
+ }
+ }
+ return
+}
+
+// Loades Month elemnents from a string separated by sep.
+func (m *Months) Parse(input, sep string) {
+ switch input {
+ case "*any", "": // Apier cannot receive empty string, hence using meta-tag
+ *m = []time.Month{}
+ default:
+ elements := strings.Split(input, sep)
+ for _, ms := range elements {
+ if month, err := strconv.Atoi(ms); err == nil {
+ *m = append(*m, time.Month(month))
+ }
+ }
+ }
+}
+
+// Dumps the months in a serialized string, similar to the one parsed
+func (m Months) Serialize(sep string) string {
+ if len(m) == 0 {
+ return "*any"
+ }
+ var mStr string
+ for idx, mt := range m {
+ if idx != 0 {
+ mStr = fmt.Sprintf("%s%s%d", mStr, sep, mt)
+ } else {
+ mStr = strconv.Itoa(int(mt))
+ }
+ }
+ return mStr
+}
+
+// Defines month days series
+type MonthDays []int
+
+func (md MonthDays) Sort() {
+ sort.Sort(md)
+}
+
+func (md MonthDays) Len() int {
+ return len(md)
+}
+
+func (md MonthDays) Swap(i, j int) {
+ md[i], md[j] = md[j], md[i]
+}
+
+func (md MonthDays) Less(j, i int) bool {
+ return md[j] < md[i]
+}
+
+// Return true if the specified date is inside the series
+func (md MonthDays) Contains(monthDay int) (result bool) {
+ result = false
+ for _, mds := range md {
+ if mds == monthDay {
+ result = true
+ break
+ }
+ }
+ return
+}
+
+// Parse MonthDay elements from string separated by sep.
+func (md *MonthDays) Parse(input, sep string) {
+ switch input {
+ case "*any", "":
+ *md = []int{}
+ default:
+ elements := strings.Split(input, sep)
+ for _, mds := range elements {
+ if day, err := strconv.Atoi(mds); err == nil {
+ *md = append(*md, day)
+ }
+ }
+ }
+}
+
+// Dumps the month days in a serialized string, similar to the one parsed
+func (md MonthDays) Serialize(sep string) string {
+ if len(md) == 0 {
+ return "*any"
+ }
+ var mdsStr string
+ for idx, mDay := range md {
+ if idx != 0 {
+ mdsStr = fmt.Sprintf("%s%s%d", mdsStr, sep, mDay)
+ } else {
+ mdsStr = strconv.Itoa(mDay)
+ }
+ }
+ return mdsStr
+}
+
+// Defines week days series
+type WeekDays []time.Weekday
+
+func (wd WeekDays) Sort() {
+ sort.Sort(wd)
+}
+
+func (wd WeekDays) Len() int {
+ return len(wd)
+}
+
+func (wd WeekDays) Swap(i, j int) {
+ wd[i], wd[j] = wd[j], wd[i]
+}
+
+func (wd WeekDays) Less(j, i int) bool {
+ return wd[j] < wd[i]
+}
+
+// Return true if the specified date is inside the series
+func (wd WeekDays) Contains(weekDay time.Weekday) (result bool) {
+ result = false
+ for _, wds := range wd {
+ if wds == weekDay {
+ result = true
+ break
+ }
+ }
+ return
+}
+
+func (wd *WeekDays) Parse(input, sep string) {
+ switch input {
+ case "*any", "":
+ *wd = []time.Weekday{}
+ default:
+ elements := strings.Split(input, sep)
+ for _, wds := range elements {
+ if day, err := strconv.Atoi(wds); err == nil {
+ *wd = append(*wd, time.Weekday(day%7)) // %7 for sunday = 7 normalization
+ }
+ }
+ }
+}
+
+// Dumps the week days in a serialized string, similar to the one parsed
+func (wd WeekDays) Serialize(sep string) string {
+ if len(wd) == 0 {
+ return "*any"
+ }
+ var wdStr string
+ for idx, d := range wd {
+ if idx != 0 {
+ wdStr = fmt.Sprintf("%s%s%d", wdStr, sep, d)
+ } else {
+ wdStr = strconv.Itoa(int(d))
+ }
+ }
+ return wdStr
+}
diff --git a/utils/dateseries_test.go b/utils/dateseries_test.go
new file mode 100644
index 000000000..dec14866a
--- /dev/null
+++ b/utils/dateseries_test.go
@@ -0,0 +1,149 @@
+/*
+Rating system designed to be used in VoIP Carriers World
+Copyright (C) 2013 ITsysCOM
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see
+*/
+
+package utils
+
+import (
+ "encoding/json"
+ "reflect"
+ "testing"
+ "time"
+)
+
+func TestMonthStoreRestoreJson(t *testing.T) {
+ m := Months{5, 6, 7, 8}
+ r, _ := json.Marshal(m)
+ if string(r) != "[5,6,7,8]" {
+ t.Errorf("Error serializing months: %v", string(r))
+ }
+ o := Months{}
+ json.Unmarshal(r, &o)
+ if !reflect.DeepEqual(o, m) {
+ t.Errorf("Expected %v was %v", m, o)
+ }
+}
+
+func TestMonthDayStoreRestoreJson(t *testing.T) {
+ md := MonthDays{24, 25, 26}
+ r, _ := json.Marshal(md)
+ if string(r) != "[24,25,26]" {
+ t.Errorf("Error serializing month days: %v", string(r))
+ }
+ o := MonthDays{}
+ json.Unmarshal(r, &o)
+ if !reflect.DeepEqual(o, md) {
+ t.Errorf("Expected %v was %v", md, o)
+ }
+}
+
+func TestWeekDayStoreRestoreJson(t *testing.T) {
+ wd := WeekDays{time.Saturday, time.Sunday}
+ r, _ := json.Marshal(wd)
+ if string(r) != "[6,0]" {
+ t.Errorf("Error serializing week days: %v", string(r))
+ }
+ o := WeekDays{}
+ json.Unmarshal(r, &o)
+ if !reflect.DeepEqual(o, wd) {
+ t.Errorf("Expected %v was %v", wd, o)
+ }
+}
+
+func TestYearsSerialize(t *testing.T) {
+ ys := &Years{}
+ yString := ys.Serialize(";")
+ expectString := "*any"
+ if expectString != yString {
+ t.Errorf("Expected: %s, got: %s", expectString, yString)
+ }
+ ys2 := &Years{2012}
+ yString2 := ys2.Serialize(";")
+ expectString2 := "2012"
+ if expectString2 != yString2 {
+ t.Errorf("Expected: %s, got: %s", expectString2, yString2)
+ }
+ ys3 := &Years{2013, 2014, 2015}
+ yString3 := ys3.Serialize(";")
+ expectString3 := "2013;2014;2015"
+ if expectString3 != yString3 {
+ t.Errorf("Expected: %s, got: %s", expectString3, yString3)
+ }
+}
+
+func TestMonthsSerialize(t *testing.T) {
+ mths := &Months{}
+ mString := mths.Serialize(";")
+ expectString := "*any"
+ if expectString != mString {
+ t.Errorf("Expected: %s, got: %s", expectString, mString)
+ }
+ mths2 := &Months{time.January}
+ mString2 := mths2.Serialize(";")
+ expectString2 := "1"
+ if expectString2 != mString2 {
+ t.Errorf("Expected: %s, got: %s", expectString2, mString2)
+ }
+ mths3 := &Months{time.January, time.December}
+ mString3 := mths3.Serialize(";")
+ expectString3 := "1;12"
+ if expectString3 != mString3 {
+ t.Errorf("Expected: %s, got: %s", expectString3, mString3)
+ }
+}
+
+func TestMonthDaysSerialize(t *testing.T) {
+ mds := &MonthDays{}
+ mdsString := mds.Serialize(";")
+ expectString := "*any"
+ if expectString != mdsString {
+ t.Errorf("Expected: %s, got: %s", expectString, mdsString)
+ }
+ mds2 := &MonthDays{1}
+ mdsString2 := mds2.Serialize(";")
+ expectString2 := "1"
+ if expectString2 != mdsString2 {
+ t.Errorf("Expected: %s, got: %s", expectString2, mdsString2)
+ }
+ mds3 := &MonthDays{1, 2, 3, 4, 5}
+ mdsString3 := mds3.Serialize(";")
+ expectString3 := "1;2;3;4;5"
+ if expectString3 != mdsString3 {
+ t.Errorf("Expected: %s, got: %s", expectString3, mdsString3)
+ }
+}
+
+func TestWeekDaysSerialize(t *testing.T) {
+ wds := &WeekDays{}
+ wdsString := wds.Serialize(";")
+ expectString := "*any"
+ if expectString != wdsString {
+ t.Errorf("Expected: %s, got: %s", expectString, wdsString)
+ }
+ wds2 := &WeekDays{time.Monday}
+ wdsString2 := wds2.Serialize(";")
+ expectString2 := "1"
+ if expectString2 != wdsString2 {
+ t.Errorf("Expected: %s, got: %s", expectString2, wdsString2)
+ }
+ wds3 := &WeekDays{time.Monday, time.Saturday, time.Sunday}
+ wdsString3 := wds3.Serialize(";")
+ expectString3 := "1;6;0"
+ if expectString3 != wdsString3 {
+ t.Errorf("Expected: %s, got: %s", expectString3, wdsString3)
+ }
+}