0%

背景

将部署在windows上的svn服务器迁移到linux

准备工作

linux服务器安装svn

1
yum -y install subversion

windows导出dump文件

1
svnadmin dump 项目地址 > 项目名.dump

linux创建版本库

1
svnadmin create /var/svn/test

linux导入dump文件

1
svnadmin load /svndata/项目名 < 项目名.dump

配置文件对照表

文件名 功能 windows linux
svnserve.conf svn服务配置 svnserve.conf svnserve.conf
authz 账号权限管理 VisualSVN-SvnAuthz.ini authz
passwd 密码配置文件 htpasswd passwd
根目录authz 所有库账号公共权限管理 VisualSVN-GlobalWinAuthz.ini 根目录authz
groups.conf svn用户群组信息 groups.conf 根目录authz

配置文件修改

svnserve.conf

1
2
3
4
anon-access = none (none匿名不可访问)
auth-access = write
password-db = /var/svn/passwd (修改为根目录的passwd)
authz-db = /var/svn/authz (修改为根目录的authz)

authz

1
2
3
4
5
6
7
8
[groups]
dev = dayi123,dayi

# windows
[/]
# linux (需要带根目录下的项目名)
[test:/]
test= rw

httpd支持

为了能通过web访问,以及兼容windows visualsvn的htpasswd,需要使用httpd服务

1
2
3
# 下载httpd和mod_dav_svn模块
yum install -y mod_dav_svn
yum install -y httpd

增加httpd的svn配置
vim /etc/httpd/conf.d/svn.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<Location /svn>
#开启svn
DAV svn
#指定父目录位置,以后所有版本库都将存放在该目录下
SVNParentPath /var/svn/
#开启svn版本库父目录功能
SVNListParentPath On
#指定授权模式
AuthType Basic
#登陆窗口标题显示提示文字
AuthName "Authorization Realm"
#指定访问版本库的账户和密码存放文件,该文件需要用apache的htpasswd命令创建
AuthUserFile /var/svn/htpasswd
#指定访问版本库的用户和组的权限控制文件
AuthzSVNAccessFile /var/svn/authz
#要求验证有效的用户才能访问
Require valid-user
</Location>

开启httpd服务

1
2
systemctl enable httpd
systemctl start httpd

如果不想使用httpd的默认端口80或者该端口被占用,则可以修改/etc/httpd/conf/httpd.conf中的默认端口

兼容windows的htpasswd
首先查看htpasswd文件中密码的格式
test:$apr1$uer$1txKFt7mXE/EV1ouN4xWK1

对比httpd官网给的几种加密方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
bcrypt
$ htpasswd -nbB myName myPassword
myName:$2y$05$c4WoMPo3SXsafkva.HHa6uXQZWr7oboPiC2bT/r7q1BB8I2s0BRqC

MD5
$ htpasswd -nbm myName myPassword
myName:$apr1$r31.....$HqJZimcKQFAMYayBlzkrA/

SHA1
$ htpasswd -nbs myName myPassword
myName:{SHA}VBPuJHI7uixaa6LQGWx4s+5GKNE=

CRYPT
$ htpasswd -nbd myName myPassword
myName:rqXexS6ZhobKA

可以看出使用的是MD5加密

使用htpasswd增加新用户

1
2
htpasswd -nbm myName myPassword
=> myName:$apr1$i/YRWA8N$sHsoGDljceyyIj5KTQJw/.

将其拷贝到httpd文件中,测试能否登陆svn即可

启动svn服务

基本命令

1
2
svnserve -d -r /var/svn/
killall svnserve

设置为systemctrl服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Unit]
Description=svn-server
After=network.target

[Service]
Type=forking
ExecStart=/usr/bin/svnserve -d -r /var/svn
ExecStop=/usr/bin/killall svnserve
PrivateTmp=true
Restart=always
RestartSec=1min

[Install]
WantedBy=multi-user.target

systemctl daemon-reload
systemctl enable svn
systemctl start svn

更换svn库地址

右键=》TortoiseSVN=》重新定位

其它一些功能

添加提交时强制写日志功能
首先拷贝hooks下面的pre-commit.tmpl为pre-commit(windows下为’pre-commit.bat’ or ‘pre-commit.exe’) 并增加执行权限
chmod -R u+x,g+x,o+x hooks

代码

1
2
3
4
5
6
7
8
9
10
11
REPOS="$1"
TXN="$2"

# Make sure that the log message contains some text.
SVNLOOK=/usr/bin/svnlook
LOGMSG=$($SVNLOOK log -t "$TXN" "$REPOS" | grep "[a-zA-Z0-9]" | wc -c)

if [ "$LOGMSG" -lt 10 ]; then
echo -e "\n Log message can't be empty! you must input change log. " 1>&2
exit 1
fi

在这里插入图片描述

增加备份功能
全量备份

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/bin/bash
# svn全量备份脚本

DATE=$(date +%Y%m%d:%H:%M:%S) #定义时间变量,用于文件名后缀
SVN_DIR=/var/svn #定义SVN库目录
SVN_BAK_DIR=/var/svndumpBak #定义备份文件存放目录

# 备份库列表
bakList=(test1 test2)

# 保留日志份数
keep_log_num=1

for rep in ${bakList[@]}
do
# 定义各个项目的初始版本号文件
svn_dir=$SVN_DIR/$rep
svn_bak_dir=$SVN_BAK_DIR/full_log/$rep
if [[ ! -d $svn_bak_dir ]]
then
mkdir -p $svn_bak_dir
fi

#开始备份
svnadmin dump $svn_dir > $svn_bak_dir/${rep}_$DATE.dump
if [ $? -eq 0 ];
then
echo "backup success...."
else
echo "backup fail...."
continue
fi

cd $svn_bak_dir
#清理过期备份
num=`expr $keep_log_num + 1`
ls -t | tail -n +$num | xargs rm -rf
done

增量备份

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#!/bin/bash
# svn增量备份脚本

DATE=$(date +%Y%m%d:%H:%M:%S) #定义时间变量,用于文件名后缀
SVN_DIR=/var/svn #定义SVN库目录
SVN_BAK_DIR=/var/svndumpBak #定义备份文件存放目录

# 备份库列表
bakList=(test1 test2)

# 保留日志份数
keep_log_num=7

for rep in ${bakList[@]}
do
# 定义各个项目的初始版本号文件
svn_dir=$SVN_DIR/$rep
svn_file=$svn_dir/conf/svn_ver_id
svn_bak_dir=$SVN_BAK_DIR/incr_log/$rep

if [[ ! -e $svn_file ]]
then
touch $svn_file
echo '0' > $svn_file
fi

if [[ ! -d $svn_bak_dir ]]
then
mkdir -p $svn_bak_dir
fi

NUM0=$(cat $svn_file)
NUM1=`expr $NUM0 + 1`
NUM2=$(svnlook youngest $svn_dir)

#没有新增跳过
if [[ NUM1 -eq NUM2 ]]
then
continue
fi

#开始备份
svnadmin dump $svn_dir -r $NUM1:$NUM2 --incremental > $svn_bak_dir/${rep}_${NUM1}_${NUM2}_$DATE.dump

#判断备份是否成功,若成功,则记录当前最新的版本号到临时文件中,用于下次增量备份使用
if [ $? -eq 0 ];
then
echo $NUM2 > $svn_file
else
echo "backup fail...."
continue
fi

#清理过期备份
cd $svn_bak_dir
num=`expr $keep_log_num + 1`
ls -t | tail -n +$num | xargs rm -rf
done

添加定时任务

1
2
3
4
# 每天3点增量备份
0 3 * * * sh /var/svn/dumpincr.sh >> /var/svndumpBak/incr_dump/incr.log 2>&1
#每周5 3点全量备份
0 3 * * 5 sh /var/svn/dumpfull.sh >> /var/svndumpBak/full_dump/full.log 2>&1

遇到的一些问题

svn could not begin a transaction svn 提交文件报错
在这里插入图片描述
原因是在svn服务器上创建仓库时使用了root身份登录服务器, 导致文件没有足够的写权限, 需更改仓库的组权限
chown -R apache:apache /var/svn

[Error output could not be translated from the native locale to UTF-8.] hooks里面有中文报错
在这里插入图片描述

经过查询是1.8之后才会解决的一个bug,可以升级到1.8以上的版本测试下下面的方法
vim /etc/httpd/conf/httpd.conf

1
2
3
4
5
<IfModule mod_dav_fs.c>
# Location of the WebDAV lock database.
DAVLockDB /var/lib/dav/lockdb
SVNUseUTF8 On
</IfModule>

重启httpd

参考

svn 从windows迁移到linux 记录

介绍

NetworkManager 是一个为系统提供检测和配置功能以便自动连接到网络的程序。包含一个守护程序、一个命令行界面(nmcli)和一个基于 curses 的界面(nmtui)。

安装命令

1
2
3
4
# ubuntu
apt-get install NetworkManager
# centos
yum install NetworkManager

服务启动

1
2
3
4
# 查看服务状态
systemctl status network-manager.service
# 启动服务
systemctl start network-manager.service

nmcli命令行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# 查看所有网络连接
# 长格式
nmcli connection show

# 短格式,缩写
nmcli con show

# 查看活动的网络连接
nmcli con show -active

# 查看指定网卡的详细信息
nmcli con show eth0

# 显示设备的连接状态
nmcli status

# 显示所有设备网络设备详情信息
nmcli device show

# 显示指定网络设备的详细信息
nmcli device show eth0

# 启用网络连接
nmcli con up eth0

# 停用网络连接(可以被自动激活)
nmcli con down eth0

# 禁用网卡,防止自动被激活
nmcli device disconnect eth0

# 删除网络连接的配置文件
nmcli con delete eth0

# 重新加载网络配置文件
nmcli con reload

# 设置网卡自启动,实际修改的是网卡配置文件 ONBOOT=yes
nmcli con mod CON-NAME connection.autoconnect yes

# 修改IP地址是静态(manual)还是dhcp动态(auto)
# 实际修改的是网卡配置文件BOOTPROTO,BOOTPROTO=none 表示静态;BOOTPROTO=dhcp 表示动态
nmcli con mod CON-NAME ipv4.method manual(static) | auto

# 修改IP配置及网关
nmcli con mod CON-NAME ipv4.addresses “10.10.10.10/24 10.10.10.1

# 修改默认网关
nmcli con mod CON-NAME ipv4.gateway 10.10.10.1

# 添加第二个IP地址
nmcli con mod CON-NAME +ipv4.addresses 10.10.10.10/16

# 删除第二个IP地址
nmcli con mod CON-NAME -ipv4.addresses 10.10.10.10/16

# 添加dns1
nmcli con mod CON-NAME ipv4.dns 114.114.114.114

# 添加dns2
nmcli con mod CON-NAME +ipv4.dns 8.8.8.8

# 一次添加多个dns
nmcli con mod CON-NAME ipv4.dns 114.114.114.114,8.8.8.8,8.8.4.4

nmtui图形化界面

ip

//npm
npm install ip
// npm readme
https://www.npmjs.com/package/ip
// github
git clone https://github.com/indutny/node-ip.git

1
2
3
4
5
let ip = require('ip');
ip.address() // my ip address
ip.isPrivate('127.0.0.1') // Check whether an IP is within a private IP address range.
ip.isV4Format('127.0.0.1'); // Check whether an IP is a IPv4 address.
ip.isV6Format('::ffff:127.0.0.1'); // Check whether an IP is a IPv6 address.

address

//npm
npm install address
// npm readme
https://www.npmjs.com/package/address
// github
git clone https://github.com/node-modules/address

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
let address = require('address');
address.ip(); // 192.168.0.125
address.ipv6(); // 192.168.0.125
// 获取默认mac地址
address.mac(function (err, addr) {
console.log(addr); // '78:ca:39:b0:e6:7d'
});

// 指定网卡获取mac地址
address.mac('enp1s0', function (err, addr) {
console.log(addr); // '0a:00:27:00:00:00'
});

// dns是通过读取/etc/resolv.conf获取的,建议用nmcli获取
address.dns((err, addrs) => {
console.log(addrs);
// ['10.13.2.1', '10.13.2.6']
});

address((err, addrs) => {
console.log(addrs.ip, addrs.ipv6, addrs.mac);
// '192.168.0.2', 'fe80::7aca:39ff:feb0:e67d', '78:ca:39:b0:e6:7d'
});

address('enp1s0', (err, addrs) => {
console.log(addrs.ip, addrs.ipv6, addrs.mac);
// '192.168.56.1', null, '0a:00:27:00:00:00'
});

address.interface('IPv4', 'eth1');
// { address: '192.168.1.1', family: 'IPv4', mac: '78:ca:39:b0:e6:7d' }

netmask

//npm
npm install netmask
// npm readme
https://www.npmjs.com/package/netmask
// github
git clone https://github.com/rs/node-netmask

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var Netmask = require('netmask').Netmask
var block = new Netmask('10.0.0.0/12');
block.base; // 10.0.0.0
block.mask; // 255.240.0.0
block.bitmask; // 12
block.hostmask; // 0.15.255.255
block.broadcast; // 10.15.255.255
block.size; // 1048576
block.first; // 10.0.0.1
block.last; // 10.15.255.254

block.contains('10.0.8.10'); // true
block.contains('10.8.0.10'); // true
block.contains('192.168.1.20'); // false

block.forEach(function(ip, long, index));

block.next() // Netmask('10.16.0.0/12')

ipaddr.js

//npm
npm install ipaddr.js
// npm readme
https://www.npmjs.com/package/ipaddr.js
// github
git clone https://github.com/whitequark/ipaddr.js

1
2
3
4
5
6
7
8
9
10
// 可以用来校验和格式化
let ipaddr = require('ipaddr.js');

// 可以获取掩码的bitmask 也可以校验掩码格式
ipaddr.IPv4.parse('255.255.255.240').prefixLengthFromSubnetMask() == 28
ipaddr.IPv4.parse('255.192.164.0').prefixLengthFromSubnetMask() == null

// bitmask转掩码
ipaddr.IPv4.subnetMaskFromPrefixLength(24) == '255.255.255.0'
ipaddr.IPv4.subnetMaskFromPrefixLength(29) == '255.255.255.248'

引入net模块

net 模块提供了异步的网络 API,用于创建基于流的 TCP 或 IPC 服务器 (net.createServer()) 和客户端 (net.createConnection())。

1
let net = require('net');

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
let net = require('net');
let server = net.createServer(socket => {
socket.on('data', data =>{
console.log('server recv data:', data);
})
socket.on('close', function(hadError) {
console.log(`sock close hadError: "${hadError}"`);
})
socket.on('end', function() {
console.log('sock end');
})
socket.on('error', function(err) {
console.log('sock err:', err);
})
// 设置最大连接数
server.maxConnections = 100;
// 获取连接数
server.getConnections((err, count) => {
console.log('server count:', count);
})
});

server.listen(10033, () => {
console.log("server is running on 10033");
})
server.on('error', function(err) {
console.log("server err:", err);
})
server.on('close', () => {
console.log("server closed")
})

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let net = require('net');
let client = net.Socket();
const host = '127.0.0.1';
client.connect(10033, host, () => {
console.log('connect the server');
client.write('send first data');
});

client.on('data', data => {
console.log('client data:', data);
})

client.on('end', () => {
console.log('client end');
})

client.on('error', (err) => {
console.log(`client error: ${err}`);
});

逻辑门

在实现加法机前需要先了解逻辑门
最基础的3个逻辑门:与(AND)、或(OR)、非 (NOT,也可以叫反相器)
真值表
真值表.png

逻辑门的画法
逻辑门.png

异或(XOR)的实现
异或门可以用基本的逻辑门来实现,非门可以用圆圈加在其它门的输入或者输出位置直接取反。
异或门.png

简化:
逻辑门1.png

半加器

半加器电路是指对两个输入数据位相加,输出一个结果位和进位,没有进位输入的加法器电路。 是实现两个一位二进制数的加法运算电路。

半加器真值表
半加器真值表.png

也就是进位与和位分别输出
和位:二进制的和位规则和异或门一致,也就是相同为0,不同为1
进位:进位规则则是两个输入都为真(1)就为1,其余为0,这和与门一致

半加器简略图.png
半加器.png

加法机

1
2
3
4
  1111
+ 1111
——————————
11110

以上面的4位2进制数为例。直接看第二位的结果,为1 + 1 的和位 再加上第一位的进位 1,所以结果为1,
因此第一位可以看成 1 + 1 的和位加上进位 0.

全加器.png
全加器1.png

参考资料

四位计算机的原理及其实现
编码的奥秘11、12章

原码

原码(true form)是一种计算机中对数字的二进制定点表示方法。原码表示法在数值前面增加了一位符号位(即最高位为符号位):正数该位为0,负数该位为1(0有两种表示:+0和-0),其余位表示数值的大小。
例如,我们用8位二进制表示一个数,+11的原码为00001011,-11的原码就是10001011
一个 byte 有 8bit,最大值是 0 1 1 1 1 1 1 1 (+127),最小值是 1 1 1 1 1 1 1 1 (-127)

反码

正数的反码是其本身(等于原码),负数的反码是符号位保持不变,其余位取反。 反码的存在是为了正确计算负数,因为原码不能用于计算负数.

计算负数

1
2
3
4
5
6
7
8
9
-8 - 1 = -9
10001000
转为反码
11110111
-00000001
——————————
11110110
转为反码
10001001 刚好就是-9

也就是说整个计算过程为 ((-8)反码 - (1)原码)反码

跨零计算
负数跨零进行计算的话,计算结果不对

1
2
3
4
5
6
7
8
9
10
-8 + 9 = 1
10001000
转为反码
11110111
+00001001
100000000
去掉超过8位的位
00000000
转为反码
11111111

可以看到明显不对,这个时候就需要补码了

补码

正数的补码是其本身,负数的补码等于其反码 +1

跨零计算

1
2
3
4
5
6
7
8
9
10
-8 + 9 = 1
10001000
转为反码
11110111
转为补码
11111000
+00001001
100000001
去掉超过8位的位
00000001

可以看到此时的值就是对的
总结下此时的计算过程 (-8)补 + (9)原

模的概念

看了上面的一些概念,肯定有人会疑问,为什么负数的补码是反码加1,其实理解了模的概念,这些反码和补码就很清楚了。

摘录自补码的百度百科

“模”是指一个计量系统的计数范围,如过去计量粮食用的斗、时钟等。计算机也可以看成一个计量机器,因为计算机的字长是定长的,即存储和处理的位数是有限的,因此它也有一个计量范围,即都存在一个“模”。如:时钟的计量范围是0~11,模=12。表示n位的计算机计量范围是,模=.“模”实质上是计量器产生“溢出”的量,它的值在计量器上表示不出来,计量器上只能表示出模的余数。任何有模的计量器,均可化减法为加法运算。
就是取反后加1。
假设当前时针指向8点,而准确时间是6点,调整时间可有以下两种拨法:一种是倒拨2小时,即8-2=6;另一种是顺拨10小时,8+10=12+6=6,即8-2=8+10=8+12-2(mod 12).在12为模的系统里,加10和减2效果是一样的,因此凡是减2运算,都可以用加10来代替。若用一般公式可表示为:a-b=a-b+mod=a+mod-b。对“模”而言,2和10互为补数。实际上,以12为模的系统中,11和1,8和4,9和3,7和5,6和6都有这个特性,共同的特点是两者相加等于模。对于计算机,其概念和方法完全一样。n位计算机,设n=8,所能表示的最大数是11111111,若再加1成100000000(9位),但因只有8位,最高位1自然丢失(相当于丢失一个模)。又回到了 00000000,所以8位二进制系统的模为2的8次方。在这样的系统中减法问题也可以化成加法问题,只需把减数用相应的补数表示就可以了。把补数用到计算机对数的处理上,就是补码。

回到上面 -8 + 9 = 1这个例子,用模的来理解(8位2进制数的模为128)就可以转换为 9 - 8 = 9 + 120 = 129
129 转为2进制表示即为 100000001,因为最多显示128,只有8位,所以最高的第9位去掉,即为00000001 = 1。

命名历史

1
2
3
4
5
6
7
8
9
10
在计算机还没有出现之前,有一种叫做电传打字机(Teletype Model 33)的玩意,每秒钟可以打10个字符。
但是它有一个问题,就是打完一行换行的时候,要用去0.2秒,正好可以打两个字符。
要是在这0.2秒里面,又有新的字符传过来,那么这个字符将丢失。
于是,研制人员想了个办法解决这个问题,就是在每行后面加两个表示结束的字符。一个叫做"回车",告诉打字机把打印头定位在左边界;另一个叫做"换行",告诉打字机把纸向下移一行。
这就是"换行"和"回车"的来历,从它们的英语名字上也可以看出一二。

"回车"(carriage return)和"换行"(line feed) 是来源机械英文打字机
"车"指的是纸车,带着纸一起左右移动的模块.
当开始打第一个字之前,要把纸车拉到最右边,上紧弹簧.随着打字,弹簧把纸车拉回去.每当打完一行后,纸车就完全收回去了.所以叫回车.
换行的概念就是:打字机左边有个"把手",往下扳动一下,纸会上移一行.

不同系统中的差异

1
2
3
4
5
6
7
后来,计算机发明了,这两个概念也就被般到了计算机上。
那时,存储器很贵,一些科学家认为在每行结尾加两个字符太浪费了,加一个就可以。
于是,就出现了分歧。

Unix系统: \n
Windows: \r\n
Mac: \r

引用文章

ChatGPT的回答

我:Why were carriage returns and line feeds created?

ChatGPT:Carriage returns and line feeds were created as a means of controlling how text was displayed on early computer systems and printers.

In the early days of computing, text was typically printed on teletype machines, which used a combination of mechanical and electrical systems to print characters on paper. These machines used a roll of paper that was fed through the machine, with characters being printed on the paper one at a time.

To control the position of the printing head, teletype machines used two separate control codes: a carriage return (CR) and a line feed (LF).

A carriage return (CR) code instructs the machine to move the printing head all the way to the left margin of the paper. This was necessary because the printing head would typically be positioned at the right side of the paper after printing a line of text, so a carriage return was necessary to reset the position of the head.

A line feed (LF) code instructs the machine to move the paper up by one line, so that the next line of text can be printed. This was necessary because the paper on a teletype machine was fed vertically through the machine, so moving the paper up by one line was necessary to print on the next line.

Today, carriage returns and line feeds are still used in some contexts, such as in programming languages or when working with text files in certain operating systems. However, the way they are used has evolved over time and varies depending on the specific context.

我:Why is the carriage return or line feed at the end of the text different in different operating systems?

ChatGPT:The reason why carriage return or line feed at the end of text is different in different operating systems is rooted in historical differences in how these operating systems handled text and line endings.

In Unix and Unix-like operating systems, such as Linux and macOS, a line feed (LF) character is used to mark the end of a line of text. This convention dates back to the early days of Unix, when it was developed as a text-based operating system. In Unix, files are stored as a sequence of lines, with each line terminated by a single LF character. This convention is also used in programming languages that were developed on Unix, such as C and its derivatives.

In contrast, in early versions of Microsoft Windows and MS-DOS, a carriage return (CR) followed by a line feed (LF) sequence is used to mark the end of a line of text. This convention goes back to the days of typewriters and teletype machines, which used both the CR and LF codes to signal the end of a line. When Microsoft developed its early operating systems, it inherited this convention.

The difference in line endings between Unix-like systems and Windows can cause issues when transferring files between systems. For example, if a text file created on a Windows machine is opened on a Unix system, the presence of the CR character may cause the lines to appear incorrectly. Similarly, if a text file created on a Unix system is opened on a Windows machine, the lack of the CR character may cause the lines to appear incorrectly.

To avoid these issues, many text editors and programming tools now have the ability to automatically convert line endings between Unix and Windows formats, or to handle both formats correctly.

我:Why is the end of the mac operating system a carriage return?

ChatGPT:Historically, Mac OS (now known as macOS) used a carriage return (CR) character to mark the end of a line of text. This convention dates back to the early days of the Macintosh computer, which was first introduced in 1984.

The Macintosh was designed to be a user-friendly computer that was accessible to non-technical users, and part of this design philosophy was reflected in the way it handled text. The original Macintosh operating system, known as System 1, used a simple, intuitive interface for editing text, which involved using the return key to create new lines of text.

To achieve this behavior, the Macintosh operating system used a single CR character to mark the end of each line of text. This was a departure from the convention used in other operating systems of the time, such as Unix and MS-DOS, which used a combination of a CR and a line feed (LF) character to mark the end of a line.

The use of a single CR character on the Macintosh made it easier for users to create and edit text, since they could simply press the return key to start a new line. However, this convention also made it difficult to exchange text files with other operating systems, which may expect a different line ending convention.

Today, macOS still uses a single CR character as the default line ending convention for text files created on the system. However, most modern text editors and programming tools are able to handle multiple line ending conventions, including the use of both CR and LF characters, to ensure compatibility with other operating systems.

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

1
Date对象是 JavaScript 原生的时间库。它以国际标准时间(UTC)1970年1月1日00:00:00作为时间的零点,可以表示的时间范围是前后各1亿天(单位为毫秒)

普通函数用法

1
2
3
4
5
6
7
// 返回一个代表当前时间的字符串
console.log(Date()); // Wed Feb 09 2022 15:38:52 GMT+0800 (中国标准时间)
// 即使带了参数也是返回当前时间, 参数个数没有影响
console.log(Date(2000, 1, 1)); // Wed Feb 09 2022 15:40:11 GMT+0800 (中国标准时间)
console.log(Date(2000));
console.log(Date(2000, 1));
console.log(Date(2000, 1, 1, 1, 1, 1, 1));

构造函数用法

1
2
3
4
5
6
7
8
9
10
11
// 获取当前的UTC时间
let now = new Date(); // 2022-02-09T07:35:37.335Z
// 时间戳转为字符串格式
let now = new Date(0); // 1970-01-01T00:00:00.000Z
let now = new Date(1644394635720); // 2022-02-09T08:17:15.720Z
// 时间戳为负数则代表1970之前的时间
let now = new Date(-1644394635720); // 1917-11-22T15:42:44.280Z
// 当前时间转化为时间戳(单位:ms)
let now1 = new Date().getTime(); // 1644394635720
// 指定字符串格式的时间转换为时间戳
let now2 = new Date('2022-02-09T07:35:37.335Z').getTime(); // 1644392137335

只要能被Date.parse()解析就可以当成字符串传入new Date()

1
2
3
4
5
6
7
8
9
10
11
// 2022-02-08T16:00:00.000Z
console.log(new Date('2022-2-9'));
console.log(new Date('2022/2/9'));
console.log(new Date('2/9/2022'));
console.log(new Date('2022-FEB-9'));
console.log(new Date('FEB, 9, 2022'));
console.log(new Date('FEB 9, 2022'));
console.log(new Date('February, 9, 2022'));
console.log(new Date('February 9, 2022'));
console.log(new Date('9 Feb 2022'));
console.log(new Date('9, February, 2022'));

new Date()多个参数

new Date(year, month, date, hours, minutes, seconds, ms)
使用当前时区中的给定组件创建日期。只有前两个参数是必须的
也就是说年和月是必须的,最少两个参数,否则被识别为时间戳了。
$ new Date(2022) // 1970-01-01T00:00:02.022Z
$ new Date(2022, 2) // 2022-02-28T16:00:00.000Z
year 必须是四位数:2013 是合法的,98 是不合法的。
month 计数从 0(一月)开始,到 11(十二月)结束。
date 是当月的具体某一天,如果缺失,则为默认值 1。
如果 hours/minutes/seconds/ms 缺失,则均为默认值 0。

静态方法

Date.parse()

Date.parse()方法用来解析日期字符串,返回该时间距离时间零点(1970年1月1日 00:00:00)的毫秒数.
日期字符串应该符合 RFC 2822 和 ISO 8061 这两个标准,即YYYY-MM-DDTHH:mm:ss.sssZ格式,其中最后的Z表示时区。

Date.now()

Date.now方法返回当前时间距离时间零点(1970年1月1日 00:00:00 UTC)的毫秒数,相当于 Unix 时间戳乘以1000。
$ console.log(Date.now()); // 1644399418450

Date.UTC()

Date.UTC方法接受年、月、日等变量作为参数,返回该时间距离时间零点(1970年1月1日 00:00:00 UTC)的毫秒数。
// 格式
Date.UTC(year, month[, date[, hrs[, min[, sec[, ms]]]]])
// 用法
Date.UTC(2011, 0, 1, 2, 3, 4, 567)
// 1293847384567