神刀安全网

用DateComponents 实现奇葩的日历

相信不少人做过关于日历的功能,常见的日历排版有两种:

  • 周日在第一列
  • 周一在第一列

另外,还要普及一个常识——周日是每周的第一天

啊?难道周一不是每周的第一天吗?

no, no, no, too simple, so naive

在阿拉伯国家,他们的上班周期是周日到周四,周五、周六放假

扯远了,让我们先看看两种日历

用DateComponents 实现奇葩的日历
周日在第一列

用DateComponents 实现奇葩的日历
周一在第一列

需求

开始进入主题吧,最近在做一个项目,有个需求

  • 列出从今天所在周之后三周的日期(好拗口,看下面的效果图)
  • 周一在第一列
用DateComponents 实现奇葩的日历
需求

要拿到这样的日期列表,只要能获取到第一个日期,其它都好办了。

那怎么获取到第一个日期呢?下面先讲一下我的思路

以下涉及到代码均以 Swift 语言为例

// v1.0 // 获取今天的 DateComponents let todayComponents = Calendar.current.dateComponents(in: TimeZone.current, from: Date())  // 新建 firstDateComponents, year, weekOfYeear与todayComponents一致, weekday 初始化为周一(值为2) var firstDateComponents = DateComponents.init() firstDateComponents.year = todayComponents.year firstDateComponents.weekOfYear = todayComponents.weekOfYear firstDateComponents.yearForWeekOfYear = todayComponents.yearForWeekOfYear firstDateComponents.weekday = 2  // 转换为日期 var firstDate = Calendar.current.date(from: firstDateComponents)

这是非常 naive 的代码,我们先铺垫讲一下 DateComponents 再回头讲代码

DateComponents

DateComponentsstruct类型,带有year month day weekday weekOfYear等日历相关的信息

其中的weekdayweekOfYear是需要重点介绍的

  • weekday 代表周一、周二这样的信息,枚举值对应的是
    • 周日 = 1
    • 周一 = 2
    • 周六 = 7
  • weekOfYear 代表一年中的第几周

分析

了解完 DateComponents 后,就能很清楚了解到上面的代码有什么问题

即:当今天为周日时,获取到的第一天与需求不符

假设今天为25号,通过上面代码获取到的 fistDate 将是26号,而显然我们想要的是19号

用DateComponents 实现奇葩的日历

所以我们需要对上面的代码进行优化,在某种情况下,需要向前翻一周

那某种情况的条件是什么呢?

我这人比较笨,遇到这种问题,我就穷举一下再找规律

假设今天是周几记为 todayWeekday ,需求中周几作为第一列记为 firstDayOfWeek

  • firstDayOfWeek 为周一时, todayWeekday 为周日,需要回滚一周
  • firstDayOfWeek 为周二时, todayWeekday 为周日、周一,需要回滚一周
  • firstDayOfWeek 为周三时, todayWeekday 为周日、周一、周二,需要回滚一周
  • 以此类推,得出结论当 firstDayOfWeek > todayWeekday 时,需要回滚一周

代码

enum Weekday: Int {     case sunday = 1, monday, tuesday, wednesday, thursday, friday, saturday      func string() -> String {         var title = ""          switch self {         case .sunday:             title = "周日"         case .monday:             title = "周一"         case .tuesday:             title = "周二"         case .wednesday:             title = "周三"         case .thursday:             title = "周四"         case .friday:             title = "周五"         case .saturday:             title = "周六"         }          return title     } }  extension Date {     /// 获取从本周开始的日期数组     ///     /// - parameter weeks:          共获取几周的数据,必须大于0     /// - parameter firstDayOfWeek: 周的第一天是周一还是周日     ///     /// - returns: 日期数组     static func datesFromThisWeek(weeks: Int, firstDayOfWeek: Weekday) -> [Date] {         var dates = [Date]()          guard weeks > 0 else {             return dates         }          let firstDate = firstDateOfThisWeek(firstDayOfWeek: firstDayOfWeek)         dates.append(firstDate)          for index in 1...(weeks * 7 - 1) {             dates.append(firstDate.add(day: index))         }          return dates     }      /// 获取当周的第一天     ///     /// - parameter firstDayOfWeek: 日历的第一列是周几     ///     /// - returns: 第一天     static func firstDateOfThisWeek(firstDayOfWeek: Weekday) -> Date {         let todayComponents = Calendar.current.dateComponents(in: TimeZone.current, from: Date())          var firstDateComponents = DateComponents.init()         firstDateComponents.year = todayComponents.year         firstDateComponents.weekOfYear = todayComponents.weekOfYear         firstDateComponents.yearForWeekOfYear = todayComponents.yearForWeekOfYear         firstDateComponents.weekday = firstDayOfWeek.rawValue          var firstDate = Calendar.current.date(from: firstDateComponents)         if firstDayOfWeek.rawValue > todayComponents.weekday! {             // 如果第一列与今天不在同一周,需要特殊处理,取上周的数据             firstDate = firstDate?.add(day: -7)         }         return firstDate!     }      /// 获取当周的最后一天     ///     /// - parameter firstDayOfWeek: 日历的第一列是周几     ///     /// - returns: 最后一天     static func lastDateOfThisWeek(firstDayOfWeek: Weekday) -> Date {         let firstDate = firstDateOfThisWeek(firstDayOfWeek: firstDayOfWeek)         return firstDate.add(day: 6)     }      /// 获得新的日期     ///     /// - parameter day: 可以为正数和负数,正数为未来的日期,负数为过去的日期     ///     /// - returns: 新的日期     func add(day: Int) -> Date {         var addingDayComponents = DateComponents.init()         addingDayComponents.day = day         return Calendar.current.date(byAdding: addingDayComponents, to: self)!     } }

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » 用DateComponents 实现奇葩的日历

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址