日常のプログラミングにおける日付関連のコードとバグ

この記事では主にJavaでよく使われる関数コードを日付と時刻に分けていくつかまとめていますので、日々のコーディングに役立てていただければ幸いです。

1. うるう年の計算

「4 年に 1 回実行し、100 年間は実行せず、400 年後に再度実行する」という言葉を思い出していただければ、どの言語に変更しても、実装コードを間違って書くことはないと思います。
どうやって理解しますか?私たちのプログラミング言語に変換すると、「

  • が 4 の倍数であり、100 の倍数ではない場合は、通常のうるう年になります。
  • が 400 の倍数であれば、1 世紀閏年になります

    /**
     * 是否是闰年
     * @param y
     * @return
     */
    public static boolean isLeapYear(int y) {
    
    
        if (y % 4 == 0 && y % 100 != 0 || y % 200 == 0) {
    
    
            return true;
        } else {
    
    
            return false;
        }
    }

2. SimpleDateFormat スレッドのセキュリティの問題

SimpleDateFormat は Java の時刻処理でよく使われる関数で、CS でも直接使われることが多く、タイムスタンプを現在フォーマットされた時刻として処理します。ただし、SimpleDateFormat、Date、およびその他の関数はシステムの単なる機能関数であり、スレッド同期関数がないため、マルチスレッド環境で SimpleDateFormat を共有することはできません。そうしないと、同じタイムスタンプが使用されてしまいます。分析 問題は出てくる時間が違うことです。
SimpleDateFormat のフォーマット ソース コードを見ると、確かに同期に関連する処理ロジックが存在しません。

   public abstract StringBuffer format(Date date, StringBuffer toAppendTo,
                                        FieldPosition fieldPosition);

    /**
     * Formats a Date into a date/time string.
     * @param date the time value to be formatted into a time string.
     * @return the formatted time string.
     */
    public final String format(Date date)
    {
    
    
        return format(date, new StringBuffer(),
                      DontCareFieldPosition.INSTANCE).toString();
    }

3.タイマーコール

3.1 カウントダウンタイマー

タイマーは Java でよく使用され、CountDownTimer は間違いなく頻繁に使用されます。

 CountDownTimer countDownTimer = new CountDownTimer(6000, 1000) {
    
    
            @Override
            public void onTick(long millisUntilFinished) {
    
    
                //每隔1s回调
            }

            @Override
            public void onFinish() {
    
    
                //6s倒计时完成回调
            }
        };

もちろん、Android の場合は、ハンドラー、Rxjava など、さらに多くの API フレームワークから選択できます。
ハンドラーの遅延実行

 new Handler().postDelayed(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                // 6s后执行的代码
            }
        }, 6000);

ただし、ここで注意する必要があります。Android で CountDownTimer を使用してカウントダウン関連のニーズを実現すると、秒が飛ぶという問題が発生します。
その理由は、ハンドラー postDealy には、メッセージ処理の最初のジャンプの問題があるためです (handler.postDealyed(..., 1000) メソッドを使用して 1 秒あたりのタイミングを測定する場合、正確ではありません。大きなエラーです。エラーの理由は、メッセージを受信して​​から handler.postDealyed を再発行するまでの時間が瞬間的ではないためです。ロジックの処理時間が多く含まれています。ロジックの処理時間がなくても、ハンドラー自体がパフォーマンスを消費するため、理想的な 1000 遅延に従ってメッセージを送信できず、エラーが蓄積されます。これを解決するにはどうすればよいですか?

  • 一方で、CountDownTimer を自分でカプセル化することでこのエラーを回避できます。
  • 一方で、Rxjava などの他のサードパーティ フレームワークを利用して実現することもできます。
package com.itbird.design.builder.dialog;

import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;

/**
 * 使用android.os.CountDownTimer的源码
 * 添加了onPause、onRestart自定义方法
 * Created by xfkang on 16/3/18.
 */

public abstract class CustomCountDownTimer {
    
    
    private static final int MSG = 1;
    /**
     * 总倒计时时间
     * Millis since epoch when alarm should stop.
     */
    private final long mMillisInFuture;
    /**
     * 倒计时间隔时间
     * The interval in millis that the user receives callbacks
     */
    private final long mCountdownInterval;
    /**
     * 记录开始之后,应该停止的时间节点
     */
    private long mStopTimeInFuture;
    /**
     * 记录暂停的时间节点
     */
    private long mPauseTimeInFuture;
    /**
     * 对应于源码中的cancle,即计时停止时
     * boolean representing if the timer was cancelled
     */
    private boolean isStop = false;
    private boolean isPause = false;

    /**
     * @param millisInFuture    总倒计时时间
     * @param countDownInterval 倒计时间隔时间
     */
    public CustomCountDownTimer(long millisInFuture, long countDownInterval) {
    
    
        // 解决秒数有时会一开始就减去了2秒问题(如10秒总数的,刚开始就8999,然后没有不会显示9秒,直接到8秒)
        if (countDownInterval > 1000) {
    
    
            millisInFuture += 15;
        }
        mMillisInFuture = millisInFuture;
        mCountdownInterval = countDownInterval;
    }

    private synchronized CustomCountDownTimer start(long millisInFuture) {
    
    
        isStop = false;
        if (millisInFuture <= 0) {
    
    
            onFinish();
            return this;
        }
        mStopTimeInFuture = SystemClock.elapsedRealtime() + millisInFuture;
        mHandler.sendMessage(mHandler.obtainMessage(MSG));
        return this;
    }

    /**
     * 开始倒计时
     */
    public synchronized final void start() {
    
    
        start(mMillisInFuture);
    }

    /**
     * 停止倒计时
     */
    public synchronized final void stop() {
    
    
        isStop = true;
        mHandler.removeMessages(MSG);
    }

    /**
     * 暂时倒计时
     * 调用{@link #restart()}方法重新开始
     */
    public synchronized final void pause() {
    
    
        if (isStop) return;

        isPause = true;
        mPauseTimeInFuture = mStopTimeInFuture - SystemClock.elapsedRealtime();
        mHandler.removeMessages(MSG);
    }

    /**
     * 重新开始
     */
    public synchronized final void restart() {
    
    
        if (isStop || !isPause) return;

        isPause = false;
        start(mPauseTimeInFuture);
    }

    /**
     * 倒计时间隔回调
     *
     * @param millisUntilFinished 剩余毫秒数
     */
    public abstract void onTick(long millisUntilFinished);

    /**
     * 倒计时结束回调
     */
    public abstract void onFinish();


    private Handler mHandler = new Handler() {
    
    

        @Override
        public void handleMessage(Message msg) {
    
    

            synchronized (CustomCountDownTimer.this) {
    
    
                if (isStop || isPause) {
    
    
                    return;
                }

                final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
                if (millisLeft <= 0) {
    
    
                    onFinish();
                } else if (millisLeft < mCountdownInterval) {
    
    
                    // no tick, just delay until done
                    sendMessageDelayed(obtainMessage(MSG), millisLeft);
                } else {
    
    
                    long lastTickStart = SystemClock.elapsedRealtime();
                    onTick(millisLeft);

                    // take into account user's onTick taking time to execute
                    long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();

                    // special case: user's onTick took more than interval to
                    // complete, skip to next interval
                    while (delay < 0) delay += mCountdownInterval;

                    sendMessageDelayed(obtainMessage(MSG), delay);
                }
            }
        }
    };
}


3.2 Rxjava.interval

Rxjava.間隔

  //每隔10s,触发一下accept
Observable.interval(10, TimeUnit.SECONDS)
        .subscribe(new Consumer<Long>() {
    
    
            @Override
            public void accept(Long aLong) throws Exception {
    
    
                Log.d(TAG + "interval", String.valueOf(aLong));//从0开始输出
            }
        });

これはタイマーと同等であり、CountDownTimer を置き換えることができます。設定された間隔時間に従って毎回イベントが送信され、イベント シーケンスが送信されます。デフォルトは 0 から始まり、整数のシーケンスは無限に増加します。
では、Rxjava.interval の実装原理は何でしょうか? 実際、このソース コードについては、以前の R xJava シリーズの記事で説明されているので、ここでは繰り返しません。興味のある方は、段階的に確認してみてください。
つまり、スレッド プールの ScheduledExecutorService を使用してタスクを定期的に実行します。

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/baobei0921/article/details/129204144