0

i'm trying to convert a date like 2022-08-09 to a julian date for send it to JDE. The problem is that default php functions like juliantojd() return a different result. The result i expected was something like this

https://nimishprabhu.com/tools/jde-julian-date-converter-online.html

I got this function from a very old code that is doing the job in .asp

function data_giuliana(d){
var pad = "000";

var anno = String(d.getFullYear()).substr(2,2);

var timestmp     = new Date().setFullYear(d.getFullYear(),0,1);
var yearFirstDay = Math.floor(timestmp / 86400000);
var today        = Math.ceil((d.getTime()) / 86400000);
var giorno       = String(today - yearFirstDay);

giorno = pad.substring(0, pad.length - giorno.length) + giorno;

data_giuliana = "1" + anno + giorno;

return data_giuliana;}

I'm trying to convert this function in PHP for use it in my laravel application:

        function data_giuliana($test)
    {
        $pad = "000";
    
        $anno = Carbon::now()->format('Y-m-d');
    
        $timestammp     = Carbon::now()->timestamp;
        $yearFirstDay = floor($timestammp / 86400000);
        $today        = ceil(Carbon::now()->toDateTimeString() / 86400000);
        $giorno       = ($today - $yearFirstDay);
    
      $giorno = substr($pad, strlen($pad) - strlen($giorno)) . $giorno;
    
      $data_giuliana = "1" . $anno . $giorno;
    
      return $data_giuliana;
      dd($data_giuliana);
    }

But it's not working.

Does somebody know any function for php? Or at least help me to convert properly the up function? Thanks a lot

  • I'm not sure about the Julien date, but `ceil(Carbon::now()->toDateTimeString() / 86400000);` is throwing an error because `Carbon::now()->toDateTimeString()` returns a string like `2022-08-09 09:37:44`, which is not a number. – aynber Aug 09 '22 at 13:38
  • If juliantojd does not return the correct result, it's something you should report to PHP for them to fix: https://github.com/php/php-src/issues They might also clarify. Because it sounds very fishy that the PHP function (that exists for 23 years) would be wrong and no one never noticed. – KyleK Aug 09 '22 at 14:10
  • 1
    I think there's a confusion on the terms. The OP wants the Julian Date, which is not the same as the Julian Day Count that `juliantojd` wants. From http://www.longpelaexpertise.com/toolsJulian.php, `We refer to a yyddd date format (yy = year, ddd=day) as a 'Julian Date' - this is the common term for such a date in mainframe and other circles. However technically, a Julian date can mean different things. Astronomers refer to a Julian date as the number of days since the beginning of the Julian Period (January 1, 4713 BC).` – aynber Aug 09 '22 at 14:36
  • 1
    (cont) `A yyddd (or similar format such as yyyyddd, yy-ddd) date is more correctly called an ordinal date. However in the mainframe world, we call them 'Julian Dates', and so we've used this term on this page.` – aynber Aug 09 '22 at 14:37
  • @KyleK that function returns 7 numbers, you can try. Maybe the issue is Oracle's JDE rules that require a more complex function –  Aug 11 '22 at 13:21

3 Answers3

1

Problem with the Accepted Answer

The accepted answer is, unfortunately, incorrect. The JDE Julian date is always six digits and does not always start with "1". To see an example, change the year to anything in the 1900s.

1999-08-09 = 099221 per https://nimishprabhu.com/tools/jde-julian-date-converter-online.html

Carbon::parse('1999-08-09')->format('1yz')+1; // 199221

Additionally, it falls apart with dates in the first 100 days of the year, because the 'z' format isn't going to pad the left with zeroes.

Carbon::parse('2022-01-10')->format('1yz')+1; // 1230

Answer to the Original Question

The built-in juliantojd(...) function returns the value for a classic Julian date, which is an entirely different thing than the JDE Julian date. JDE format is CYYDDD which is century, year, and day of the year, respectively. The century starts with 19xx = 0, so we need to compute the first digit.

function data_giuliana($year, $month, $day){
    $date_obj = (new \DateTime())->setDate($year, $month, $day);
    
    // Year and Day of Year are easy to get
    $year = $date_obj->format('y');
    // Keep in mind that PHP date of year starts at 0, but JDE starts at 1
    $day = $date_obj->format('z') + 1;
    
    // For Century, we take the full year, subtract the year we found already,
    // subtract 1900 (since, with JDE, 1900 = 0) and then divide by 100 so that
    // the century 2xxx is 1, not 100.
    $full_year = $date_obj->format('Y');
    $century = ($full_year - $year - 1900) / 100;

    // Now we can just piece it together.
    return $century 
        . str_pad($year, 2, '0', STR_PAD_LEFT) 
        . str_pad($day, 3, '0', STR_PAD_LEFT);
}

You could also do this with Carbon, which reduces the code substantially.

function data_giuliana($date){
    $date_obj = Carbon::parse($date);
    // Using floor here rather than subtracting the short year has the same effect
    $century = floor(($date_obj->year - 1900) / 100);

    return $century 
        . $date_obj->format('y')
        . str_pad($date_obj->dayOfYear, 3, '0', STR_PAD_LEFT);
}
blhylton
  • 699
  • 4
  • 16
  • Good point on JDE (crazy) formatting with the leading zeroes. A bit simplified version: `function to_JDEJulian($date_obj){ $day = $date_obj->format('z') + 1; $full_year = $date_obj->format('Y'); return str_pad(substr($full_year-1900,0,3),3,'0',STR_PAD_LEFT) . str_pad($day, 3, '0', STR_PAD_LEFT); }` And call it like: `to_JDEJulian(new DateTime(date("Y-m-d")` – Attila Jan 25 '23 at 15:40
  • @Attila Good idea to combine the century and year into one part. That said, you wouldn't need the `substr` since 0 < Y - 1900 < 1000 (unless you expect this function to survive to 2900 A.D. anyway). – blhylton Jan 25 '23 at 18:43
0

Final result for a JDE Oracle julian date:

           function data_giuliana($date = null)
    {   

        $date_parse = Carbon::parse($date);
        $date_now = Carbon::now();

        if($date) {
            $anno = $date_parse->format('y'); // 2 digit year
    
            $timestamp = $date_parse->copy()->firstOfYear()->timestamp;
            $yearFirstDay = floor($timestamp / 86400);
            $today = ceil($date_parse->timestamp / 86400);
            $giorno = ($today - $yearFirstDay + 1); // aggiunto +1
            dump($giorno);

            if($giorno < 100 && $giorno < 10) {
                $data_giuliana = "1" . $anno . '00' . $giorno;  
                return $data_giuliana;
            }
            if($giorno < 100) {
                $data_giuliana = "1" . $anno . '0' . $giorno;  
                return $data_giuliana;
            } else {
                $data_giuliana = "1" . $anno . $giorno; 
                return $data_giuliana;
            }
        } else {

            $anno = $date_now->format('y'); // 2 digit year
        
            $timestamp = $date_now->copy()->firstOfYear()->timestamp;
            $yearFirstDay = floor($timestamp / 86400);
            $today = ceil($date_now->timestamp / 86400);
            $giorno = ($today - $yearFirstDay); 
        
            $data_giuliana = "1" . $anno . $giorno;
        
            return $data_giuliana;
        }
    }

If the $date is not passed as the param the function will go to the else condition and will get the timestamp.

Ex: 22/08/2022 = 122234

  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Aug 26 '22 at 13:33
-1

There's a few things that you have to tweak for javascript vs PHP. For instance, javascript uses milliseconds while PHP does not. So you need to divide by 86400, not 86400000, and this also makes the padding unnecessary. You're also trying to append and divide using string dates instead of timestamps, which throws it off. So changing the divisors, fixing the timestamps, and also changing $anno so it only gets the last 2 digits of the year fixes the code like this:

function data_giuliana($date = null)
{
    $cdate = $date ? Carbon::parse($date) : Carbon::now();
    $anno = $cdate->format('y'); // 2 digit year

    $timestamp = $cdate->copy()->firstOfYear()->timestamp;
    $yearFirstDay = floor($timestamp / 86400);
    $today = ceil($cdate->timestamp / 86400);
    $giorno = ($today - $yearFirstDay);

    $data_giuliana = "1" . $anno . $giorno;

    return $data_giuliana;
}

After reading up on what a Julian date is, you can do this much easier with Carbon. z gets the day of the year, starting from 0, so you just need to add 1 to it, and start the format with 1.

echo Carbon::now()->format('1yz')+1; // 122221
aynber
  • 22,380
  • 8
  • 50
  • 63
  • This is definitly the best answer to this question on stackoverflow! Thank you very much it's working great! The second way is also very interesting, thanks a lot again! –  Aug 11 '22 at 13:19
  • The code works very good, but it always returns 'today' .. what i have to change to make it read the param i pass to the function? For example i call the function data_giuliana('2000/01/01'). I already have a function that returns timestamp if data is not passed. The function from my question just has to convert the data it gets white param. –  Aug 12 '22 at 08:39
  • 1
    I'll update the code, but you just need to pass the parameter in and use `Carbon::parse()` to get the Carbon object. – aynber Aug 15 '22 at 11:51
  • Thank you so much again! I was trying to do it by myself, the Carbon::parse() was the way. I edited a bit your last code review, because if there is a passed date param $giorno = ($today - $yearFirstDay); needs a +1. If there is not any param the else will get the timestamp. Everything seems to be fine, thanks a lot! I will add the final code too for those who need –  Aug 22 '22 at 09:52
  • I also add a condition for days less than 100 and 10 that don't convert the zero before –  Aug 22 '22 at 10:43