Рассчитать номер дня из метки времени Unix математическим способом? - PullRequest
7 голосов
/ 21 августа 2011

Как я могу вычислить номер дня из метки времени unix, математически, без использования каких-либо функций и в простой математической формуле.

1313905026 -> 8 (сегодня 21.08.2011)

Ответы [ 3 ]

7 голосов
/ 21 марта 2017

A unix timestamp не включает високосных секунд , поэтому нам не нужно об этом беспокоиться. Вот алгоритм без ветвей 1 , без петель для получения полей y / m / d из метки времени unix :

#include <iostream>

int
main()
{
    int s = 1313905026;
    int z = s / 86400 + 719468;
    int era = (z >= 0 ? z : z - 146096) / 146097;
    unsigned doe = static_cast<unsigned>(z - era * 146097);
    unsigned yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365;
    int y = static_cast<int>(yoe) + era * 400;
    unsigned doy = doe - (365*yoe + yoe/4 - yoe/100);
    unsigned mp = (5*doy + 2)/153;
    unsigned d = doy - (153*mp+2)/5 + 1;
    unsigned m = mp + (mp < 10 ? 3 : -9);
    y += (m <= 2);
    std::cout << m << '/' << d << '/' << y << '\n'; // 8/21/2011
}

Это выводит:

8/21/2011

Поскольку вас не интересуют y и m (только в d), вы можете исключить последние пару строк из приведенного выше вычисления.

Этот алгоритм описан в мучительных деталях здесь . Ссылка включает в себя полную деривацию и юнит-тесты, охватывающие миллионы лет (что является избыточным).


1 Без ветвей: то, что выглядит как маленькие ветки в вышеприведенном алгоритме, оптимизируется с помощью clang при -O3 в macOS:

__Z14get_day_numberi:                   ## @_Z14get_day_numberi
    .cfi_startproc
## BB#0:
    pushq   %rbp
Ltmp0:
    .cfi_def_cfa_offset 16
Ltmp1:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp2:
    .cfi_def_cfa_register %rbp
    movslq  %edi, %rax
    imulq   $-1037155065, %rax, %rcx ## imm = 0xFFFFFFFFC22E4507
    shrq    $32, %rcx
    addl    %ecx, %eax
    movl    %eax, %ecx
    shrl    $31, %ecx
    sarl    $16, %eax
    leal    (%rax,%rcx), %edx
    leal    719468(%rax,%rcx), %esi
    testl   %esi, %esi
    leal    573372(%rax,%rcx), %eax
    cmovnsl %esi, %eax
    cltq
    imulq   $963315389, %rax, %rcx  ## imm = 0x396B06BD
    movq    %rcx, %rsi
    shrq    $63, %rsi
    shrq    $32, %rcx
    sarl    $15, %ecx
    addl    %esi, %ecx
    imull   $146097, %ecx, %ecx     ## imm = 0x23AB1
    movl    %eax, %esi
    subl    %ecx, %esi
    subl    %eax, %esi
    leal    719468(%rsi,%rdx), %eax
    movl    %eax, %ecx
    shrl    $2, %ecx
    imulq   $1506180313, %rcx, %rdx ## imm = 0x59C67CD9
    shrq    $39, %rdx
    movl    %eax, %esi
    subl    %edx, %esi
    imulq   $963321983, %rcx, %rcx  ## imm = 0x396B207F
    shrq    $43, %rcx
    addl    %esi, %ecx
    movl    %eax, %edx
    shrl    $4, %edx
    imulq   $7525953, %rdx, %rdx    ## imm = 0x72D641
    shrq    $36, %rdx
    subl    %edx, %ecx
    imulq   $1729753953, %rcx, %rsi ## imm = 0x6719F361
    shrq    $32, %rsi
    movl    %ecx, %r8d
    subl    %ecx, %eax
    movl    %ecx, %edi
    movl    $3855821599, %edx       ## imm = 0xE5D32B1F
    imulq   %rcx, %rdx
    subl    %esi, %ecx
    shrl    %ecx
    addl    %esi, %ecx
    shrl    $8, %ecx
    imull   $365, %ecx, %ecx        ## imm = 0x16D
    subl    %ecx, %r8d
    shrl    $2, %edi
    imulq   $1506180313, %rdi, %rcx ## imm = 0x59C67CD9
    shrq    $39, %rcx
    shrq    $47, %rdx
    addl    %r8d, %eax
    subl    %ecx, %eax
    leal    (%rax,%rdx), %ecx
    leal    2(%rcx,%rcx,4), %esi
    movl    $3593175255, %edi       ## imm = 0xD62B80D7
    imulq   %rsi, %rdi
    shrq    $39, %rdi
    imull   $153, %edi, %edi
    subl    %edi, %esi
    leal    4(%rcx,%rcx,4), %ecx
    subl    %esi, %ecx
    movl    $3435973837, %esi       ## imm = 0xCCCCCCCD
    imulq   %rcx, %rsi
    shrq    $34, %rsi
    leal    1(%rax,%rdx), %eax
    subl    %esi, %eax
    popq    %rbp
    retq
    .cfi_endproc
3 голосов
/ 21 августа 2011

Нет простой формулы для этого.Вы должны были бы вычесть количество лет (учитывая високосные годы), начиная с эпохи, что, вероятно, потребовало бы цикла или дискретного вычисления некоторого вида.Затем используйте цикл определенного типа, чтобы вычесть количество секунд в каждом месяце текущего года.То, что у вас осталось, это количество секунд, которые в данный момент находятся в месяце.

Я бы сделал что-то вроде этого.

x = ...//the number of seconds
year = 1970

while (x > /*one year*/){
 x = x - /*seconds in january, and march-december*/
 if(year % 4 == 0){
  x -= /*leapeay seconds in february*/
 }else{
  x -= /*regular seconds in february*/
 }
}

//Then something like this:

if(x > /*seconds in january*/){
 x -= /*seconds in january*/
}
if(x > /*seconds in february*/){
 x -= /*seconds in january*/
}

.
.
.

//After that just get the number of days from x seconds and you're set.

Редактировать

Я рекомендую использовать функции даты для простоты, но здесь возможнозацикленный альтернативный ответ на тот случай, если кому-то это понадобится, или он захочет его развивать.

Сначала пусть t будет текущим временем в секундах с начала эпохи.

Пусть F - количество секунд в четырех годах.Это три обычных года и один високосный год.Это должно быть: 126230400.

Теперь, если вы заберете все время, выделенное F, вы получите остаток: y.

Итак, y = n% F.

В настоящее время существует несколько случаев: 1. у меньше одного года 2. у меньше двух лет 3. у меньше трех лет и меньше двух месяцев 4. у меньше трех лет и больше двух месяцев5. y меньше четырех лет

Обратите внимание, что 1972 год был високосным, поэтому, если вы посчитаете четыре на 1970 год, то, где бы вы ни остановились, будет високосный год через два года.

пусть jan, feb, febLY, mar, may, ..., dec будет количеством секунд в каждом месяце (вам нужно его вычислить).

d представляет номер дня текущегомесяц и D представляет количество секунд в дне (86400).y представляет количество секунд в обычном году, а yLY представляет количество секунд в високосном году.

y = (t % F)
if(y < Y){
 if(y > jan){
  y -= jan
 }
 if(y > feb){
  y -= feb
 }
 .
 .
 .
 d = y % D
}
else if(y < 2 * y){
 y = y - Y
 if(y > jan){
  y -= jan
 }
 if(y > feb){
  y -= feb
 }
 .
 .
 .
 d = y % D
}
else if(y < 2 * y + yLY){
 y = y - 2 * Y
 if(y > jan){
  y -= jan
 }
 if(y > febLY){
  y -= febLY
 }
 .
 .
 .
 d = y % D
}
else{
 y = y - 2 * Y - yLY
 if(y > jan){
  y -= jan
 }
 if(y > feb){
  y -= feb
 }
 .
 .
 .
 d = y % D
}

Не проверено .Кроме того, поскольку Земля не вращается ровно за 1 оборот / 24 часа, они время от времени вносили коррективы во время.Вам нужно сделать небольшой исследовательский фактор, который в.

2 голосов
/ 16 марта 2017
t = unix time
second = t MOD 60  
minute = INT(t / 60) MOD 60  
hour = INT(t / 60 / 60) MOD 24  
days = INT(t / 60 / 60 / 24)  
years = INT(days / 365.25)  
year = 1970 + years + 1

1970 начался с четверга, поэтому мы можем вычислить день недели:

weekday = (days + 4) MOD 7

Если воскресенье - день 0. Если вы хотите, чтобы воскресенье было днем ​​1, просто добавьте 1.

Теперь давайте выясним, сколько дней мы находимся в рассматриваемом году.

days = days - years * 365 - leapdays

Наконец, мы находим месяц и день месяца.

IF year MOD 4 = 0 THEN ly = 1 ELSE ly = 0
WHILE month <= 12
    month = month + 1
    IF month = 2 THEN
        DaysInMonth = 28 + NOT(year MOD 4) + NOT(year MOD 100)
            + NOT(year MOD 400)
    ELSE
        DaysInMonth = 30 + (month + (month < 7)) MOD 2
    END IF
    IF days > DaysInMonth THEN days = days - DaysInMonth
END WHILE

Предполагается, что логические значения TRUE = 1, FALSE = 0, NOT TRUE = 0 и NOT FALSE = 1.

Теперь у нас есть год, месяц, день месяца, часы, минуты и секунды, рассчитанные с учетом поправок на високосные годы.

...