基于32位bit位操作,实现用户签到工具类
2021-06-10 本文已影响0人
每天都是奥利给
之前项目需要做用户签到,便查阅了相关资料,整理了一个工具类
/**
* 基于bit位运算 实现用户签到功能类
*/
public class UserSginUtils {
private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
/**
* 用户签到
* @param sginTag 本月已签到标识 未签到则为0
* @param monthOfDay 当前天 如 07-25 则传25
* @return 返回新 签到标识 -> sginTag
*/
public int doSgin(int sginTag, int monthOfDay){
int offset = monthOfDay - 1; //偏移量从0开始 所以day-1
int sgin_offset = 1 << offset; //日期从1号开始 所以从1开始左偏移offset位
return sginTag + sgin_offset;
}
/**
* 该月累计签到次数
* 如 该月 1,2,3,6号用户签到了 则 sginTag = 39 则用户该月供签到4次
* @param sginTag
* @return
*/
public int sginCount(int sginTag){
return Integer.bitCount(sginTag);
}
/**
* 该月首次签到日期
* 向左取bit 第一位为1的偏移量
* @param sginTag
* @return
*/
public int monthTopSginDate(int sginTag){
int r = 1; //偏移量从0开始 所以+1
if ((sginTag & 0xFFFF) == 0) {
sginTag >>= 16;
r += 16;
}
if ((sginTag & 0xFF) == 0) {
sginTag >>= 8;
r += 8;
}
if ((sginTag & 0x0F) == 0) {
sginTag >>= 4;
r += 4;
}
if ((sginTag & 0x03) == 0) {
sginTag >>= 2;
r += 2;
}
if ((sginTag & 0x01) == 0) {
r += 1;
}
return r;
}
/**
* 该月最后签到日期
* 向右取第一个位1的偏移量
* @param sginTag
* @return
*/
public int monthLastSginDate(int sginTag){
int r = 0;
if ((sginTag & 0xffff0000) != 0) {
sginTag >>>= 16;
r += 16;
}
if ((sginTag & 0xff00) != 0) {
sginTag >>>= 8;
r += 8;
}
if ((sginTag & 0xf0) != 0) {
sginTag >>>= 4;
r += 4;
}
if ((sginTag & 0x0c) != 0) {
sginTag >>>= 2;
r += 2;
}
if ((sginTag & 0x02) != 0) {
sginTag >>>= 1;
r += 1;
}
if ((sginTag & 0x01) != 0) {
r += 1;
}
return r;
}
/**
* 判断当天是否签到
* @param sginTag 签到标识值
* @param monthOfDay 天
* @return
*/
public boolean isSgin(int sginTag, int monthOfDay){
int offset = monthOfDay - 1; //偏移量从0开始
int daySgin = 1 << offset; //每月1号 bit偏移 offset 为monthday的bit位
return (sginTag & daySgin) > 0;
}
/**
* 当月连续签到的天数(按传入日期 往前推算)
* @param sginTag 当月签到标识
* @param checkDate 检查日期
* @return
*/
public int continuousNum(int sginTag, Date checkDate){
int sginCount = 0; //签到次数
Calendar calendar = Calendar.getInstance();
calendar.setTime(checkDate);
int day = calendar.get(Calendar.DAY_OF_MONTH); //获取当前月份天数
// day = day - 1; //不考虑当天
while (day > 0){
if (isSgin(sginTag, day)){
sginCount++;
}else { //只要有一次未签到 则退出循环
break;
}
day--;
}
return sginCount;
}
/**
* 获取当月1号开始 连续签到次数
* @param sginTag
* @param year
* @param month
* @return
*/
public int continuousFirstDay(int sginTag,int year, int month){
int sginCount = 0;
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month-1);
calendar.set(Calendar.DAY_OF_MONTH,calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
//获取当月最大日期
int maxDay = calendar.get(Calendar.DAY_OF_MONTH);
for (int day = 1; day <= maxDay; day++){
if (isSgin(sginTag, day)){
sginCount++;
}else { //只要有一次未签到 则退出循环
break;
}
}
return sginCount;
}
/**
* 获取当月1号开始 连续签到次数
* @param sginTag
* @param year
* @param month
* @return
*/
public List<String> continuousFirstDayOfDays(int sginTag,int year, int month){
List<String> list = new ArrayList<>();
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month-1);
calendar.set(Calendar.DAY_OF_MONTH,calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
//获取当月最大日期
int maxDay = calendar.get(Calendar.DAY_OF_MONTH);
for (int day = 1; day <= maxDay; day++){
if (isSgin(sginTag, day)){
calendar.set(Calendar.DAY_OF_MONTH, day);
list.add(format.format(calendar.getTime()));
}else { //只要有一次未签到 则退出循环
break;
}
}
return list;
}
/**
* 获取当月(连续) 连续签到 指定次数的日期集合 如指定连续签到 contiousDay = 7 则 连续签到 7天或以后的所有日期会返回 否则返回空集合
* @param sginTag
* @param year
* @param month
* @return
*/
public List<String> getContinousDays(int sginTag, int lastSginTag, int year, int month, int contiousDay){
List<String> list = new ArrayList<>();
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month - 1);
calendar.set(Calendar.DAY_OF_MONTH,calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
//上月连续签到天数
int lastSignCount = this.monthLastDateSginNum(lastSginTag, year, month);
//获取当月最大日期
int maxDay = calendar.get(Calendar.DAY_OF_MONTH);
int sginCount = lastSignCount;
for (int day = 1; day <= maxDay; day++){
if (isSgin(sginTag, day)){
sginCount++;
}else { //只要有一次未签到 则重置
sginCount = 0;
}
if (sginCount >= contiousDay){ //连续签到超过指定天数的 加入集合
calendar.set(Calendar.DAY_OF_MONTH, day);
list.add(format.format(calendar.getTime()));
}
}
return list;
}
/**
* 当月最后一天往前推 连续签到天数
* @param sginTag
* @param year
* @param month
* @return
*/
public int monthLastDateSginNum(int sginTag, int year, int month){
int sginCount = 0;
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month - 1);
calendar.set(Calendar.DAY_OF_MONTH,calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
int day = calendar.get(Calendar.DAY_OF_MONTH);
while (day > 0){
if (isSgin(sginTag, day)){
sginCount++;
}else { //只要有一次未签到 则退出循环
break;
}
day--;
}
return sginCount;
}
/**
* 获取用户签到视图数据
* @param sginTag 签到标识
* @param month 签到月份
* @param year 签到年份
* @return
*/
public List<Map<String,Object>> sginMapDetails(int sginTag, int year, int month){
List<Map<String,Object>> sginListMap = new ArrayList<>();
//设置日期
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, year);
month = month - 1; //日历类 月份从0开始
calendar.set(Calendar.MONTH, month);
//当月最大日期
int maxMonthOfday = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
int day = 1; //月份第一天
String yearOfMonth = String.valueOf(year).concat("-".concat(month+1 < 10 ? "0".concat(String.valueOf(month+1)) : String.valueOf(month+1))); // yyyy-MM
while (day <= maxMonthOfday){
Map<String, Object> dateMap = new HashMap<>();
dateMap.put("yearOfMonth", yearOfMonth);
String monthOfDay = String.valueOf(day);
if (day < 10){
monthOfDay = "0" + day;
}
String yearOfMonthDay = yearOfMonth
.concat("-")
.concat(monthOfDay);
dateMap.put("yearOfMonthDay", yearOfMonthDay);
dateMap.put("monthOfDay", monthOfDay);
boolean isSgin = false;
if (this.isSgin(sginTag, day)){
isSgin = true;
}
dateMap.put("isSgin", isSgin);
sginListMap.add(dateMap);
day ++;
}
return sginListMap;
}
/**
* 测试
* @param args
*/
// public static void main(String[] args) {
// UserSginUtils userSginUtils = new UserSginUtils();
// //签到
// //假设用户A 首次签到
// int sginTag = 0; //首次签到 签到标示值为0 该值存于数据库 或 redis缓存 key-val key:sgin:userId:yearMonthOfDay value:sginTag
// int monthOfDay = 1;// 签到日期
// int newSginTag = userSginUtils.doSgin(sginTag, monthOfDay);
// System.out.println("1. 用户1号签到,sginTag:"+newSginTag);
// //本月签到次数
// System.out.println("2. 用户本月签到:"+userSginUtils.sginCount(newSginTag)+"次");
// //用户A 在2号签到
// monthOfDay = 2;
// newSginTag = userSginUtils.doSgin(newSginTag, monthOfDay);
// System.out.println("3. 用户2号签到,sginTag:"+newSginTag);
// //本月签到次数
// System.out.println("4. 用户本月签到:"+userSginUtils.sginCount(newSginTag)+"次");
// //用户A 在4号签到
// monthOfDay = 4;
// newSginTag = userSginUtils.doSgin(newSginTag, monthOfDay);
// System.out.println("5. 用户4号签到,sginTag:"+newSginTag);
// //本月签到次数
// System.out.println("6. 用户本月签到:"+userSginUtils.sginCount(newSginTag)+"次");
// //用户A 在4号签到
// monthOfDay = 5;
// newSginTag = userSginUtils.doSgin(newSginTag, monthOfDay);
// System.out.println("5. 用户5号签到,sginTag:"+newSginTag);
// //本月签到次数
// System.out.println("6. 用户本月签到:"+userSginUtils.sginCount(newSginTag)+"次");
// //用户A 在4号签到
// monthOfDay = 6;
// newSginTag = userSginUtils.doSgin(newSginTag, monthOfDay);
// System.out.println("7. 用户6号签到,sginTag:"+newSginTag);
// //本月签到次数
// System.out.println("8. 用户本月签到:"+userSginUtils.sginCount(newSginTag)+"次");
// //用户A 在4号签到
// monthOfDay = 7;
// newSginTag = userSginUtils.doSgin(newSginTag, monthOfDay);
// System.out.println("9. 用户7号签到,sginTag:"+newSginTag);
// //本月签到次数
// System.out.println("10. 用户本月签到:"+userSginUtils.sginCount(newSginTag)+"次");
// //用户A 在4号签到
// monthOfDay = 8;
// newSginTag = userSginUtils.doSgin(newSginTag, monthOfDay);
// System.out.println("11. 用户8号签到,sginTag:"+newSginTag);
// //本月签到次数
// System.out.println("12. 用户本月签到:"+userSginUtils.sginCount(newSginTag)+"次");
// //判断用户是否在 5号签到
// System.out.println("13. 用户在5号签到了?结果:"+userSginUtils.isSgin(newSginTag, 5));
// //判断用户是否在 11号签到
// System.out.println("14. 用户在11号签到了?结果:"+userSginUtils.isSgin(newSginTag, 11));
// //检测连续签到
// //假设业务 连续签到7天有奖励
// // 用户A 准备在9号 准备签到 需要判断9号之前连续签到的情况 则:
// Calendar calendar = Calendar.getInstance();
// calendar.setTime(new Date());
// calendar.set(Calendar.DAY_OF_MONTH, 9);
// //4 5 6 7 8签到了 所以连续签到为 5
// System.out.println("15. 用户A在9号之前连续签到了:"+ userSginUtils.continuousNum(newSginTag, calendar.getTime())+"天");
// calendar.set(Calendar.DAY_OF_MONTH, 10);
// //9号未签到 所以10号之前连续签到为0
// System.out.println("16. 用户A在10号之前连续签到了:"+ userSginUtils.continuousNum(newSginTag, calendar.getTime())+"天");
// //本月签到视图
// System.out.println(userSginUtils.sginMapDetails(newSginTag, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH)+1));
// }
}