6

ライブラリ/モジュールなしでエポック秒(NTPエポック1900-01-01 00:00以降)から日時文字列(MM / DD / YY、hh:mm:ss)に変換するための最良の方法を見つけようとしています/組み込みデバイスでは使用できないため、外部機能。

私が最初に考えたのは、Pythonのdatetimeモジュールのソースコードを調べることでしたが、それは私にはあまり役に立ちませんでした。

Pythonでの最初の試みでは、0001-01-01から現在までの日数をC ++ソースgetDateFromJulianDayからPythonに適合させて変換し、モジュロ演算と組み合わせて時間を取得します。それは機能しますが、より良い方法はありますか?

def getDateFromJulianDay(julianDay):
    # Gregorian calendar starting from October 15, 1582
    # This algorithm is from:
    # Henry F. Fliegel and Thomas C. van Flandern. 1968.
    # Letters to the editor:
    #     a machine algorithm for processing calendar dates.
    # Commun. ACM 11, 10 (October 1968), 657-. DOI=10.1145/364096.364097
    # http://doi.acm.org/10.1145/364096.364097
    ell = julianDay + 68569;
    n = (4 * ell) / 146097;
    ell = ell - (146097 * n + 3) / 4;
    i = (4000 * (ell + 1)) / 1461001;
    ell = ell - (1461 * i) / 4 + 31;
    j = (80 * ell) / 2447;
    d = ell - (2447 * j) / 80;
    ell = j / 11;
    m = j + 2 - (12 * ell);
    y = 100 * (n - 49) + i + ell;
    return y,m,d

# NTP response (integer portion) for Monday, March 25, 2013 at 6:40:43 PM
sec_since_1900 = 3573225643

# 2415021 is the number of days between 0001-01-01 and 1900-01-01,
#     the start of the NTP epoch
(year,month,day) =  getDateFromJulianDay(2415021 + sec_since_1900/60/60/24)

seconds_into_day = sec_since_1900 % 86400
(hour, sec_past_hour) = divmod(seconds_into_day,3600)
(min, sec) = divmod(sec_past_hour,60)
print 'year:',year,'month:',month,'day:',day
print 'hour:',hour,'min:',min,'sec:',sec

なぜこれを行うのか:NTPサーバーから現在の時刻を取得し、日付、時刻、およびタイムゾーンのみを受け入れるハードウェアリアルタイムクロック(RTC)を更新するために、この時刻を額面どおりに取得しています:MM / DD / YY、hh:mm:ss、±zz。後日、真のNTP機能を実装する予定です。時間同期方法の説明は、この質問など、他の場所に残すのが最適です。

ノート:

  • 私の組み込みデバイスは、Python1.5.2+を実行するTelitGC-864セルラーモデムであり、限られた演算子(ほとんどはC演算子のみ)のみを備え、モジュールはなく、予想される組み込みPythonタイプの一部を備えています。興味があれば、正確な機能はここにあります。私はこのデバイス用にCコードを書いているかのようにPythonを書いていますが、Pythonicではありません。
  • NTPは時間オフセットにのみ使用するのが最適ですが、オプションが限られているため、絶対時間ソースとしてNTPを使用しています(2036年にNTPロールオーバーのチェックを追加して、さらに136年間の運用を可能にすることができます)。
  • 最新のファームウェアを搭載したGC-864-V2デバイスにはNTP機能がありますが、使用する必要のあるGC-864は以前のリリースのファームウェアのままです。
4

2 に答える 2

7

最初に提案された関数は、大きな変数、または元々C ++で記述された変数に対するgetDateFromJulianDay多くの乗算および除算演算を含む、組み込みデバイスで効果的に使用するには計算量が多すぎます。longlonglong

私は、組み込みデバイスの効率的なエポックトゥデートアルゴリズムを探し出したと思います。

実りのないグーグルの後、私はStack Overflowに戻り、エポックタイムを「実際の」日付/時刻に変換するという質問を見つけました。これは、自作のエポックタイムトゥデートの実装について尋ね、適切なアルゴリズムを提供します。質問に対するこの回答は、 gmtime.cソースコードを参照し、Python変換アルゴリズムを作成するために必要なCIのソースを提供します。

/*
 * gmtime - convert the calendar time into broken down time
 */
/* $Header: /opt/proj/minix/cvsroot/src/lib/ansi/gmtime.c,v 1.1.1.1 2005/04/21 14:56:05 beng Exp $ */

#include        <time.h>
#include        <limits.h>
#include        "loc_time.h"

struct tm *
gmtime(register const time_t *timer)
{
        static struct tm br_time;
        register struct tm *timep = &br_time;
        time_t time = *timer;
        register unsigned long dayclock, dayno;
        int year = EPOCH_YR;

        dayclock = (unsigned long)time % SECS_DAY;
        dayno = (unsigned long)time / SECS_DAY;

        timep->tm_sec = dayclock % 60;
        timep->tm_min = (dayclock % 3600) / 60;
        timep->tm_hour = dayclock / 3600;
        timep->tm_wday = (dayno + 4) % 7;       /* day 0 was a thursday */
        while (dayno >= YEARSIZE(year)) {
                dayno -= YEARSIZE(year);
                year++;
        }
        timep->tm_year = year - YEAR0;
        timep->tm_yday = dayno;
        timep->tm_mon = 0;
        while (dayno >= _ytab[LEAPYEAR(year)][timep->tm_mon]) {
                dayno -= _ytab[LEAPYEAR(year)][timep->tm_mon];
                timep->tm_mon++;
        }
        timep->tm_mday = dayno + 1;
        timep->tm_isdst = 0;

        return timep;
}

さらに、質問の分析なぜgmtimeがこのように実装されているのですか?gmtime関数がかなり効率的であることを確認するのに役立ちました。

raspberryginger.com minix Doxygenドキュメントサイトを使用して、 loc_time.hからgmtime.cに含まれているCマクロと定数を見つけることができました。関連するコードスニペット:

#define YEAR0           1900                    /* the first year */
#define EPOCH_YR        1970            /* EPOCH = Jan 1 1970 00:00:00 */
#define SECS_DAY        (24L * 60L * 60L)
#define LEAPYEAR(year)  (!((year) % 4) && (((year) % 100) || !((year) % 400)))
#define YEARSIZE(year)  (LEAPYEAR(year) ? 366 : 365)
#define FIRSTSUNDAY(timp)       (((timp)->tm_yday - (timp)->tm_wday + 420) % 7)
#define FIRSTDAYOF(timp)        (((timp)->tm_wday - (timp)->tm_yday + 420) % 7)
#define TIME_MAX        ULONG_MAX
#define ABB_LEN         3

extern const int _ytab[2][10];

そして、それはmisc.cextern const int _ytabで定義されました:

const int _ytab[2][12] = {
                { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
                { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
        };

私が見つけた他のいくつかのもの:

  • gmtime.cファイルリファレンスは、依存関係を見つけるのに非常に役立ちました。
  • このgmtime関数は、月、曜日、および年の日のインデックス作成を数値0(それぞれ、最大範囲は0〜11、0〜6、0〜365)で開始しますが、日は数値1から開始します。 (1-31)、IBMgmtime()リファレンスを参照してください。

gmtimePython1.5.2+の関数を書き直しました。

def is_leap_year(year):
    return ( not ((year) % 4) and ( ((year) % 100) or (not((year) % 400)) ) )

def year_size(year):
    if is_leap_year(year):
        return 366
    else:
        return 365

def ntp_time_to_date(ntp_time):
    year = 1900         # EPOCH_YR for NTP
    ytab =  [ [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
              [ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] ]

    (dayno,dayclock) = divmod(ntp_time, 86400L)
    dayno = int(dayno)

    # Calculate time of day from seconds on the day's clock.
    (hour, sec_past_hour) = divmod(dayclock,3600)
    hour = int(hour)
    (min, sec) = divmod(int(sec_past_hour),60)

    while (dayno >= year_size(year)):
        dayno = dayno - year_size(year)
        year = year + 1
    month = 1                           # NOTE: month range is (1-12)
    while (dayno >= ytab[is_leap_year(year)][month]):
        dayno = dayno - ytab[is_leap_year(year)][month]
        month = month + 1
    day = dayno + 1

    return (year, month, day, hour, min, sec)

gmtimeC++関数をPython関数にリファクタリングした変更ntp_time_to_date(ntp_time)

  • エポックを1970年のUNIXエポックから1900年のNTPエポック(NTPのプライムエポック)に変更しました。
  • わずかに合理化された時刻計算。
    • の時刻計算のgmtime比較ntp_time_to_date
      • との両方が舞台裏で発生(dayclock % 3600) / 60します。dayclock / 3600divmod(dayclock,3600)divmod(sec_past_hour,60)
      • 唯一の本当の違いは、を介して(0-86399)divmod(sec_past_hour,60)のモジュロを60で回避し、代わりに。内で(0-3599)のモジュロを60で回避することです。dayclockdayclock % 60sec_past_hourdivmod(sec_past_hour,60)
  • 曜日など、不要な変数とコードを削除しました。
  • 月のインデックスを1から開始するように変更したため、月の範囲は(0-11)ではなく(1-12)になります
  • long値が65535未満になるとすぐに キャスト変数を型変換して、コードの実行時間を大幅に短縮します。
    • 必要な長い変数は次のとおりです。
      • ntp_time、1900年からの秒数(0-4294967295)
      • dayclock、1日の秒数(0-86399)
    • 残りの変数の最大のものは、日付内の計算された年です。

Pythonntp_time_to_date関数(およびその依存関係)は、Python1.5.2+の組み込みバージョンのTelitGC-864およびPython2.7.3で正常に実行されますが、可能であれば、もちろん日時ライブラリを使用してください。

于 2013-03-26T23:28:27.757 に答える
0

TL; DR

Telit GC-864を使用している場合、Pythonインタープリターは、コード実行の各行の間にある種の遅延を挿入しているように見えます。

Telit GC-864の場合、私の質問getDateFromJulianDay(julianDay)の関数は私の答えの関数よりも高速ですntp_time_to_date(ntp_time)

より詳しく

コード行の数は、コードの複雑さよりもGC-864の実行時間を支配します-奇妙なことですが、私は知っています。私の質問の関数getDateFromJulianDay(julianDay)には、15行のコードなど、いくつかの複雑な操作があります。私の答えの関数はntp_time_to_date(ntp_time)計算がより複雑ですが、whileループによって100行以上のコードが実行されます。

  • 1つのループは1900年から現在の年までカウントされます
  • 別のループは1か月目から当月までカウントされます

試験結果

各試行に同じNTP時間入力を使用して実際のGC-864(注: GC-864-V2ではない)で実行されるタイミングテスト結果(各関数は「3/25/201318:40」を出力します)。タイミングはprintfステートメントのデバッグを使用して実行され、コンピューターのシリアル端末はGC-864によって送信された各行にタイムスタンプを付けます。

getDateFromJulianDay(julianDay)試験:

  • 0.3802秒
  • 0.3370秒
  • 0.3370秒
  • 平均:0.3514秒

ntp_time_to_date(ntp_time)試験:

  • 0.8899秒
  • 0.9072秒
  • 0.8986秒
  • 平均:0.8986秒

変動性は、セルラーネットワークタスクに定期的にサービスを提供するGC-864セルモデムに部分的に起因します。

long完全を期すために、変数をintできるだけ早く型キャストする最適化ntp_time_to_date(ntp_time)は、かなり重要な効果があります。この最適化なし:

  • 2.3155秒
  • 1.5034秒
  • 1.5293秒
  • 2.0995秒
  • 2.0909秒
  • 平均:1.9255秒

Python1.5.2+で.pyoファイルを実行しているTelitGC-864で計算に関係することを行うのは、良い考えではありません。NTP機能が組み込まれているGC-864-V2を使用することは、この問題に遭遇した人にとって可能な解決策です。さらに、新しいマシンツーマシン(M2M)、別名モノのインターネット(IoT)携帯電話モデムははるかに高性能です。

GC-864で同様の問題が発生した場合は、より新しく、より最新の携帯電話モデムの使用を検討してください。

于 2016-04-13T14:00:21.510 に答える