tdesign-common icon indicating copy to clipboard operation
tdesign-common copied to clipboard

fix(date-picker): add dayjs updateLocale plugin to support correct week numbers

Open Summer-Shen opened this issue 1 year ago • 12 comments

🤔 这个 PR 的性质是?

  • [x] 日常 bug 修复
  • [ ] 新特性提交
  • [ ] 文档改进
  • [ ] 演示代码改进
  • [ ] 组件样式/交互改进
  • [ ] CI/CD 改进
  • [ ] 重构
  • [ ] 代码风格优化
  • [ ] 测试用例
  • [ ] 分支合并
  • [ ] 其他

🔗 相关 Issue

https://github.com/Tencent/tdesign-vue-next/issues/3785

💡 需求背景和解决方案

更新 dayjs 已有的语言配置 以获取正确的周数。

  • firstDayOfWeek 为一周第 1 天,则这周的第 4 天我们记为星期 x
  • 本年度第一个星期 x 所在的星期是本年度的第一周。

date-picker-week-number

参考:https://github.com/iamkun/dayjs/issues/2237

📝 更新日志

  • fix(date-picker): 添加 dayjs updateLocale 插件,以支持正确的周数

  • [ ] 本条 PR 不需要纳入 Changelog

☑️ 请求合并前的自查清单

⚠️ 请自检并全部勾选全部选项。⚠️

  • [x] 文档已补充或无须补充
  • [x] 代码演示已提供或无须提供
  • [x] TypeScript 定义已补充或无须补充
  • [x] Changelog 已提供或无须提供

Summer-Shen avatar Jan 11 '24 16:01 Summer-Shen

非常感谢 PR 🌹 稍后会有同事帮忙验证

chaishi avatar Jan 13 '24 07:01 chaishi

非常感谢 PR 🌹 稍后会有同事帮忙验证

非常感谢你的pr 🌹 原来的问题是:当鼠标悬浮或者点击时,输入框中的值是准确符合实际的,只是采用的ISO的标准导致对周的定义跟面板中的展示形式不一致,比如不管firstDayOfWeek传入什么,在zh-ch下,始终将周一作为本周第一天,因此不管firstDayOfWeek传入啥,24年的1号到7号都是被认为是24年的第一周,但是周数的逻辑是正确无误的,比如23年12月31号就是23年第52周。 image 采用了你这个方法后,发现当日期选择了12月31号时,周数的逻辑有点问题,23年12月31号成了23年的第一周,辛苦再看一下? image @Summer-Shen

azx1573 avatar Jan 13 '24 11:01 azx1573

@azx1573 感谢!想问下从选择周的角度来说,是否点在 2023-12-31 和 2024-01-01 应该显示两种不同的结果,还是统一为 2024 年第 1 周?

Summer-Shen avatar Jan 13 '24 12:01 Summer-Shen

@azx1573 感谢!想问下从选择周的角度来说,是否点在 2023-12-31 和 2024-01-01 应该显示两种不同的结果,还是统一为 2024 年第 1 周?

  1. 关于以什么为主:确切的来说,站在用户的角度(这里应该为该组件的使用者)来说,比如我选择23年12月31日,onChange返回给ta的结果应该是23年的52周,具体来说应该是以实际情况为主,另外这里还有个bug就是面板的高亮逻辑是用实际值去判断的,所以再次展开时会出现面板高亮和实际不符的情况。所以在设置了firstDayOfWeek的情况下,只需以实际情况为主,不然会导致结果不是组件使用者想要的。
  2. 理想的预期:以firstDayOfWeek为6为例 1> 如果我选择了23年12月30或者31,值为23年52周,高亮当前日期所在行 2> 如果我选择了24年1月的1号-24年1月的5号,值为24年第一周,依然高亮这行,因为这7天为同一周。 @Summer-Shen

azx1573 avatar Jan 13 '24 12:01 azx1573

@azx1573 感谢,那面板上周数如何标识?比如说当前面板上是 2023 年 12 月,那最后一周左侧应该是 52 周还是 1 周?

这里还有个bug就是面板的高亮逻辑是用实际值去判断的,所以再次展开时会出现面板高亮和实际不符的情况。

我测试了一下 tdesign-vue-next 好像没有上述这个问题。

Summer-Shen avatar Jan 13 '24 13:01 Summer-Shen

@azx1573 感谢,那面板上周数如何标识?比如说当前面板上是 2023 年 12 月,应该是 52 周还是 1 周?

这里还有个bug就是面板的高亮逻辑是用实际值去判断的,所以再次展开时会出现面板高亮和实际不符的情况。

我测试了一下 tdesign-vue-next 好像没有上述这个问题。

这是我刚想要说的一点, 突然发现之前这里是52,现在变成1开始了。 你是直接改的getweeks,而面板数组就是通过这个方法生成的,所以日期面板数组的值及顺序都被改了哦,首先这可能会对已使用这个组件的业务功能会造成影响哈(具体以实际评估结果为准,只是推测可能)。

之前存在高亮这个问题的你可以试试,但是你将这个数组顺序改了后就不存在了,因为23年31号被认为是23年第一周😂,等号前面值为1,1-5号是24年第一周,值也为1,选这周的任何一个值value都为1,后面是数组第一列表示周的值,之前52现在变成1,两个值相等所以高亮的问题不存在了😂 image image image @Summer-Shen

azx1573 avatar Jan 13 '24 13:01 azx1573

你是直接改的getweeks,而面板数组就是通过这个方法生成的,所以日期面板数组的值及顺序都被改了哦,首先这可能会对已使用这个组件的业务功能会造成影响哈(具体以实际评估结果为准,只是推测可能)。

@azx1573 只针对这一点来说,请问如果抽出一个函数计算周的序号可以么?还是从其他地方(比如各个技术栈的展示)入手比较合适?

Summer-Shen avatar Jan 13 '24 13:01 Summer-Shen

我理解是只要我们不对已有功能造成影响或可能造成影响,你可以根据你的思路信马由缰去发挥,千万别被别人的思路桎梏😄,期待你的更优解👏 @Summer-Shen

azx1573 avatar Jan 13 '24 14:01 azx1573

简单测试了一下,dayjs 的 format 输出如下:

const dayjs = require("dayjs");
const weekOfYear = require("dayjs/plugin/weekOfYear");
const updateLocale = require("dayjs/plugin/updateLocale");
const advancedFormat = require("dayjs/plugin/advancedFormat");

dayjs.extend(weekOfYear);
dayjs.extend(updateLocale);
dayjs.extend(advancedFormat);

dayjs.updateLocale("zh-cn", {
  weekStart: 7, // week starts at Sunday
});

function parseDateToWeekNumber(date) {
  return dayjs(date).locale("zh-cn").format("YYYY-wo");
}

console.log(parseDateToWeekNumber("2025-12-31")); // 2025-1st
console.log(parseDateToWeekNumber("2026-01-01")); // 2026-1st

可能打算去给 dayjs 提 issue;and/or 我们要自己实现 format.ts 中的逻辑了。

另一方面来说,ISO 8601 的定义是基于一周的第一天是周一,我们可能需要讨论一下这个定义、我再考虑一下。

Summer-Shen avatar Jan 13 '24 16:01 Summer-Shen

简单测试了一下,dayjs 的 format 输出如下:

const dayjs = require("dayjs");
const weekOfYear = require("dayjs/plugin/weekOfYear");
const updateLocale = require("dayjs/plugin/updateLocale");
const advancedFormat = require("dayjs/plugin/advancedFormat");

dayjs.extend(weekOfYear);
dayjs.extend(updateLocale);
dayjs.extend(advancedFormat);

dayjs.updateLocale("zh-cn", {
  weekStart: 7, // week starts at Sunday
});

function parseDateToWeekNumber(date) {
  return dayjs(date).locale("zh-cn").format("YYYY-wo");
}

console.log(parseDateToWeekNumber("2025-12-31")); // 2025-1st
console.log(parseDateToWeekNumber("2026-01-01")); // 2026-1st

可能打算去给 dayjs 提 issue;and/or 我们要自己实现 format.ts 中的逻辑了。

另一方面来说,ISO 8601 的定义是基于一周的第一天是周一,我们可能需要讨论一下这个定义、我再考虑一下。

ISO 8601 的标准我们也没法改变,antd我大概看了下印象中好像都不支持自定义周起始日,tdesigin已经支持了并且内外部已经有不少项目使用了这个事实也是没法改变的😂。你下午提了后,我也用了两个demo,一个是你这边的代码,另一个是独立使用dayjs的,粗略验证了下dayjs里部分特殊日期的值确是有问题的,所以想着看看你这边能不能有更独特,更优更新颖的解法。

azx1573 avatar Jan 13 '24 16:01 azx1573

@azx1573

最近做了一些测试和思考,考虑到既有的值格式为 YYYY-wo,可能的方案是不修改 value 的格式,仅修改:

  • 如何比较两个日期是否在同一周。在跨年前后,这个比较有两种含义,即

    • 两个日期是否对应一个 YYYY-wo,以及

    • 两个日期是否在 firstDayOfWeek 的条件下处于同一周,比如各个技术栈仓库中

      parseToDayjs(value, format).locale(dayjsLocale).week() === 
      parseToDayjs(targetValue, format).locale(dayjsLocale).week()
      
  • 如何计算周数,比如 format.ts 文件的 parseToDayjs 函数中。

不过这也仍然是 breaking change:当 firstDayOfWeek 不是默认值时,原有 hover 时输入框的逻辑对周一及之后产生准确的周数,但对周日及以前则产生错误结果、也就是上一周。因此对于用户来说一个 YYYY-wo 值所指向的时间范围是发生了变化的,无论如何修改都难以避免。

另一方面而言,我注意到 dayjs 其实提供了另一种 format gggg可以代替 YYYY 表示日期对应周数的年份

从这两点来说,更好的方案可能是:

  • 给定一个日期,找出在这个日期之前最近的 firstDayOfWeek,记这一天为 startOfWeek
  • 然后取 startOfWeek.week() 作为周数,取 startOfWeek.format("gggg-wo") 作为输出值。

不知道您怎么看?感谢!

另外之后可能会重开一个 PR,之前的 solution 爆炸半径大了些。

Summer-Shen avatar Feb 13 '24 17:02 Summer-Shen

@azx1573

最近做了一些测试和思考,考虑到既有的值格式为 YYYY-wo,可能的方案是不修改 value 的格式,仅修改:

  • 如何比较两个日期是否在同一周。在跨年前后,这个比较有两种含义,即

    • 两个日期是否对应一个 YYYY-wo,以及
    • 两个日期是否在 firstDayOfWeek 的条件下处于同一周,比如各个技术栈仓库中
      parseToDayjs(value, format).locale(dayjsLocale).week() === 
      parseToDayjs(targetValue, format).locale(dayjsLocale).week()
      
  • 如何计算周数,比如 format.ts 文件的 parseToDayjs 函数中。

不过这也仍然是 breaking change:当 firstDayOfWeek 不是默认值时,原有 hover 时输入框的逻辑对周一及之后产生准确的周数,但对周日及以前则产生错误结果、也就是上一周。因此对于用户来说一个 YYYY-wo 值所指向的时间范围是发生了变化的,无论如何修改都难以避免。

另一方面而言,我注意到 dayjs 其实提供了另一种 format gggg可以代替 YYYY 表示日期对应周数的年份

从这两点来说,更好的方案可能是:

  • 给定一个日期,找出在这个日期之前最近的 firstDayOfWeek,记这一天为 startOfWeek
  • 然后取 startOfWeek.week() 作为周数,取 startOfWeek.format("gggg-wo") 作为输出值。

不知道您怎么看?感谢!

另外之后可能会重开一个 PR,之前的 solution 爆炸半径大了些。

非常感谢,年后一起看看❤️🌹 @Summer-Shen

azx1573 avatar Feb 17 '24 09:02 azx1573