跳到主要内容

windows 版本的 docker desktop

· 阅读需 1 分钟
1adybug
子虚伊人

之前为了便于 wsl 中的系统直接使用 windows 的代理,我将 wsl 的网络模式都设置为了 mirrored,这样就导致了无法使用 ip 访问 docker 中的应用,所以必须将网络模式设置为 NAT,这样就可以通过 ip 访问 docker 中的应用了。

[wsl2]
# 注释掉或者改为 NAT
# networkingMode=mirrored
networkingMode=NAT

关闭 wsl,然后重启 docker desktop,这样就可以通过 ip 访问 docker 中的应用了。

wsl --shutdown

contentRect、contentBoxSize 和 borderBoxSize 之间的区别

· 阅读需 1 分钟
1adybug
子虚伊人

这些尺寸属性之间的主要区别如下:

  1. contentRect:

    • 表示元素的内容区域的尺寸
    • 不包含内边距(padding)和边框(border)
    • 是最内层的测量值
  2. contentBoxSize:

    • 与contentRect测量相同的区域
    • 同样只包含内容区域的尺寸
    • 主要区别在于返回值的格式不同:contentBoxSize返回一个包含width和height的对象
  3. borderBoxSize:

    • 测量元素的完整尺寸
    • 包含内容区域、内边距和边框
    • 是最外层的测量值
    • 计算公式:内容区域 + padding * 2 + border * 2
    • inlineSize:沿着文本方向的尺寸,一般为 width
    • blockSize:垂直于文本方向的尺寸,一般为 height

这些属性在实际应用中的使用场景:

  • contentRect/contentBoxSize:当你需要知道元素实际内容区域大小时使用,比如计算文本区域
  • borderBoxSize:当你需要知道元素在页面上占据的总空间时使用,比如布局计算

如果你对某个具体的使用场景有疑问,或者想要了解更多细节,请随时告诉我

同步 Cursor 设置

· 阅读需 1 分钟
1adybug
子虚伊人

使用办法

  1. 覆盖保存

    • 下载 settings.jsonC:\Users\用户名\AppData\Roaming\Cursor\User 目录下
    • 下载 global.code-snippetsC:\Users\用户名\AppData\Roaming\Cursor\User\snippets 目录下
  2. 使用 zixulu 进行同步

    npx zixulu sc

设置

代码片段

Excel 日期处理中的一个 bug

· 阅读需 7 分钟
1adybug
子虚伊人

最近在使用 SheetJSxlsx 库(以下用 SheetJS 代称)处理 Excel 的数据时,遇到一个非常有趣的 bug。

在读取 xlsx 文件时,我并没有设置读取的配置,所有读取出来的数据都是 Record<string, string> 类型。在处理日期数据时,我发现返回的数据是一个整数字符串,例如 44297,而不是我期望的日期字符串,例如 2024-12-30。询问 Claude 得到以下回答:


让我解释一下 Excel 中日期的存储机制。

Excel 将日期存储为序列号(serial number),这是从一个特定的起始日期开始计算的天数。具体来说:

  1. 对于 Windows 版本的 Excel,起始日期是 1900 年 1 月 1 日,这一天的序列号是 1
  2. 对于 Mac 版本的 Excel,起始日期是 1904 年 1 月 1 日,这一天的序列号是 0

你看到的 45293 就是这样的序列号。让我用代码演示一下如何处理这个数字:

从上面的演示可以看出,序列号 45293 对应的是 2024 年 1 月 2 日。这种存储方式有几个特点:

  1. 整数部分表示天数
  2. 小数部分表示时间(例如 45293.5 表示当天的中午 12 点)
  3. 这种格式便于进行日期计算,比如两个日期相减就能得到相差的天数

当你在编程中处理 Excel 文件时,可以使用像 SheetJS 这样的库来正确解析这些数值。如果你在用 Pythonpandas 库也能自动处理这种转换。如果需要,我可以给你展示更多的日期处理示例。


真是坑爹,WindowsMac 版本的 Excel 竟然表现还不一致,不过手头没有 Mac,就无法测试差异了。

了解了原理,就尝试自己处理这个问题。我使用了 dayjs 库来处理日期,代码如下:

import dayjs from "dayjs"

import "dayjs/locale/zh-cn"

dayjs.locale("zh-cn")

const start = dayjs("1900-01-01 00:00:00")

function parser(serial: number) {
return start.add(serial - 1, "day").format("YYYY-MM-DD HH:mm:ss")
}

console.log(parser(45293))

得到的结果是 2024-01-03 00:00:00

可以看到时间是正确的,但是日期是错误的,我想是不是时区转换引起的。尝试使用 UTC 时间来实现 start

// ...
const start = dayjs("1900-01-01T00:00:00.000Z")
// ...
注意

注意,dayjs 会默认进行本地化

我擦,问题又出现了,得到的结果是 2024-01-03 00:05:43!我可以理解,小时是由于时区的差异产生的错误,但是分钟和秒怎么会有偏差呢?那肯定是某个时间点出问题了:

console.log(new Date("1900-01-01T00:00:00.000Z").toString())

得到的结果是 Mon Jan 01 1900 08:05:43 GMT+0805 (中国标准时间)!再次询问 Claude,得到以下回答:


让我解释这个有趣的现象。这涉及到历史上的时区变迁和JavaScript处理日期的方式。 原因主要有两点:

1900年时的中国采用的是地方时间(Local Mean Time, LMT),而不是现在的 UTC+8 时区。具体来说,当时北京使用的是 UTC+8:05:43。 JavaScriptnew Date() 在处理历史日期时会考虑历史时区信息。当你创建一个 1900 年的日期时,JavaScript 会使用当时的历史时区偏移,而不是现代的 UTC+8。


OK,那我们采用另一种策略来处理这个问题:我们把 Excel 中的时间就当做是 UTC 时间,然后在处理时加上 8 小时:

import dayjs from "dayjs"
import utc from "dayjs/plugin/utc"

dayjs.extend(utc)

const start = dayjs.utc("1900-01-01 00:00:00.00")

function parser(serial: number) {
return start.add(serial - 1, "day").format("YYYY-MM-DD HH:mm:ss")
}

console.log(parser(45293))

得到的结果是 2024-01-03 00:00:00,问题解决!日期仍然错误,继续询问 Claude


让我帮你分析一下这个差异。我会用 SheetJS 来测试。

啊!现在我明白为什么会有这个差异了。这是因为 ExcelSheetJS 在处理日期时有一个历史遗留问题:

  1. Excel 有一个著名的 1900 年闰年 bug:Excel 错误地认为 1900 年是闰年,在 1900 年 2 月 28 日之后多算了一天。

  2. 所以对于 2024/1/2 这个日期:

    • 不考虑 bug 时的序列号是:45292(这是实际天数)
    • 考虑 bug 后的序列号是:45293(这是 SheetJSExcel 实际使用的数字)

所以如果你使用 SheetJS 读取到的是 45293,这是完全正确的。让我给你一个正确处理这个序列号的代码:

function parser(serial: number) {
// 修正 1900 年闰年 bug:如果序列号大于 60,需要减 1
const adjustedSerial = serial > 60 ? serial - 1 : serial

const epoch = new Date("1900-01-01T00:00:00+08:00")
const millisecondsPerDay = 24 * 60 * 60 * 1000

return new Date(epoch.getTime() + (adjustedSerial - 1) * millisecondsPerDay)
}

// 使用示例
const serial = 45293 // `SheetJS` 读取的值

const date = parser(serial)
// 将输出 2024/1/2

总结一下:

  1. 你获取到 45293 是正确的,这是 SheetJSExcel 实际使用的序列号
  2. 这个数字比实际天数大 1,是因为 Excel 的 1900 年闰年 bug
  3. 在处理这类日期时,建议:
    • 如果你使用 SheetJS 的内置函数(如 XLSX.SSF.parse_date_code),直接使用 45293
    • 如果你自己写转换逻辑,记得处理这个 +1 的差异

这就是为什么我们会看到这个差异。这是 Excel 中一个著名的特性,为了保持兼容性,现代的库(包括 SheetJS)都会遵循这个行为。


算了,我还是用 SheetJS 的内置函数来处理这个问题吧,这样就不用自己处理这个 +1 了。

import xlsx from "xlsx"

const date = xlsx.SSF.parse_date_code(45293)
console.log(date)

这里又有一个坑,SheetJS 中的 SSF 类型是 any,但是它引用是 ssf 这个库,所以我们需要安装这个库:

import ssf from "ssf"

type SSFDate = {
D: number /* number of whole days since relevant epoch, 0 <= D */
y: number /* integral year portion, epoch_year <= y */
m: number /* integral month portion, 1 <= m <= 12 */
d: number /* integral day portion, subject to gregorian YMD constraints */
q: number /* integral day of week (0=Sunday .. 6=Saturday) 0 <= q <= 6 */

T: number /* number of seconds since midnight, 0 <= T < 86400 */
H: number /* integral number of hours since midnight, 0 <= H < 24 */
M: number /* integral number of minutes since the last hour, 0 <= M < 60 */
S: number /* integral number of seconds since the last minute, 0 <= S < 60 */
u: number /* sub-second part of time, 0 <= u < 1 */
}

const date = ssf.parse_date_code(45293)
console.log(date)
{
"D": 45293,
"y": 2024,
"m": 1,
"d": 2,
"q": 2,
"T": 0,
"H": 0,
"M": 0,
"S": 0,
"u": 0
}

配合 zod 实现的解析:

import { parse_date_code } from "ssf"
import { z } from "zod"

export const schema = z
.string({ message: "无效的日期" })
.trim()
.regex(/^[1-9]\d*$/, "无效的日期")
.transform(value => {
const { y, m, d } = parse_date_code(Number(value))
return new Date(y, m - 1, d)
})

使用 npm 安装最新的包

· 阅读需 1 分钟
1adybug
子虚伊人

有时候我们在更新了自己的 npm 包之后,需要立即在项目中进行更新,但是 registry 上的包并没有及时更新。这时候我们可以使用 npm i 命令的 @latest 标记配合 --registry https://registry.npmjs.com 来安装最新的包。

npm i soda-next@latest --registry https://registry.npmjs.com

使用 PowerShell,可以在 $PROFILE 中配置:

function inpm {
$package = $args | ForEach-Object { "$_@latest" }
npm i $package --registry=https://registry.npmjs.org
}

在 jsdoc 中定义和复用对象类型

· 阅读需 2 分钟
1adybug
子虚伊人

我来介绍几种在 JSDoc 中定义和复用对象类型的方法:

  1. 使用 @typedef 定义类型:

    /**
    * @typedef {Object} User
    * @property {string} name - 用户名
    * @property {number} age - 年龄
    * @property {string} [email] - 可选的邮箱
    */

    /**
    * 创建新用户
    * @param {User} userData - 用户数据
    * @returns {User}
    */
    function createUser(userData) {
    // ...
    }
  2. 使用 @typedef 定义嵌套对象:

    /**
    * @typedef {Object} Address
    * @property {string} street
    * @property {string} city
    */

    /**
    * @typedef {Object} UserWithAddress
    * @property {string} name
    * @property {Address} address - 用户地址
    */

    /**
    * @param {UserWithAddress} user
    */
    function printUserAddress(user) {
    // ...
    }
  3. 使用 @callback 定义包含函数的对象类型:

    /**
    * @typedef {Object} ConfigOptions
    * @property {string} baseUrl
    * @property {function(Error): void} onError - 错误处理回调
    */

    /**
    * @param {ConfigOptions} config
    */
    function initialize(config) {
    // ...
    }
  4. 继承和扩展已有类型:

    /**
    * @typedef {Object} BaseConfig
    * @property {string} env - 环境变量
    */

    /**
    * @typedef {BaseConfig} ProductConfig
    * @property {string} productId - 产品ID
    */

    /**
    * @param {ProductConfig} config
    */
    function setupProduct(config) {
    // ...
    }
  5. 使用命名空间组织类型:

    /**
    * @namespace API
    */

    /**
    * @typedef {Object} API.RequestConfig
    * @property {string} url
    * @property {string} method
    */

    /**
    * @typedef {Object} API.Response
    * @property {number} status
    * @property {*} data
    */

    /**
    * @param {API.RequestConfig} config
    * @returns {Promise<API.Response>}
    */
    function request(config) {
    // ...
    }

这些类型定义可以在整个项目中重复使用。一些实用建议:

  • 将常用的类型定义放在单独的文件中,例如 types.js
  • 为复杂对象添加详细的属性描述
  • 使用 @property 的可选标记 [] 来表示可选属性
  • 用命名空间避免类型名称冲突
  • IDE(如 VSCode)会根据这些类型定义提供代码提示

这样可以让你的代码更容易维护,并提供更好的开发体验。

useQuery 中的状态

· 阅读需 1 分钟
1adybug
子虚伊人

@tanstack/react-queryuseQuery 分别提供了四种状态:

  • isPending:是否在等待数据,只要有数据存在,无论是缓存的,还是请求的,都不会是 isPending
  • isFetching:是否在请求数据
  • isRefetching:是否在重新请求数据
  • isLoading:是否在首次请求数据,等同于 isPending && isFetching
提示

同一个 queryKey 的这些状态是共享的

Docker 安装

· 阅读需 2 分钟
1adybug
子虚伊人

使用 apt 命令安装很有可能失败,参考官方教程 install-from-a-package

  1. 转至 https://download.docker.com/linux/ubuntu/dists/

  2. 在列表中选择 Ubuntu 版本

  3. 转到 pool/stable/ 并选择适用的架构(amd64armhfarm64s390x

  4. 下载 Docker EngineCLIcontainerdDocker Compose 软件包的以下 deb 文件:

    • containerd.io_<version>_<arch>.deb
    • docker-ce_<version>_<arch>.deb
    • docker-ce-cli_<version>_<arch>.deb
    • docker-buildx-plugin_<version>_<arch>.deb
    • docker-compose-plugin_<version>_<arch>.deb
  5. 安装 .deb 软件包。将以下示例中的路径更新为您下载 Docker 软件包的位置。

    sudo dpkg -i ./containerd.io_<version>_<arch>.deb \
    ./docker-ce_<version>_<arch>.deb \
    ./docker-ce-cli_<version>_<arch>.deb \
    ./docker-buildx-plugin_<version>_<arch>.deb \
    ./docker-compose-plugin_<version>_<arch>.deb

    Docker 守护进程自动启动。

  6. 通过运行 hello-world 镜像来验证安装是否成功:

    sudo service docker start
    sudo docker run hello-world

    此命令下载测试映像并在容器中运行。容器运行时,它会打印一条确认消息并退出。

现已成功安装并启动了 Docker Engine

修改 Docker 镜像

· 阅读需 1 分钟
1adybug
子虚伊人
  1. 创建目录 /etc/docker

    sudo mkdir -p /etc/docker
  2. 创建并编辑文件 /etc/docker/daemon.json

    sudo vim /etc/docker/daemon.json

    添加以下内容:

    {
    "registry-mirrors": ["https://docker.sunzishaokao.com", "https://hub.hxui.site", "https://docker.1ms.run"],
    "exec-opts": ["native.cgroupdriver=systemd"]
    }

    其中 registry-mirrors 为镜像地址,根据实际情况替换。

  3. 重启 Docker 服务:

    sudo systemctl daemon-reload
    sudo systemctl restart docker

修改 Ubuntu 镜像源

· 阅读需 1 分钟
1adybug
子虚伊人
  1. 首先确定 Ubuntu 版本代号:

    lsb_release -c

    常见的版本代号:

    • 16.04 xenial
    • 18.04 bionic
    • 20.04 focal
    • 22.04 jammy
    • 24.04 noble
  2. 备份原有的源文件:

    sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
  3. 修改源文件:

    sudo vim /etc/apt/sources.list

    将文件内容替换为以下内容:

    # 中科大镜像源
    deb https://mirrors.ustc.edu.cn/ubuntu/ noble main restricted universe multiverse
    deb https://mirrors.ustc.edu.cn/ubuntu/ noble-updates main restricted universe multiverse
    deb https://mirrors.ustc.edu.cn/ubuntu/ noble-backports main restricted universe multiverse
    deb https://mirrors.ustc.edu.cn/ubuntu/ noble-security main restricted universe multiverse

    其中 noble 为 Ubuntu 版本代号,根据实际情况替换。

  4. 更新源:

    sudo apt update