Yoko.Tool 2.6.0-rc.3

This is a prerelease version of Yoko.Tool.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Yoko.Tool --version 2.6.0-rc.3                
NuGet\Install-Package Yoko.Tool -Version 2.6.0-rc.3                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Yoko.Tool" Version="2.6.0-rc.3" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Yoko.Tool --version 2.6.0-rc.3                
#r "nuget: Yoko.Tool, 2.6.0-rc.3"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Yoko.Tool as a Cake Addin
#addin nuget:?package=Yoko.Tool&version=2.6.0-rc.3&prerelease

// Install Yoko.Tool as a Cake Tool
#tool nuget:?package=Yoko.Tool&version=2.6.0-rc.3&prerelease                

[TOC]

# Yoko.Tool

懒人必备工具箱

NuGet Badge

更新日志

推荐使用 Yoko.Tool.Core

2.6.0-rc.3

【修复】修复方法 GetComplementPeriods 出现多余的值问题

2.6.0-rc.2

【修改】

  • 判断时间段是否与一组时间段相交/包含,并返回所有相交/包含的时间段 IntersectWith

【新增】

  • 获取所有的补集时间段( 默认过滤差值为1min ) GetComplementPeriods
var range = new DateTimeRange(new DateTime(2021, 9, 3, 7, 8, 10)
              , new DateTime(2021, 9, 4, 18, 0, 0));
Console.WriteLine($"时段:{range}");
Console.WriteLine();
Console.WriteLine();
var ranges = new List<DateTimeRange>()
{
    new DateTimeRange(new DateTime(2021, 9, 3, 10, 0, 0), new DateTime(2021, 9, 3, 15, 0, 0)),
    new DateTimeRange(new DateTime(2021, 9, 3, 20, 0, 0), new DateTime(2021, 9, 3, 23, 59, 59)),
    new DateTimeRange(new DateTime(2021, 9, 4, 0, 0, 0), new DateTime(2021, 9, 4, 8, 0, 0)),
    new DateTimeRange(new DateTime(2021, 9, 4, 12, 0, 0), new DateTime(2021, 9, 4, 18, 30, 59)),
    new DateTimeRange(new DateTime(2021, 9, 10, 0, 0, 0), new DateTime(2021, 9, 10, 23, 59, 59))
};

var chargingPeriods = range.GetComplementPeriods(ranges, TimeSpan.FromMinutes(1));

foreach (var chargingPeriod in chargingPeriods)
{
    Console.WriteLine($"补集时段:{chargingPeriod}");
}

2.6.0-rc.1

【修改】切换至 netstandard2.0;netstandard2.1 框架

【新增】判断时间段是否与一组时间段相交,并返回所有相交的时间段 IntersectWith

var range = new DateTimeRange(new DateTime(2021, 9, 3, 10, 5, 5)
              , new DateTime(2021, 9, 4, 18, 12, 35));
var ranges = new List<DateTimeRange>()
{
    new DateTimeRange(new DateTime(2021, 9, 3, 0, 0, 0), new DateTime(2021, 9, 3, 23, 59, 59)),
    new DateTimeRange(new DateTime(2021, 9, 4, 0, 0, 0), new DateTime(2021, 9, 4, 15, 0, 0)),
    new DateTimeRange(new DateTime(2021, 9, 4, 15, 0, 0), new DateTime(2021, 9, 4, 23, 59, 59)),
    new DateTimeRange(new DateTime(2021, 9, 8, 0, 0, 0), new DateTime(2021, 9, 8, 23, 59, 59)),
    new DateTimeRange(new DateTime(2021, 9, 10, 0, 0, 0), new DateTime(2021, 9, 10, 23, 59, 59))
};

var intersections = range.IntersectWith(ranges);

foreach (var intersection in intersections)
{
    Console.WriteLine($"相交的时间段:{intersection.range}");
}

【新增】枚举操作:typeof(MyEnum).GetDescriptionItems 获取枚举值和描述的字典映射

枚举值必须携带 [Description("自定义")] [注意] 暂不支持根据枚举成员获取Display的属性Name

【优化】使用 AppVersion.Current 时,它将返回入口程序一个 不包含 元数据的版本号

【优化】使用 AppVersions.Current 时,它将返回入口程序一个 包含 元数据的版本号

【新增】递归获取指定路径下的所有文件路径 GetFilePath

【新增】将包含逗号分隔的字符串转换为 long 类型的数组:SplitCsvToLong

2.5.4

【新增】获取程序版本号 AppVersion.Current

2.5.3

Yoko.Tool.Core 移植功能

【新增】查询系统资源信息 (适合Windows和大部分的Linux发行版)

  • SystemInfo.GetLocalIPAddress(); // 获取本机当前正在使用的IP地址
  • SystemInfo.GetAllMacAddresses(); //获取本机所有MAC地址
  • SystemInfo.GetOSVersion(); //获取操作系统版本
  • SystemInfo.GetDriveSpaceInfo(); //获取所有硬盘的可用空间和总空间。单位:GB
  • SystemInfo.GetMemorySpaceInfo(); //获取内存的可用空间和总空间。单位:GB

2.5.2

  • 【优化】优化部分方法的异常提示及返回值

2.5.1

  • 【优化】优化部分网络图片转Stream异常问题

2.5.0

  • 【新增】URL 操作

    1、分解URL信息

    string url = "https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png?authorization=bce-auth-v1%2Fe8a94e73dad54d3a9";
    
    // 分解URL信息  UrlSplit() 
    var data = url.UrlSplit();
    
    // 协议
    data.Scheme;
    // 服务器地址
    data.Host;
    // 端口号
    data.Port;
    // 绝对路径
    data.AbsolutePath;
    // 本地路径
    data.LocalPath;
    // 路径的结构数组
    data.Segments;
    // 查询信息部分
    data.Query;
    // 问号(?) 分割后的部分 
    data.PathAndQuery;
    

    json解读:

    {
        "Scheme": "https",
        "Host": "www.baidu.com",
        "Port": 443,
        "AbsolutePath": "/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png",
        "LocalPath": "/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png",
        "Segments": [
            "/",
            "img/",
            "PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png"
        ],
        "Query": "?authorization=bce-auth-v1%2Fe8a94e73dad54d3a9",
        "PathAndQuery": "/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png?authorization=bce-auth-v1%2Fe8a94e73dad54d3a9"
    }
    

    2、获取一个网络文件(图片)的文件名

    string url = "https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png?authorization=bce-auth-v1%2Fe8a94e73dad54d3a95bb502a395696f3%2F2022-09-05T01";
    
    //获取一个网络图片的文件名
    var data = url.GetFileName();
    // 输出 PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png
    
    // 并更改文件扩展名
    var data2 = url.GetFileName(".jpg");
    // 输出 PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.jpg
    
    

2.4.4

  • 【新增】ASCII排序严格模式与非严格模式

2.4.3

  • 【新增】日期时间的扩展

    //返回标准时间格式(yyyy-MM-dd HH:mm:ss)
    DateTime.Now.GetDateTime();
    // 获取该时间相对于1970-01-01 00:00:00的秒数
    DateTime.Now.GetTotalSeconds(); 
    // 获取该时间相对于1970-01-01 00:00:00的毫秒数
    DateTime.Now.GetTotalMilliseconds(); 
    

2.4.2

  • 【新增】DataTable 转 List、转 Model

2.4.1

  • 【更新】修复OrderByASCII排序问题

2.4.0

  • 【新增】恢复4.7.2、4.8 支持

  • 【优化】缩减包体积,优化性能。移除对2.1.0的不停机更新支持,后续可能会单独发布包,如需使用,请保持2.1.0

2.2.0

  • 【新增】枚举操作

2.1.1

  • 【新增】将集合参数ASCII码从小到大排序 dict.OrderByASCII();
  • 【新增】对集合进行分组ChunkList
  • 【新增】求一个包含经纬度集合的距离和 YokoLngLat.GetDistanceSum(dict);

2.1.0

  • 【新增】不停机更新。通过上传 zip 文件,更新部署到正在IIS上运行的 Asp.Net Core 应用程序

2.0.0

  • 优化适配部分Net6特性方法

2.0之后不再支持 .NET Framework

1.6.2.8

  • 新增支持对集合进行分组拆分为多个
  • 优化ImageToBase64对部分格式图片不支持的问题、并更改调用方式

1.6.2.7

  • 新增邮件发送
  • Base64编码与图片互转
  • 生成缩略图 (图像压缩请使用Yoko.Images.Webp)

💎 一般常用操作拓展

// 值类型转换
.ToInt32()
.ToBool()
.ToLong()
.ToDouble()
.ToDecimal()
.ToFloat()
    
// 时间操作
.ToDateTime()  // 字符串 转 时间 
.ToStartTime() // DateTime转换成开始时间00:00:00
.ToEndTime()   // DateTime转换成结束时间23:59:59
.ToUnixTime()  // DateTime时间转Unix时间戳(默认精确到毫秒13位)
.ToUnixTimeS() // DateTime时间转Unix时间戳(默认精确到秒10位)
.ToLocalTime() // Unix时间戳转DateTime本地时间(yyyy/MM/dd hh:MM:ss)
.DateDiff()    // 返回时间差(N小时前,N分钟前···)
.GetDiffTime() // 计算2个时间差(相差多少年月日时分秒)
.GetDaysOfMonth()      // 本月有多少天
.GetWeekNameOfDay()    // 返回当前日期的星期名称
DateTime.Now.GetDateTime();    //返回标准时间格式(yyyy-MM-dd HH:mm:ss)
DateTime.Now.GetDateTimeF();    //返回标准时间格式(yyyy-MM-dd HH:mm:ss:fffffff)
DateTime.Now.GetTotalSeconds(); // 获取该时间相对于1970-01-01 00:00:00的秒数
DateTime.Now.GetTotalMilliseconds(); // 获取该时间相对于1970-01-01 00:00:00的毫秒数
DateTime.Now.GetTotalMicroseconds(); // 获取该时间相对于1970-01-01 00:00:00的微秒数

//字符串操作
.SplitCsv()      // 分割逗号的字符串为List[]、string[]
.SplitCsvToInt() // 分割逗号的字符串为int[]   
.ToIntList()     // string数组转int数组
.ToFilterStr()   // 过滤特殊字符[]^-_*×――(^)|'$%~!@#$…&%¥—+=<>《》!!???::•`·、。,;,.;\"‘’“”-
.ToByteArray()   // string转换成字节数组
.ToJson()        // 将一个对象转为Json格式字符串
.ToDynamic()     // 将Json转为一个动态类型(运行时解析)
.ToMask()        // 字符串敏感信息掩码  (123***789)
.ToMaskEmail()   // 邮箱敏感信息掩码  (123****@163.com)

//值计算
//除法计算 a/b
//type=1 浮点数,保留2位小数(四舍五入):12.34
//type=2 百分比:12.23%
//type=3 带有逗号分隔符,保留小数位数(四舍五入):2,500,000.00
//type=4 货币表示,保留2位小数(四舍五入):¥12.34
DividedBy()     

//集合操作
// 指定要拆分成几个集合,集合中子集个数动态分配。如果原集合中子集的个数小于等于要分组数,既source.Count <= size,建议使用source.Chunk(1).ToList()
source.ChunkList(3);  //对集合进行分组

//将集合参数ASCII码从小到大排序(默认严格模式)
dict.OrderByASCII();

//DataTable 操作
DataTableHelper<T>.ToList(dt);
DataTableHelper<T>.ToModel(dt);

//URL 操作
UrlSplit();   // 分解URL信息
GetFileName(); // 获取网络文件(图片)的文件名

💎 值判断

// 在范围?  
/**
int num = 50;
if (num.IsInRange(100, 1000)) { }
string value = "a";
if (value.IsIn("a", "b", "c")) { }
*/
IsInRange() 
IsInRange() 
IsIn()
IsContainsIn()

IsNullOrEmpty()   // 是null或"" ? 
IsValuable()      // 有值?(与IsNullOrEmpty相反)
IsZero()          // 是零?
IsInt()           // 是Int?
IsNoInt()         // 不是Int?
IsMoney()         // 是金钱? 
IsDate()          // 是时间?
IsDate()          // 是邮箱?
IsMobile()        // 是手机号?(弱验证)
IsPhoneNumber()   // 是手机号?(强验证)
IsTelephone()     // 是座机?  
IsIDcard()        // 是身份证?
IsFax()           // 是传真? 
IsMatch()         // 是适合正则匹配? 
IsTrue()          // 是true?
IsFalse()         // 是false?

💎 随机数

// 按照年月时分秒随机数生成的文件名
YokoRandom.GetFileRndName() 

// 生成随机数字  
YokoRandom.GetFormatedNumeric(1, 100)

// 生成随机汉字  
YokoRandom.GetRandChinese(6)

// 根据指定的样式生成:"?"代表一个字符,"#"代表一个一位数字,"*"代表一个字符串或一个一位数字
YokoRandom.GetRandStringByPattern(@"##??*")

// 获取指定长度的纯字母随机字符串  
YokoRandom.GetRandWord(10)

// 获取指定长度的随机字符串
YokoRandom.GetRandomString(10)

💎 一般密码加解密/验证

// MD5 加密、加盐 
"123456".ToMD5()
"123456".ToMD5("abc123")

// 两次加密、加盐  
"123456".ToMD5Double()
"123456".ToMD5Double("abc123")
// AES加密、解密(密钥)  
"123456".ToAES()
"123456".ToAESDecrypt()
// DES加密、解密(密钥)  
"123456".ToDES()
"123456".ToDESDecrypt()
// RSA加密、解密
RsaKey rsaKey = RsaCrypt.GenerateRsaKeys(); //生成RSA密钥对
string encrypt = "123456".RSAEncrypt(rsaKey.PublicKey); //公钥加密
string s = encrypt.RSADecrypt(rsaKey.PrivateKey);//私钥解密
// 用于生成一般账号的加密密码  
// 默认MD5+哈希。生成密码组成: 标识头 + 迭代 + 哈希大小 + salt值 + 哈希值
// 设置标识头Key:YokoPassWord.Key = "yoko";
// 值表现为 : yoko:64000:18:salt值:哈希值 
"123456".ToPassWord("要MD5加密?")

// 验证密码 
 "目标比对密码".IsTrue("输入的密码")

💎 经纬度操作

//根据经纬度计算两点间距离
YokoLngLat.GetDistance(x1,y1,x2,y2)
    
//求一个包含经纬度集合的距离和
YokoLngLat.GetDistanceSum(dict);

//坐标系互转
YokoLngLat.GPSToAMap()   // GPS转高德(WGS84 --> GCJ-02)
YokoLngLat.AMapToGPS()   // 高德转GPS(GCJ-02 --> WGS84)
YokoLngLat.AMapToBMap()  // 高德转百度(GCJ-02 --> BD-09)
YokoLngLat.BMapToAMap()  // 百度转高德(BD-09 --> GCJ-02)

💎 文件/图像操作

// 解压rar文件
YokoFile.UnRar("rar源文件目录地址",'解压到哪?')

//简单的图片操作
YokoFile.UrlToImage()       // 通过Url获取到Image格式的文件
//YokoFile.ImageToBase64()    // 根据http/https图片路径获取图片并转换为base64格式字符串 
"WebImgUrl".ImageToBase64()    // 把weburl图片转换为base64格式字符串 [1.6.2.8]
"base64编码".Base64ToImage()   // 将Base64编码转换成图片

//生成缩略图
Image image = Image.FromFile(@"D:\Demo001.jpg");
image.ImageToThumbnail(@"D:\Demo001_1.jpg", 80, 50, CutMode.LockWidth);

    
// 图片绘制
// 在图片上绘制文字
YokoFile.DrawStringWord(new DrawStyle
{
	SourcePath = sourcePath,       		// 底图画布的路径
	TargetPath = targetPath,      		// 保存图像的目标路径
	TextContent = "Hello \n WoCao",		// 文字内容
	FamilyName = "宋体",				   // 字体
	FontSize = 15,						// 文字大小
	Color = Color.White,				// 颜色 
	X = 100,							// 距左上角的 x 坐标
	Y = 100,							// 距左上角的 y 坐标
	Width = 100,						// 限定文本内容的宽
	Height = 100						// 限定文本内容的高
});

// 递归获取指定路径下的所有文件路径
//var path = "D:\\Demo";
//foreach (var item in path.GetFilePath())
//{
//    Console.WriteLine(item);
//}

🔍 图片文件操作 Yoko.Images.Webp 从静态路径或者网络路径转换、调整大小和压缩 jpeg、png 和 webp 图像

💎 时间段判断

🔍 判断是否在某个时间段内、是否包含某个时间段、两个时间段是否相交、连接两个时间段

var range = new DateTimeRange("2021-9-3", "2021-9-5".ToDateTime());

//连接两个时间段,结果:2021-9-3~2021-9-6
range.Union("2021-9-4".ToDateTime(), "2021-9-6".ToDateTime()); 

//判断是否在某个时间段内,true
range.In("2021-9-3".ToDateTime(), "2021-9-6".ToDateTime());

//两个时间段是否相交,(true,2021-9-3~2021-9-4)
var (intersected,range2) = range.Intersect("2021-9-4".ToDateTime(), "2021-9-6".ToDateTime());

//判断是否包含某个时间段,true
range.Contains("2021-9-3".ToDateTime(), "2021-9-4".ToDateTime());


💎 邮件发送

// 异步发送邮件 SendEmailAsync  
// 同步发送 SendEmail
new Email()
{
    SmtpServer = "smtp.qq.com",// SMTP服务器
    SmtpPort = 587, // SMTP服务器端口
    EnableSsl = true,//使用SSL
    Username = "88888888888@qq.com",// 邮箱用户名
    Password = "123456789",// 邮箱密码
    Tos = "111111111@qq.com,22222222@qq.com", //多个收件人用,隔开
    Subject = "测试邮件",//邮件标题
    Body = "Hello,WoCao",//邮件内容
}.SendEmailAsync(s =>
{
    // 发送成功后的回调
    // 异步发送邮件,如果回调方法中参数不为"true"则表示发送失败
    Console.WriteLine(s);
});

💎 雪花ID优化版

  • 雪花算法实现方法有很多,它生成的ID更短、速度更快

  • 可在单机或分布式环境生成数字型唯一ID,新增预留位,支持服务器时间回拨

  • 兼容传统的经典雪花算法

🔍 调用示例

1、全局初始化,只需全局一次(必须在第2步之前设置)

//初始化  1.0.4+
YokoId.Init(1);
    
//初始化
YokoId.SetIdGenerator(new IdGeneratorOptions(1));

//WorkerId  [1,63]

2、生成ID

var newId = YokoId.NextId();

🔍 参数设置

  • WorkerId,机器码,最重要参数,无默认值,必须 全局唯一,必须 程序设定,最大值63 特别提示:如果一台服务器部署多个独立服务,需要为每个服务指定不同的 WorkerId。
  • WorkerIdBitLength,机器码位长,决定 WorkerId 的最大值,默认值6,取值范围 [1, 19]

💎 自动更新 IIS 上的 Asp.Net Core应用程序 2.1.1 ~ 2.2.0

  • 通过上传 zip 文件,更新部署到正在IIS上运行的 Asp.Net Core 应用程序

注意事项:

1、将要发布更新的应用程序,全部 压缩打包成 publish.zip 文件

2、zip 部署待更新文件应与站点位于同一位置。例如,web.config 应该直接在 zip 文件内,而不是在 zip 文件内的文件夹内

3、上传尽量使用临时文件名,直到上传完成,上传完成后再重命名为 publish.zip

4、上传完publish.zip后,会自动解压更新程序,并产生对应的临时文件。此时刷新应用程序,测试程序调用是否正常,临时文件会陆续自动删除

打开Startup.cs

using Yoko.Tool.ZipDeploy;

public void ConfigureServices(IServiceCollection services)
{
     services.AddZipDeploy(c =>
     {
         //指定要忽略的文件或路径(例:"DebugLog.txt"、"Data"、"Log\Today")
         c.IgnorePathStarting("Log");
         c.IgnorePathStarting("Data");
     });
    ······
 }

💎 枚举操作

// 获取枚举对象Key与显示名称的字典
var dict = typeof(枚举类).GetDictionary();
// 获取枚举值对应的字符串
var enumStr = 0.ToEnumString(typeof(枚举类));
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
3.0.0-beta.1 75 4/22/2024
2.6.0-rc.3 103 12/14/2023 2.6.0-rc.3 is deprecated because it is no longer maintained.
2.6.0-rc.2 93 12/14/2023 2.6.0-rc.2 is deprecated because it is no longer maintained.
2.6.0-rc.1 102 12/14/2023 2.6.0-rc.1 is deprecated because it is no longer maintained.
2.5.4 191 5/22/2023
2.5.3 277 2/24/2023
2.5.2 443 9/7/2022
2.5.1 388 9/7/2022
2.5.0 403 9/5/2022
2.4.4 417 8/5/2022
2.4.3 425 8/2/2022
2.4.2 417 7/27/2022
2.4.1 407 7/25/2022
2.4.0 448 7/11/2022
2.2.0 446 6/20/2022
2.1.1 419 6/14/2022
2.1.0 446 5/7/2022
2.0.0 427 4/29/2022
1.6.3 449 6/14/2022
1.6.2.8 459 4/29/2022
1.6.2.7 478 4/21/2022
1.6.2.6 445 3/17/2022
1.6.2.4 328 12/17/2021
1.6.2.3 321 12/13/2021
1.6.2.2 303 12/2/2021
1.6.2.1 368 11/15/2021
1.6.2 352 11/2/2021
1.6.1.1 382 10/25/2021
1.6.1 349 10/22/2021
1.6.0.1 368 9/24/2021
1.6.0 350 9/17/2021
1.5.8.1 358 9/15/2021
1.5.7 547 9/10/2021
1.5.6 399 9/10/2021
1.5.5 376 9/9/2021
1.5.4 372 9/5/2021
1.5.3 353 9/3/2021
1.5.2 341 9/3/2021
1.5.1 353 9/3/2021
1.5.0 343 9/2/2021
1.4.3 359 9/2/2021
1.4.2 334 9/2/2021
1.4.1 326 9/2/2021
1.4.0 363 8/24/2021
1.3.0 355 8/24/2021