/// month->days.
Map<int, int> MONTH_DAY = {
  1: 31,
  2: 28,
  3: 31,
  4: 30,
  5: 31,
  6: 30,
  7: 31,
  8: 31,
  9: 30,
  10: 31,
  11: 30,
  12: 31,
};

/// ISO 8601 标准的时间格式字符串
/// 时间与日期的格式化工具类（自定义基于Dart原生Api-DateTime实现）
class DateTimeUtils {
  static String full = 'yyyy-MM-dd HH:mm:ss';  //如 2023-05-25 12:05:03

  // ===================================== 反格式化时间 ↓ =====================================

  /*
     根据指定日期格式得到原生的DateTime对象

     使用：
     final date = DateTimeUtils.getDateTime('2023-10-21');
     打印结果 DateTime 对象为：2023-10-21 00:00:00.000

     final date = DateTimeUtils.getDateTime('2023-10-21 12:08:55');
     打印结果 DateTime 对象为：2023-10-21 12:08:55.000

     final date = DateTimeUtils.getDateTime('2023-10-21 12:55');
     打印结果 DateTime 对象为：2023-10-21 12:55:00.000

     final date = DateTimeUtils.getDateTime('2023/10/21 12:08:55');
     这样就不行，因为 2023/10/21 12:08:55 不是标准格式无法转换为 DateTime 对象，必须要 2012-02-27 或 2012-02-27 13:27:00 标准格式
   */
  static DateTime? getDateTime(String dateStr, {bool? isUtc}) {
    DateTime? dateTime = DateTime.tryParse(dateStr);
    if (isUtc != null) {
      if (isUtc) {
        dateTime = dateTime?.toUtc();
      } else {
        dateTime = dateTime?.toLocal();
      }
    }
    return dateTime;
  }


  /*
     根据指定的毫秒时间戳得到原生的DateTime对象

     使用：
     final date = DateTimeUtils.getDateTimeByMs(1682304352000)
     打印结果 DateTime 对象为：2023-04-24 10:45:52.000
   */
  static DateTime getDateTimeByMs(int ms, {bool isUtc = false}) {
    return DateTime.fromMillisecondsSinceEpoch(ms, isUtc: isUtc);
  }


  /*
     根据日期格式得到对应的毫秒值

     使用：
     final date = DateTimeUtils.getDateMsByTimeStr('2023-10-21');
     打印结果为：1697817600000

     final date = DateTimeUtils.getDateMsByTimeStr('2023-10-21 12:08:55');
     打印结果为：1697861335000

     final date = DateTimeUtils.getDateMsByTimeStr('2023-10-21 12:55');
     打印结果为：1697864100000
   */
  static int? getDateMsByTimeStr(String dateStr, {bool? isUtc}) {
    DateTime? dateTime = getDateTime(dateStr, isUtc: isUtc);
    return dateTime?.millisecondsSinceEpoch;
  }


  /*
     获取当前系统日期的毫秒值

     使用：
     final date = DateTimeUtils.getNowDateMs();
     打印结果为：1697817600000
   */
  static int getNowDateMs() {
    return DateTime.now().millisecondsSinceEpoch;
  }

  // ===================================== 反格式化时间 ↑ =====================================


  // ===================================== 格式化时间 ↓ =====================================

  /*
     获取当前系统日期的格式化时间值。默认格式为 yyyy-MM-dd HH:mm:ss

     使用：
     final date = DateTimeUtils.getNowDateStr();
     打印结果为：2023-10-09 09:54:26

     final date = DateTimeUtils.getNowDateStr(format: 'yyyy-MM-dd');
     打印结果为：2023-10-09

     final date = DateTimeUtils.getNowDateStr(format: 'yyyy-MM-dd HH:mm');
     打印结果为：2023-10-09 09:55

     final date = DateTimeUtils.getNowDateStr(format: 'yyyy-MMM-dd HH:mm');
     打印结果为：2023-Oct-09 09:56

     final date = DateTimeUtils.getNowDateStr(format: 'yyyy/MMM/dd HH:mm');
     打印结果为：2023/Oct/09 09:57

     final date = DateTimeUtils.getNowDateStr(format: 'dd MMM yyyy HH:mm');
     打印结果为：09 Oct 2023 09:58
   */
  static String formatNowDateStr({String? format, bool isMonthEn = true, bool isMonthEnShort = true}) {
    return formatDate(DateTime.now(), format: format, isMonthEn: isMonthEn, isMonthEnShort: isMonthEnShort);
  }

  /*
     根据指定的毫秒时间戳格式化时间值。默认格式为 yyyy-MM-dd HH:mm:ss

     使用：
     final date = DateTimeUtils.formatDateMs(1682304352000,format: 'yyyy-MM-dd')
     打印结果为：2023-04-24

     final date = DateTimeUtils.formatDateMs(1682304352000,format: 'dd MMM yyyy HH:mm');
     打印结果为：24 Apr 2023 10:45
   */
  static String formatDateMs(int ms,
      {bool isUtc = false, String? format, bool isMonthEn = true, bool isMonthEnShort = true}) {
    return formatDate(getDateTimeByMs(ms, isUtc: isUtc),
        format: format, isMonthEn: isMonthEn, isMonthEnShort: isMonthEnShort);
  }


  /*
     根据指定的日期字符串格式化时间值。默认格式为 yyyy-MM-dd HH:mm:ss

     @Parmas dateStr 参数必须是标准格式如 2012-02-27 或 2012-02-27 13:27:00 ，format的转换格式可以随便写

     使用：
     final date = DateTimeUtils.formatDateStr('2023-10-21 12:08:55',format: 'dd MMM yyyy');
     打印结果为：21 Oct 2023 12:08

     final date = DateTimeUtils.formatDateStr('2023-10-21 12:08:55',format: 'yyyy-MM-dd');
     打印结果为：2023-10-21

     final date = DateTimeUtils.formatDateStr('2023/10/21 12:08:55',format: 'yyyy-MM-dd');
     这样就不行，因为 2023/10/21 12:08:55 不是标准格式无法转换为 DateTime 对象
   */
  static String formatDateStr(String dateStr,
      {bool? isUtc, String? format, bool isMonthEn = true, bool isMonthEnShort = true}) {
    return formatDate(getDateTime(dateStr, isUtc: isUtc),
        format: format, isMonthEn: isMonthEn, isMonthEnShort: isMonthEnShort);
  }


  /*
     总方法 - 根据标准的 DateTime 来格式化时间。默认格式为 yyyy-MM-dd HH:mm:ss

     使用：
     final date = DateTimeUtils.formatDate(date, format: "yyyy-MM-dd");
     打印结果为：2023-10-09

   */
  static String formatDate(DateTime? dateTime, {String? format, bool isMonthEn = true, bool isMonthEnShort = true}) {
    if (dateTime == null) return '';
    format = format ?? full;

    //对Year的处理
    if (format.contains('yy')) {
      String year = dateTime.year.toString();
      if (format.contains('yyyy')) {
        format = format.replaceAll('yyyy', year);
      } else {
        format = format.replaceAll('yy', year.substring(year.length - 2, year.length));
      }
    }

    //对Month的处理
    int month = dateTime.month;
    if (format.contains("M")) {
      if (format.contains("MM")) {
        if (format.contains("MMM")) {
          if (format.contains("MMMM")) {
            //处理4个M，英文显示
            format = format.replaceAll("MMMM", getMonthEnStr(month, true, false));
          } else {
            //处理3个M，英文显示
            format = format.replaceAll("MMM", getMonthEnStr(month, isMonthEn, isMonthEnShort));
          }
        } else {
          //处理2个M
          format = format.replaceAll("MM", month < 10 ? '0$month' : month.toString());
        }
      } else {
        //处理1个M
        format = format.replaceAll("M", month.toString());
      }
    }

    //对其他的处理
    format = _comFormat(dateTime.day, format, 'd', 'dd');
    format = _comFormat(dateTime.hour, format, 'H', 'HH');
    format = _comFormat(dateTime.minute, format, 'm', 'mm');
    format = _comFormat(dateTime.second, format, 's', 'ss');
    format = _comFormat(dateTime.millisecond, format, 'S', 'SSS');

    return format;
  }

  //格式化1位数为2位数，前面加0  如：2008-01-05 08:05:04
  static String _comFormat(int value, String format, String single, String full) {
    if (format.contains(single)) {
      if (format.contains(full)) {
        format = format.replaceAll(full, value < 10 ? '0$value' : value.toString());
      } else {
        format = format.replaceAll(single, value.toString());
      }
    }
    return format;
  }

  // 返回自己的月份字符串信息
  static String getMonthEnStr(int month, bool isMonthEn, bool isMonthEnShort) {
    String monthStr = month.toString();
    switch (month) {
      case 1:
        monthStr = !isMonthEn ? month.toString() : isMonthEnShort ? "Jan" : "January";
        break;
      case 2:
        monthStr = !isMonthEn ? month.toString() : isMonthEnShort ? "Feb" : "February";
        break;
      case 3:
        monthStr = !isMonthEn ? month.toString() : isMonthEnShort ? "Mar" : "March";
        break;
      case 4:
        monthStr = !isMonthEn ? month.toString() : isMonthEnShort ? "Apr" : "April";
        break;
      case 5:
        monthStr = !isMonthEn ? month.toString() : isMonthEnShort ? "May" : "May";
        break;
      case 6:
        monthStr = !isMonthEn ? month.toString() : isMonthEnShort ? "Jun" : "June";
        break;
      case 7:
        monthStr = !isMonthEn ? month.toString() : isMonthEnShort ? "Jul" : "July";
        break;
      case 8:
        monthStr = !isMonthEn ? month.toString() : isMonthEnShort ? "Aug" : "August";
        break;
      case 9:
        monthStr = !isMonthEn ? month.toString() : isMonthEnShort ? "Sep" : "September";
        break;
      case 10:
        monthStr = !isMonthEn ? month.toString() : isMonthEnShort ? "Oct" : "October";
        break;
      case 11:
        monthStr = !isMonthEn ? month.toString() : isMonthEnShort ? "Nov" : "November";
        break;
      case 12:
        monthStr = !isMonthEn ? month.toString() : isMonthEnShort ? "Dec" : "December";
        break;
    }
    return monthStr;
  }

  /// 获取输入时间是星期几
  static String getWeekdayByMs(int milliseconds, {bool isUtc = false, String languageCode = 'en', bool short = false}) {
    DateTime dateTime = getDateTimeByMs(milliseconds, isUtc: isUtc);
    return getWeekday(dateTime, languageCode: languageCode, short: short);
  }

  /*
     根据标准的 DateTime 对象，获取 weekDay 属性，并返回自己的星期字符串
   */
  static String getWeekday(DateTime? dateTime, {String languageCode = 'zh', bool short = false}) {
    if (dateTime == null) return "";
    String weekday = "";
    switch (dateTime.weekday) {
      case 1:
        weekday = languageCode == 'zh' ? '星期一' : 'Monday';
        break;
      case 2:
        weekday = languageCode == 'zh' ? '星期二' : 'Tuesday';
        break;
      case 3:
        weekday = languageCode == 'zh' ? '星期三' : 'Wednesday';
        break;
      case 4:
        weekday = languageCode == 'zh' ? '星期四' : 'Thursday';
        break;
      case 5:
        weekday = languageCode == 'zh' ? '星期五' : 'Friday';
        break;
      case 6:
        weekday = languageCode == 'zh' ? '星期六' : 'Saturday';
        break;
      case 7:
        weekday = languageCode == 'zh' ? '星期日' : 'Sunday';
        break;
      default:
        break;
    }
    return languageCode == 'zh'
        ? (short ? weekday.replaceAll('星期', '周') : weekday)
        : weekday.substring(0, short ? 3 : weekday.length);
  }



  /// 在今年的第几天.
  static int getDayOfYear(DateTime dateTime) {
    int year = dateTime.year;
    int month = dateTime.month;
    int days = dateTime.day;
    for (int i = 1; i < month; i++) {
      days = days + MONTH_DAY[i]!;
    }
    if (isLeapYearByYear(year) && month > 2) {
      days = days + 1;
    }
    return days;
  }

  /// 在今年的第几天.
  static int getDayOfYearByMs(int ms, {bool isUtc = false}) {
    return getDayOfYear(DateTime.fromMillisecondsSinceEpoch(ms, isUtc: isUtc));
  }

  /// 是否是当天.
  static bool isToday(int? milliseconds, {bool isUtc = false, int? locMs}) {
    if (milliseconds == null || milliseconds == 0) return false;
    DateTime old = DateTime.fromMillisecondsSinceEpoch(milliseconds, isUtc: isUtc);
    DateTime now;
    if (locMs != null) {
      now = DateTimeUtils.getDateTimeByMs(locMs);
    } else {
      now = isUtc ? DateTime.now().toUtc() : DateTime.now().toLocal();
    }
    return old.year == now.year && old.month == now.month && old.day == now.day;
  }

  /// 是否是昨天.
  static bool isYesterday(DateTime dateTime, DateTime locDateTime) {
    if (yearIsEqual(dateTime, locDateTime)) {
      int spDay = getDayOfYear(locDateTime) - getDayOfYear(dateTime);
      return spDay == 1;
    } else {
      return ((locDateTime.year - dateTime.year == 1) &&
          dateTime.month == 12 &&
          locDateTime.month == 1 &&
          dateTime.day == 31 &&
          locDateTime.day == 1);
    }
  }

  /// 是否是昨天.
  static bool isYesterdayByMs(int ms, int locMs) {
    return isYesterday(DateTime.fromMillisecondsSinceEpoch(ms), DateTime.fromMillisecondsSinceEpoch(locMs));
  }

  /// 是否是本周.
  static bool isWeek(int? ms, {bool isUtc = false, int? locMs}) {
    if (ms == null || ms <= 0) {
      return false;
    }
    DateTime _old = DateTime.fromMillisecondsSinceEpoch(ms, isUtc: isUtc);
    DateTime _now;
    if (locMs != null) {
      _now = DateTimeUtils.getDateTimeByMs(locMs, isUtc: isUtc);
    } else {
      _now = isUtc ? DateTime.now().toUtc() : DateTime.now().toLocal();
    }

    DateTime old = _now.millisecondsSinceEpoch > _old.millisecondsSinceEpoch ? _old : _now;
    DateTime now = _now.millisecondsSinceEpoch > _old.millisecondsSinceEpoch ? _now : _old;
    return (now.weekday >= old.weekday) &&
        (now.millisecondsSinceEpoch - old.millisecondsSinceEpoch <= 7 * 24 * 60 * 60 * 1000);
  }

  /// 是否同年.
  static bool yearIsEqual(DateTime dateTime, DateTime locDateTime) {
    return dateTime.year == locDateTime.year;
  }

  /// 是否同年.
  static bool yearIsEqualByMs(int ms, int locMs) {
    return yearIsEqual(DateTime.fromMillisecondsSinceEpoch(ms), DateTime.fromMillisecondsSinceEpoch(locMs));
  }

  /// 是否是闰年
  static bool isLeapYear(DateTime dateTime) {
    return isLeapYearByYear(dateTime.year);
  }

  /// 是否是闰年
  static bool isLeapYearByYear(int year) {
    return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
  }

  /// 输入时间距离当前时间有多少年
  static int distanceCurrentYear(DateTime date) {
    Duration difference = DateTime.now().difference(date);
    return (difference.inDays / 365).floor();
  }

  /// 输入时间距离当前时间有多少天
  static int distanceCurrentDay(DateTime date) {
    Duration difference = DateTime.now().difference(date);
    return difference.inDays;
  }

}
