Erlang时间戳转换时发现的问题

昨天在任务里自己手写了个时间戳转换函数,期间发现根据时间戳转换过来的时间怎么都对不上当前时间,试了很久发现了问题和解决方案。

其实解决方案很简单,开门见山直接说,就是用世界时间转本地时间判断时分秒的时候转化就差了8个小时,发现是北京时间的时差,手动加上calendar:universal_time_to_local_time(DataTime).即可。虽然就一两行就解决问题,但那不是目的,这里顺便复习一下Erlang里日期、时间处理和时间戳相互转换的问题。

获取当前时间

Erlang里获取当前时间有很多方式,比如什么erlang:now(),os:timestamp()等,

这些都是从GMT零点开始到当前的时间,返回的是从1970-1-1 00:00:00到当前时间元组 {MegaSecs, Secs, MicroSecs}。

3> erlang:now().
{
    
    1616,730132,577025}
4> erlang:now().
{
    
    1616,730856,937025}
5> os:timestamp().
{
    
    1616,730864,175000}
6> os:timestamp().
{
    
    1616,730871,917000}
7>

10位时间戳的得根据这个自己计算

timestamp() ->
    {
    
    M, S, _} = os:timestamp(),
    M * 1000000 + S.

运行

1> test:timestamp().
1616984171
2> 

时间戳太高级了,水平还没上去平时用的少 还是喜欢年月日时分秒那种

获取世界时间

% 本地时间
local_time() ->
    calendar:local_time().

% 世界时间
world_time() ->
    calendar:universal_time().

运行

1> calendar:local_time().
{
    
    {
    
    2021,3,29},{
    
    9,47,51}}
2> calendar:universal_time().
{
    
    {
    
    2021,3,29},{
    
    1,47,59}}
3> erlang:timestamp().
{
    
    1616,983999,958998}
4> os:timestamp().
{
    
    1616,984114,372000}
5> 

可以发现世界标准时间和本地时间差了8小时哦(废话,北京在东八区 +8h),

5> {
    
    {
    
    Year, Month, Day}, {
    
    Hour, Minite, Second}} = calendar:local_time().
{
    
    {
    
    2021,3,29},{
    
    10,25,47}}
6> Year.
2021
7> Hour.
10

咱做好匹配,要什么取什么 不香嘛

其他功能函数

计算时分秒的秒数

8> calendar:time_to_seconds({
    
    10,25,47}).
37547
9> calendar:time_to_seconds({
    
    23,59,59}).
86399
10> calendar:time_to_seconds({
    
    0,1,0}).
60

时间戳(10位)和年月日时分秒的标准转化

% 时间转时间戳,格式:{<!-- -->{2021,3,29},{10,25,47}}
datetime_to_timestamp(DateTime) ->
    calendar:datetime_to_gregorian_seconds(DateTime) -
        calendar:datetime_to_gregorian_seconds({
    
    {
    
    1970,1,1}, {
    
    0,0,0}}).


% 时间戳转时间
timestamp_to_datetime(Timestamp) ->
    calendar:gregorian_seconds_to_datetime(Timestamp +
        calendar:datetime_to_gregorian_seconds({
    
    {
    
    1970,1,1}, {
    
    0,0,0}})).

实验下

1> test:datetime_to_timestamp({
    
    {
    
    2021,3,29},{
    
    10,25,47}}).
1617013547
2> test:timestamp_to_datetime(1617013547).
{
    
    {
    
    2021,3,29},{
    
    10,25,47}}
3> test:timestamp_to_datetime(test:timestamp()).
{
    
    {
    
    2021,3,29},{
    
    2,38,33}}

简单好用,可以发现用标准时间跟实际有时差,如果有时区问题记得加一行这个

时区改进版

% 时间转时间戳,格式:{<!-- -->{2021,3,29},{10,25,47}}
datetime_to_timestamp(DateTime) ->
    calendar:datetime_to_gregorian_seconds(DateTime) -
        calendar:datetime_to_gregorian_seconds({
    
    {
    
    1970,1,1}, {
    
    0,0,0}}).


% 时间戳转时间
timestamp_to_datetime(Timestamp) ->
    DT = calendar:gregorian_seconds_to_datetime(Timestamp +
        calendar:datetime_to_gregorian_seconds({
    
    {
    
    1970,1,1}, {
    
    0,0,0}})),
    calendar:universal_time_to_local_time(DT).

再实验

1> test:timestamp_to_datetime(test:timestamp()).
{
    
    {
    
    2021,3,29},{
    
    10,40,9}}
2> 

秒转时分秒时间

6> calendar:seconds_to_daystime(86000).
{
    
    0,{
    
    23,53,20}}
7> calendar:seconds_to_daystime(86400).
{
    
    1,{
    
    0,0,0}}
8> calendar:seconds_to_daystime(1086400).
{
    
    12,{
    
    13,46,40}}
9> calendar:seconds_to_daystime(2086400).
{
    
    24,{
    
    3,33,20}}
10> calendar:seconds_to_daystime(3086400).
{
    
    35,{
    
    17,20,0}}

calendar接口里还有很多有趣的函数,都可以玩一下

检测时间是否有效

13> calendar:valid_date(2021, 3, 31).
true
14> calendar:valid_date(2021, 3, 32).
false

计算两个时间的差值

15> calendar:time_difference({
    
    {
    
    2021,3,28},{
    
    10,25,47}}, {
    
    {
    
    2021,3,29},{
    
    10,25,47}}).
{
    
    1,{
    
    0,0,0}}

计算今天星期几

16> calendar:day_of_the_week({
    
    2021,3,28}).
7

自定义API

检测时间有效(带时分秒)

我们发现calendar库里只能检测年月日,我们可以加一个时分秒组合检测

is_valid(time, {
    
    H, I, S}) ->
    H >= 0 andalso H < 24 andalso I >= 0 andalso I < 60 andalso S >= 0 andalso S < 60;
is_valid(datetime, {
    
    Date, Time}) ->
    calendat:valid_date(Date) andalso is_valid(time, Time);
is_valid(_, _) ->
    false.

获取当天0点

%%我们先获取当前时间戳
get_0() ->
    {
    
    M, S, MS} = ?MODULE:now(),
    %%拿到当前时间戳,如:{1617,67916,239852}
    {
    
    _, Time} = calendar:now_to_local_time({
    
    M, S, MS}),
    %%拿出时分秒,如:{_, Time} = {
    
    {2021,3,30},{9,31,56}}
    %%计算当天时分秒过去了多少秒
    M * 1000000 + S - calendar:time_to_seconds(Time).
%%即可得到当天0点的时间戳

获取指定时间那天的0点

比如给一个时间戳是2020/12/12 12:12:12的,求那天0点的时间戳

get_0(Time) ->
    %%先获取今天0点的时间戳
    Today_0 = get_0(),
    case Time > Today_0 of
        true ->
            %%如果求得是未来的时间,就直接先减,看差的秒数,整除86400,
            %%表示不满一天不算,大于一天小于两天算一天,没毛病吧,再乘一天的秒数
            %%得到相差的天数秒(86400 * n),再加上今天0点的就可以啦
            (Time - Today_0) div 86400 * 86400 + Today_0;
        false ->
            %%用今天的0点时间戳减去你求的那个时间戳,然后除以一天的秒数86400
            %%得到一个小数天数向上取整,比如你求的昨天的时间,用今天零点做减法得到零点几天
            %%向上取整就是一天,就是1 * 86400,再用今天0点时间戳减去86400就是前一天的0点时间戳啦
            Today - ceil((Today - Time) / 86400) * 86400
            %%下面放上ceil的实现
    end.


拓展ceil、floor

关于这两个函数名字的我的理解是,ceil(天花板函数,找上面那个整数),floor(地板函数,找下面的那个整数),不知道你们怎么理解的哈哈哈

%%我们知道erlang工具理里有一个trunc函数,功能是砍掉小数位,比如5.64464 -> 5,
%%可以借此实现ceil函数,这个其实很多语言都实现了,ceil floor 等,可以一并写出


%%求大于N的最小整数
ceil(N) ->
    M = erlang:trunc(N),
    case N > M of
        true -> M + 1;
        _ -> M
    end.
%%为什么会有砍掉小数还会比原来的数大的情况呢?你考虑负数没有咯



%%求小于N的最大整数
floor(N) ->
    M = erlang:trunc(N),
    case N < M of
        true -> M - 1;
        _ -> M
    end.
%%不解释了吧

判断两个时间是不是同一天

这里我们借助上面的0点函数来判断

is_same_day(Time1, Time2) ->
    get_0(Time1) =:= get_0(Time2).

还能水一个

判断一个时间是不是今天

看看今天的0点和这个时间的0点相不相同

is_today(Time) ->
    get_0() =:= get_0(Time)

求两个unixtime相差的天数

这个也是借助上面那个零点函数实现,首先搞清楚两天相差的天数,如果是相邻两天我们返回1,今天和昨天差一天没毛病吧

%%我们把小的天数放前面,然后大减小得到时间差,如果差秒除以一天秒(86400)小于1则返回0表示差的没有一天
%%如果大于1则用round函数,四舍五入就完了
day_diff(Time1, Time2) when Time2 > Time1 ->
    Time1_0 = get_0(Time1),
    Time2_0 = get_0(Time2),
    case (Time2_0 - Time1_0) / 86400 of
        Diff when Diff < 0 -> 0;
        Diff -> round(Diff)
    end;
day_diff(Time1, Time2) when Time1 =:= Time2 -> 0;
day_diff(TIme1, Time2) -> day_diff(Time2, Time1).

当前时间距离未来某时间相差秒数

拿到当前时间的时分秒,把相差的天数N*一天的秒数加上需要做差那天的时分秒的秒数再减去当前时间时分秒的秒数就好啦(可能有点绕,看不懂看代码就ok啦)

sss_diff(N, {
    
    H, I, S}) when is_integer(N), N >= 0 ->
    {
    
    _, Time} = ?MODULE:now(),
    N * 86400 + calendar:time_to_seconds({
    
    H, I, S}) - calendar:time_to_seconds(Time).

猜你喜欢

转载自blog.csdn.net/weixin_43876186/article/details/115320191