Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculate day number from an unix-timestamp in a math way?

How can i calculate day number from a unix-timestamp, in a mathematical way and without using any functions and in simple math formula.

1313905026 --> 8 (Today 08/21/2011)

like image 436
Aram Alipoor Avatar asked Aug 21 '11 04:08

Aram Alipoor


People also ask

How do I calculate a date from a timestamp?

If you'd like to calculate the difference between the timestamps in seconds, multiply the decimal difference in days by the number of seconds in a day, which equals 24 * 60 * 60 = 86400 , or the product of the number of hours in a day, the number of minutes in an hour, and the number of seconds in a minute.

How do I get the day of the week from a Unix timestamp?

In summary: day of week = (floor(T / 86400) + 4) mod 7. (This assumes that you want the day of week in UTC. If you want to calculate it for another time zone, you need to perform some addition or subtraction of hours and minutes on T first.)

How much is a day on Unix time?

1 Day: 86,400 seconds. 1 Week: 604,800 seconds.

How is epoch day calculated?

POSIX defines that you can deduce the number of days since The Epoch (1970-01-01 00:00:00Z) by dividing the timestamp by 86400.


2 Answers

A unix timestamp doesn't include leap seconds, so we don't have to worry about that. Here is a branch-less1, loop-less algorithm for getting the y/m/d fields from a unix timestamp:

#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
}

This outputs:

8/21/2011

As you're not interested in y and m (only in d), you can eliminate the last couple of lines from the above computation.

This algorithm is described in excruciating detail here. The link includes a complete derivation, and unit tests spanning millions of years (which is overkill).


1 Branch-less: What looks like small branches in the algorithm above are optimized away by clang at -O3 on 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
like image 108
Howard Hinnant Avatar answered Oct 13 '22 00:10

Howard Hinnant


There is no simple formula to do this. You would need to subtract the number of years (accounting for leap years) since the epoch, which would probably require a loop or a discrete calculation of some kind. Then use some type of loop to subtract out the number of seconds in each month for the current year. What you are left with is the number of seconds currently into the month.

I would do something like this.

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.

Edit

I recommend using date functions for simplicity, but here is a possible non-loopy alternative answer in case anyone needs it, or would care to develop it further.

First let t be the current time in seconds since the epoch.

Let F be the number of seconds in four years. That is three regular years and one leap year. That should be: 126230400.

Now if you take away all of the time contributed by F, you will get a remainder: y.

So y = n % F.

There are several cases now: 1. y is less that one year 2. y is less than two years 3. y is less than three years and less than two months 4. y is less than three years and greater than two months 5. y is less than four years

Note that 1972 was a leap year, so if you count up by four from 1970, wherever you left off will be a leap year in two years.

let jan, feb, febLY, mar, may, ..., dec be the number of seconds in each month (you'd need to calculate it out).

d represents the day number of the current month and D represents the number of seconds in a day (86400). y represents the number of seconds in a regular year, and yLY represents the number of seconds in a leap year.

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
}

Not tested. Also, since the Earth doesn't spin at EXACTLY 1 rotation / 24 hours, they've occasionally made adjustments to time. You need to do a bit of research factor that in.

like image 38
JSideris Avatar answered Oct 13 '22 01:10

JSideris