flutter中使用Intl完成日期格式化和数字格式化

5,530 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

pub地址:pub.dev/packages/in…

Github地址:github.com/dart-lang/i…

主要使用的类是DateFormat、NumberFormat。

在使用任何格式化的方法之前,都应加载环境内容。

import 'package:intl/date_symbol_data_local.dart';
initializeDateFormatting();

为了统一演示结果,我们给一个默认的环境。

//设置默认的环境为英文
setState(
  () {
    //手动切换中文
    S.load(const Locale("en"));
  },
);

数字格式化

要格式化数字,需要先创建NumberFormat实例,并设置格式化样式。

比较常用的就是“#”和“0”,#表示可选的输出(不补零),0表示必须输出(没有则补零)。

在使用“#”和“0”的时候,要注意进位的问题,Intl使用的是银行家算法进位,特别是在一些位数要求比较严格的场合上,没控制好使用,可能造成很多不必要的麻烦。大家仔细看控制台输出的内容。

NumberFormat nf = NumberFormat("#,###.##");
print("1234567890.123 => #,###.## = ${nf.format(1234567890.123)}");
//?使用00就可以保留指定的小数位
nf = NumberFormat("#,###.00");
print("#,###.00 = ${nf.format(1234567890.09876)}");
nf = NumberFormat("#,###.000%");
print("#,###.000% = ${nf.format(0.09876)}");
nf = NumberFormat("%#,###.000");
print("%#,###.000 = ${nf.format(0.09876)}");
nf = NumberFormat("#,###.000‰");
print("#,###.000‰ = ${nf.format(0.09876)}");
//? 只能显示货币的名称,不能显示符号。
nf = NumberFormat("¤#,###.000");
print("¤#,###.000 = ${nf.format(09876)}");
nf = NumberFormat("¤#,###.000", "ZH");
print("¤#,###.000 = ${nf.format(09876)}");

输出内容

flutter: 1234567890.123 => #,###.## = 1,234,567,890.12
flutter: #,###.00 = 1,234,567,890.10
flutter: #,###.000% = 9.876%
flutter: %#,###.000 = %9.876
flutter: #,###.000‰ = 98.760‰
flutter: ¤#,###.000 = USD9,876.000
flutter: ¤#,###.000 = CNY9,876.000

目前已知的限制是:

1、货币格式只会印上货币名称,而不支持货币符号.

2、科学格式并不真正符合科学记数法。

3、数字解析尚未实现。

进位采用的是银行家舍入法

print("===============进位使用银行家舍入法================");
//! 银行家舍入法进位
//!四舍六入,五考虑,五后有值进位,五后没值看前位,奇数舍去,偶数进位。
nf = NumberFormat("#.#");
print("1.14,四舍${nf.format(1.14)}");
print("1.16,六入${nf.format(1.16)}");
print("1.151,五后有值进位)${nf.format(1.151)}");
print("1.15,五后没值看前位,奇数舍去${nf.format(1.15)}");
print("1.25,五后没值看前位,偶数进位${nf.format(1.25)}");

输出内容

flutter: ===============进位使用银行家算法================
flutter: 1.14,四舍1.1
flutter: 1.16,六入1.2
flutter: 1.151,五后有值进位)1.2
flutter: 1.15,五后没值看前位,奇数舍去1.1
flutter: 1.25,五后没值看前位,偶数进位1.3

日期格式化

要格式化日期时间,先创建一个DateFormat实例。然后设置需要的格式化符号。

//打印当天的年份
print("当天年份(y):${DateFormat.y().format(now)}");
//控制台输出: 当天年份(y):2022

//打印当天的月份
print("当天月份(M):${DateFormat.M().format(now)}");
//控制台输出: 当天月份(M):6

//打印当天的日期
print("当天日期(d):${DateFormat.d().format(now)}");
//控制台输出: 当天日期(d):9

//打印当天的星期
print("当天星期(E):${DateFormat.E().format(now)}");
//控制台输出: 当天星期(E):Thu

//打印当天的时间
print("当天时间(Hms):${DateFormat.Hms().format(now)}");
//控制台输出: 当天时间(Hms):21:44:41

print("y():${DateFormat.y().format(now)}");
//控制台输出: y():2022

print("yM():${DateFormat.yM().format(now)}");
//控制台输出: yM():6/2022

print("YMEd():${DateFormat.yMEd().format(now)}");
//控制台输出: YMEd():Thu, 6/9/2022

print("yMMM():${DateFormat.yMMM().format(now)}");
//控制台输出: yMMM():Jun 2022

print("yMMMEd():${DateFormat.yMMMEd().format(now)}");
//控制台输出: yMMMEd():Thu, Jun 9, 2022

print("yMMMM():${DateFormat.yMMMM().format(now)}");
//控制台输出: yMMMM():June 2022

print("yMMMMEEEEd():${DateFormat.yMMMMEEEEd().format(now)}");
//控制台输出: yMMMMEEEEd():Thursday, June 9, 2022

print("yMMMMd():${DateFormat.yMMMMd().format(now)}");
//控制台输出: yMMMMd():June 9, 2022

print("yMd():${DateFormat.yMd().format(now)}");
//控制台输出: yMd():6/9/2022

print(DateFormat.jm().format(now));
//控制台输出: 9:44 PM

print(DateFormat.j().format(now));
//控制台输出: 9 PM

print(DateFormat.jms().format(now));
//控制台输出: 9:44:41 PM

print(DateFormat.Hm().format(now));
//控制台输出: 21:44

可以组合使用,例如

print("yM():${DateFormat.yM().add_Hm().format(now)}");
//控制台输出: yM():6/2022 21:44

当然是你还可以设置语言:

//打印当天的年份
print("当天年份(y):${DateFormat.y("zh").format(now)}");
//控制台输出: 当天年份(y):2022年

//打印当天的月份
print("当天月份(M):${DateFormat.M("zh").format(now)}");
//控制台输出: 当天月份(M):6月

//打印当天的日期
print("当天日期(d):${DateFormat.d("zh").format(now)}");
//控制台输出: 当天日期(d):9日

//打印当天的星期
print("当天星期(E):${DateFormat.E("zh").format(now)}");
//控制台输出: 当天星期(E):周四

//打印当天的时间
print("当天时间(Hms):${DateFormat.Hms("zh").format(now)}");
//控制台输出: 当天时间(Hms):21:44:41

print("y():${DateFormat.y("zh").format(now)}");
//控制台输出: y():2022年

print("yM():${DateFormat.yM("zh").format(now)}");
//控制台输出: yM():2022年6月

print("YMEd():${DateFormat.yMEd("zh").format(now)}");
//控制台输出: YMEd():2022/6/9周四

print("yMMM():${DateFormat.yMMM("zh").format(now)}");
//控制台输出: yMMM():2022年6月

print("yMMMEd():${DateFormat.yMMMEd("zh").format(now)}");
//控制台输出: yMMMEd():2022年6月9日周四

print("yMMMM():${DateFormat.yMMMM("zh").format(now)}");
//控制台输出: yMMMM():2022年6月

print("yMMMMEEEEd():${DateFormat.yMMMMEEEEd("zh").format(now)}");
//控制台输出: yMMMMEEEEd():2022年6月9日星期四

print("yMMMMd():${DateFormat.yMMMMd("zh").format(now)}");
//控制台输出: yMMMMd():2022年6月9日

print("yMd():${DateFormat.yMd("zh").format(now)}");
//控制台输出: yMd():2022/6/9

print(DateFormat.jm("zh").format(now));
//控制台输出: 下午9:44

print(DateFormat.j("zh").format(now));
//控制台输出: 下午9时

print(DateFormat.jms("zh").format(now));
//控制台输出: 下午9:44:41

print(DateFormat.Hm("zh").format(now));
//控制台输出: 21:44

print("yM():${DateFormat.yM("zh").add_Hm().format(now)}");
//控制台输出: yM():2022年6月 21:44

最后,展示我最喜欢的方式,应该是java代码写多了,我还是觉得这么写最舒服。

print("yyyy():${DateFormat("yyyy").format(now)}");
//控制台输出: yyyy():2022

print("yyyy-MM():${DateFormat("yyyy-MM").format(now)}");
//控制台输出: yyyy-MM():2022-06

print("yyyy-MM-dd():${DateFormat("yyyy-MM-dd").format(now)}");
//控制台输出: yyyy-MM-dd():2022-06-09

print(
  "yyyy-MM-dd HH:mm:ss():${DateFormat("yyyy-MM-dd HH:mm:ss").format(now)}");
//控制台输出: yyyy-MM-dd HH:mm:ss():2022-06-09 21:44:41
  
print(DateFormat("一年中的第D天,一年中的第Q个季度").format(now));
//控制台输出: 一年中的第160天,一年中的第2个季度

我在使用时候发现,intl对日期的处理不符合我的要求,其实我期望的是一个数字的星期“1”,但是他就是不能给我输出这个数字“1”。还是要使用Dart的日期处理能力来解决,这个使用确实不舒服。

我理解是Intl对ICU中的“w”以及“W”符号还没有支持的原因吧,也许对这两个符号支持后就好了。

print("E:${DateFormat("E").format(now)}");
//控制台输出: E:Thu

print("E(zh):${DateFormat("E", "zh").format(now)}");
//控制台输出: E(zh):周四

print("DateTime.now().weekday:${DateTime.now().weekday}");
//控制台输出: DateTime.now().weekday:4

\

加载环境的3种方式

官方推荐在执行任何格式化方法之前,都需要加载一个本地环境。提供了3中加载的方式,个人觉得啊从本地环境加载最方便呢,剩下的两种扩展性会更好,但是这个国际化真的需要这么高的扩展性吗?可能做产品的和我这种写代码的想法不一样吧。

  • 从本地环境中加载,这个方法最方便。在方法的定义上能看到local和ignored是可以为空的参数,实际上这里传任何值都没有意义,intl会忽律这两个参数,因为所有语言环境的数据都是直接可用的。
import 'package:intl/date_symbol_data_local.dart';
//方法的定义,local和ignored都可以为空,所以直接调用放就可以了,
//Future<void> initializeDateFormatting([String? locale, String? ignored])
  
initializeDateFormatting();
  • 如果你自己有写好的环境文件,也可以是加载本地文件。
import 'package:intl/date_symbol_data_file.dart';
Future<void> initializeDateFormatting(String locale, String filePath)
  • 最后一种是加载网络上的环境文件。
import 'package:intl/date_symbol_data_http_request.dart';
Future<void> initializeDateFormatting(String locale, String url)

已知限制: 

1、时区尚不支持。Dart DateTime对象没有时区,因此是本地或UTC。

2、格式化和解析持续时间尚未实现。

\

附件内容:

数字格式化支持的符号

/// - `0` 一个位数,没有值用0补齐
/// - `#` 一个位数, 值为0时忽律。
/// - `.` 十进制分隔符
/// - `-` 减号
/// - `,` 分组符号
/// - `E` 将尾数和指数分开
/// - `+` - 在指数之前,要说它应该加上加号。
/// - `%` - 作为前缀或者后缀使用,数值会乘以100,变成百分数。
/// - `‰ (\u2030)` 作为前缀或者后缀使用,数值会乘以100,变成百分数。
/// - `¤ (\u00A4)` 货币符号,替换为货币名称
/// - `'` 用于引用特殊字符
/// - `;` 用于分离正反模式 (如果两者都存在)

银行家舍入法

日期格式化支持的符号-ICU格式列表

测试发现目前的Intl还没有实现全部的ICU/JDK符号,(这个链接是ICU所有的格式列表,有兴趣的了解一下。)下边这个表格,是目前Intl已经实现的ICU符号,够不够用的看大家的业务吧。

ICU Name                   Skeleton
 --------                   --------
 DAY                          d
 ABBR_WEEKDAY                 E
 WEEKDAY                      EEEE
 ABBR_STANDALONE_MONTH        LLL
 STANDALONE_MONTH             LLLL
 NUM_MONTH                    M
 NUM_MONTH_DAY                Md
 NUM_MONTH_WEEKDAY_DAY        MEd
 ABBR_MONTH                   MMM
 ABBR_MONTH_DAY               MMMd
 ABBR_MONTH_WEEKDAY_DAY       MMMEd
 MONTH                        MMMM
 MONTH_DAY                    MMMMd
 MONTH_WEEKDAY_DAY            MMMMEEEEd
 ABBR_QUARTER                 QQQ
 QUARTER                      QQQQ
 YEAR                         y
 YEAR_NUM_MONTH               yM
 YEAR_NUM_MONTH_DAY           yMd
 YEAR_NUM_MONTH_WEEKDAY_DAY   yMEd
 YEAR_ABBR_MONTH              yMMM
 YEAR_ABBR_MONTH_DAY          yMMMd
 YEAR_ABBR_MONTH_WEEKDAY_DAY  yMMMEd
 YEAR_MONTH                   yMMMM
 YEAR_MONTH_DAY               yMMMMd
 YEAR_MONTH_WEEKDAY_DAY       yMMMMEEEEd
 YEAR_ABBR_QUARTER            yQQQ
 YEAR_QUARTER                 yQQQQ
 HOUR24                       H
 HOUR24_MINUTE                Hm
 HOUR24_MINUTE_SECOND         Hms
 HOUR                         j
 HOUR_MINUTE                  jm
 HOUR_MINUTE_SECOND           jms
 HOUR_MINUTE_GENERIC_TZ       jmv   (not yet implemented)
 HOUR_MINUTE_TZ               jmz   (not yet implemented)
 HOUR_GENERIC_TZ              jv    (not yet implemented)
 HOUR_TZ                      jz    (not yet implemented)
 MINUTE                       m
 MINUTE_SECOND                ms
 SECOND                       s

源码地址:gitee.com/radium/flut…

视频地址:space.bilibili.com/1159595523