Size and Time formatting

Share your advanced PureBasic knowledge/code with the community.
kinglestat
Enthusiast
Enthusiast
Posts: 746
Joined: Fri Jul 14, 2006 8:53 pm
Location: Malta
Contact:

Size and Time formatting

Post by kinglestat »

I needed 2 routines to format time and size....could not find them anywhere. I did the following.

Code: Select all

; Size and time  formatting functions
; (c) KingLestat, 2007
;

Procedure.s     SizeFormat ( Value.q )
  
  Protected     work.d
  Protected     temp.s, dot.s, front.s
  Protected     ends.s = " bytes"  
  Protected     i.l, j.l, k.l, l.l
    
  work.d = Value   
   
  If work > ( 1048576 * 1024 )
      work / 1048576
      work / 1024
      ends = " GB"
  ElseIf work > 1048576
      work / 1048576
      ends = " MB"
  ElseIf work > 1024
      work / 1024
      ends = " KB"
  EndIf

  temp = StrD ( work, 2 )
    
  k = Len ( temp )
  j = k - 3
  
  If Right ( temp, 1 ) = "0"
    k - 1
    If Mid ( temp, k, 1 ) = "0"
      k - 2
      j = 0
    EndIf
    temp = Left ( temp, k )    
  EndIf
  
  If j 
    dot = Right ( temp, k - j )
    front = Left ( temp, j )
  Else
    dot = ""
    front = temp
  EndIf    
  
  If j > 3  
    l = ( ( ( j - 2 ) * 10 ) / 3 )
    front = Space ( l )
    k = l
    i = 0    
    
    While ( j > 0 )
      j - 1
      k - 1
      i + 1
      PokeC ( @front + k, PeekC ( @temp + j ) )      
      If i % 3 = 0 And j > 1
        k - 1
        PokeC ( @front + k, 44 )      
      EndIf      
    Wend    
  EndIf    
    
  ProcedureReturn LTrim ( front + dot + ends )

EndProcedure


Procedure.s     TimeFormat ( Value.l )

  Protected     temp.l
  Protected     days.s = ""
  Protected     hours.s = ""
  Protected     minutes.s = ""
  Protected     seconds.s = ""
  Protected     secs.f
  Protected     result.s = ""
    
  If Value > 86400000
    temp  = Value / 86400000
    Value % 86400000
        
    If temp > 1
      days = Str ( temp ) + " days"
    Else
      days = "1 day"
    EndIf
  EndIf
  
  If Value > 3600000
    temp  = Value / 3600000
    Value % 3600000
 
    If temp > 1
      hours = Str ( temp ) + " hours"
    Else
      days = "1 hour"
    EndIf    
  
  EndIf
  
  If Value > 60000
    temp  = Value / 60000
    Value % 60000
  
    If temp > 1
      minutes = Str ( temp ) + " minutes"
    Else
      minutes = "1 minute"
    EndIf    
  
  EndIf
  
  secs  = Value / 1000    
  seconds = StrF ( secs, 2 )
  temp = Len ( seconds )
  
  While ( Right ( seconds, 1 ) = "0" Or Right ( seconds, 1 ) = "." )
    temp - 1
    seconds = Left ( seconds, temp )
  Wend
        
  If days > ""
    result = days
  EndIf

  If hours > ""
    If result > ""
      result + ", " + hours
    Else
      result = hours
    EndIf
  EndIf
 
  If minutes > ""
    If result > ""
      result + ", " + minutes
    Else
      result = minutes
    EndIf
  EndIf
  
  If seconds > ""
    If result > ""
      result + ", " + seconds
    Else
      result = seconds
    EndIf    
    result + " seconds"
  EndIf
  
  ProcedureReturn result

EndProcedure

  
Debug SizeFormat ( 128 )
Debug SizeFormat ( 1280 )
Debug SizeFormat ( 12800 )
Debug SizeFormat ( 128000 )
Debug SizeFormat ( 1280000 )
Debug SizeFormat ( 12800000 )
Debug SizeFormat ( 128000000 )
Debug SizeFormat ( 1280000000 )
Debug SizeFormat ( 12800000000 )
Debug SizeFormat ( 128000000000 )
Debug SizeFormat ( 1280000000000 )
Debug SizeFormat ( 12800000000000 )
Debug SizeFormat ( 128000000000000 )
Debug SizeFormat ( 1280000000000000 )
Debug SizeFormat ( 12800000000000000 )
Debug SizeFormat ( 128000000000000000 )

Debug TimeFormat ( 120 )
Debug TimeFormat ( 12000 )
Debug TimeFormat ( 51214700 )
Debug TimeFormat ( 151214700 )
[/code]
I may not help with your coding
Just ask about mental issues!

http://www.lulu.com/spotlight/kingwolf
http://www.sen3.net
AND51
Addict
Addict
Posts: 1040
Joined: Sun Oct 15, 2006 8:56 pm
Location: Germany
Contact:

Post by AND51 »

SizeFormat ?

Mine hasn't got the grouping-n umbers function (a comma after each 3 digits), but it's smaller and faster than yours:

Code: Select all

Procedure.s bytecalc(byte.q, NbDecimals.l=0) 
   Protected unit.l 
   If byte 
      unit=Round(Log(byte)/Log(1024), 0) 
   EndIf 
   ProcedureReturn StrD(byte/Pow(1024, unit), NbDecimals*(unit And 1))+" "+StringField("Byte,KB,MB,GB,TB,PB,EB", unit+1, ",") 
EndProcedure 

debug bytecalc(103756)
debug bytecalc(-1) ; for example, there could be a wrong number being given
Working with PB 4.01+

:wink:

There was a competition in the german forum ( http://www.purebasic.fr/german/viewtopi ... yterechner ) that aim it was to create the best byte-calculator.
PB 4.30

Code: Select all

onErrorGoto(?Fred)
kinglestat
Enthusiast
Enthusiast
Posts: 746
Joined: Fri Jul 14, 2006 8:53 pm
Location: Malta
Contact:

Post by kinglestat »

well, though I wasn't aware of any competition and my intention was to share somehting I didnt find, I tested your routine against mine...without the comma seperation part...and my routine works faster :)

Code: Select all

Procedure.s bytecalc(byte.q, NbDecimals.l=0) 
   Protected unit.l 
   If byte 
      unit=Round(Log(byte)/Log(1024), 0) 
   EndIf 
   ProcedureReturn StrD(byte/Pow(1024, unit), NbDecimals*(unit And 1))+" "+StringField("Byte,KB,MB,GB,TB,PB,EB", unit+1, ",") 
EndProcedure 

Procedure.s     SizeFormat ( Value.q )
  
  Protected     work.d
  Protected     temp.s, dot.s, front.s
  Protected     ends.s = " bytes"  
  Protected     i.l, j.l, k.l, l.l
    
  work.d = Value   
   
  If work > ( 1048576 * 1024 )
      work / 1048576
      work / 1024
      ends = " GB"
  ElseIf work > 1048576
      work / 1048576
      ends = " MB"
  ElseIf work > 1024
      work / 1024
      ends = " KB"
  EndIf
    
  ProcedureReturn StrD ( work, 2 ) + ends

EndProcedure

#REP = 195000

Define.l    i, j
Define.s    temp1, temp2

j = ElapsedMilliseconds()
For i = 0 To #REP
  temp1 = bytecalc ( 16671767712, 2 )
  temp2 = bytecalc ( 6755433212221, 2 )
Next

MessageRequester ( "bytecalc", temp1 + #CRLF$ + temp2 + #CRLF$ + Str ( ElapsedMilliseconds() - j ) )

j = ElapsedMilliseconds()
For i = 0 To #REP
  temp1 = SizeFormat ( 16671767712 )
  temp2 = SizeFormat ( 6755433212221  )
Next  

MessageRequester ( "SizeFormat", temp1 + #CRLF$ + temp2 + #CRLF$ + Str ( ElapsedMilliseconds() - j ) )
I may not help with your coding
Just ask about mental issues!

http://www.lulu.com/spotlight/kingwolf
http://www.sen3.net
User avatar
DoubleDutch
Addict
Addict
Posts: 3220
Joined: Thu Aug 07, 2003 7:01 pm
Location: United Kingdom
Contact:

Post by DoubleDutch »

:shock:

AND51: How could yours possibly be faster with this:

Code: Select all

unit=Round(Log(byte)/Log(1024), 0) 
and this:

Code: Select all

byte/Pow(1024, unit)
:?:
https://deluxepixel.com <- My Business website
https://reportcomplete.com <- School end of term reports system
AND51
Addict
Addict
Posts: 1040
Joined: Sun Oct 15, 2006 8:56 pm
Location: Germany
Contact:

Post by AND51 »

Sorry, I don't understand you... :oops:
Can you please post a complete code showing your aim?

// Edit:
Ah... Do you want to know, why my code containing these two lines should be faster than the SizeFormat() function?
PB 4.30

Code: Select all

onErrorGoto(?Fred)
User avatar
DoubleDutch
Addict
Addict
Posts: 3220
Joined: Thu Aug 07, 2003 7:01 pm
Location: United Kingdom
Contact:

Post by DoubleDutch »

Yes

How can those 2 lines take less cycles than 4 divides and a multiply (I know it's not that simple because they are .d, but your method should still be slower and will get slower still if PB is extended to true 64 bit)?
https://deluxepixel.com <- My Business website
https://reportcomplete.com <- School end of term reports system
AND51
Addict
Addict
Posts: 1040
Joined: Sun Oct 15, 2006 8:56 pm
Location: Germany
Contact:

Post by AND51 »

Oh, you're right. I also measured his grouping-numbers function. Maybe that was stupid from me.
I just saw his 3 string-variables and thought, "omg, this lus the mid(), left() and right() mus be slow"

Indeed, your routine is faster than mine...

But you should implement a check for invalid values (e. g. -1 byte).
And I'm sure, you could optimize your routine; if byte < 1024 immediately return!
PB 4.30

Code: Select all

onErrorGoto(?Fred)
AND51
Addict
Addict
Posts: 1040
Joined: Sun Oct 15, 2006 8:56 pm
Location: Germany
Contact:

Post by AND51 »

Kinglestat, your code is wrong!
:idea: You must youse >= instead of >... Your code returns 1024 as 1024 KB...

I've extended this code, to have KB, MB, GB, TB, PB and EB - there is also a check for byte<1024 (in this case, you can immediately return without running thorught the other If's. You can optionally determine the number of decimal numbers.

Code: Select all

Procedure.s bytecalc(byte.q, NbDecimals.l=0)
	If byte < 1024
		If byte < 0
			byte=0
		EndIf
		ProcedureReturn Str(byte)+" Byte"
	ElseIf byte >= 1<<60
		ProcedureReturn StrD(byte/1<<60, NbDecimals)+" EB"
	ElseIf byte >= 1<<50
		ProcedureReturn StrD(byte/1<<50, NbDecimals)+" PB"
	ElseIf byte >= 1<<40
		ProcedureReturn StrD(byte/1<<40, NbDecimals)+" TB"
	ElseIf byte >= 1<<30
		ProcedureReturn StrD(byte/1<<30, NbDecimals)+" GB"
	ElseIf byte >= 1<<20
		ProcedureReturn StrD(byte/1<<20, NbDecimals)+" MB"
	Else
		ProcedureReturn StrD(byte/1024, NbDecimals)+" KB"
	EndIf
EndProcedure
  • Edit 1: Senseless line removed - this makes my procedure slightly faster than yours, Kinglestat!
Last edited by AND51 on Tue Oct 16, 2007 7:24 pm, edited 1 time in total.
PB 4.30

Code: Select all

onErrorGoto(?Fred)
AND51
Addict
Addict
Posts: 1040
Joined: Sun Oct 15, 2006 8:56 pm
Location: Germany
Contact:

Post by AND51 »

More dynamic time-calc!

My timecalc() turns any quad-number into a user-defined format and acts similar to FormatDate():
There are these tokens:
  • %yy Years
  • Month cannot be calculated, because a month can have 28-31 days
  • %dd Days
  • %hh Hours
  • %ii Minutes
  • %ss Seconds
  • %ms Milliseconds
My procedure is "intelligent" in some way, so if you leave out %yy, it will give you 365 days instead.
Example:
  • 12 345
    %ss'' %ms''' => 12'' 345'''
    %ms''' => 12345'''
Here is the code:

Code: Select all

Procedure.s timecalc(mask$, time.q) ; %yy %dd %hh %ii %ss %ms
	If FindString(mask$, "%yy", 1)
		Protected yy.l=time/31536000000
		time-yy*31536000000
		mask$=ReplaceString(mask$, "%yy", Str(yy))
	EndIf
; 	Month cannot be calculated, because a month can have 28-31 days
	If FindString(mask$, "%dd", 1)
		Protected dd.l=time/86400000
		time-dd*86400000
		mask$=ReplaceString(mask$, "%dd", Str(dd))
	EndIf
	If FindString(mask$, "%hh", 1)
		Protected hh.q=time/3600000
		time-hh*3600000
		mask$=ReplaceString(mask$, "%hh", Str(hh))
	EndIf
	If FindString(mask$, "%ii", 1)
		Protected ii.q=time/60000
		time-ii*60000
		mask$=ReplaceString(mask$, "%ii", Str(ii))
	EndIf
	If FindString(mask$, "%ss", 1)
		Protected ss.q=time/1000
		time-ss*1000
		mask$=ReplaceString(mask$, "%ss", Str(ss))
	EndIf
	If FindString(mask$, "%ms", 1)
		mask$=ReplaceString(mask$, "%ms", Str(time))
	EndIf
	ProcedureReturn mask$
EndProcedure


Debug timecalc("Short: %yyy %ddd %hhh %iim %sss %msms", 34041598234)
Debug timecalc("Normal: %yy years %dd days %hh hours %ii minutes %ss seconds %ms milliseconds", 34041598234)
Debug timecalc("Abbreviations: %yy yrs %dd days %hh hrs %ii mins %ss sec %ms msec", 34041598234)
Debug timecalc("Math: %yyy %ddd %hh:%ii'%ss''%ms'''", 34041598234)
I think, the mask$-method is one of the best methods, because
- you can determine which units you want to turn the time into
- choosing only 1 or 2 units increases performance
- it is possible to handle multiple languages
PB 4.30

Code: Select all

onErrorGoto(?Fred)
Post Reply