text: module.exports = (vrp) => {
      vrp.provider('dateUtils', dateUtilsProvider);
      dateUtilsProvider.$inject = [];
  
      function dateUtilsProvider() {
          const _dateRegExp = {
              humanDate: /^\d+([.\-\/\\])\d+[.\-\/\\]\d+$/,
              monthDate: /^[A-Za-z]+[\s\-_'.]+\d{2,4}$/,
              dotnetDate: /\/Date\([\d\-]+\)\//,
              unixDate: /^\d{13,}$/,
              isoDate: /^\d{4}-\d{2}-\d{2}T.*$/
          };
          const _dateFormat = {
              humanDate: ['DD.MM.YYYY', 'DD.MM.YY', 'DD-MM-YYYY', 'YYYY-MM-DD', 'DD-MM-YY', 'MM/DD/YY', 'MM/DD/YYYY'],
              monthDate: ['MMM YYYY', 'MMM YY', 'MMMM YYYY', 'MM.YY', 'MM/YY', 'MM.YYYY', 'MM/YYYY']
          };
          let _instance = null;
  
          class APDateUtils {
              constructor(){
                  this._dateRegExp = _dateRegExp;
                  this._dateFormat = _dateFormat;
              }
              get dateRegExp(){
                  return this._dateRegExp
              }
              get dateFormat(){
                  return this._dateFormat
              }
              get now(){
                  return moment().startOf('D').toDate()
              }
              get prevMonth(){
                  return moment().subtract(1, 'M').startOf('D').toDate()
              }
              get nextMonth(){
                  moment().add(1, 'M').startOf('D').toDate()
              }
              get prevYear(){
                  return moment().subtract(1, 'Y').startOf('D').toDate()
              }
              get nextYear(){
                  moment().add(1, 'Y').startOf('D').toDate()
              }
  
              startOfMonth(month = new Date().getMonth(), year = new Date().getFullYear(), date = new Date()){
                  if (month instanceof Date){
                      return moment(this.utcAsLocal(month)).startOf('M').startOf('D').toDate()
                  }
                  return moment([year, month, 15]).startOf('M').startOf('D').toDate()
              }
              endOfMonth (month = new Date().getMonth(), year = new Date().getFullYear(), date = new Date()){
                  if (month instanceof Date){
                      return moment(this.utcAsLocal(month)).endOf('M').startOf('D').toDate()
                  }
                  return moment([year, month, 15]).endOf('M').startOf('D').toDate()
              }
              parseDate(input) {
                  let output = false;
                  if (!input){
                      return false;
                  }
                  if (typeof(input) === 'string'){
                      if (this.dateRegExp.humanDate.test(input)) {
                          output = moment(input, this.dateFormat.humanDate, true);
                      }
                      else if (this.dateRegExp.monthDate.test(input)){
                          output = moment(input.replace(/[^A-Za-z0-9]/g, ' ').replace(/\s+/g, ' ').trim(), this.dateFormat.monthDate, true);
                      }
                      else if (this.dateRegExp.unixDate.test(input)){
                          output = moment(parseInt(input))
                      }
                      else if (this.dateRegExp.dotnetDate.test(input) || this.dateRegExp.isoDate.test(input)){
                          output = moment(input)
                      }
                  }
                  else if (input instanceof Date || this.dateRegExp.unixDate.test(input.toString())){
                      output = moment(input);
                  }
  
                  return output && output.isValid() && output.toDate();
              }
              formatDate(date){
                  return date ? moment(date).format('DD.MM.YY') : ''
              }
              getFormat(dateString = ''){
                  dateString = dateString.trim();
                  const formats = this.dateRegExp.humanDate.test(dateString) ? this.dateFormat.humanDate : this.dateFormat.monthDate;
                  return formats.find((item) => dateString.length === item.length && moment(dateString, item, true).isValid());
              }
              findMonthName(part, format) {
                  const mdate = moment([2000, 0, 1]);
                  const monthFormat = format.replace(/[^M]/g,'');
                  for (let i = 0; i < 12; i++){
                      let monthName = mdate.format(monthFormat);
                      if (monthName.toLowerCase().startsWith(part.toLowerCase())){
                          return monthName;
                      }
                      mdate.add(1, 'month');
                  }
                  return null;
              }
              localAsUtc(date){
                  const tz = date.getTimezoneOffset();
                  const tdate = new Date(date);
                  if (date.getHours() === 0 && tz !== 0){
                      if (tz > 0){
                          tdate.setMinutes(-1 * tz);
                      } else {
                          tdate.setHours(this.getTZHours(date));
                      }
                  }
                  return tdate;
              }
              utcAsLocal(date){
                  if (date.getHours() !== 0){
                      return new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
                  }
                  return new Date(date);
              }
              getTZHours(date){
                  const tz = date.getTimezoneOffset();
                  return tz > 0 ? 24 - Math.ceil(tz / 60) : Math.floor(Math.abs(tz) / 60);
              }
              sameDate(dateA, dateB){
                  try {
                      return dateA.getFullYear() === dateB.getFullYear() &&
                          dateA.getMonth() === dateB.getMonth() &&
                          dateA.getDate() === dateB.getDate();
                  } catch(e) {
                      return false;
                  }
              }
              maxDate(...dates){
                  const validDates = dates.filter((d) => !!d && d instanceof Date);
                  return validDates.length ? validDates.reduce((res, date) => date >= res ? date : res, new Date(null)) : null;
              }
              minDate(...dates){
                  const validDates = dates.filter((d) => !!d && d instanceof Date);
                  return validDates.length ? validDates.reduce((res, date) => date <= res ? date : res, new Date()) : null;
              }
              isWeekend(date){
                  const dayNum = date.getDay();
                  return dayNum === 0 || dayNum === 6;
              }
              getDateFilter(start, end){
                  return (d) => (!start || !start.getTime || d.getTime() >= start.getTime()) && (!end || !end.getTime || d.getTime() <= end.getTime())
              }
              getDateArray(start, end){
                  const sd = moment.isMoment(start) ? start : moment(start);
                  const ed = moment.isMoment(end) ? end : moment(end);
                  const res = [];
                  do {
                      res[res.length] = sd.toDate();
                      sd.add(1, 'd');
                  } while (sd.isSameOrBefore(ed));
                  return res;
              }
              transformObject(obj, isSrcLocal){
                  const handler = isSrcLocal ? this.localAsUtc.bind(this) : this.utcAsLocal.bind(this);
                  if (!obj){
                      return obj;
                  }
                  else if (typeof(obj) === 'string'){
                      let result;
                      try {
                          result = JSON.parse(obj);
                          return JSON.stringify(this.transformObject(result, isSrcLocal));
                      } catch(e) {
                          result = this.parseDate(obj);
                          return result ? handler(result) : obj;
                      }
                  }
                  else if (obj instanceof Date){
                      const date = this.parseDate(obj);
                      return date ? handler(date) : obj;
                  }
                  else if (Array.isArray(obj)){
                      return obj.map((item) => this.transformObject(item, isSrcLocal))
                  }
                  else if (typeof(obj) === 'object'){
                      return Object.keys(obj).reduce((res, key) => {
                          res[key] = this.transformObject(obj[key], isSrcLocal);
                          return res;
                      }, {})
                  }
                  else {
                      return obj;
                  }
              }
          }
  
          _instance = new APDateUtils();
          Object.defineProperties(this, {
              utils: {
                  get: () => _instance
              }
          });
          this.$get = function() {
              return _instance;
          };
      }
  };